/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorolamobility.studio.android.db.core.ui.view; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IMemento; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.services.IEvaluationService; import com.motorola.studio.android.common.log.StudioLogger; import com.motorolamobility.studio.android.db.core.DbCoreActivator; import com.motorolamobility.studio.android.db.core.DbRootNodeReader; import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent; import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager; import com.motorolamobility.studio.android.db.core.event.IDatabaseModelListener; import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode; import com.motorolamobility.studio.android.db.core.ui.ITreeNode; import com.motorolamobility.studio.android.db.core.ui.RootNode; import com.motorolamobility.studio.android.db.core.ui.tree.DatabaseExplorerTreeContentProvider; import com.motorolamobility.studio.android.db.core.ui.tree.DatabaseExplorerTreeLabelProvider; /** * View containing a {@link TreeViewer} that is expanded as the nodes from type {@link AbstractTreeNode} are double-clicked */ public class MOTODEVDatabaseExplorerView extends ViewPart implements IDatabaseModelListener { public static final String VIEW_ID = "com.motorola.studio.android.db.databaseView"; //$NON-NLS-1$ public static final String DB_EXPLORER_VIEW_HELP = DbCoreActivator.PLUGIN_ID + ".dbexplorer"; //$NON-NLS-1$ public static final String DB_EXPLORER_VIEW_CONTR_BROWSE_TABLE_ID = "com.motorolamobility.studio.android.db.core.createTableCommand"; private TreeViewer treeViewer; /* (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl(Composite parent) { GridLayout layout = new GridLayout(); layout.numColumns = 1; layout.verticalSpacing = 1; layout.marginWidth = 0; layout.marginHeight = 2; parent.setLayout(layout); GridData layoutData = new GridData(); layoutData = new GridData(); layoutData.grabExcessHorizontalSpace = true; layoutData.grabExcessVerticalSpace = true; layoutData.horizontalAlignment = GridData.FILL; layoutData.verticalAlignment = GridData.FILL; treeViewer = new TreeViewer(parent, SWT.SINGLE | SWT.VIRTUAL); //It is required due to ILazyTreeContentProvider treeViewer.setContentProvider(new DatabaseExplorerTreeContentProvider(treeViewer)); treeViewer.setUseHashlookup(true); treeViewer.setAutoExpandLevel(0); treeViewer.setLabelProvider(new DatabaseExplorerTreeLabelProvider()); ColumnViewerToolTipSupport.enableFor(treeViewer); treeViewer.setInput(getInitalInput()); treeViewer.getControl().setLayoutData(layoutData); treeViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { ISelection selection = event.getSelection(); if (selection instanceof TreeSelection) { TreeSelection treeSelection = (TreeSelection) selection; Object element = treeSelection.getFirstElement(); if (element instanceof AbstractTreeNode) { if (!treeViewer.getExpandedState(element)) { treeViewer.expandToLevel(element, 1); } else { treeViewer.collapseToLevel(element, 1); } } } } }); hookContextMenu(); getSite().setSelectionProvider(treeViewer); //register listener for model changes DatabaseModelEventManager.getInstance().addListener(this); // add context help PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, DB_EXPLORER_VIEW_HELP); PlatformUI.getWorkbench().getHelpSystem() .setHelp(treeViewer.getTree(), DB_EXPLORER_VIEW_HELP); } /** * Loads the contributions from extension point com.motorolamobility.studio.android.db.core.dbRootNode * @return root node (invisible) that contains as children the contributed tree nodes subclassing {@link AbstractTreeNode}. */ private Object getInitalInput() { HashMap<String, AbstractTreeNode> model = new HashMap<String, AbstractTreeNode>(); RootNode rootNode = new RootNode(); try { DbRootNodeReader.loadRootNode(model); Set<Entry<String, AbstractTreeNode>> modelEntries = model.entrySet(); List<ITreeNode> childrenList = new ArrayList<ITreeNode>(modelEntries.size()); for (Map.Entry<String, AbstractTreeNode> childNode : modelEntries) { childrenList.add(childNode.getValue()); } rootNode.putChildren(childrenList); } catch (PartInitException e) { StudioLogger.error(MOTODEVDatabaseExplorerView.class, "Problem creating MOTODEV Database Explorer Tree", e); //$NON-NLS-1$ } return rootNode; } private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(treeViewer.getControl()); treeViewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, treeViewer); } private void fillContextMenu(IMenuManager manager) { // Other plug-ins can contribute there actions here // manager.add(openClose); manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } /* (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ @Override public void setFocus() { treeViewer.getTree().setFocus(); } /** * @return the treeViewer */ public TreeViewer getTreeViewer() { return treeViewer; } /** * Get items selected in the tree * @return */ public ITreeNode getSelectedItemOnTree() { ITreeNode result = null; ITreeSelection treeSelection = null; ISelection selection = treeViewer.getSelection(); if (selection instanceof ITreeSelection) { treeSelection = (ITreeSelection) selection; result = (ITreeNode) treeSelection.getFirstElement(); } return result; } /** * Collapse all nodes of the tree viewer. **/ public void collapseAllTreeItems() { treeViewer.collapseAll(); } /* (non-Javadoc) * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento) */ @Override public void saveState(IMemento memento) { SaveStateManager.getInstance().saveState(); super.saveState(memento); } public void handleNodeAdditionEvent(final DatabaseModelEvent databaseModelEvent) { Display display = Display.getDefault(); if (!display.isDisposed()) { display.syncExec(new Runnable() { public void run() { ITreeNode treeNodeItem = databaseModelEvent.getTreeNodeItem(); ITreeNode parentNode = treeNodeItem.getParent(); if (getTreeViewer().getExpandedState(parentNode)) { getTreeViewer().add(parentNode, treeNodeItem); } else { List<ITreeNode> children = parentNode.getChildren(); if (children.size() > 0) { getTreeViewer().setChildCount(parentNode, children.size()); } } } }); } } public void handleNodeRemovalEvent(final DatabaseModelEvent databaseModelEvent) { Display display = Display.getDefault(); if (!display.isDisposed()) { display.asyncExec(new Runnable() { public void run() { if (!treeViewer.getTree().isDisposed()) { getTreeViewer().remove(databaseModelEvent.getTreeNodeItem()); ITreeNode parentNode = databaseModelEvent.getTreeNodeItem().getParent(); getTreeViewer().setChildCount(parentNode, parentNode.getChildren().size()); } } }); } } public void handleNodeUpdateEvent(final DatabaseModelEvent databaseModelEvent) { Display display = Display.getDefault(); if (!display.isDisposed()) { display.asyncExec(new Runnable() { public void run() { ITreeNode treeNode = databaseModelEvent.getTreeNodeItem(); getTreeViewer().update(treeNode, null); //re-evaluate all properties to properly update commands status //it's required since nodes may change theirs state/status without firing SelectionChanged events for (String property : DbCoreActivator.getPluginProperties()) { IEvaluationService service = (IEvaluationService) PlatformUI.getWorkbench().getService( IEvaluationService.class); service.requestEvaluation(property); //properties are defined in plugin.xml (propertyTesters) } } }); } } public void handleNodeClearEvent(final DatabaseModelEvent databaseModelEvent) { Display display = Display.getDefault(); if (!display.isDisposed()) { display.asyncExec(new Runnable() { public void run() { ITreeNode parentNode = databaseModelEvent.getTreeNodeItem(); if (parentNode.isLoading()) { treeViewer.remove(parentNode, parentNode.getChildren().toArray()); //Removing all children before calling refresh seems to resolve the AccessViolation - see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=131209 treeViewer.refresh(parentNode); } else { treeViewer.collapseToLevel(parentNode, TreeViewer.ALL_LEVELS); } } }); } } public void handleNodeRefreshEvent(final DatabaseModelEvent databaseModelEvent) { Display display = Display.getDefault(); if (!display.isDisposed()) { display.asyncExec(new Runnable() { public void run() { ITreeNode node = databaseModelEvent.getTreeNodeItem(); getTreeViewer().refresh(node); } }); } } /** * Closing the view */ @Override public void dispose() { super.dispose(); //remove listener for model changes DatabaseModelEventManager.getInstance().removeListeners(this); } /* (non-Javadoc) * @see com.motorolamobility.studio.android.db.core.event.IDatabaseModelListener#handleNodeExpandEvent(com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent) */ public void handleNodeExpandEvent(final DatabaseModelEvent databaseModelEvent) { Display display = Display.getDefault(); if (!display.isDisposed()) { display.asyncExec(new Runnable() { public void run() { treeViewer.expandToLevel(databaseModelEvent.getTreeNodeItem(), 1); } }); } } /* (non-Javadoc) * @see com.motorolamobility.studio.android.db.core.event.IDatabaseModelListener#handleNodeSelectEvent(com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent) */ public void handleNodeSelectEvent(final DatabaseModelEvent databaseModelEvent) { Display display = Display.getDefault(); if (!display.isDisposed()) { display.asyncExec(new Runnable() { public void run() { treeViewer.setSelection( new StructuredSelection(databaseModelEvent.getTreeNodeItem()), true); } }); } } }