| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.dialogs; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.resource.JFaceColors; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyleRange; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.events.*; |
| import org.eclipse.swt.graphics.*; |
| import org.eclipse.swt.program.Program; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.internal.AboutItem; |
| import org.eclipse.ui.internal.WorkbenchMessages; |
| |
| /** |
| * Abstract superclass of about dialogs |
| */ |
| |
| public abstract class ProductInfoDialog extends Dialog{ |
| private static final String ATT_HTTP = "http://"; //$NON-NLS-1$ |
| private AboutItem item; |
| private boolean webBrowserOpened; |
| private String webBrowser = null; |
| private Cursor handCursor; |
| private Cursor busyCursor; |
| private boolean mouseDown = false; |
| private boolean dragEvent = false; |
| |
| /** |
| * Create an instance of this Dialog |
| */ |
| public ProductInfoDialog(Shell parentShell) { |
| super(parentShell); |
| } |
| |
| /** |
| * Adds listeners to the given styled text |
| */ |
| protected void addListeners(StyledText styledText) { |
| styledText.addMouseListener(new MouseAdapter() { |
| public void mouseDown(MouseEvent e) { |
| if (e.button != 1) { |
| return; |
| } |
| mouseDown = true; |
| } |
| public void mouseUp(MouseEvent e) { |
| mouseDown = false; |
| StyledText text = (StyledText)e.widget; |
| int offset = text.getCaretOffset(); |
| if (dragEvent) { |
| // don't activate a link during a drag/mouse up operation |
| dragEvent = false; |
| if (item != null && item.isLinkAt(offset)) { |
| text.setCursor(handCursor); |
| } |
| } else if (item != null && item.isLinkAt(offset)) { |
| text.setCursor(busyCursor); |
| openLink(item.getLinkAt(offset)); |
| StyleRange selectionRange = getCurrentRange(text); |
| text.setSelectionRange(selectionRange.start, selectionRange.length); |
| text.setCursor(null); |
| } |
| } |
| }); |
| |
| styledText.addMouseMoveListener(new MouseMoveListener() { |
| public void mouseMove(MouseEvent e) { |
| // Do not change cursor on drag events |
| if (mouseDown) { |
| if (!dragEvent) { |
| StyledText text = (StyledText)e.widget; |
| text.setCursor(null); |
| } |
| dragEvent = true; |
| return; |
| } |
| StyledText text = (StyledText)e.widget; |
| int offset = -1; |
| try { |
| offset = text.getOffsetAtLocation(new Point(e.x, e.y)); |
| } catch (IllegalArgumentException ex) { |
| // leave value as -1 |
| } |
| if (offset == -1) |
| text.setCursor(null); |
| else if (item != null && item.isLinkAt(offset)) |
| text.setCursor(handCursor); |
| else |
| text.setCursor(null); |
| } |
| }); |
| |
| styledText.addTraverseListener(new TraverseListener() { |
| public void keyTraversed(TraverseEvent e) { |
| StyledText text = (StyledText)e.widget; |
| switch (e.detail) { |
| case SWT.TRAVERSE_ESCAPE: |
| e.doit = true; |
| break; |
| case SWT.TRAVERSE_TAB_NEXT: |
| //Previously traverse out in the backward direction? |
| Point nextSelection = text.getSelection(); |
| int charCount = text.getCharCount(); |
| if ((nextSelection.x == charCount) && (nextSelection.y == charCount)){ |
| text.setSelection(0); |
| } |
| StyleRange nextRange = findNextRange(text); |
| if (nextRange == null) { |
| // Next time in start at beginning, also used by |
| // TRAVERSE_TAB_PREVIOUS to indicate we traversed out |
| // in the forward direction |
| text.setSelection(0); |
| e.doit = true; |
| } else { |
| text.setSelectionRange(nextRange.start, nextRange.length); |
| e.doit = true; |
| e.detail = SWT.TRAVERSE_NONE; |
| } |
| break; |
| case SWT.TRAVERSE_TAB_PREVIOUS: |
| //Previously traverse out in the forward direction? |
| Point previousSelection = text.getSelection(); |
| if ((previousSelection.x == 0) && (previousSelection.y == 0)) |
| text.setSelection(text.getCharCount()); |
| StyleRange previousRange = findPreviousRange(text); |
| if (previousRange == null) { |
| // Next time in start at the end, also used by |
| // TRAVERSE_TAB_NEXT to indicate we traversed out |
| // in the backward direction |
| text.setSelection(text.getCharCount()); |
| e.doit = true; |
| } |
| else { |
| text.setSelectionRange(previousRange.start, previousRange.length); |
| e.doit = true; |
| e.detail = SWT.TRAVERSE_NONE; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| }); |
| |
| //Listen for Tab and Space to allow keyboard navigation |
| styledText.addKeyListener(new KeyAdapter() { |
| public void keyPressed (KeyEvent event){ |
| StyledText text = (StyledText)event.widget; |
| if(event.character == ' ' || event.character == SWT.CR){ |
| if(item != null){ |
| //Be sure we are in the selection |
| int offset = text.getSelection().x + 1; |
| |
| if (item.isLinkAt(offset)) { |
| text.setCursor(busyCursor); |
| openLink(item.getLinkAt(offset)); |
| StyleRange selectionRange = getCurrentRange(text); |
| text.setSelectionRange(selectionRange.start, selectionRange.length); |
| text.setCursor(null); |
| } |
| } |
| return; |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Gets the busy cursor. |
| * @return the busy cursor |
| */ |
| protected Cursor getBusyCursor() { |
| return busyCursor; |
| } |
| |
| /** |
| * Sets the busy cursor. |
| * @param busyCursor the busy cursor |
| */ |
| protected void setBusyCursor(Cursor busyCursor) { |
| this.busyCursor = busyCursor; |
| } |
| |
| /** |
| * Gets the hand cursor. |
| * @return Returns a hand cursor |
| */ |
| protected Cursor getHandCursor() { |
| return handCursor; |
| } |
| |
| /** |
| * Sets the hand cursor. |
| * @param handCursor The hand cursor to set |
| */ |
| protected void setHandCursor(Cursor handCursor) { |
| this.handCursor = handCursor; |
| } |
| |
| /** |
| * Gets the about item. |
| * @return the about item |
| */ |
| protected AboutItem getItem() { |
| return item; |
| } |
| |
| /** |
| * Sets the about item. |
| * @param item about item |
| */ |
| protected void setItem(AboutItem item) { |
| this.item = item; |
| } |
| |
| /** |
| * Find the range of the current selection. |
| */ |
| protected StyleRange getCurrentRange(StyledText text){ |
| StyleRange[] ranges = text.getStyleRanges(); |
| int currentSelectionEnd = text.getSelection().y; |
| int currentSelectionStart = text.getSelection().x; |
| |
| for (int i = 0; i < ranges.length; i++) { |
| if((currentSelectionStart >= ranges[i].start) && |
| (currentSelectionEnd <= (ranges[i].start + ranges[i].length))) { |
| return ranges[i]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Find the next range after the current |
| * selection. |
| */ |
| protected StyleRange findNextRange(StyledText text){ |
| StyleRange[] ranges = text.getStyleRanges(); |
| int currentSelectionEnd = text.getSelection().y; |
| |
| for (int i = 0; i < ranges.length; i++) { |
| if(ranges[i].start >= currentSelectionEnd) |
| return ranges[i]; |
| } |
| return null; |
| } |
| |
| /** |
| * Find the previous range before the current selection. |
| */ |
| protected StyleRange findPreviousRange(StyledText text){ |
| StyleRange[] ranges = text.getStyleRanges(); |
| int currentSelectionStart = text.getSelection().x; |
| |
| for (int i = ranges.length - 1; i > -1; i--) { |
| if((ranges[i].start + ranges[i].length - 1) < currentSelectionStart) |
| return ranges[i]; |
| } |
| return null; |
| } |
| |
| //TODO: Move browser support from Help system, remove this method |
| private Process openWebBrowser(String href) throws IOException{ |
| Process p = null; |
| if (webBrowser == null) { |
| try { |
| webBrowser = "netscape"; //$NON-NLS-1$ |
| p = Runtime.getRuntime().exec(webBrowser + " " + href); //$NON-NLS-1$; |
| } catch (IOException e) { |
| p = null; |
| webBrowser = "mozilla"; //$NON-NLS-1$ |
| } |
| } |
| |
| if (p==null) { |
| try { |
| p = Runtime.getRuntime().exec(webBrowser + " " + href); //$NON-NLS-1$; |
| } catch (IOException e) { |
| p = null; |
| throw e; |
| } |
| } |
| return p; |
| } |
| |
| /** |
| * display an error message |
| */ |
| private void openWebBrowserError(Display display) { |
| display.asyncExec(new Runnable() { |
| public void run() { |
| MessageDialog.openError(getShell(), WorkbenchMessages.getString("ProductInfoDialog.errorTitle"), //$NON-NLS-1$ |
| WorkbenchMessages.getString("ProductInfoDialog.unableToOpenWebBrowser")); //$NON-NLS-1$ |
| } |
| }); |
| } |
| /** |
| * Open a link |
| */ |
| protected void openLink(String href) { |
| // format the href for an html file (file:///<filename.html> |
| // required for Mac only. |
| if (href.startsWith("file:")) { //$NON-NLS-1$ |
| href = href.substring(5); |
| while (href.startsWith("/")) { //$NON-NLS-1$ |
| href = href.substring(1); |
| } |
| href = "file:///" + href; //$NON-NLS-1$ |
| } |
| final String localHref = href; |
| |
| final Display d = Display.getCurrent(); |
| String platform = SWT.getPlatform(); |
| |
| if ("win32".equals(platform)) { //$NON-NLS-1$ |
| Program.launch(localHref); |
| } else if ("carbon".equals(platform)) { //$NON-NLS-1$ |
| try { |
| Runtime.getRuntime().exec("/usr/bin/open " + localHref); //$NON-NLS-1$ |
| } catch (IOException e) { |
| openWebBrowserError(d); |
| } |
| } else { |
| Thread launcher = new Thread("About Link Launcher") {//$NON-NLS-1$ |
| public void run() { |
| try { |
| if (webBrowserOpened) { |
| Runtime.getRuntime().exec(webBrowser + " -remote openURL(" + localHref + ")"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| Process p = openWebBrowser(localHref); |
| webBrowserOpened = true; |
| try { |
| if (p != null) |
| p.waitFor(); |
| } catch (InterruptedException e) { |
| openWebBrowserError(d); |
| } finally { |
| webBrowserOpened = false; |
| } |
| } |
| } catch (IOException e) { |
| openWebBrowserError(d); |
| } |
| } |
| }; |
| launcher.start(); |
| } |
| } |
| |
| /** |
| * Sets the styled text's bold ranges |
| */ |
| protected void setBoldRanges(StyledText styledText, int[][] boldRanges) { |
| for (int i = 0; i < boldRanges.length; i++) { |
| StyleRange r = new StyleRange(boldRanges[i][0], boldRanges[i][1], null, null, SWT.BOLD); |
| styledText.setStyleRange(r); |
| } |
| } |
| |
| /** |
| * Sets the styled text's link (blue) ranges |
| */ |
| protected void setLinkRanges(StyledText styledText, int[][] linkRanges) { |
| Color fg = JFaceColors.getHyperlinkText(styledText.getShell().getDisplay()); |
| for (int i = 0; i < linkRanges.length; i++) { |
| StyleRange r = new StyleRange(linkRanges[i][0], linkRanges[i][1], fg, null); |
| styledText.setStyleRange(r); |
| } |
| } |
| |
| /** |
| * Scan the contents of the about text |
| */ |
| protected AboutItem scan(String s) { |
| int max = s.length(); |
| int i = s.indexOf(ATT_HTTP); |
| ArrayList linkRanges = new ArrayList(); |
| ArrayList links = new ArrayList(); |
| while (i != -1) { |
| int start = i; |
| // look for the first whitespace character |
| boolean found = false; |
| i += ATT_HTTP.length(); |
| while (!found && i < max) { |
| found = Character.isWhitespace(s.charAt(i++)); |
| } |
| if (i!=max) i--; |
| linkRanges.add(new int[] {start, i - start}); |
| links.add(s.substring(start, i)); |
| i = s.indexOf(ATT_HTTP, i); |
| } |
| return new AboutItem( |
| s, |
| (int[][])linkRanges.toArray(new int[linkRanges.size()][2]), |
| (String[])links.toArray(new String[links.size()])); |
| } |
| |
| } |