package org.eclipse.ui.internal.dialogs; /* * (c) Copyright IBM Corp. 2000, 2001. * All Rights Reserved. */ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.*; import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.resource.JFaceColors; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.*; import org.eclipse.ui.help.WorkbenchHelp; import org.eclipse.ui.internal.*; import org.eclipse.ui.part.EditorPart; /** * A "fake" editor to show a welcome page * The contents of this page are supplied in the product configuration * * @private * This class is internal to the workbench and must not be called outside the workbench */ public class WelcomeEditor extends EditorPart { private final String TAG_TITLE = "title";//$NON-NLS-1$ private final String TAG_INTRO = "intro";//$NON-NLS-1$ private final String TAG_ITEM = "item";//$NON-NLS-1$ private final String TAG_CONTENTS = "contents";//$NON-NLS-1$ private final static int HORZ_SCROLL_INCREMENT = 20; private final static int VERT_SCROLL_INCREMENT = 20; private Composite editorComposite; private Composite infoArea; private Cursor handCursor; private Cursor busyCursor; private IWorkbench workbench; private WelcomeParser parser; private Image image; private ArrayList hyperlinkRanges = new ArrayList(); private ArrayList texts = new ArrayList(); private StyledText lastNavigatedText = null; private ScrolledComposite scrolledComposite; private IPropertyChangeListener colorListener; /** * The keyListener innerClass for the welcome editor * navigation */ private KeyListener welcomeListener = new KeyListener(){ public void keyReleased(KeyEvent e){ //Ignore a key release } public void keyPressed (KeyEvent event){ StyledText text = lastNavigatedText; if(event.character == '\t' && event.stateMask == SWT.NULL){ StyleRange nextRange = findStartRange(text,false); if(nextRange == null){ if(text != null) text.setSelection(0,0); focusOn(nextText(text)); } else{ text.setSelection(nextRange.start,nextRange.start + nextRange.length); text.setFocus(); } return; } if(event.character == ' ' || event.character == SWT.CR){ if(text != null){ WelcomeItem item = (WelcomeItem)text.getData(); //Be sure we are in the selection int offset = text.getSelection().x + 1; if (item.isLinkAt(offset)) { text.setCursor(busyCursor); item.triggerLinkAt(offset); text.setCursor(null); } } return; } if(event.keyCode == SWT.PAGE_DOWN){ focusOn(nextText(text)); return; } if(event.keyCode == SWT.PAGE_UP){ focusOn(previousText(text)); return; } } /** * Update the welcome page to start at the * beginning of the text. */ private void focusOn(StyledText newText){ if(newText == null) return; newText.setFocus(); newText.setCaretOffset(0); scrolledComposite.setOrigin(0,newText.getLocation().y); lastNavigatedText = newText; } /** * Find the next text */ private StyledText nextText(StyledText text){ int index = 0; if(text == null) return (StyledText) texts.get(0); else index = texts.indexOf(text); //If we are not at the end.... if(index < texts.size() - 1) return (StyledText) texts.get(index + 1); else return (StyledText) texts.get(0); } /** * Find the previous text */ private StyledText previousText(StyledText text){ int index = 0; if(text == null) return (StyledText) texts.get(0); else index = texts.indexOf(text); //If we are not at the end.... if(index == 0) return (StyledText) texts.get(texts.size() - 1); else return (StyledText) texts.get(index - 1); } /** * Find the next range after the current * selection. */ private StyleRange findStartRange(StyledText text, boolean returnDefault){ if(text == null) return null; StyleRange[] ranges = text.getStyleRanges(); int currentSelectionStart = text.getSelection().x; for (int i = 0; i < ranges.length; i++) { if(ranges[i].start > currentSelectionStart) return ranges[i]; } //If the default one is good then return it if(ranges.length > 0 && returnDefault) return ranges[0]; else return null; } }; /** * Create a new instance of the welcome editor */ public WelcomeEditor() { super(); setTitle(WorkbenchMessages.getString("WelcomeEditor.title")); //$NON-NLS-1$ } /** * Adds listeners to the given styled text */ private void addListeners(StyledText styledText) { styledText.addMouseListener(new MouseAdapter() { public void mouseUp(MouseEvent e) { StyledText text = (StyledText)e.widget; WelcomeItem item = (WelcomeItem)e.widget.getData(); int offset = text.getCaretOffset(); if (item.isLinkAt(offset)) { text.setCursor(busyCursor); item.triggerLinkAt(offset); text.setCursor(null); } } }); styledText.addMouseMoveListener(new MouseMoveListener() { public void mouseMove(MouseEvent e) { StyledText text = (StyledText)e.widget; WelcomeItem item = (WelcomeItem)e.widget.getData(); int offset = getOffsetAtLocation(text, e.x, e.y); if (offset == -1) text.setCursor(null); else if (item.isLinkAt(offset)) text.setCursor(handCursor); else text.setCursor(null); } }); //Listen for Tab and Space to allow keyboard navigation styledText.addKeyListener(welcomeListener); } /** * Creates the wizard's title area. * * @param parent the SWT parent for the title area composite * @return the created info area composite */ private Composite createInfoArea(Composite parent) { // Create the title area which will contain // a title, message, and image. this.scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL); this.scrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); Composite infoArea = new Composite(this.scrolledComposite, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 10; layout.verticalSpacing = 5; layout.numColumns = 2; infoArea.setLayout(layout); GridData data = new GridData(GridData.FILL_HORIZONTAL); infoArea.setLayoutData(data); // Get the background color for the title area Display display = parent.getDisplay(); Color bg = JFaceColors.getBannerBackground(display); infoArea.setBackground(bg); StyledText sampleStyledText = null; // Create the intro item WelcomeItem item = getIntroItem(); if (item != null) { StyledText styledText = new StyledText(infoArea, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.BORDER); this.texts.add(styledText); sampleStyledText = styledText; styledText.setCursor(null); styledText.setBackground(bg); styledText.setText(getIntroItem().getText()); setBoldRanges(styledText, item.getBoldRanges()); setLinkRanges(styledText, item.getActionRanges()); setLinkRanges(styledText, item.getHelpRanges()); GridData gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; gd.horizontalIndent = 20; styledText.setLayoutData(gd); styledText.setData(item); addListeners(styledText); Label spacer = new Label(infoArea, SWT.NONE); spacer.setBackground(bg); gd = new GridData(); gd.horizontalSpan = 2; spacer.setLayoutData(gd); } // Create the welcome items WelcomeItem[] items = getItems(); for (int i = 0; i < items.length; i++) { Label image = new Label(infoArea, SWT.NONE); image.setBackground(bg); image.setImage(WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_OBJS_WELCOME_ITEM)); GridData gd = new GridData(); gd.horizontalIndent = 20; gd.verticalAlignment = gd.VERTICAL_ALIGN_BEGINNING; image.setLayoutData(gd); StyledText styledText = new StyledText(infoArea, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.BORDER); this.texts.add(styledText); sampleStyledText = styledText; styledText.setCursor(null); styledText.setBackground(bg); styledText.setText(items[i].getText()); setBoldRanges(styledText, items[i].getBoldRanges()); setLinkRanges(styledText, items[i].getActionRanges()); setLinkRanges(styledText, items[i].getHelpRanges()); gd = new GridData(GridData.FILL_HORIZONTAL); gd.verticalSpan = 2; styledText.setLayoutData(gd); styledText.setData(items[i]); addListeners(styledText); Label spacer = new Label(infoArea, SWT.NONE); spacer.setBackground(bg); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = 2; spacer.setLayoutData(gd); } this.scrolledComposite.setContent(infoArea); // Adjust the scrollbar increments if (sampleStyledText == null) { this.scrolledComposite.getVerticalBar().setIncrement(VERT_SCROLL_INCREMENT); } else { GC gc = new GC(sampleStyledText); int width = gc.getFontMetrics().getAverageCharWidth(); gc.dispose(); this.scrolledComposite.getVerticalBar().setIncrement(sampleStyledText.getLineHeight()); } this.scrolledComposite.addControlListener(new ControlAdapter() { public void controlResized(ControlEvent e) { ScrolledComposite localSC = (ScrolledComposite)e.widget; ScrollBar vertical = localSC.getVerticalBar(); Rectangle clientArea = localSC.getClientArea(); vertical.setPageIncrement(clientArea.height - vertical.getIncrement()); } }); return infoArea; } /** * Creates the SWT controls for this workbench part. *
* Clients should not call this method (the workbench calls this method at * appropriate times). *
** For implementors this is a multi-step process: *
IActionService
.IActionService
.ISelectionService
* (optional). WorkbenchPart
implementation of this
* IWorkbenchPart
method disposes the title image
* loaded by setInitializationData
. Subclasses may extend.
*/
public void dispose() {
super.dispose();
if (busyCursor != null)
busyCursor.dispose();
if (handCursor != null)
handCursor.dispose();
JFacePreferences.getPreferenceStore().
removePropertyChangeListener(this.colorListener);
}
/* (non-Javadoc)
* Saves the contents of this editor.
*
* Subclasses must override this method to implement the open-save-close lifecycle
* for an editor. For greater details, see IEditorPart
*
* Subclasses must override this method to implement the open-save-close lifecycle
* for an editor. For greater details, see IEditorPart
*
null
*/
private WelcomeItem getIntroItem() {
return parser.getIntroItem();
}
/**
* Returns the welcome items
*/
private WelcomeItem[] getItems() {
return parser.getItems();
}
/**
* Returns the text offset at the given pixwl location
* Returns -1 if there is no offset at the location
*/
private int getOffsetAtLocation(StyledText styledText, int x, int y) {
int charCount = styledText.getCharCount();
if (charCount == 0 || x < 0 || y < 0)
return -1;
// get the line at the y coordinate
int line = (y + styledText.getTopPixel()) / styledText.getLineHeight();
// find an offset in the line
int low = -1;
int high = charCount;
int offset = 0;
int currentLine;
while (high - low > 1) {
offset = (high + low) / 2;
currentLine = styledText.getLineAtOffset(offset);
if (currentLine == line)
break;
if (currentLine > line)
high = offset;
else
low = offset;
}
currentLine = styledText.getLineAtOffset(offset);
// find the offset at x
int delta;
Point loc = styledText.getLocationAtOffset(offset);
if (loc.x == x)
return offset;
else if (loc.x < x)
delta = 1;
else
delta = -1;
int newOffset = offset + delta;
Point newLoc = styledText.getLocationAtOffset(newOffset);
while (currentLine == styledText.getLineAtOffset(newOffset)) {
if (delta == 1) {
if (newLoc.x > x)
return offset;
} else {
if (newLoc.x < x)
return offset;
}
offset = newOffset;
loc = newLoc;
newOffset = offset + delta;
if (newOffset < 0 || newOffset > charCount)
return -1;
newLoc = styledText.getLocationAtOffset(newOffset);
}
return -1;
}
/* (non-Javadoc)
* Sets the cursor and selection state for this editor to the passage defined
* by the given marker.
*
* Subclasses may override. For greater details, see IEditorPart
*
* Subclasses of EditorPart
must implement this method. Within
* the implementation subclasses should verify that the input type is acceptable
* and then save the site and input. Here is sample code:
*
* if (!(input instanceof IFileEditorInput)) * throw new PartInitException("Invalid Input: Must be IFileEditorInput"); * setSite(site); * setInput(editorInput); **/ public void init(IEditorSite site, IEditorInput input) throws PartInitException { setSite(site); setInput(new WelcomeEditorInput()); } /* (non-Javadoc) * Returns whether the contents of this editor have changed since the last save * operation. *
* Subclasses must override this method to implement the open-save-close lifecycle
* for an editor. For greater details, see IEditorPart
*
* Subclasses must override this method to implement the open-save-close lifecycle
* for an editor. For greater details, see IEditorPart
*
* Clients should not call this method (the workbench calls this method at * appropriate times). *
*/ public void setFocus() { if (editorComposite != null) { editorComposite.setFocus(); } } /** * Sets the styled text's link (blue) ranges */ private void setLinkRanges(StyledText styledText, int[][] linkRanges) { //Color fg = styledText.getDisplay().getSystemColor(SWT.COLOR_BLUE); 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); hyperlinkRanges.add(r); } } }