/******************************************************************************* * Copyright (c) 2002, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.cheatsheets.views; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.ListIterator; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; //import org.eclipse.help.ui.internal.views.HelpTray; //import org.eclipse.help.ui.internal.views.IHelpPartPage; //import org.eclipse.help.ui.internal.views.ReusableHelpPart; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.TrayDialog; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.IMemento; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.cheatsheets.CheatSheetListener; import org.eclipse.ui.cheatsheets.ICheatSheetEvent; import org.eclipse.ui.cheatsheets.ICheatSheetViewer; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ImageHyperlink; import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin; import org.eclipse.ui.internal.cheatsheets.CheatSheetStopWatch; import org.eclipse.ui.internal.cheatsheets.ICheatSheetResource; import org.eclipse.ui.internal.cheatsheets.Messages; import org.eclipse.ui.internal.cheatsheets.actions.IMenuContributor; import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetModel; import org.eclipse.ui.internal.cheatsheets.composite.views.CompositeCheatSheetPage; import org.eclipse.ui.internal.cheatsheets.data.CheatSheet; import org.eclipse.ui.internal.cheatsheets.data.CheatSheetParser; import org.eclipse.ui.internal.cheatsheets.data.CheatSheetSaveHelper; import org.eclipse.ui.internal.cheatsheets.data.ICheatSheet; import org.eclipse.ui.internal.cheatsheets.data.IParserTags; import org.eclipse.ui.internal.cheatsheets.data.ParserInput; import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetElement; import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader; import org.eclipse.ui.internal.cheatsheets.state.DefaultStateManager; import org.eclipse.ui.internal.cheatsheets.state.ICheatSheetStateManager; import org.eclipse.ui.internal.cheatsheets.state.NoSaveStateManager; //import org.eclipse.ui.internal.cheatsheets.state.TrayStateManager; import org.osgi.framework.Bundle; public class CheatSheetViewer implements ICheatSheetViewer, IMenuContributor { //CS Elements private CheatSheetElement contentElement; private ParserInput parserInput; private String currentID; private int currentItemNum; // Used to indicate if an invalid cheat sheet id was specified via setInput. private boolean invalidCheatSheetId = false; // Used to indicate if a null cheat sheet id was specified via setInput. private boolean nullCheatSheetId = false; private CheatSheetParser parser; private ICheatSheet model; private CheatSheetManager manager; private CheatSheetSaveHelper saveHelper; private CheatSheetExpandRestoreAction expandRestoreAction; private Action copyAction; //ITEMS private ViewItem currentItem; //Lists private ArrayList expandRestoreList = new ArrayList(); private ArrayList viewItemList = new ArrayList(); //Composites protected Composite control; private Cursor busyCursor; // The page currently displayed, may be a CheatSheetPage, CompositeCheatSheetPage // or ErrorPage private Page currentPage; private Label howToBegin; private boolean inDialog; private Listener listener; private ICheatSheetStateManager stateManager; // The state manager to use when saving // RAP [bm] not needed // private ICheatSheetStateManager preTrayManager; // The state manager in use before a tray was opened private String restorePath; private int dialogReturnCode; private boolean isRestricted; /** * The constructor. * * @param inDialog whether or not this viewer will be placed in a modal dialog */ public CheatSheetViewer(boolean inDialog) { currentItemNum = -1; this.inDialog = inDialog; saveHelper = new CheatSheetSaveHelper(); } public void advanceIntroItem() { if (getViewItemAtIndex(0) == null) { return; // Cheat Sheet has no items or was not opened correctly } resetItemState(); /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_DEACTIVATED, introItem); currentItemNum = 1; ViewItem nextItem = getViewItemAtIndex(currentItemNum); if (nextItem.item.isDynamic()) { nextItem.handleButtons(); } nextItem.setAsCurrentActiveItem(); /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_ACTIVATED, nextItem); collapseAllButCurrent(false); saveCurrentSheet(); } /** * Reset the state of all the items in this cheatsheet */ private void resetItemState() { IntroItem introItem = (IntroItem) getViewItemAtIndex(0); boolean isStarted = introItem.isCompleted(); expandRestoreList = new ArrayList(); if(expandRestoreAction != null) expandRestoreAction.setCollapsed(false); clearBackgrounds(); clearIcons(); collapseAllButtons(); if(isStarted) initManager(); for (Iterator iter = viewItemList.iterator(); iter.hasNext();) { ViewItem item = (ViewItem) iter.next(); if (item instanceof CoreItem) { CoreItem c = (CoreItem) item; ArrayList l = c.getListOfSubItemCompositeHolders(); if (l != null) for (int j = 0; j < l.size(); j++) { ((SubItemCompositeHolder) l.get(j)).setSkipped(false); ((SubItemCompositeHolder) l.get(j)).setCompleted(false); } } } if (isStarted) getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_RESTARTED); else getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_STARTED); isStarted = true; introItem.setAsNormalCollapsed(); introItem.setComplete(); introItem.setRestartImage(); } /*package*/ /* * This function can do one of three things * 1. If this item has a completion message which has not been displayed, display it * 2. Otherwise if this is the final item return to the introduction * 3. If neither condition 1 or 2 is satisfied move to the next item */ void advanceItem(ImageHyperlink link, boolean markAsCompleted) { currentItem = (ViewItem) link.getData(); int indexNextItem = getIndexOfItem(currentItem) +1; boolean isFinalItem = indexNextItem >= viewItemList.size(); if (markAsCompleted && currentItem.hasCompletionMessage() && !currentItem.isCompletionMessageExpanded()) { currentItem.setCompletionMessageExpanded(isFinalItem); currentItem.setComplete(); if (isFinalItem) { getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_COMPLETED); } saveCurrentSheet(); return; } if (indexNextItem < currentItemNum) { ViewItem vi = getViewItemAtIndex(currentItemNum); vi.setAsNormalNonCollapsed(); } if (currentItem != null) { //set that item to it's original color. currentItem.setAsNormalCollapsed(); //set that item as complete. if (markAsCompleted) { if (!currentItem.isCompleted()) { currentItem.setComplete(); } /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_COMPLETED, currentItem); // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_DEACTIVATED, currentItem); } else { currentItem.setSkipped(); /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_SKIPPED, currentItem); // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_DEACTIVATED, currentItem); } } if (!isFinalItem) { ViewItem nextItem = getViewItemAtIndex(indexNextItem); currentItemNum = indexNextItem; if (nextItem != null) { //Handle lazy button instantiation here. if (nextItem.item.isDynamic()) { ((CoreItem) nextItem).handleButtons(); } nextItem.setAsCurrentActiveItem(); /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_ACTIVATED, nextItem); currentItem = nextItem; } FormToolkit.ensureVisible(currentItem.getMainItemComposite()); } else if (indexNextItem == viewItemList.size()) { if (!currentItem.isCompletionMessageExpanded()) { // The event will already have been fired getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_COMPLETED); } showIntroItem(); } saveCurrentSheet(); } private void showIntroItem() { ViewItem item = getViewItemAtIndex(0); item.setAsCurrentActiveItem(); } /*package*/ void advanceSubItem(ImageHyperlink link, boolean markAsCompleted, int subItemIndex) { Label l = null; ArrayList list = null; SubItemCompositeHolder sich = null; CoreItem ciws = null; currentItem = (ViewItem) link.getData(); if (currentItem instanceof CoreItem) ciws = (CoreItem) currentItem; if (ciws != null) { list = ciws.getListOfSubItemCompositeHolders(); sich = (SubItemCompositeHolder) list.get(subItemIndex); l = sich.getCheckDoneLabel(); } if (l != null) { if (markAsCompleted) { sich.setCompleted(true); sich.setSkipped(false); /* LP-subitem event */ // fireManagerSubItemEvent(ICheatSheetItemEvent.ITEM_COMPLETED, ciws, subItemID); } else { sich.setSkipped(true); sich.setCompleted(false); /* LP-subitem event */ // fireManagerSubItemEvent(ICheatSheetItemEvent.ITEM_SKIPPED, ciws, subItemID); } ciws.refreshItem(); } boolean allAttempted = checkAllAttempted(list); boolean anySkipped = checkContainsSkipped(list); if (allAttempted && !anySkipped) { advanceItem(link, true); return; } else if (allAttempted && anySkipped) { advanceItem(link, false); return; } setFocus(); saveCurrentSheet(); } private boolean checkAllAttempted(ArrayList list) { for (int i = 0; i < list.size(); i++) { SubItemCompositeHolder s = (SubItemCompositeHolder) list.get(i); if (s.isCompleted() || s.isSkipped()) { continue; } return false; } return true; } private boolean checkContainsSkipped(ArrayList list) { for (int i = 0; i < list.size(); i++) { SubItemCompositeHolder s = (SubItemCompositeHolder) list.get(i); if (s.isSkipped()) { return true; } } return false; } private boolean loadState() { try { Properties props = stateManager.getProperties(); manager = stateManager.getCheatSheetManager(); // There is a bug which causes the background of the buttons to // remain white, even though the color is set. So instead of calling // clearBackgrounds() only the following line should be needed. D'oh! // ((ViewItem) viewItemList.get(0)).setOriginalColor(); clearBackgrounds(); if (props == null) { getViewItemAtIndex(0).setAsCurrentActiveItem(); /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_ACTIVATED, items[0]); return true; } boolean buttonIsDown = (Integer.parseInt((String) props.get(IParserTags.BUTTON)) == 0) ? false : true; int itemNum = Integer.parseInt((String) props.get(IParserTags.CURRENT)); ArrayList completedStatesList = (ArrayList) props.get(IParserTags.COMPLETED); ArrayList expandedStatesList = (ArrayList) props.get(IParserTags.EXPANDED); expandRestoreList = (ArrayList) props.get(IParserTags.EXPANDRESTORE); String cid = (String) props.get(IParserTags.ID); Hashtable completedSubItems = (Hashtable) props.get(IParserTags.SUBITEMCOMPLETED); Hashtable skippedSubItems = (Hashtable) props.get(IParserTags.SUBITEMSKIPPED); ArrayList completedSubItemsItemList = new ArrayList(); ArrayList skippedSubItemsItemList = new ArrayList(); Enumeration e = completedSubItems.keys(); while (e.hasMoreElements()) completedSubItemsItemList.add(e.nextElement()); Enumeration e2 = skippedSubItems.keys(); while (e2.hasMoreElements()) skippedSubItemsItemList.add(e2.nextElement()); if (cid != null) currentID = cid; if (itemNum >= 0) { currentItemNum = itemNum; currentItem = getViewItemAtIndex(itemNum); CheatSheetStopWatch.startStopWatch("CheatSheetViewer.checkSavedState()"); //$NON-NLS-1$ for (int i = 0; i < viewItemList.size(); i++) { ViewItem item = getViewItemAtIndex(i); if (i > 0 && item.item.isDynamic() && i <= currentItemNum) { item.handleButtons(); item.setOriginalColor(); } if (completedStatesList.contains(Integer.toString(i))) { item.setComplete(); item.setRestartImage(); } else { if (i < currentItemNum) { item.setSkipped(); } } if (expandedStatesList.contains(Integer.toString(i))) { item.setExpanded(); } else { item.setCollapsed(); } if (i > currentItemNum) { item.setButtonsVisible(false); item.setCompletionMessageCollapsed(); } else { item.setButtonsVisible(true); if (i >currentItemNum || item.isCompleted()) { item.setCompletionMessageExpanded(i + 1 >= viewItemList.size()); } else { item.setCompletionMessageCollapsed(); } } if (expandRestoreList.contains(Integer.toString(i))) { item.setCollapsed(); } if (completedSubItemsItemList.contains(Integer.toString(i))) { String subItemNumbers = (String) completedSubItems.get(Integer.toString(i)); StringTokenizer st = new StringTokenizer(subItemNumbers, ","); //$NON-NLS-1$ if (item instanceof CoreItem) { CoreItem coreitemws = (CoreItem) item; ArrayList subItemCompositeHolders = coreitemws.getListOfSubItemCompositeHolders(); if (subItemCompositeHolders != null) { while (st.hasMoreTokens()) { String token = st.nextToken(); ((SubItemCompositeHolder) subItemCompositeHolders.get(Integer.parseInt(token))).setCompleted(true); ArrayList l = subItemCompositeHolders; SubItemCompositeHolder s = (SubItemCompositeHolder) l.get(Integer.parseInt(token)); if (s != null && s.getStartButton() != null) { s.getStartButton().setImage(CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.CHEATSHEET_ITEM_BUTTON_RESTART)); s.getStartButton().setToolTipText(Messages.get().RESTART_TASK_TOOLTIP); } } } } } if (skippedSubItemsItemList.contains(Integer.toString(i))) { String subItemNumbers = (String) skippedSubItems.get(Integer.toString(i)); StringTokenizer st = new StringTokenizer(subItemNumbers, ","); //$NON-NLS-1$ if (item instanceof CoreItem) { CoreItem coreitemws = (CoreItem) item; while (st.hasMoreTokens()) { String token = st.nextToken(); ((SubItemCompositeHolder) coreitemws.getListOfSubItemCompositeHolders().get(Integer.parseInt(token))).setSkipped(true); } } } CheatSheetStopWatch.printLapTime("CheatSheetViewer.checkSavedState()", "Time in CheatSheetViewer.checkSavedState() after loop #"+i+": "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } CheatSheetStopWatch.printLapTime("CheatSheetViewer.checkSavedState()", "Time in CheatSheetViewer.checkSavedState() after loop: "); //$NON-NLS-1$ //$NON-NLS-2$ if (buttonIsDown) { if(expandRestoreAction != null) expandRestoreAction.setCollapsed(true); } // If the last item is the current one and it is complete then // we should collapse the last item and set the focus on intro. // For all other cases, set the current item as the active item. if(viewItemList.size()-1 == itemNum && currentItem.isCompleted()) { currentItem.setCollapsed(); getViewItemAtIndex(0).getMainItemComposite().setFocus(); // The cheat sheet has been restored but is also completed so fire both events getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_RESTORED); getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_COMPLETED); } else { currentItem.setAsCurrentActiveItem(); // If the intro item is completed, than the cheat sheet has been restored. if(getViewItemAtIndex(0).isCompleted()) getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_RESTORED); } /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_ACTIVATED, currentItem); } else { getViewItemAtIndex(0).setAsCurrentActiveItem(); /* LP-item event */ // fireManagerItemEvent(ICheatSheetItemEvent.ITEM_ACTIVATED, items[0]); } return true; } catch(Exception e) { // An exception while restoring the saved state data usually only occurs if // the cheat sheet has been modified since this previous execution. This most // often occurs during development of cheat sheets and as such an end user is // not as likely to encounter this. boolean reset = MessageDialog.openConfirm(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), Messages.get().CHEATSHEET_STATE_RESTORE_FAIL_TITLE, Messages.get().CHEATSHEET_STATE_RESET_CONFIRM); if (reset) { restart(); return true; } // Log the exception String stateFile = saveHelper.getStateFile(currentID).toOSString(); String message = NLS.bind(Messages.get().ERROR_APPLYING_STATE_DATA_LOG, (new Object[] {stateFile, currentID})); IStatus status = new Status(IStatus.ERROR, ICheatSheetResource.CHEAT_SHEET_PLUGIN_ID, IStatus.OK, message, e); CheatSheetPlugin.getPlugin().getLog().log(status); // Set the currentID to null so it is not saved during internalDispose() currentID = null; internalDispose(); // Reinitialize a few variables because there is no currentItem or currentPage now parserInput = null; currentItem = null; currentItemNum = -1; currentPage = null; expandRestoreList = new ArrayList(); viewItemList = new ArrayList(); // Create the errorpage to show the user createErrorPage(Messages.get().ERROR_APPLYING_STATE_DATA); return false; } } private void clearBackgrounds() { for (Iterator iter = viewItemList.iterator(); iter.hasNext();) { ViewItem item = (ViewItem) iter.next(); item.setOriginalColor(); } } private void clearIcons() { for (Iterator iter = viewItemList.iterator(); iter.hasNext();) { ViewItem item = (ViewItem) iter.next(); item.setOriginalColor(); if (item.isCompleted() || item.isExpanded() || item.isSkipped()) item.setIncomplete(); } } private void collapseAllButCurrent(boolean fromAction) { expandRestoreList = new ArrayList(); try { ViewItem current = getViewItemAtIndex(currentItemNum); for (ListIterator iter = viewItemList.listIterator(viewItemList.size()); iter.hasPrevious();) { ViewItem item = (ViewItem) iter.previous(); if (item != current && item.isExpanded()) { item.setCollapsed(); if (fromAction) expandRestoreList.add(Integer.toString(getIndexOfItem(item))); } } } catch (Exception e) { } } private void collapseAllButtons() { for (Iterator iter = viewItemList.listIterator(1); iter.hasNext();) { ViewItem item = (ViewItem) iter.next(); item.setButtonsVisible(false); item.setCompletionMessageCollapsed(); } } private void createErrorPage(String message) { setCollapseExpandButtonEnabled(false); if(message != null) { currentPage = new ErrorPage(message); } else { currentPage = new ErrorPage(); } currentPage.createPart(control); control.layout(true); } private void showStartPage() { setCollapseExpandButtonEnabled(false); internalDispose(); howToBegin = new Label(control, SWT.WRAP); howToBegin.setText(Messages.get().INITIAL_VIEW_DIRECTIONS); howToBegin.setLayoutData(new GridData(GridData.FILL_BOTH)); currentPage = null; control.layout(true); } private void createErrorPage(IStatus status) { setCollapseExpandButtonEnabled(false); currentPage = new ErrorPage(status); currentPage.createPart(control); control.layout(true); } /** * Creates the SWT controls for this workbench part. * <p> * Clients should not call this method (the workbench calls this method at * appropriate times). * </p> * <p> * For implementors this is a multi-step process: * <ol> * <li>Create one or more controls within the parent.</li> * <li>Set the parent layout as needed.</li> * <li>Register any global actions with the <code>IActionService</code>.</li> * <li>Register any popup menus with the <code>IActionService</code>.</li> * <li>Register a selection provider with the <code>ISelectionService</code> * (optional). </li> * </ol> * </p> * * @param parent the parent control */ public void createPartControl(Composite parent) { control = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 0; layout.horizontalSpacing = 0; layout.numColumns = 1; control.setLayout(layout); control.addDisposeListener(new DisposeListener(){ public void widgetDisposed(DisposeEvent e) { dispose(); } }); showStartPage(); Display display = parent.getDisplay(); // RAP [if] cursor ctor // busyCursor = new Cursor(display, SWT.CURSOR_WAIT); busyCursor = display.getSystemCursor( SWT.CURSOR_WAIT ); if(contentElement != null) { initCheatSheetView(); } } // RAP [if] Help system not supported // /** // * Called when any TrayDialog is opened. The viewer must react by disabling // * itself and moving the cheat sheet to the dialog's tray if the current item // * was flagged as one that opens a modal dialog. // * // * @param dialog the dialog that was opened // */ // private void dialogOpened(final TrayDialog dialog) { // if (isActive()) { // HelpTray tray = (HelpTray)dialog.getTray(); // if (tray == null) { // tray = new HelpTray(); // dialog.openTray(tray); // } // ReusableHelpPart helpPart = tray.getHelpPart(); // IHelpPartPage page = helpPart.createPage(CheatSheetHelpPart.ID, null, null); // page.setVerticalSpacing(0); // page.setHorizontalMargin(0); // ICheatSheetStateManager trayManager = new TrayStateManager(); // preTrayManager = stateManager; // stateManager = trayManager; // saveCurrentSheet(); // Save the state into the tray manager // helpPart.addPart(CheatSheetHelpPart.ID, new CheatSheetHelpPart(helpPart.getForm().getForm().getBody(), helpPart.getForm().getToolkit(), page.getToolBarManager(), contentElement, trayManager)); // page.addPart(CheatSheetHelpPart.ID, true); // helpPart.addPage(page); // helpPart.showPage(CheatSheetHelpPart.ID); // // /* // * Disable the viewer until the tray is closed, then show it again. // */ // control.setVisible(false); // Display.getCurrent().removeFilter(SWT.Show, listener); // // helpPart.getControl().addListener(SWT.Dispose, new Listener() { // public void handleEvent(Event event) { // control.setVisible(true); // Display.getCurrent().addFilter(SWT.Show, listener); // if (preTrayManager != null) { // loadState(); // Load from the tray manager // stateManager = preTrayManager; // preTrayManager = null; // } // dialogReturnCode = dialog.getReturnCode(); // } // }); // } // } /** * Disposes of this cheat sheet viewer. */ private void dispose() { internalDispose(); // RAP [if] // if (busyCursor != null) // busyCursor.dispose(); } /* * Returns the cheat sheet being viewed. */ public ICheatSheet getCheatSheet() { return model; } /* (non-Javadoc) * @see org.eclipse.ui.cheatsheets.ICheatSheetViewer#getCheatSheetID() */ public String getCheatSheetID() { if(getContent() != null) { return getContent().getID(); } return null; } /** * Returns the current content. * * @return CheatSheetElement */ /*package*/ CheatSheetElement getContent() { return contentElement; } /* (non-Javadoc) * @see org.eclipse.ui.cheatsheets.ICheatSheetViewer#getControl() */ public Control getControl() { return control; } private int getIndexOfItem(ViewItem item) { int index = viewItemList.indexOf(item); if(index != -1) { return index; } return 0; } /*package*/ CheatSheetManager getManager() { if (manager == null) { getNewManager(); } return manager; } private CheatSheetManager getNewManager(){ manager = new CheatSheetManager(contentElement); return manager; } private CheatSheetManager initManager(){ CheatSheetManager csManager = getManager(); csManager.setData(new Hashtable()); return csManager; } private ViewItem getViewItemAtIndex(int index) { if (viewItemList != null && !viewItemList.isEmpty()) { return (ViewItem) viewItemList.get(index); } return null; } /** * Returns whether or not this viewer contains the given Control, which * is currently in focus. * * @param control the Control currently in focus * @return whether this viewer contains the given Control or not */ public boolean hasFocusControl(Control control) { return (control == this.control) || (currentPage.getControl() == control); } /** * If in a dialog-opening step, will add the appropriate listener for * the cheatsheet to jump into the dialog's tray once opened. * * Should be called before executing any action. */ private void hookDialogListener() { /* * org.eclipse.help.ui is an optional dependency; only perform this * step is this plugin is present. */ if (!inDialog && isInDialogItem() && (Platform.getBundle("org.eclipse.help.ui") != null)) { //$NON-NLS-1$ listener = new Listener() { public void handleEvent(Event event) { if (isTrayDialog(event.widget)) { // RAP [if] Help system not supported // dialogOpened((TrayDialog)((Shell)event.widget).getData()); } } }; Display.getCurrent().addFilter(SWT.Show, listener); } } /** * Removes the dialog-opening listener, if it was added. * * Should be called after executing any action. */ private void unhookDialogListener() { if (listener != null) { Display.getCurrent().removeFilter(SWT.Show, listener); } } /* * return true if a cheat sheet was opened successfully */ private boolean initCheatSheetView() { CheatSheetStopWatch.startStopWatch("CheatSheetViewer.initCheatSheetView()"); //$NON-NLS-1$ //Re-initialize list to store items collapsed by expand/restore action on c.s. toolbar. expandRestoreList = new ArrayList(); // re set that action to turned off. if(expandRestoreAction != null) expandRestoreAction.setCollapsed(false); //reset current item to be null; next item too. currentItem = null; currentItemNum = 0; viewItemList = new ArrayList(); // Reset the page variable currentPage = null; if(howToBegin != null) { howToBegin.dispose(); howToBegin = null; } // If a null cheat sheet id was specified, return leaving the cheat sheet empty. if(nullCheatSheetId) { return false; } if(invalidCheatSheetId) { createErrorPage(Messages.get().ERROR_CHEATSHEET_DOESNOT_EXIST); return false; } // read our contents, if there are problems reading the file an error page should be created. CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() before readFile() call: "); //$NON-NLS-1$ //$NON-NLS-2$ IStatus parseStatus = readFile(); CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() after readFile() call: "); //$NON-NLS-1$ //$NON-NLS-2$ if (!parseStatus.isOK()) { CheatSheetPlugin.getPlugin().getLog().log(parseStatus); } if(parseStatus.getSeverity() == Status.ERROR){ // Error during parsing. // Something is wrong with the Cheat sheet content file at the xml level. createErrorPage(parseStatus); return false; } control.setRedraw(false); if (model instanceof CheatSheet) { CheatSheet cheatSheetModel = (CheatSheet)model; if (isRestricted && cheatSheetModel.isContainsCommandOrAction()) { boolean isOK = MessageDialog.openConfirm(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), Messages.get().CHEATSHEET_FROM_URL_WITH_EXEC_TITLE, Messages.get().CHEATSHEET_FROM_URL_WITH_EXEC); if (!isOK) { control.setRedraw(true); showStartPage(); return true; } } currentPage = new CheatSheetPage(cheatSheetModel, viewItemList, this); setCollapseExpandButtonEnabled(true); } else if (model instanceof CompositeCheatSheetModel) { CompositeCheatSheetModel compositeCheatSheetModel = ((CompositeCheatSheetModel)model); compositeCheatSheetModel.setId(currentID); currentPage = new CompositeCheatSheetPage(compositeCheatSheetModel, stateManager); compositeCheatSheetModel.setCheatSheetManager(initManager()); setCollapseExpandButtonEnabled(false); } CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() after CheatSheetPage() call: "); //$NON-NLS-1$ //$NON-NLS-2$ currentPage.createPart(control); CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() after CheatSheetPage.createPart() call: "); //$NON-NLS-1$ //$NON-NLS-2$ if (model instanceof CheatSheet) { CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() after fireEvent() call: "); //$NON-NLS-1$ //$NON-NLS-2$ if(!loadState()) { // An error occurred when apply the saved state data. control.setRedraw(true); control.layout(); return true; } getManager().fireEvent(ICheatSheetEvent.CHEATSHEET_OPENED); } CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() after checkSavedState() call: "); //$NON-NLS-1$ //$NON-NLS-2$ currentPage.initialized(); control.setRedraw(true); control.layout(); CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() after layout() call: "); //$NON-NLS-1$ //$NON-NLS-2$ if (currentItem != null && !currentItem.isCompleted()) currentItem.setFocus(); CheatSheetStopWatch.printLapTime("CheatSheetViewer.initCheatSheetView()", "Time in CheatSheetViewer.initCheatSheetView() at end of method: "); //$NON-NLS-1$ //$NON-NLS-2$ return true; } private void internalDispose() { if(manager != null) manager.fireEvent(ICheatSheetEvent.CHEATSHEET_CLOSED); saveCurrentSheet(); for (Iterator iter = viewItemList.iterator(); iter.hasNext();) { ViewItem item = (ViewItem) iter.next(); item.dispose(); } if(currentPage != null) { currentPage.dispose(); } manager = null; } // RAP [bm]: not needed // /** // * Returns whether or not the cheat sheet viewer is currently active. This // * means it is visible to the user and enabled. // * // * @return whether or not this viewer is active // */ // private boolean isActive() { // Control control = getControl(); // if (control != null && !control.isDisposed()) { // Control parent = control.getParent(); // return (parent != null && !parent.isDisposed() && parent.isVisible() && parent.isEnabled()); // } // return false; // } /* * Show the collapse/expand button if we have access to the toolbar */ private void setCollapseExpandButtonEnabled(boolean enable) { if (expandRestoreAction != null) { expandRestoreAction.setEnabled(enable); } } /** * Returns whether or not the currently active item requires opening a * modal dialog. * * @return whether the current item opens a modal dialog */ private boolean isInDialogItem() { if (currentItem != null) { return currentItem.getItem().isDialog(); } return false; } /** * Returns whether or not this cheat sheet viewer is inside a modal * dialog. * * @return whether this viewer is inside a modal dialog */ public boolean isInDialogMode() { return inDialog; } /** * Returns whether the given widget is a TrayDialog. * * @param widget the widget to check * @return whether or not the widget is a TrayDialog */ private boolean isTrayDialog(Widget widget) { return (widget instanceof Shell && ((Shell)widget).getData() instanceof TrayDialog); } /** * Read the contents of the cheat sheet file * @return true if the file was read and parsed without error */ private IStatus readFile() { if(parser == null) parser = new CheatSheetParser(); // If the cheat sheet was registered then // search for a specific type - composite or simple int cheatSheetKind = CheatSheetParser.ANY; if (contentElement.isRegistered()) { if (contentElement.isComposite()) { cheatSheetKind = CheatSheetParser.COMPOSITE_ONLY; } else { cheatSheetKind = CheatSheetParser.SIMPLE_ONLY; } } model = parser.parse(parserInput, cheatSheetKind); return parser.getStatus(); } private void restoreExpandStates() { try { for (int i = 0; i < expandRestoreList.size(); i++) { int index = Integer.parseInt(((String) expandRestoreList.get(i))); ViewItem item = getViewItemAtIndex(index); if (!item.isExpanded()) { item.setExpanded(); } } expandRestoreList = null; } catch (Exception e) { } } /*package*/ void runPerformExecutable(ImageHyperlink link) { link.setCursor(busyCursor); currentItem = (ViewItem) link.getData(); CoreItem coreItem = (CoreItem) currentItem; if (coreItem != null) { try { hookDialogListener(); dialogReturnCode = -1; IStatus status = coreItem.runExecutable(getManager()); if (status.isOK() && dialogReturnCode != Window.CANCEL) { coreItem.setRestartImage(); if (!coreItem.hasConfirm()) { //set that item as complete. advanceItem(link, true); saveCurrentSheet(); } } if ( status.getSeverity() == IStatus.ERROR) { CheatSheetPlugin.getPlugin().getLog().log(status); org.eclipse.jface.dialogs.ErrorDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), null, null, status); } } finally { unhookDialogListener(); } } link.setCursor(null); } /*package*/ void runSubItemPerformExecutable(ImageHyperlink link, int subItemIndex) { CoreItem coreItem = null; link.setCursor(busyCursor); currentItem = (ViewItem) link.getData(); coreItem = (CoreItem) currentItem; try { if (coreItem != null) { hookDialogListener(); if (coreItem.runSubItemExecutable(getManager(), subItemIndex) == ViewItem.VIEWITEM_ADVANCE && !coreItem.hasConfirm(subItemIndex)) { ArrayList l = coreItem.getListOfSubItemCompositeHolders(); SubItemCompositeHolder s = (SubItemCompositeHolder) l.get(subItemIndex); s.getStartButton().setImage(CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.CHEATSHEET_ITEM_BUTTON_RESTART)); s.getStartButton().setToolTipText(Messages.get().RESTART_TASK_TOOLTIP); advanceSubItem(link, true, subItemIndex); saveCurrentSheet(); } } } catch (RuntimeException e) { IStatus status = new Status(IStatus.ERROR, ICheatSheetResource.CHEAT_SHEET_PLUGIN_ID, IStatus.OK, Messages.get().ERROR_RUNNING_ACTION, e); CheatSheetPlugin.getPlugin().getLog().log(status); org.eclipse.jface.dialogs.ErrorDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), null, null, status); } finally { unhookDialogListener(); link.setCursor(null); } } /*package*/ void saveCurrentSheet() { if(currentID != null) { if (currentPage instanceof CheatSheetPage) { Properties properties = saveHelper.createProperties(currentItemNum, viewItemList, getExpandRestoreActionState(), expandRestoreList, currentID, restorePath); IStatus status = stateManager.saveState(properties, getManager()); if (!status.isOK()) { CheatSheetPlugin.getPlugin().getLog().log(status); } } else if (currentPage instanceof CompositeCheatSheetPage) { ((CompositeCheatSheetPage)currentPage).saveState(); } } } private boolean getExpandRestoreActionState() { boolean expandRestoreActionState = false; if(expandRestoreAction != null) expandRestoreActionState = expandRestoreAction.isCollapsed(); return expandRestoreActionState; } /*package*/ void setContent(CheatSheetElement element, ICheatSheetStateManager inputStateManager) { CheatSheetStopWatch.startStopWatch("CheatSheetViewer.setContent(CheatSheetElement element)"); //$NON-NLS-1$ // Cleanup previous contents internalDispose(); // Set the current content to new content contentElement = element; stateManager = inputStateManager; stateManager.setElement(element); currentID = null; parserInput = null; if (element != null) { initInputFields(element); } CheatSheetStopWatch.printLapTime("CheatSheetViewer.setContent(CheatSheetElement element)", "Time in CheatSheetViewer.setContent() before initCheatSheetView() call: "); //$NON-NLS-1$ //$NON-NLS-2$ // Initialize the view with the new contents boolean cheatSheetOpened = false; if (control != null) { cheatSheetOpened = initCheatSheetView(); } if (!cheatSheetOpened) { contentElement = null; stateManager = null; } // If the cheat sheet failed to open clear the content element so we don't see an CheatSheetStopWatch.printLapTime("CheatSheetViewer.setContent(CheatSheetElement element)", "Time in CheatSheetViewer.setContent() after initCheatSheetView() call: "); //$NON-NLS-1$ //$NON-NLS-2$ } private void initInputFields(CheatSheetElement element) { currentID = element.getID(); String contentXml = element.getContentXml(); URL contentURL = null; restorePath = element.getRestorePath(); String errorMessage = null; if (contentXml != null) { parserInput = new ParserInput(contentXml, element.getHref()); return; } // The input was not an XML string, find the content URL Bundle bundle = null; if(element != null && element.getConfigurationElement() != null) try{ String pluginId = element.getConfigurationElement().getContributor().getName(); bundle = Platform.getBundle(pluginId); } catch (Exception e) { // do nothing } if (bundle != null) { contentURL = FileLocator.find(bundle, new Path(element.getContentFile()), null); if (contentURL == null && element.getContentFile() != null) { errorMessage = NLS.bind(Messages.get().ERROR_OPENING_FILE_IN_PARSER, (new Object[] {element.getContentFile()})); } } if (contentURL == null) { try { contentURL = new URL(element.getHref()); } catch (MalformedURLException mue) { } if (contentURL == null && element.getHref() != null) { errorMessage = NLS.bind(Messages.get().ERROR_OPENING_FILE_IN_PARSER, (new Object[] {element.getHref()})); } } String pluginId = bundle != null ? bundle.getSymbolicName() : null; parserInput = new ParserInput(contentURL, pluginId, errorMessage); } /*package*/ void setExpandRestoreAction(CheatSheetExpandRestoreAction action) { expandRestoreAction = action; } /** * Passing the focus request to the viewer's control. */ public void setFocus() { //need this to have current item selected. (Assumes that when you reactivate the view you will work with current item.) if (currentItem != null) { currentItem.setFocus(); } else { getControl().setFocus(); } } /* (non-Javadoc) * @see org.eclipse.ui.cheatsheets.ICheatSheetViewer#setInput(java.lang.String) */ public void setInput(String id) { setInput(id, new DefaultStateManager()); } public void setInput(String id, ICheatSheetStateManager inputStateManager) { CheatSheetStopWatch.startStopWatch("CheatSheetViewer.setInput(String id)"); //$NON-NLS-1$ CheatSheetElement element = null; if(id == null) { nullCheatSheetId = true; } else { nullCheatSheetId = false; element = CheatSheetRegistryReader.getInstance().findCheatSheet(id); if(element == null) { String message = NLS.bind(Messages.get().ERROR_INVALID_CHEATSHEET_ID, (new Object[] {id})); IStatus status = new Status(IStatus.ERROR, ICheatSheetResource.CHEAT_SHEET_PLUGIN_ID, IStatus.OK, message, null); CheatSheetPlugin.getPlugin().getLog().log(status); invalidCheatSheetId = true; } else { invalidCheatSheetId = false; this.isRestricted = false; } } CheatSheetStopWatch.printLapTime("CheatSheetViewer.setInput(String id)", "Time in CheatSheetViewer.setInput(String id) before setContent() call: "); //$NON-NLS-1$ //$NON-NLS-2$ setContent(element, inputStateManager); CheatSheetStopWatch.printLapTime("CheatSheetViewer.setInput(String id)", "Time in CheatSheetViewer.setInput(String id) after setContent() call: "); //$NON-NLS-1$ //$NON-NLS-2$ // Update most recently used cheat sheets list. CheatSheetPlugin.getPlugin().getCheatSheetHistory().add(element); CheatSheetStopWatch.printLapTime("CheatSheetViewer.setInput(String id)", "Time in CheatSheetViewer.setInput(String id) after getCheatSheetHistory() call: "); //$NON-NLS-1$ //$NON-NLS-2$ } /* (non-Javadoc) * @see org.eclipse.ui.cheatsheets.ICheatSheetViewer#setInput(java.lang.String, java.lang.String, java.net.URL) */ public void setInput(String id, String name, URL url) { setInput(id, name, url, new DefaultStateManager(), false); } public void setInputFromXml(String id, String name, String xml, String basePath) { if (id == null || name == null || xml == null) { throw new IllegalArgumentException(); } CheatSheetElement element = new CheatSheetElement(name); element.setID(id); element.setContentXml(xml); element.setHref(basePath); nullCheatSheetId = false; invalidCheatSheetId = false; isRestricted = false; setContent(element, new NoSaveStateManager()); } public void setInput(String id, String name, URL url, ICheatSheetStateManager inputStateManager, boolean isRestricted) { if (id == null || name == null || url == null) { throw new IllegalArgumentException(); } CheatSheetElement element = new CheatSheetElement(name); element.setID(id); element.setHref(url.toString()); nullCheatSheetId = false; invalidCheatSheetId = false; this.isRestricted = isRestricted; setContent(element, inputStateManager); } /*package*/ void toggleExpandRestore() { if(expandRestoreAction == null) return; if (expandRestoreAction.isCollapsed()) { restoreExpandStates(); expandRestoreAction.setCollapsed(false); } else { collapseAllButCurrent(true); expandRestoreAction.setCollapsed(true); } } public Action getCopyAction() { return copyAction; } public void setCopyAction(Action copyAction) { this.copyAction = copyAction; } // RAP [if] Clipboard // public void copy() { // if (currentItem!=null) // currentItem.copy(); // } public void addListener(CheatSheetListener listener) { if (contentElement != null ) { getManager().addListener(listener); } } public int contributeToViewMenu(Menu menu, int index) { if (currentPage instanceof IMenuContributor) { return ((IMenuContributor)currentPage).contributeToViewMenu(menu, index); } return index; } public void restart() { resetItemState(); currentItemNum = 0; collapseAllButCurrent(false); IntroItem introItem = (IntroItem) getViewItemAtIndex(0); introItem.setIncomplete(); showIntroItem(); } public void saveState(IMemento memento) { if (currentPage instanceof CheatSheetPage) { Properties properties = saveHelper.createProperties(currentItemNum, viewItemList, getExpandRestoreActionState(), expandRestoreList, currentID, restorePath); saveHelper.saveToMemento(properties, getManager(), memento); } } public void reset(Map cheatSheetData) { if (currentPage instanceof CheatSheetPage) { restart(); getManager().setData(cheatSheetData); } else if (currentPage instanceof CompositeCheatSheetPage) { ((CompositeCheatSheetPage)currentPage).restart(cheatSheetData); } } public void showError(String message) { internalDispose(); if(howToBegin != null) { howToBegin.dispose(); howToBegin = null; } createErrorPage(message); } }