001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.cheatsheets.composite.views;
011:
012: import java.util.HashMap;
013: import java.util.Map;
014: import java.util.Observable;
015: import java.util.Observer;
016:
017: import org.eclipse.jface.viewers.ISelection;
018: import org.eclipse.jface.viewers.ISelectionChangedListener;
019: import org.eclipse.jface.viewers.IStructuredSelection;
020: import org.eclipse.jface.viewers.SelectionChangedEvent;
021: import org.eclipse.jface.viewers.StructuredSelection;
022: import org.eclipse.swt.SWT;
023: import org.eclipse.swt.custom.SashForm;
024: import org.eclipse.swt.events.ControlAdapter;
025: import org.eclipse.swt.events.ControlEvent;
026: import org.eclipse.swt.events.SelectionAdapter;
027: import org.eclipse.swt.events.SelectionEvent;
028: import org.eclipse.swt.graphics.Point;
029: import org.eclipse.swt.layout.GridData;
030: import org.eclipse.swt.layout.GridLayout;
031: import org.eclipse.swt.widgets.Composite;
032: import org.eclipse.swt.widgets.Control;
033: import org.eclipse.swt.widgets.Menu;
034: import org.eclipse.swt.widgets.MenuItem;
035: import org.eclipse.ui.forms.FormColors;
036: import org.eclipse.ui.forms.HyperlinkSettings;
037: import org.eclipse.ui.forms.IFormColors;
038: import org.eclipse.ui.forms.ManagedForm;
039: import org.eclipse.ui.forms.events.HyperlinkAdapter;
040: import org.eclipse.ui.forms.events.HyperlinkEvent;
041: import org.eclipse.ui.forms.widgets.FormText;
042: import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin;
043: import org.eclipse.ui.internal.cheatsheets.ICheatSheetResource;
044: import org.eclipse.ui.internal.cheatsheets.Messages;
045: import org.eclipse.ui.internal.cheatsheets.actions.IMenuContributor;
046: import org.eclipse.ui.internal.cheatsheets.composite.explorer.RestartAllAction;
047: import org.eclipse.ui.internal.cheatsheets.composite.explorer.TreeExplorerMenu;
048: import org.eclipse.ui.internal.cheatsheets.composite.model.AbstractTask;
049: import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetModel;
050: import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetSaveHelper;
051: import org.eclipse.ui.internal.cheatsheets.composite.model.EditableTask;
052: import org.eclipse.ui.internal.cheatsheets.composite.parser.ICompositeCheatsheetTags;
053: import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader;
054: import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader.TaskExplorerNode;
055: import org.eclipse.ui.internal.cheatsheets.state.ICheatSheetStateManager;
056: import org.eclipse.ui.internal.cheatsheets.views.Page;
057: import org.eclipse.ui.internal.cheatsheets.views.ViewUtilities;
058: import org.eclipse.ui.internal.provisional.cheatsheets.ICompositeCheatSheetTask;
059: import org.eclipse.ui.internal.provisional.cheatsheets.TaskEditor;
060: import org.eclipse.ui.internal.provisional.cheatsheets.TaskExplorer;
061: import org.eclipse.ui.part.PageBook;
062:
063: /**
064: * A page which represents a composite cheat sheet
065: */
066:
067: public class CompositeCheatSheetPage extends Page implements
068: ISelectionChangedListener, IMenuContributor {
069:
070: public static final String REVIEW_TAG = "__review__"; //$NON-NLS-1$
071: public static final String END_REVIEW_TAG = "__endReview__"; //$NON-NLS-1$
072: public static final String GOTO_TASK_TAG = "__goto__"; //$NON-NLS-1$
073: public static final String START_HREF = "__start__"; //$NON-NLS-1$
074: public static final String SKIP_HREF = "__skip__"; //$NON-NLS-1$
075:
076: private ManagedForm mform;
077: private PageBook explorerContainer;
078: private PageBook taskEditorContainer;
079: private CompositeCheatSheetModel model;
080: private TaskExplorer currentExplorer;
081: private DescriptionPanel descriptionPanel;
082:
083: private CompositeCheatSheetSaveHelper saveHelper;
084:
085: private ICompositeCheatSheetTask selectedTask;
086: private boolean initialized = false;;
087:
088: public CompositeCheatSheetPage(CompositeCheatSheetModel model,
089: ICheatSheetStateManager stateManager) {
090: this .model = model;
091: saveHelper = new CompositeCheatSheetSaveHelper(stateManager);
092: }
093:
094: public void createPart(Composite parent) {
095: init(parent.getDisplay());
096: toolkit.getHyperlinkGroup().setHyperlinkUnderlineMode(
097: HyperlinkSettings.UNDERLINE_HOVER);
098: form = toolkit.createScrolledForm(parent);
099: form.setLayoutData(new GridData(GridData.FILL_BOTH));
100: FormColors colors = toolkit.getColors();
101: /*
102: colors.initializeSectionToolBarColors();
103: Color gbg = colors.getColor(FormColors.TB_GBG);
104: Color bg = colors.getBackground();
105: form.getForm().setTextBackground(new Color[]{bg, gbg}, new int [] {100}, true);
106: form.getForm().setSeparatorColor(colors.getColor(FormColors.TB_BORDER));
107: form.getForm().setSeparatorVisible(true);
108: */
109: toolkit.decorateFormHeading(form.getForm());
110: mform = new ManagedForm(toolkit, form);
111: GridLayout glayout = new GridLayout();
112: glayout.marginHeight = 0;
113: glayout.marginWidth = 0;
114: form.getBody().setLayout(glayout);
115: final SashForm sash = new SashForm(form.getBody(), SWT.NULL);
116: GridData gd = new GridData(GridData.FILL_BOTH);
117: gd.widthHint = 10;
118: gd.heightHint = 10;
119: sash.setLayoutData(gd);
120: sash.addControlListener(new ControlAdapter() {
121: public void controlResized(ControlEvent e) {
122: Point size = sash.getSize();
123: if (size.x > size.y)
124: sash.setOrientation(SWT.HORIZONTAL);
125: else
126: sash.setOrientation(SWT.VERTICAL);
127: updateSashPanelMargins(sash);
128: }
129: });
130: sash.setBackground(colors.getColor(IFormColors.TB_BG));
131:
132: Composite explorerPanel = new Composite(sash, SWT.NULL);
133: explorerPanel.setBackground(colors
134: .getColor(IFormColors.TB_BORDER));
135: GridLayout playout = new GridLayout();
136: playout.marginWidth = 0;
137: playout.marginTop = 2;
138: playout.marginBottom = 0;
139: explorerPanel.setLayout(playout);
140: toolkit.adapt(explorerPanel);
141: explorerContainer = new PageBook(explorerPanel, SWT.NULL);
142: explorerContainer
143: .setLayoutData(new GridData(GridData.FILL_BOTH));
144: Composite editorPanel = new Composite(sash, SWT.NULL);
145: playout = new GridLayout();
146: playout.marginWidth = 0;
147: playout.marginHeight = 0;
148: editorPanel.setLayout(playout);
149: editorPanel.setBackground(colors
150: .getColor(IFormColors.TB_BORDER));
151: taskEditorContainer = new PageBook(editorPanel, SWT.NULL);
152: toolkit.adapt(taskEditorContainer);
153: taskEditorContainer.setLayoutData(new GridData(
154: GridData.FILL_BOTH));
155: }
156:
157: private void updateSashPanelMargins(SashForm sash) {
158: Control[] children = sash.getChildren();
159: int orientation = sash.getOrientation();
160: // update task explorer panel
161: GridLayout layout = (GridLayout) ((Composite) children[0])
162: .getLayout();
163: if (orientation == SWT.HORIZONTAL) {
164: layout.marginBottom = 0;
165: layout.marginRight = 1;
166: } else {
167: layout.marginBottom = 1;
168: layout.marginRight = 0;
169: }
170: // update task editor panel
171: layout = (GridLayout) ((Composite) children[1]).getLayout();
172: if (orientation == SWT.HORIZONTAL) {
173: layout.marginTop = 0;
174: layout.marginLeft = 1;
175: } else {
176: layout.marginTop = 1;
177: layout.marginLeft = 0;
178: }
179: ((Composite) children[0]).layout();
180: ((Composite) children[1]).layout();
181: }
182:
183: public void dispose() {
184: mform.dispose();
185: super .dispose();
186: }
187:
188: private void setInputModel(CompositeCheatSheetModel model,
189: Map layout) {
190: this .model = model;
191: mform.getForm().setText(
192: ViewUtilities.escapeForLabel(model.getName()));
193: String explorerId = model.getTaskExplorerId();
194: setCurrentExplorerFromId(explorerId);
195: String selectedTaskId = (String) layout
196: .get(ICompositeCheatsheetTags.SELECTED_TASK);
197: ICompositeCheatSheetTask selectedTask = null;
198: if (selectedTaskId != null) {
199: selectedTask = model.getDependencies().getTask(
200: selectedTaskId);
201: if (selectedTask != null) {
202: currentExplorer.setSelection(new StructuredSelection(
203: selectedTask), true);
204: }
205: }
206: if (selectedTask != null) {
207: updateSelectedTask(selectedTask);
208: } else {
209: updateSelectedTask(model.getRootTask());
210: }
211: model.addObserver(new Observer() {
212: public void update(Observable o, Object arg) {
213: ICompositeCheatSheetTask task = (ICompositeCheatSheetTask) arg;
214: if (currentExplorer != null)
215: currentExplorer.taskUpdated(task);
216: updateTask(task);
217: }
218: });
219: }
220:
221: private void setCurrentExplorerFromId(String explorerId) {
222: TaskExplorer explorer = getTaskExplorer(explorerId);
223: if (explorer != null) {
224: explorer.setCompositeCheatSheet(this .model);
225: explorer.setFocus();
226: setExplorerVisible(explorerId);
227: }
228: }
229:
230: private void setExplorerVisible(String id) {
231: Control[] excontrols = explorerContainer.getChildren();
232: TaskExplorer explorer = null;
233: for (int i = 0; i < excontrols.length; i++) {
234: Control excontrol = excontrols[i];
235: explorer = (TaskExplorer) excontrol
236: .getData(ICompositeCheatsheetTags.EXPLORER);
237: if (explorer.getId().equals(id)) {
238: explorerContainer.showPage(excontrol);
239: setCurrentExplorer(explorer);
240: return;
241: }
242: }
243: }
244:
245: private void setCurrentExplorer(TaskExplorer explorer) {
246: if (currentExplorer != null) {
247: currentExplorer.getSelectionProvider()
248: .removeSelectionChangedListener(this );
249: }
250: currentExplorer = explorer;
251: currentExplorer.getSelectionProvider()
252: .addSelectionChangedListener(this );
253: updateForSelection(currentExplorer.getSelectionProvider()
254: .getSelection());
255: }
256:
257: private TaskExplorer getTaskExplorer(String id) {
258: Control[] excontrols = explorerContainer.getChildren();
259: TaskExplorer explorer = null;
260: for (int i = 0; i < excontrols.length; i++) {
261: Control excontrol = excontrols[i];
262: Object data = excontrol
263: .getData(ICompositeCheatsheetTags.EXPLORER);
264: if (data instanceof TaskExplorer) {
265: explorer = (TaskExplorer) data;
266: if (explorer.getId().equals(id)) {
267: return explorer;
268: }
269: }
270: }
271:
272: // Load the explorer from an extension point
273:
274: TaskExplorerManager explorerManager = TaskExplorerManager
275: .getInstance();
276: explorer = explorerManager.getExplorer(id);
277: if (explorer != null) {
278: explorer.createControl(explorerContainer, mform
279: .getToolkit());
280: explorer.getControl().setData(
281: ICompositeCheatsheetTags.EXPLORER, explorer);
282: new TreeExplorerMenu(explorer);
283: }
284: return explorer;
285: }
286:
287: public void selectionChanged(SelectionChangedEvent event) {
288: updateForSelection(event.getSelection());
289: }
290:
291: private void updateForSelection(ISelection selection) {
292: Object selectedElement = ((IStructuredSelection) selection)
293: .getFirstElement();
294: if (selectedElement instanceof ICompositeCheatSheetTask) {
295: updateSelectedTask((ICompositeCheatSheetTask) selectedElement);
296: }
297: }
298:
299: private void updateSelectedTask(ICompositeCheatSheetTask task) {
300: selectedTask = task;
301: updateTask(selectedTask);
302: }
303:
304: /*
305: * Update can be called as a result of a selection change or a state change
306: * If this is not the selected task wait till it is selected to display it
307: */
308: private void updateTask(ICompositeCheatSheetTask task) {
309: if (task == null || task != selectedTask)
310: return;
311: if (task instanceof EditableTask) {
312: EditableTask editable = (EditableTask) task;
313: if (editable.getState() == ICompositeCheatSheetTask.IN_PROGRESS) {
314: showEditor(editable);
315: return;
316: } else if (editable.isUnderReview()) {
317: if (editable.getState() == ICompositeCheatSheetTask.COMPLETED) {
318: showEditor(editable);
319: } else {
320: endReview(editable);
321: }
322: return;
323: }
324: }
325: showDescription(task);
326: }
327:
328: public void saveState() {
329: Map layout = new HashMap();
330: if (selectedTask != null) {
331: layout.put(ICompositeCheatsheetTags.SELECTED_TASK,
332: selectedTask.getId());
333: }
334: saveHelper.saveCompositeState(model, layout);
335: }
336:
337: private void showDescription(final ICompositeCheatSheetTask task) {
338: if (descriptionPanel == null) {
339: createDescriptionPanel();
340: }
341: descriptionPanel.showDescription(task);
342: setCurrentEditor(descriptionPanel.getControl());
343: }
344:
345: private void createDescriptionPanel() {
346: descriptionPanel = new DescriptionPanel(mform,
347: taskEditorContainer);
348: descriptionPanel
349: .addHyperlinkListener(new DescriptionLinkListener());
350: }
351:
352: /*
353: * Ensure that if this task is visible and in a runnable state that it has been started
354: */
355: private void showEditor(EditableTask task) {
356: if (task == selectedTask) {
357: TaskEditor editor = getTaskEditor(task);
358: if (editor != null) {
359: if (!task.isEditorInitialized()) {
360: task.setInput(model.getTaskMemento(task.getId()));
361: }
362: setCurrentEditor(editor.getControl().getParent());
363: }
364: }
365: }
366:
367: private void addHyperlink(StringBuffer buf, String href,
368: String imageRef, String message) {
369: buf.append("<p><a href=\""); //$NON-NLS-1$
370: buf.append(href);
371: buf.append("\">"); //$NON-NLS-1$
372: buf.append("<img href=\""); //$NON-NLS-1$
373: buf.append(imageRef);
374: buf.append("\"/> "); //$NON-NLS-1$
375: buf.append(message);
376: buf.append("</a></p>"); //$NON-NLS-1$
377: }
378:
379: private void reviewTask(EditableTask task) {
380: TaskEditor taskEditor = getTaskEditor(task);
381: Composite container = taskEditor.getControl().getParent();
382: Composite separator = toolkit
383: .createCompositeSeparator(container);
384: GridData data = new GridData();
385: data.heightHint = 1;
386: data.horizontalAlignment = SWT.FILL;
387: separator.setLayoutData(data);
388: FormText text = toolkit.createFormText(container, false);
389: text.setImage(DescriptionPanel.REVIEW_IMAGE, CheatSheetPlugin
390: .getPlugin().getImage(
391: ICheatSheetResource.COMPOSITE_TASK_REVIEW));
392: text.addHyperlinkListener(getEndReviewListener());
393: text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
394: StringBuffer buf = new StringBuffer();
395: buf.append("<form>"); //$NON-NLS-1$
396: addHyperlink(buf, END_REVIEW_TAG + task.getId(),
397: DescriptionPanel.REVIEW_IMAGE,
398: Messages.COMPOSITE_PAGE_END_REVIEW);
399: buf.append("</form>"); //$NON-NLS-1$
400: text.setText(buf.toString(), true, false);
401: task.setUnderReview(true);
402: container.layout(true);
403: showEditor(task);
404: }
405:
406: private void endReview(EditableTask task) {
407: TaskEditor taskEditor = getTaskEditor(task);
408: Control editorControl = taskEditor.getControl();
409: Composite container = editorControl.getParent();
410: Control[] children = container.getChildren();
411: for (int i = children.length - 2; i < children.length; i++) {
412: children[i].dispose();
413: }
414: task.setUnderReview(false);
415: showDescription(task);
416: container.layout();
417: }
418:
419: private void setCurrentEditor(Control c) {
420: taskEditorContainer.showPage(c);
421: }
422:
423: /**
424: * Class which responds to hyperlink events originating from the
425: * description panel.
426: */
427: private final class DescriptionLinkListener extends
428: HyperlinkAdapter {
429: public void linkActivated(HyperlinkEvent e) {
430: String ref = (String) e.getHref();
431: if (ref.equals(START_HREF)) {
432: Object data = descriptionPanel.getControl().getData(
433: ICompositeCheatsheetTags.TASK);
434: if (data instanceof EditableTask) {
435: EditableTask task = (EditableTask) data;
436: task.setStarted();
437: }
438: }
439: if (ref.equals(SKIP_HREF)) {
440: Object data = descriptionPanel.getControl().getData(
441: ICompositeCheatsheetTags.TASK);
442: if (data instanceof AbstractTask) {
443: AbstractTask task = (AbstractTask) data;
444: task.setState(ICompositeCheatSheetTask.SKIPPED);
445: }
446: }
447: if (ref.equals(REVIEW_TAG)) {
448: Object data = descriptionPanel.getControl().getData(
449: ICompositeCheatsheetTags.TASK);
450: if (data instanceof EditableTask) {
451: reviewTask((EditableTask) data);
452: }
453: }
454: if (ref.startsWith(GOTO_TASK_TAG)) {
455: String next = ref.substring(GOTO_TASK_TAG.length());
456: AbstractTask nextTask = model.getDependencies()
457: .getTask(next);
458: currentExplorer.setSelection(new StructuredSelection(
459: nextTask), true);
460: }
461: }
462: }
463:
464: /**
465: * Class which responds to hyperlink events originating from the
466: * end review panel
467: */
468: private final class EndReviewListener extends HyperlinkAdapter {
469: public void linkActivated(HyperlinkEvent e) {
470: String ref = (String) e.getHref();
471: if (ref.startsWith(END_REVIEW_TAG)) {
472: String next = ref.substring(END_REVIEW_TAG.length());
473: AbstractTask task = model.getDependencies().getTask(
474: next);
475: endReview((EditableTask) task);
476: }
477: }
478: }
479:
480: private EndReviewListener endReviewListener;
481:
482: private EndReviewListener getEndReviewListener() {
483: if (endReviewListener == null) {
484: endReviewListener = new EndReviewListener();
485: }
486: return endReviewListener;
487: }
488:
489: /*
490: * Get the task editor for this task. If no editor exists create one
491: */
492: private TaskEditor getTaskEditor(EditableTask editable) {
493: if (editable.getEditor() == null) {
494: // Create a new editor using the extension point data
495: TaskEditor editor = TaskEditorManager.getInstance()
496: .getEditor(editable.getKind());
497: if (editor != null) {
498: Composite editorPanel = new Composite(
499: taskEditorContainer, SWT.NULL);
500: GridLayout layout = new GridLayout();
501: layout.marginWidth = 0;
502: layout.marginHeight = 0;
503: editorPanel.setLayout(layout);
504: mform.getToolkit().adapt(editorPanel);
505: editor.createControl(editorPanel, mform.getToolkit());
506: editable.setEditor(editor);
507: GridData gridData = new GridData(GridData.FILL_BOTH);
508: editor.getControl().setLayoutData(gridData);
509: }
510: }
511: return editable.getEditor();
512: }
513:
514: public Control getControl() {
515: return form;
516: }
517:
518: protected String getTitle() {
519: return model.getName();
520: }
521:
522: public void initialized() {
523: // Open the model
524: model.setSaveHelper(saveHelper);
525: Map layout = new HashMap();
526: model.loadState(layout);
527: setInputModel(model, layout);
528: initialized = true;
529: }
530:
531: public int contributeToViewMenu(Menu menu, int index) {
532: if (!initialized) {
533: return index;
534: }
535: index = contributeExplorerMenuItem(menu, index);
536: return contributeRestartMenuItem(menu, index);
537: }
538:
539: private int contributeRestartMenuItem(Menu menu, int index) {
540: MenuItem item = new MenuItem(menu, SWT.PUSH, index++);
541: item.setText(Messages.RESTART_ALL_MENU);
542: item.setImage(CheatSheetPlugin.getPlugin().getImage(
543: ICheatSheetResource.COMPOSITE_RESTART_ALL));
544: item.addSelectionListener(new SelectionAdapter() {
545: public void widgetSelected(SelectionEvent e) {
546: if (model != null) {
547: if (RestartAllAction.confirmRestart()) {
548: restart(null);
549: }
550: }
551: }
552: });
553: return index;
554: }
555:
556: private int contributeExplorerMenuItem(Menu menu, int index) {
557: String[] explorerIds = CheatSheetRegistryReader.getInstance()
558: .getExplorerIds();
559: if (explorerIds.length == 1) {
560: return index; // no other explorer to chosse from
561: }
562: MenuItem menuItem = new MenuItem(menu, SWT.CASCADE, index++);
563:
564: menuItem.setText(Messages.EXPLORER_PULLDOWN_MENU);
565:
566: Menu subMenu = new Menu(menu);
567: menuItem.setMenu(subMenu);
568:
569: for (int i = 0; i < explorerIds.length; i++) {
570: final String id = explorerIds[i];
571: TaskExplorerNode node = CheatSheetRegistryReader
572: .getInstance().findTaskExplorer(id);
573: boolean isCurrentExplorer = id.equals(currentExplorer
574: .getId());
575: int style = isCurrentExplorer ? SWT.RADIO : SWT.PUSH;
576: MenuItem item = new MenuItem(subMenu, style);
577: item.setText(node.getName());
578: item.setSelection(isCurrentExplorer);
579: item.setImage(TaskExplorerManager.getInstance()
580: .getImage(id));
581: item.addSelectionListener(new SelectionAdapter() {
582: public void widgetSelected(SelectionEvent e) {
583: setCurrentExplorerFromId(id);
584: }
585: });
586: }
587:
588: return index;
589: }
590:
591: public void restart(Map cheatSheetData) {
592: model.resetAllTasks(cheatSheetData);
593: currentExplorer.setSelection(new StructuredSelection(model
594: .getRootTask()), true);
595:
596: }
597:
598: }
|