| /******************************************************************************* |
| * Copyright (c) 2005, 2017 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.composite.views; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Observable; |
| import java.util.Observer; |
| |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.SashForm; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Point; |
| 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.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.ui.forms.FormColors; |
| import org.eclipse.ui.forms.HyperlinkSettings; |
| import org.eclipse.ui.forms.IFormColors; |
| import org.eclipse.ui.forms.ManagedForm; |
| import org.eclipse.ui.forms.events.HyperlinkAdapter; |
| import org.eclipse.ui.forms.events.HyperlinkEvent; |
| import org.eclipse.ui.forms.widgets.FormText; |
| import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin; |
| 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.explorer.RestartAllAction; |
| import org.eclipse.ui.internal.cheatsheets.composite.explorer.TreeExplorerMenu; |
| import org.eclipse.ui.internal.cheatsheets.composite.model.AbstractTask; |
| import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetModel; |
| import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetSaveHelper; |
| import org.eclipse.ui.internal.cheatsheets.composite.model.EditableTask; |
| import org.eclipse.ui.internal.cheatsheets.composite.parser.ICompositeCheatsheetTags; |
| import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader; |
| import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader.TaskExplorerNode; |
| import org.eclipse.ui.internal.cheatsheets.state.ICheatSheetStateManager; |
| import org.eclipse.ui.internal.cheatsheets.views.Page; |
| import org.eclipse.ui.internal.cheatsheets.views.ViewUtilities; |
| import org.eclipse.ui.internal.provisional.cheatsheets.ICompositeCheatSheetTask; |
| import org.eclipse.ui.internal.provisional.cheatsheets.TaskEditor; |
| import org.eclipse.ui.internal.provisional.cheatsheets.TaskExplorer; |
| import org.eclipse.ui.part.PageBook; |
| |
| /** |
| * A page which represents a composite cheat sheet |
| */ |
| |
| public class CompositeCheatSheetPage extends Page implements ISelectionChangedListener, IMenuContributor { |
| |
| public static final String REVIEW_TAG = "__review__"; //$NON-NLS-1$ |
| public static final String END_REVIEW_TAG = "__endReview__"; //$NON-NLS-1$ |
| public static final String GOTO_TASK_TAG = "__goto__"; //$NON-NLS-1$ |
| public static final String START_HREF = "__start__"; //$NON-NLS-1$ |
| public static final String SKIP_HREF = "__skip__"; //$NON-NLS-1$ |
| |
| private ManagedForm mform; |
| private PageBook explorerContainer; |
| private PageBook taskEditorContainer; |
| private CompositeCheatSheetModel model; |
| private TaskExplorer currentExplorer; |
| private DescriptionPanel descriptionPanel; |
| |
| private CompositeCheatSheetSaveHelper saveHelper; |
| |
| private ICompositeCheatSheetTask selectedTask; |
| private boolean initialized = false;; |
| |
| public CompositeCheatSheetPage(CompositeCheatSheetModel model, ICheatSheetStateManager stateManager) { |
| this.model = model; |
| saveHelper = new CompositeCheatSheetSaveHelper(stateManager); |
| } |
| |
| @Override |
| public void createPart(Composite parent) { |
| init(parent.getDisplay()); |
| toolkit.getHyperlinkGroup().setHyperlinkUnderlineMode(HyperlinkSettings.UNDERLINE_HOVER); |
| form = toolkit.createScrolledForm(parent); |
| form.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| FormColors colors = toolkit.getColors(); |
| /* |
| colors.initializeSectionToolBarColors(); |
| Color gbg = colors.getColor(FormColors.TB_GBG); |
| Color bg = colors.getBackground(); |
| form.getForm().setTextBackground(new Color[]{bg, gbg}, new int [] {100}, true); |
| form.getForm().setSeparatorColor(colors.getColor(FormColors.TB_BORDER)); |
| form.getForm().setSeparatorVisible(true); |
| */ |
| toolkit.decorateFormHeading(form.getForm()); |
| mform = new ManagedForm(toolkit, form); |
| GridLayout glayout = new GridLayout(); |
| glayout.marginHeight = 0; |
| glayout.marginWidth = 0; |
| form.getBody().setLayout(glayout); |
| final SashForm sash = new SashForm(form.getBody(), SWT.NULL); |
| GridData gd = new GridData(GridData.FILL_BOTH); |
| gd.widthHint = 10; |
| gd.heightHint = 10; |
| sash.setLayoutData(gd); |
| sash.addControlListener(new ControlAdapter() { |
| @Override |
| public void controlResized(ControlEvent e) { |
| Point size = sash.getSize(); |
| if (size.x>size.y) |
| sash.setOrientation(SWT.HORIZONTAL); |
| else |
| sash.setOrientation(SWT.VERTICAL); |
| updateSashPanelMargins(sash); |
| } |
| }); |
| sash.setBackground(colors.getColor(IFormColors.TB_BG)); |
| |
| Composite explorerPanel = new Composite(sash, SWT.NULL); |
| explorerPanel.setBackground(colors.getColor(IFormColors.TB_BORDER)); |
| GridLayout playout = new GridLayout(); |
| playout.marginWidth = 0; |
| playout.marginTop = 2; |
| playout.marginBottom = 0; |
| explorerPanel.setLayout(playout); |
| toolkit.adapt(explorerPanel); |
| explorerContainer = new PageBook(explorerPanel, SWT.NULL); |
| explorerContainer.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| Composite editorPanel = new Composite(sash, SWT.NULL); |
| playout = new GridLayout(); |
| playout.marginWidth = 0; |
| playout.marginHeight = 0; |
| editorPanel.setLayout(playout); |
| editorPanel.setBackground(colors.getColor(IFormColors.TB_BORDER)); |
| taskEditorContainer = new PageBook(editorPanel, SWT.NULL); |
| toolkit.adapt(taskEditorContainer); |
| taskEditorContainer.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| } |
| |
| private void updateSashPanelMargins(SashForm sash) { |
| Control [] children = sash.getChildren(); |
| int orientation = sash.getOrientation(); |
| // update task explorer panel |
| GridLayout layout = (GridLayout)((Composite)children[0]).getLayout(); |
| if (orientation==SWT.HORIZONTAL) { |
| layout.marginBottom = 0; |
| layout.marginRight = 1; |
| } |
| else { |
| layout.marginBottom = 1; |
| layout.marginRight = 0; |
| } |
| // update task editor panel |
| layout = (GridLayout)((Composite)children[1]).getLayout(); |
| if (orientation==SWT.HORIZONTAL) { |
| layout.marginTop = 0; |
| layout.marginLeft = 1; |
| } |
| else { |
| layout.marginTop = 1; |
| layout.marginLeft = 0; |
| } |
| ((Composite)children[0]).layout(); |
| ((Composite)children[1]).layout(); |
| } |
| |
| @Override |
| public void dispose() { |
| mform.dispose(); |
| super.dispose(); |
| } |
| |
| private void setInputModel(CompositeCheatSheetModel model, Map<String, String> layout) { |
| this.model = model; |
| mform.getForm().setText(ViewUtilities.escapeForLabel(model.getName())); |
| String explorerId = model.getTaskExplorerId(); |
| setCurrentExplorerFromId(explorerId); |
| String selectedTaskId = layout.get(ICompositeCheatsheetTags.SELECTED_TASK); |
| ICompositeCheatSheetTask selectedTask= null; |
| if (selectedTaskId != null) { |
| selectedTask = model.getDependencies().getTask(selectedTaskId); |
| if (selectedTask != null) { |
| currentExplorer.setSelection(new StructuredSelection(selectedTask), true); |
| } |
| } |
| if (selectedTask != null) { |
| updateSelectedTask(selectedTask); |
| } else { |
| updateSelectedTask(model.getRootTask()); |
| } |
| model.addObserver(new Observer() { |
| @Override |
| public void update(Observable o, Object arg) { |
| ICompositeCheatSheetTask task = (ICompositeCheatSheetTask)arg; |
| if (currentExplorer!=null) |
| currentExplorer.taskUpdated(task); |
| updateTask(task); |
| } |
| }); |
| } |
| |
| private void setCurrentExplorerFromId(String explorerId) { |
| TaskExplorer explorer = getTaskExplorer(explorerId); |
| if (explorer!=null) { |
| explorer.setCompositeCheatSheet(this.model); |
| explorer.setFocus(); |
| setExplorerVisible(explorerId); |
| } |
| } |
| |
| private void setExplorerVisible(String id) { |
| Control [] excontrols = explorerContainer.getChildren(); |
| TaskExplorer explorer=null; |
| for (int i=0; i<excontrols.length; i++) { |
| Control excontrol = excontrols[i]; |
| explorer = (TaskExplorer)excontrol.getData(ICompositeCheatsheetTags.EXPLORER); |
| if (explorer.getId().equals(id)) { |
| explorerContainer.showPage(excontrol); |
| setCurrentExplorer(explorer); |
| return; |
| } |
| } |
| } |
| |
| private void setCurrentExplorer(TaskExplorer explorer) { |
| if (currentExplorer!=null) { |
| currentExplorer.getSelectionProvider().removeSelectionChangedListener(this); |
| } |
| currentExplorer = explorer; |
| currentExplorer.getSelectionProvider().addSelectionChangedListener(this); |
| updateForSelection(currentExplorer.getSelectionProvider().getSelection()); |
| } |
| |
| private TaskExplorer getTaskExplorer(String id) { |
| Control [] excontrols = explorerContainer.getChildren(); |
| TaskExplorer explorer=null; |
| for (int i=0; i<excontrols.length; i++) { |
| Control excontrol = excontrols[i]; |
| Object data = excontrol.getData(ICompositeCheatsheetTags.EXPLORER); |
| if (data instanceof TaskExplorer) { |
| explorer = (TaskExplorer)data; |
| if (explorer.getId().equals(id)) { |
| return explorer; |
| } |
| } |
| } |
| |
| // Load the explorer from an extension point |
| |
| TaskExplorerManager explorerManager = TaskExplorerManager.getInstance(); |
| explorer = explorerManager.getExplorer(id); |
| if (explorer != null) { |
| explorer.createControl(explorerContainer, mform.getToolkit()); |
| explorer.getControl().setData(ICompositeCheatsheetTags.EXPLORER, explorer); |
| new TreeExplorerMenu(explorer); |
| } |
| return explorer; |
| } |
| |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| updateForSelection(event.getSelection()); |
| } |
| |
| private void updateForSelection(ISelection selection) { |
| Object selectedElement = ((IStructuredSelection)selection).getFirstElement(); |
| if (selectedElement instanceof ICompositeCheatSheetTask) { |
| updateSelectedTask((ICompositeCheatSheetTask)selectedElement); |
| } |
| } |
| |
| private void updateSelectedTask(ICompositeCheatSheetTask task) { |
| selectedTask = task; |
| updateTask(selectedTask); |
| } |
| |
| /* |
| * Update can be called as a result of a selection change or a state change |
| * If this is not the selected task wait till it is selected to display it |
| */ |
| private void updateTask(ICompositeCheatSheetTask task) { |
| if (task==null || task != selectedTask) return; |
| if ( task instanceof EditableTask) { |
| EditableTask editable = (EditableTask)task; |
| if (editable.getState() == ICompositeCheatSheetTask.IN_PROGRESS) { |
| showEditor(editable); |
| return; |
| } else if (editable.isUnderReview()) { |
| if (editable.getState() == ICompositeCheatSheetTask.COMPLETED) { |
| showEditor(editable); |
| } else { |
| endReview(editable); |
| } |
| return; |
| } |
| } |
| showDescription(task); |
| } |
| |
| public void saveState() { |
| Map<String, String> layout = new HashMap<>(); |
| if (selectedTask != null) { |
| layout.put(ICompositeCheatsheetTags.SELECTED_TASK, selectedTask.getId()); |
| } |
| saveHelper.saveCompositeState(model, layout); |
| } |
| |
| private void showDescription(final ICompositeCheatSheetTask task) { |
| if (descriptionPanel==null) { |
| createDescriptionPanel(); |
| } |
| descriptionPanel.showDescription(task); |
| setCurrentEditor(descriptionPanel.getControl()); |
| } |
| |
| private void createDescriptionPanel() { |
| descriptionPanel = new DescriptionPanel(mform, taskEditorContainer); |
| descriptionPanel.addHyperlinkListener(new DescriptionLinkListener()); |
| } |
| |
| /* |
| * Ensure that if this task is visible and in a runnable state that it has been started |
| */ |
| private void showEditor(EditableTask task) { |
| if (task == selectedTask) { |
| TaskEditor editor = getTaskEditor(task); |
| if (editor!= null) { |
| if (!task.isEditorInitialized()) { |
| task.setInput(model.getTaskMemento(task.getId())); |
| } |
| setCurrentEditor(editor.getControl().getParent()); |
| } |
| } |
| } |
| |
| private void addHyperlink(StringBuffer buf, String href, String imageRef, String message) { |
| buf.append("<p><a href=\""); //$NON-NLS-1$ |
| buf.append(href); |
| buf.append("\">"); //$NON-NLS-1$ |
| buf.append("<img href=\""); //$NON-NLS-1$ |
| buf.append(imageRef); |
| buf.append("\"/> "); //$NON-NLS-1$ |
| buf.append(message); |
| buf.append("</a></p>"); //$NON-NLS-1$ |
| } |
| |
| private void reviewTask(EditableTask task) { |
| TaskEditor taskEditor = getTaskEditor(task); |
| Composite container = taskEditor.getControl().getParent(); |
| Composite separator = toolkit.createCompositeSeparator(container); |
| GridData data = new GridData(); |
| data.heightHint = 1; |
| data.horizontalAlignment = SWT.FILL; |
| separator.setLayoutData(data); |
| FormText text = toolkit.createFormText(container, false); |
| text.setImage(DescriptionPanel.REVIEW_IMAGE, CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.COMPOSITE_TASK_REVIEW)); |
| text.addHyperlinkListener(getEndReviewListener()); |
| text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| StringBuffer buf = new StringBuffer(); |
| buf.append("<form>"); //$NON-NLS-1$ |
| addHyperlink(buf, END_REVIEW_TAG + task.getId(), DescriptionPanel.REVIEW_IMAGE, Messages.COMPOSITE_PAGE_END_REVIEW ); |
| buf.append("</form>"); //$NON-NLS-1$ |
| text.setText(buf.toString(), true, false); |
| task.setUnderReview(true); |
| container.layout(true); |
| showEditor(task); |
| } |
| |
| private void endReview(EditableTask task) { |
| TaskEditor taskEditor = getTaskEditor(task); |
| Control editorControl = taskEditor.getControl(); |
| Composite container = editorControl.getParent(); |
| Control[] children = container.getChildren(); |
| for (int i = children.length -2; i < children.length; i++) { |
| children[i].dispose(); |
| } |
| task.setUnderReview(false); |
| showDescription(task); |
| container.layout(); |
| } |
| |
| private void setCurrentEditor(Control c) { |
| taskEditorContainer.showPage(c); |
| } |
| |
| /** |
| * Class which responds to hyperlink events originating from the |
| * description panel. |
| */ |
| private final class DescriptionLinkListener extends HyperlinkAdapter { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| String ref = (String)e.getHref(); |
| if (ref.equals(START_HREF)) { |
| Object data = descriptionPanel.getControl().getData(ICompositeCheatsheetTags.TASK); |
| if (data instanceof EditableTask) { |
| EditableTask task = (EditableTask)data; |
| task.setStarted(); |
| } |
| } |
| if (ref.equals(SKIP_HREF)) { |
| Object data = descriptionPanel.getControl().getData(ICompositeCheatsheetTags.TASK); |
| if (data instanceof AbstractTask) { |
| AbstractTask task = (AbstractTask)data; |
| task.setState(ICompositeCheatSheetTask.SKIPPED); |
| } |
| } |
| if (ref.equals(REVIEW_TAG)) { |
| Object data = descriptionPanel.getControl().getData(ICompositeCheatsheetTags.TASK); |
| if (data instanceof EditableTask) { |
| reviewTask((EditableTask) data); |
| } |
| } |
| if (ref.startsWith(GOTO_TASK_TAG)) { |
| String next = ref.substring(GOTO_TASK_TAG.length()); |
| AbstractTask nextTask = |
| model.getDependencies().getTask(next); |
| currentExplorer.setSelection |
| (new StructuredSelection(nextTask), true); |
| } |
| } |
| } |
| |
| /** |
| * Class which responds to hyperlink events originating from the |
| * end review panel |
| */ |
| private final class EndReviewListener extends HyperlinkAdapter { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| String ref = (String)e.getHref(); |
| if (ref.startsWith(END_REVIEW_TAG)) { |
| String next = ref.substring(END_REVIEW_TAG.length()); |
| AbstractTask task = |
| model.getDependencies().getTask(next); |
| endReview((EditableTask)task); |
| } |
| } |
| } |
| |
| private EndReviewListener endReviewListener; |
| |
| private EndReviewListener getEndReviewListener() { |
| if (endReviewListener == null) { |
| endReviewListener = new EndReviewListener(); |
| } |
| return endReviewListener; |
| } |
| |
| |
| /* |
| * Get the task editor for this task. If no editor exists create one |
| */ |
| private TaskEditor getTaskEditor(EditableTask editable) { |
| if (editable.getEditor() == null) { |
| // Create a new editor using the extension point data |
| TaskEditor editor = TaskEditorManager.getInstance().getEditor(editable.getKind()); |
| if (editor != null) { |
| Composite editorPanel = new Composite(taskEditorContainer, SWT.NULL); |
| GridLayout layout = new GridLayout(); |
| layout.marginWidth = 0; |
| layout.marginHeight = 0; |
| editorPanel.setLayout(layout); |
| mform.getToolkit().adapt(editorPanel); |
| editor.createControl(editorPanel, mform.getToolkit()); |
| editable.setEditor(editor); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| editor.getControl().setLayoutData(gridData); |
| } |
| } |
| return editable.getEditor(); |
| } |
| |
| @Override |
| public Control getControl() { |
| return form; |
| } |
| |
| @Override |
| protected String getTitle() { |
| return model.getName(); |
| } |
| |
| @Override |
| public void initialized() { |
| // Open the model |
| model.setSaveHelper(saveHelper); |
| Map<String, String> layout = new HashMap<>(); |
| model.loadState(layout); |
| setInputModel(model, layout); |
| initialized = true; |
| } |
| |
| @Override |
| public int contributeToViewMenu(Menu menu, int index) { |
| if (!initialized) { |
| return index; |
| } |
| index = contributeExplorerMenuItem(menu, index); |
| return contributeRestartMenuItem(menu, index); |
| } |
| |
| private int contributeRestartMenuItem(Menu menu, int index) { |
| MenuItem item = new MenuItem(menu, SWT.PUSH, index++); |
| item.setText(Messages.RESTART_ALL_MENU); |
| item.setImage(CheatSheetPlugin.getPlugin().getImage(ICheatSheetResource.COMPOSITE_RESTART_ALL)); |
| item.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (model != null) { |
| if (RestartAllAction.confirmRestart()) { |
| restart(null); |
| } |
| } |
| } |
| }); |
| return index; |
| } |
| |
| private int contributeExplorerMenuItem(Menu menu, int index) { |
| String[] explorerIds = CheatSheetRegistryReader.getInstance().getExplorerIds(); |
| if (explorerIds.length == 1) { |
| return index; // no other explorer to chosse from |
| } |
| MenuItem menuItem = new MenuItem(menu, SWT.CASCADE, index++); |
| |
| menuItem.setText(Messages.EXPLORER_PULLDOWN_MENU); |
| |
| Menu subMenu = new Menu(menu); |
| menuItem.setMenu(subMenu); |
| |
| for (int i = 0; i < explorerIds.length; i++) { |
| final String id = explorerIds[i]; |
| TaskExplorerNode node = CheatSheetRegistryReader.getInstance().findTaskExplorer(id); |
| boolean isCurrentExplorer = id.equals(currentExplorer.getId()); |
| int style = isCurrentExplorer ? SWT.RADIO: SWT.PUSH; |
| MenuItem item = new MenuItem(subMenu, style); |
| item.setText(node.getName()); |
| item.setSelection(isCurrentExplorer); |
| item.setImage(TaskExplorerManager.getInstance().getImage(id)); |
| item.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| setCurrentExplorerFromId(id); |
| } |
| }); |
| } |
| |
| return index; |
| } |
| |
| public void restart(Map<String, String> cheatSheetData) { |
| model.resetAllTasks(cheatSheetData); |
| currentExplorer.setSelection |
| (new StructuredSelection(model.getRootTask()), true); |
| |
| } |
| |
| } |