Java tutorial
/******************************************************************************* * Copyright (c) 2010 McGill University * 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: * Jonathan Faubert * Martin Robillard *******************************************************************************/ package ca.mcgill.cs.swevo.qualyzer.editors.pages; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; 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.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.forms.editor.FormPage; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.navigator.CommonNavigator; import ca.mcgill.cs.swevo.qualyzer.QualyzerActivator; import ca.mcgill.cs.swevo.qualyzer.dialogs.NewCodeDialog; import ca.mcgill.cs.swevo.qualyzer.dialogs.RenameCodeDialog; import ca.mcgill.cs.swevo.qualyzer.editors.inputs.CodeTableInput; import ca.mcgill.cs.swevo.qualyzer.editors.inputs.CodeTableInput.CodeTableRow; import ca.mcgill.cs.swevo.qualyzer.model.Code; import ca.mcgill.cs.swevo.qualyzer.model.CodeEntry; import ca.mcgill.cs.swevo.qualyzer.model.CodeListener; import ca.mcgill.cs.swevo.qualyzer.model.Facade; import ca.mcgill.cs.swevo.qualyzer.model.Fragment; import ca.mcgill.cs.swevo.qualyzer.model.ListenerManager; import ca.mcgill.cs.swevo.qualyzer.model.Memo; import ca.mcgill.cs.swevo.qualyzer.model.MemoListener; import ca.mcgill.cs.swevo.qualyzer.model.PersistenceManager; import ca.mcgill.cs.swevo.qualyzer.model.Project; import ca.mcgill.cs.swevo.qualyzer.model.ProjectListener; import ca.mcgill.cs.swevo.qualyzer.model.Transcript; import ca.mcgill.cs.swevo.qualyzer.model.TranscriptListener; import ca.mcgill.cs.swevo.qualyzer.model.ListenerManager.ChangeType; import ca.mcgill.cs.swevo.qualyzer.providers.CodeTableContentProvider; import ca.mcgill.cs.swevo.qualyzer.providers.CodeTableLabelProvider; import ca.mcgill.cs.swevo.qualyzer.providers.CodeTreeContentProvider; import ca.mcgill.cs.swevo.qualyzer.providers.CodeTreeLabelProvider; import ca.mcgill.cs.swevo.qualyzer.providers.Node; import ca.mcgill.cs.swevo.qualyzer.providers.TableDragListener; import ca.mcgill.cs.swevo.qualyzer.providers.TableFilter; import ca.mcgill.cs.swevo.qualyzer.providers.TreeDragListener; import ca.mcgill.cs.swevo.qualyzer.providers.TreeDropListener; import ca.mcgill.cs.swevo.qualyzer.providers.TreeModel; import ca.mcgill.cs.swevo.qualyzer.ui.ResourcesUtil; /** * The page for the code editor. */ public class CodeEditorPage extends FormPage implements CodeListener, ProjectListener, TranscriptListener, MemoListener { private static final int FONT_SIZE = 10; private static final int DESCRIPTION_HEIGHT = 15; private static final GridData LARGE_LAYOUT = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1); private static final int NAME_WIDTH = 180; private static final int FREQ_WIDTH = 80; private static final int TREE_NAME_WIDTH = 180; private static final int TREE_FREQ_WIDTH = 60; private static final String EMPTY = ""; //$NON-NLS-1$ private static final String DELETE_CODE = Messages.getString("editors.pages.CodeEditorPage.deleteCode"); //$NON-NLS-1$ private Project fProject; private TableViewer fTableViewer; private CodeTableSorter fSorter; private CodeTableRow fCurrentRow; private Label fCodeName; private StyledText fDescription; private boolean fIsDirty; private ScrolledForm fForm; private TreeViewer fTreeViewer; private Composite fTreeArea; private Composite fTableArea; private TreeModel fTreeModel; private Button fFilterButton; private ArrayList<MenuItem> fTableCanDisable; private ArrayList<MenuItem> fTreeCanDisable; /** * Constructor. * @param editor */ public CodeEditorPage(FormEditor editor, Project project) { super(editor, Messages.getString("editors.pages.CodeEditorPage.codeEditor"), //$NON-NLS-1$ Messages.getString("editors.pages.CodeEditorPage.codeEditor")); //$NON-NLS-1$ fProject = project; fIsDirty = false; fTableCanDisable = new ArrayList<MenuItem>(); fTreeCanDisable = new ArrayList<MenuItem>(); ListenerManager listenerManager = Facade.getInstance().getListenerManager(); listenerManager.registerCodeListener(fProject, this); listenerManager.registerProjectListener(fProject, this); listenerManager.registerTranscriptListener(fProject, this); listenerManager.registerMemoListener(fProject, this); } /* (non-Javadoc) * @see org.eclipse.ui.forms.editor.FormPage#createFormContent(org.eclipse.ui.forms.IManagedForm) */ @Override protected void createFormContent(IManagedForm managedForm) { fForm = managedForm.getForm(); FormToolkit toolkit = managedForm.getToolkit(); Composite body = fForm.getBody(); fForm.setText(Messages.getString("editors.pages.CodeEditorPage.codes")); //$NON-NLS-1$ body.setLayout(new GridLayout(1, true)); Button button = toolkit.createButton(body, Messages.getString("editors.pages.CodeEditorPage.swap"), //$NON-NLS-1$ SWT.PUSH); Composite mainArea = toolkit.createComposite(body, SWT.NULL); mainArea.setLayout(new GridLayout(2, true)); mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Composite leftArea = toolkit.createComposite(mainArea, SWT.NULL); leftArea.setLayout(new GridLayout(1, true)); leftArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); buildTableViewer(toolkit, leftArea); Composite rightArea = toolkit.createComposite(mainArea, SWT.NULL); rightArea.setLayout(new GridLayout(1, true)); rightArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); createTreeViewer(toolkit, rightArea); fCodeName = toolkit.createLabel(body, Messages.getString("editors.pages.CodeEditorPage.selectedCode")); //$NON-NLS-1$ fCodeName.setFont(new Font(Display.getCurrent(), new FontData("", FONT_SIZE, SWT.BOLD))); //$NON-NLS-1$ fCodeName.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false)); fDescription = new StyledText(body, SWT.WRAP | SWT.V_SCROLL | SWT.BORDER); fDescription.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, DESCRIPTION_HEIGHT)); fDescription.addKeyListener(createKeyAdapter()); toolkit.paintBordersFor(body); fTableViewer.addSelectionChangedListener(createTableSelectionListener()); fFilterButton.addSelectionListener(setFilter(fFilterButton)); createTableContextMenu(); button.addSelectionListener(createToggleAdapter()); updateSelection(); } /** * Swaps the list and the hierarchy. * @return */ private SelectionListener createToggleAdapter() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Composite lParent = fTreeArea.getParent(); Composite rParent = fTableArea.getParent(); fTreeArea.setParent(rParent); fTableArea.setParent(lParent); } }; } /** * Create the layout for the TreeViewer. Defines all the columns and adds drag and drop * support, the sorter, and the various listeners. * @param toolkit * @param composite * @return */ private void createTreeViewer(FormToolkit toolkit, Composite parent) { fTreeArea = toolkit.createComposite(parent); fTreeArea.setLayout(new GridLayout(2, false)); fTreeArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label label = toolkit.createLabel(fTreeArea, Messages.getString("editors.pages.CodeEditorPage.hierarchy")); //$NON-NLS-1$ label.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, true, false, 1, 1)); Button button = toolkit.createButton(fTreeArea, "", SWT.PUSH); //$NON-NLS-1$ button.setLayoutData(new GridData(SWT.RIGHT, SWT.BOTTOM, false, false, 1, 1)); button.setVisible(false); button.setEnabled(false); fTreeViewer = new TreeViewer(fTreeArea, SWT.SINGLE | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.BORDER); Tree tree = fTreeViewer.getTree(); TreeColumn col = new TreeColumn(tree, SWT.NONE); col.setText(Messages.getString("editors.pages.CodeEditorPage.code")); //$NON-NLS-1$ col.setWidth(TREE_NAME_WIDTH); col = new TreeColumn(tree, SWT.NONE); col.setText(Messages.getString("editors.pages.CodeEditorPage.count")); //$NON-NLS-1$ col.setWidth(TREE_FREQ_WIDTH); col = new TreeColumn(tree, SWT.NONE); col.setText(Messages.getString("editors.pages.CodeEditorPage.totalCount")); //$NON-NLS-1$ col.setWidth(TREE_FREQ_WIDTH); tree.setHeaderVisible(true); tree.setLinesVisible(true); tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); fTreeViewer.setContentProvider(new CodeTreeContentProvider()); fTreeViewer.setLabelProvider(new CodeTreeLabelProvider()); fTreeModel = TreeModel.getTreeModel(fProject); fTreeViewer.setInput(fTreeModel.getRoot()); fTreeModel.addListener(fTreeViewer); int operations = DND.DROP_COPY | DND.DROP_MOVE; Transfer[] transferTypes = new Transfer[] { TextTransfer.getInstance() }; fTreeViewer.addDropSupport(operations, transferTypes, new TreeDropListener(fTreeViewer, this)); fTreeViewer.addDragSupport(operations, transferTypes, new TreeDragListener(fTreeViewer)); fTreeViewer.addSelectionChangedListener(createTreeSelectionListener()); fTreeViewer.setSorter(new ViewerSorter()); fTreeViewer.addDoubleClickListener(createDoubleClickListenerTree()); createTreeContextMenu(); } /** * Whenever the selection changes in the tree change the selection in the table to the same code. * @return */ private ISelectionChangedListener createTreeSelectionListener() { return new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { Node node = (Node) ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement(); if (node != null) { setTreeItemsEnabled(true); int i = 0; CodeTableRow row = (CodeTableRow) fTableViewer.getElementAt(i); while (row != null) { if (node.getPersistenceId().equals(row.getPersistenceId())) { fTableViewer.setSelection(new StructuredSelection(row)); break; } i++; row = (CodeTableRow) fTableViewer.getElementAt(i); } } else { Node[] elements = fTreeModel.getRoot().getChildren().values().toArray(new Node[0]); if (elements.length > 0) { fTreeViewer.setSelection(new StructuredSelection(elements[0])); setTreeItemsEnabled(true); } else { setTreeItemsEnabled(false); } } } }; } /** * Change the enablement of the tree context menu items that are dependent on the selection. * @param b */ protected void setTreeItemsEnabled(boolean b) { for (MenuItem item : fTreeCanDisable) { item.setEnabled(b); } } /** * Change the enablement of the table context menu items that are dependent on the selection. * @param b */ protected void setTableItemsEnabled(boolean b) { for (MenuItem item : fTableCanDisable) { item.setEnabled(b); } } /** * Define the actions for the tree context menu. */ private void createTreeContextMenu() { Menu menu = new Menu(fTreeViewer.getTree()); MenuItem item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.newRootCode")); //$NON-NLS-1$ item.addSelectionListener(newRootCodeSelected()); item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.newSubCode")); //$NON-NLS-1$ item.addSelectionListener(createSubCodeSelected()); fTreeCanDisable.add(item); item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.renameCode")); //$NON-NLS-1$ item.addSelectionListener(renameCodeSelectedTree()); fTreeCanDisable.add(item); item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.removeCode")); //$NON-NLS-1$ item.addSelectionListener(removeCodeSelected()); fTreeCanDisable.add(item); item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.viewFragments")); //$NON-NLS-1$ item.addSelectionListener(viewFragmentsSelectedTree()); fTreeCanDisable.add(item); fTreeViewer.getTree().setMenu(menu); setTreeItemsEnabled(false); } /** * The action to take if view fragment is selected on the tree. * @return */ private SelectionListener viewFragmentsSelectedTree() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Code toView = null; Node node = (Node) ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement(); for (Code aCode : fProject.getCodes()) { if (aCode.getCodeName().equals(node.getCodeName())) { toView = aCode; break; } } if (toView != null) { ResourcesUtil.openEditor(getSite().getPage(), toView); } } }; } /** * The action to take if rename code is selected on the tree. * @return */ private SelectionListener renameCodeSelectedTree() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Code code = null; Node node = (Node) ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement(); for (Code aCode : fProject.getCodes()) { if (aCode.getCodeName().equals(node.getCodeName())) { code = aCode; break; } } RenameCodeDialog dialog = new RenameCodeDialog(getSite().getShell(), code); dialog.open(); if (dialog.getReturnCode() == Window.OK) { String name = dialog.getName(); code.setCodeName(name); Facade.getInstance().saveCodes(new Code[] { code }); CommonNavigator view = (CommonNavigator) PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage().findView(QualyzerActivator.PROJECT_EXPLORER_VIEW_ID); view.getCommonViewer().refresh(); } } }; } /** * The action to take if remove code is selected (on the tree). * @return */ private SelectionAdapter removeCodeSelected() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IStructuredSelection selection = (IStructuredSelection) fTreeViewer.getSelection(); Node node = (Node) selection.getFirstElement(); boolean check = true; if (!node.getChildren().isEmpty()) { check = MessageDialog.openConfirm(getSite().getShell(), Messages.getString("editors.pages.CodeEditorPage.removeCode"), //$NON-NLS-1$ Messages.getString("editors.pages.CodeEditorPage.removeConfirm")); //$NON-NLS-1$ } if (check) { fTreeModel.removeNode(node); fTreeModel.getRoot().computeFreq(); fTreeViewer.refresh(); if (fFilterButton.getSelection()) { fTableViewer.refresh(); } setDirty(); } } }; } /** * The action to take if New Root Code is created (on the tree). * @return */ private SelectionListener newRootCodeSelected() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Node node = fTreeModel.getRoot(); if (node == null) { return; } NewCodeDialog dialog = new NewCodeDialog(getEditor().getSite().getShell(), fProject); dialog.create(); if (dialog.open() == Window.OK) { Code code = Facade.getInstance().createCode(dialog.getName(), dialog.getDescription(), fProject); CommonNavigator view = (CommonNavigator) PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage().findView(QualyzerActivator.PROJECT_EXPLORER_VIEW_ID); view.getCommonViewer().refresh(); new Node(node, code.getCodeName(), code.getPersistenceId(), 0); fTreeViewer.refresh(); fTreeViewer.expandToLevel(node, 1); if (fFilterButton.getSelection()) { fTableViewer.refresh(); } setDirty(); } } }; } /** * The action to take if New Sub-Code is selected (on the tree). * @return */ private SelectionListener createSubCodeSelected() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IStructuredSelection selection = (IStructuredSelection) fTreeViewer.getSelection(); Node node = (Node) selection.getFirstElement(); if (node == null) { return; } NewCodeDialog dialog = new NewCodeDialog(getEditor().getSite().getShell(), fProject); dialog.create(); if (dialog.open() == Window.OK) { Code code = Facade.getInstance().createCode(dialog.getName(), dialog.getDescription(), fProject); CommonNavigator view = (CommonNavigator) PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage().findView(QualyzerActivator.PROJECT_EXPLORER_VIEW_ID); view.getCommonViewer().refresh(); new Node(node, code.getCodeName(), code.getPersistenceId(), 0); fTreeViewer.refresh(); fTreeViewer.expandToLevel(node, 1); if (fFilterButton.getSelection()) { fTableViewer.refresh(); } setDirty(); } } }; } /** * Create the layout for the table viewer and attach all the listeners, the sorter, and * drag and drop support. * @param body * */ private void buildTableViewer(FormToolkit toolkit, Composite body) { fTableArea = toolkit.createComposite(body, SWT.NULL); fTableArea.setLayout(new GridLayout(2, false)); fTableArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label label = toolkit.createLabel(fTableArea, Messages.getString("editors.pages.CodeEditorPage.list")); //$NON-NLS-1$ label.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, true, false, 1, 1)); fFilterButton = toolkit.createButton(fTableArea, Messages.getString("editors.pages.CodeEditorPage.filter"), //$NON-NLS-1$ SWT.TOGGLE); fFilterButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BOTTOM, false, false, 1, 1)); fTableViewer = new TableViewer(fTableArea, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL); TableColumn col = new TableColumn(fTableViewer.getTable(), SWT.NONE); col.setText(Messages.getString("editors.pages.CodeEditorPage.codeName")); //$NON-NLS-1$ col.setWidth(NAME_WIDTH); col.addSelectionListener(createColSortListener(0, col)); col.setMoveable(false); col = new TableColumn(fTableViewer.getTable(), SWT.NONE); col.setText(Messages.getString("editors.pages.CodeEditorPage.count")); //$NON-NLS-1$ col.setWidth(FREQ_WIDTH); col.addSelectionListener(createColSortListener(1, col)); col.setMoveable(false); fTableViewer.setContentProvider(new CodeTableContentProvider()); fTableViewer.setLabelProvider(new CodeTableLabelProvider()); fTableViewer.setInput(new CodeTableInput(fProject)); fTableViewer.getTable().setHeaderVisible(true); fSorter = new CodeTableSorter(); fTableViewer.setSorter(fSorter); fTableViewer.addDragSupport(DND.DROP_COPY | DND.DROP_MOVE, new Transfer[] { TextTransfer.getInstance() }, new TableDragListener(fTableViewer)); fTableViewer.addDoubleClickListener(createDoubleClickListenerTable()); fTableViewer.getTable().setSortColumn(fTableViewer.getTable().getColumn(0)); fTableViewer.getTable().setSortDirection(SWT.DOWN); fTableViewer.getTable().setLayoutData(LARGE_LAYOUT); } /** * The action to take on double click on the table. * @return */ private IDoubleClickListener createDoubleClickListenerTable() { return new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { IStructuredSelection selection = (IStructuredSelection) fTableViewer.getSelection(); CodeTableRow row = (CodeTableRow) selection.getFirstElement(); Code code = row.getCode(); if (code != null) { ResourcesUtil.openEditor(getSite().getPage(), code); } } }; } /** * Toggle the filter (on the table) whenever the filter button is pressed. * @return */ private SelectionListener setFilter(final Button button) { return new SelectionAdapter() { private TableFilter fFilter = new TableFilter(fTreeModel); @Override public void widgetSelected(SelectionEvent e) { if (button.getSelection()) { if (fCurrentRow != null) { fCurrentRow.setDescription(fDescription.getText()); } if (!fFilter.select(fTableViewer, null, fCurrentRow)) { fCurrentRow = null; } fTableViewer.addFilter(fFilter); fTableViewer.refresh(); updateSelection(); } else { if (fCurrentRow != null) { fCurrentRow.setDescription(fDescription.getText()); } fTableViewer.removeFilter(fFilter); fTableViewer.refresh(); updateSelection(); } } }; } /** * The double click action on the tree. * @return */ private IDoubleClickListener createDoubleClickListenerTree() { return new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { Code toView = null; if (fFilterButton.getSelection()) { Node node = (Node) ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement(); for (Code aCode : fProject.getCodes()) { if (aCode.getCodeName().equals(node.getCodeName())) { toView = aCode; break; } } } else { IStructuredSelection selection = (IStructuredSelection) fTableViewer.getSelection(); toView = ((CodeTableRow) selection.getFirstElement()).getCode(); } if (toView != null) { ResourcesUtil.openEditor(getSite().getPage(), toView); } } }; } /** * Toggles sorting on the table whenever a column header is clicked. * @param colIndex * @param column * @return */ private SelectionListener createColSortListener(final int colIndex, final TableColumn column) { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { fSorter.setColumn(colIndex); int dir = fTableViewer.getTable().getSortDirection(); if (fTableViewer.getTable().getSortColumn() == column) { dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; } else { dir = SWT.DOWN; } fTableViewer.getTable().setSortColumn(column); fTableViewer.getTable().setSortDirection(dir); fTableViewer.refresh(); } }; } /** * Builds the context menu that gives access to the New Code, Delete Code, View Fragments, and * Rename Code actions on the table. * . */ private void createTableContextMenu() { Menu menu = new Menu(fTableViewer.getTable()); MenuItem item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.newCode")); //$NON-NLS-1$ item.addSelectionListener(newCodeSelected()); item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.renameCode")); //$NON-NLS-1$ item.addSelectionListener(renameCodeSelected()); fTableCanDisable.add(item); item = new MenuItem(menu, SWT.PUSH); item.setText(DELETE_CODE); item.addSelectionListener(deleteCodeSelected()); fTableCanDisable.add(item); item = new MenuItem(menu, SWT.PUSH); item.setText(Messages.getString("editors.pages.CodeEditorPage.viewFragments")); //$NON-NLS-1$ item.addSelectionListener(viewFragmentsSelected()); fTableCanDisable.add(item); fTableViewer.getTable().setMenu(menu); setTableItemsEnabled(false); } /** * The action taken when rename code is selected on the table. * @return */ private SelectionListener renameCodeSelected() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IStructuredSelection selection = (IStructuredSelection) fTableViewer.getSelection(); Code code = ((CodeTableRow) selection.getFirstElement()).getCode(); RenameCodeDialog dialog = new RenameCodeDialog(getSite().getShell(), code); dialog.open(); if (dialog.getReturnCode() == Window.OK) { String name = dialog.getName(); code.setCodeName(name); Facade.getInstance().saveCodes(new Code[] { code }); CommonNavigator view = (CommonNavigator) PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage().findView(QualyzerActivator.PROJECT_EXPLORER_VIEW_ID); view.getCommonViewer().refresh(); } } }; } /** * Handles the selection of the View Associated Fragments Action on the table. * @return */ private SelectionListener viewFragmentsSelected() { return new SelectionAdapter() { /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { CodeTableRow row = (CodeTableRow) ((IStructuredSelection) fTableViewer.getSelection()) .getFirstElement(); Code toView = row.getCode(); if (toView != null) { ResourcesUtil.openEditor(getSite().getPage(), toView); } } }; } /** * Handles the selection of the Delete Code Action on the table. * Checks if there are any memos stopping the deletion. * Then finds all the fragments that contain the code. * Displays a warning/confirmation. * Removes the code from all associated fragments and then deletes the code. * @return */ private SelectionAdapter deleteCodeSelected() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IStructuredSelection selection = (IStructuredSelection) fTableViewer.getSelection(); Code toDelete = ((CodeTableRow) selection.getFirstElement()).getCode(); List<Memo> hardConflicts = detectHardConflicts(toDelete); if (!hardConflicts.isEmpty()) { String message = buildErrorString(hardConflicts); MessageDialog.openError(getSite().getShell(), Messages.getString("editors.pages.CodeEditorPage.unableToDelete"), message); //$NON-NLS-1$ return; } List<Fragment> conflicts = detectConflicts(toDelete); boolean check = false; check = MessageDialog.openConfirm(getSite().getShell(), DELETE_CODE, getConfirmMessage(conflicts.size())); if (check && conflicts.size() > 0) { removeCodeFromFragments(toDelete, conflicts); } if (check) { Facade.getInstance().deleteCode(toDelete); CommonNavigator view = (CommonNavigator) PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage().findView(QualyzerActivator.PROJECT_EXPLORER_VIEW_ID); view.getCommonViewer().refresh(); } } }; } /** * Returns the proper confirmation dialog message for the code deletion. Based on the size of the conflicts. * @param conflicts * @return */ private String getConfirmMessage(int size) { String message; if (size < 0) { message = ""; //$NON-NLS-1$ } else if (size == 0) { message = Messages.getString("editors.pages.CodeEditorPage.confirm"); //$NON-NLS-1$ } else if (size == 1) { message = Messages.getString("editors.pages.CodeEditorPage.confirmOne"); //$NON-NLS-1$ } else { message = Messages.getString("editors.pages.CodeEditorPage.confirmMany") + //$NON-NLS-1$ size + Messages.getString("editors.pages.CodeEditorPage.confirmMany2"); //$NON-NLS-1$ } return message; } /** * Builds the string displayed in the error message if the code cannot be deleted. * @param conflicts * @return */ protected String buildErrorString(List<Memo> conflicts) { String message = Messages.getString("editors.pages.CodeEditorPage.conflicts"); //$NON-NLS-1$ for (Memo memo : conflicts) { message += Messages.getString("editors.pages.CodeEditorPage.memo") + memo.getName(); //$NON-NLS-1$ } return message; } /** * Goes through the fragments and removes the code from them, saving each one. * @param toDelete * @param conflicts */ private void removeCodeFromFragments(Code toDelete, List<Fragment> conflicts) { for (Fragment fragment : conflicts) { for (int i = 0; i < fragment.getCodeEntries().size(); i++) { CodeEntry entry = fragment.getCodeEntries().get(i); if (entry.getCode().equals(toDelete)) { fragment.getCodeEntries().remove(i); Facade.getInstance().saveDocument(fragment.getDocument()); break; } } if (fragment.getCodeEntries().isEmpty()) { Facade.getInstance().deleteFragment(fragment); } } } /** * Finds any memos that reference the code to be deleted. * @param code the code that will be deleted. * @return The list of memos "about" it. */ private List<Memo> detectHardConflicts(Code code) { List<Memo> memos = new ArrayList<Memo>(); for (Memo memo : fProject.getMemos()) { if (code.equals(memo.getCode())) { memos.add(memo); } } return memos; } /** * Handles the selection of the new code action on the table. * @return */ private SelectionAdapter newCodeSelected() { return new SelectionAdapter() { /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { NewCodeDialog dialog = new NewCodeDialog(getEditor().getSite().getShell(), fProject); dialog.create(); if (dialog.open() == Window.OK) { Facade.getInstance().createCode(dialog.getName(), dialog.getDescription(), fProject); CommonNavigator view = (CommonNavigator) PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage().findView(QualyzerActivator.PROJECT_EXPLORER_VIEW_ID); view.getCommonViewer().refresh(); } } }; } /** * Finds all the fragments that reference this code. * @param toDelete * @return */ protected List<Fragment> detectConflicts(Code toDelete) { List<Fragment> conflicts = new ArrayList<Fragment>(); for (Transcript transcript : fProject.getTranscripts()) { Transcript lTranscript = Facade.getInstance().forceTranscriptLoad(transcript); for (Fragment fragment : lTranscript.getFragments().values()) { for (CodeEntry entry : fragment.getCodeEntries()) { if (entry.getCode().equals(toDelete)) { conflicts.add(fragment); } } } } for (Memo memo : fProject.getMemos()) { Memo lMemo = Facade.getInstance().forceMemoLoad(memo); for (Fragment fragment : lMemo.getFragments().values()) { for (CodeEntry entry : fragment.getCodeEntries()) { if (entry.getCode().equals(toDelete)) { conflicts.add(fragment); } } } } return conflicts; } /** * Handles updating the dirty state. * @return */ private KeyListener createKeyAdapter() { return new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (!fIsDirty && (!fDescription.getText().equals(fCurrentRow.getDescription()))) { fIsDirty = true; getEditor().editorDirtyStateChanged(); } } }; } /** * Toggle the dirty state to clean. */ public void notDirty() { fIsDirty = false; getEditor().editorDirtyStateChanged(); } /** * Updates Description boxes as the selected item in the table changes. Also updates the local * information so that changes are not lost. * @return */ private ISelectionChangedListener createTableSelectionListener() { return new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection) fTableViewer.getSelection(); CodeTableRow row = (CodeTableRow) selection.getFirstElement(); if (row == null) { fDescription.setText(EMPTY); return; } if (fForm.getMessageType() == IMessageProvider.ERROR) { fTableViewer.setSelection(new StructuredSelection(fCurrentRow)); } if (row != fCurrentRow) { if (fCurrentRow != null) { fCurrentRow.setDescription(fDescription.getText().trim()); fTableViewer.refresh(fCurrentRow); } fCurrentRow = row; fDescription.setText(fCurrentRow.getDescription()); fCodeName.setText(Messages.getString("editors.pages.CodeEditorPage.selectedCode") //$NON-NLS-1$ + fCurrentRow.getName()); } } }; } @Override public boolean isDirty() { return fIsDirty; } @Override public void codeChanged(ChangeType cType, Code[] codes, Facade facade) { List<CodeTableRow> list = null; if (fIsDirty) { list = getDirtyRows(); } fProject = PersistenceManager.getInstance().getProject(fProject.getName()); fTableViewer.setInput(new CodeTableInput(fProject)); if (fIsDirty) { updateDescriptions(list); } updateSelection(); fTableArea.layout(); fTableArea.redraw(); } /** * Makes sure that something is selected in the table. Preferably the same thing that was before. */ private void updateSelection() { CodeTableRow row; if (fCurrentRow == null) { row = (CodeTableRow) fTableViewer.getElementAt(0); } else { int index = 0; while ((row = (CodeTableRow) fTableViewer.getElementAt(index)) != null) { if (row.getName().equals(fCurrentRow.getName())) { break; } index++; } } if (row == null) { row = (CodeTableRow) fTableViewer.getElementAt(0); } if (row != null) { fTableViewer.setSelection(new StructuredSelection(row)); setTableItemsEnabled(true); } else { setTableItemsEnabled(false); } } @Override public void projectChanged(ChangeType cType, Project project, Facade facade) { if (cType == ChangeType.DELETE) { getEditor().close(false); } else if (cType == ChangeType.RENAME) { ResourcesUtil.closeEditor(getSite().getPage(), getEditorInput().getName()); } } @Override public void dispose() { ListenerManager listenerManager = Facade.getInstance().getListenerManager(); listenerManager.unregisterCodeListener(fProject, this); listenerManager.unregisterProjectListener(fProject, this); listenerManager.unregisterTranscriptListener(fProject, this); listenerManager.unregisterMemoListener(fProject, this); if (fTreeModel != null) { fTreeModel.removeListener(fTreeViewer); } super.dispose(); } /* (non-Javadoc) * @see ca.mcgill.cs.swevo.qualyzer.model.TranscriptListener#transcriptChanged( * ca.mcgill.cs.swevo.qualyzer.model.ListenerManager.ChangeType, * ca.mcgill.cs.swevo.qualyzer.model.Transcript[], ca.mcgill.cs.swevo.qualyzer.model.Facade) */ @Override public void transcriptChanged(ChangeType cType, Transcript[] transcripts, Facade facade) { if (cType == ChangeType.MODIFY || cType == ChangeType.DELETE) { List<CodeTableRow> list = null; if (fIsDirty) { list = getDirtyRows(); } fProject = PersistenceManager.getInstance().getProject(fProject.getName()); CodeTableInput input = new CodeTableInput(fProject); fTableViewer.setInput(input); if (fIsDirty) { updateDescriptions(list); } fTreeModel.updateFrequencies(input); fTreeViewer.refresh(); updateSelection(); } } /* (non-Javadoc) * @see ca.mcgill.cs.swevo.qualyzer.model.MemoListener#memoChanged( * ca.mcgill.cs.swevo.qualyzer.model.ListenerManager.ChangeType, ca.mcgill.cs.swevo.qualyzer.model.Memo[], * ca.mcgill.cs.swevo.qualyzer.model.Facade) */ @Override public void memoChanged(ChangeType cType, Memo[] memos, Facade facade) { if (cType == ChangeType.MODIFY || cType == ChangeType.DELETE) { List<CodeTableRow> list = null; if (fIsDirty) { list = getDirtyRows(); } fProject = PersistenceManager.getInstance().getProject(fProject.getName()); CodeTableInput input = new CodeTableInput(fProject); fTableViewer.setInput(input); if (fIsDirty) { updateDescriptions(list); } fTreeModel.updateFrequencies(input); fTreeViewer.refresh(); updateSelection(); } } /** * Gets the codes that need to be saved with their new descriptions. * @return */ public Code[] getCodes() { if (fCurrentRow != null) { fCurrentRow.setDescription(fDescription.getText().trim()); } List<Code> codes = new ArrayList<Code>(); int index = 0; CodeTableRow row = (CodeTableRow) fTableViewer.getElementAt(index); while (row != null) { Code codeToSave = row.getCodeToSave(); if (codeToSave != null) { codes.add(codeToSave); } index++; row = (CodeTableRow) fTableViewer.getElementAt(index); } return codes.toArray(new Code[0]); } /** * Finds all the rows in the table that have been modified since the last save. * @return */ private List<CodeTableRow> getDirtyRows() { if (fCurrentRow != null) { fCurrentRow.setDescription(fDescription.getText().trim()); } ArrayList<CodeTableRow> list = new ArrayList<CodeTableRow>(); int i = 0; CodeTableRow row = (CodeTableRow) fTableViewer.getElementAt(i); while (row != null) { if (row.isDirty()) { list.add(row); } i++; row = (CodeTableRow) fTableViewer.getElementAt(i); } return list; } /** * Updates the descriptions of the codes in the table to match the previously dirty descriptions. * This is called when there are dirty rows that would be lost due to an update from elsewhere. * @param list */ private void updateDescriptions(List<CodeTableRow> list) { for (CodeTableRow row : list) { int i = 0; CodeTableRow tableRow = (CodeTableRow) fTableViewer.getElementAt(i); while (tableRow != null) { if (tableRow.getPersistenceId().equals(row.getPersistenceId())) { tableRow.setDescription(row.getDescription()); break; } i++; tableRow = (CodeTableRow) fTableViewer.getElementAt(i); } } } /* (non-Javadoc) * @see org.eclipse.ui.forms.editor.FormPage#doSave(org.eclipse.core.runtime.IProgressMonitor) */ @Override public void doSave(IProgressMonitor monitor) { fTreeModel.save(); } /** * */ public void setDirty() { if (!fIsDirty) { fIsDirty = true; getEditor().editorDirtyStateChanged(); } } /** * @return */ public TreeModel getTreeModel() { return fTreeModel; } /** * Refreshes the table and tree viewers. */ public void refresh() { fTableViewer.refresh(); fTreeViewer.refresh(); } }