/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * 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 org.jkiss.dbeaver.ui.navigator; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.resources.IResource; import org.eclipse.jface.action.*; import org.eclipse.jface.viewers.*; import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.*; import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.ui.menus.UIElement; import org.eclipse.ui.part.IPageSite; import org.eclipse.ui.services.IServiceLocator; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.DBeaverPreferences; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.CoreCommands; import org.jkiss.dbeaver.core.DBeaverCore; import org.jkiss.dbeaver.model.*; import org.jkiss.dbeaver.model.navigator.*; import org.jkiss.dbeaver.model.navigator.meta.DBXTreeNodeHandler; import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; import org.jkiss.dbeaver.model.struct.DBSObject; import org.jkiss.dbeaver.model.struct.DBSObjectFilter; import org.jkiss.dbeaver.model.struct.DBSObjectSelector; import org.jkiss.dbeaver.model.struct.DBSWrapper; import org.jkiss.dbeaver.ui.ActionUtils; import org.jkiss.dbeaver.ui.IActionConstants; import org.jkiss.dbeaver.ui.UIUtils; import org.jkiss.dbeaver.ui.actions.navigator.NavigatorActionSetActiveObject; import org.jkiss.dbeaver.ui.actions.navigator.NavigatorHandlerObjectOpen; import org.jkiss.dbeaver.ui.actions.navigator.NavigatorHandlerRefresh; import org.jkiss.dbeaver.ui.controls.ViewerColumnController; import org.jkiss.dbeaver.ui.dnd.DatabaseObjectTransfer; import org.jkiss.dbeaver.ui.dnd.TreeNodeTransfer; import org.jkiss.dbeaver.ui.navigator.database.DatabaseNavigatorView; import org.jkiss.dbeaver.ui.navigator.database.NavigatorViewBase; import org.jkiss.dbeaver.ui.navigator.project.ProjectNavigatorView; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; import java.util.*; /** * Navigator utils */ public class NavigatorUtils { public static final String MB_NAVIGATOR_ADDITIONS = "navigator_additions"; private static final Log log = Log.getLog(NavigatorUtils.class); public static DBNNode getSelectedNode(ISelectionProvider selectionProvider) { if (selectionProvider == null) { return null; } return getSelectedNode(selectionProvider.getSelection()); } public static DBNNode getSelectedNode(ISelection selection) { if (selection.isEmpty()) { return null; } if (selection instanceof IStructuredSelection) { Object selectedObject = ((IStructuredSelection)selection).getFirstElement(); if (selectedObject instanceof DBNNode) { return (DBNNode) selectedObject; } } return null; } /** * Find selected node for specified UI element * @param element ui element * @return ndoe or null */ public static DBNNode getSelectedNode(UIElement element) { ISelectionProvider selectionProvider = UIUtils.getSelectionProvider(element.getServiceLocator()); if (selectionProvider != null) { return NavigatorUtils.getSelectedNode(selectionProvider); } else { return null; } } public static DBSObject getSelectedObject(IStructuredSelection selection) { if (selection.isEmpty()) { return null; } return DBUtils.getFromObject(selection.getFirstElement()); } public static List<DBSObject> getSelectedObjects(ISelection selection) { if (selection.isEmpty()) { return Collections.emptyList(); } List<DBSObject> result = new ArrayList<>(); if (selection instanceof IStructuredSelection) { for (Iterator iter = ((IStructuredSelection)selection).iterator(); iter.hasNext(); ) { DBSObject selectedObject = DBUtils.getFromObject(iter.next()); if (selectedObject != null) { result.add(selectedObject); } } } return result; } public static void addContextMenu(final IWorkbenchSite workbenchSite, final Viewer viewer) { addContextMenu(workbenchSite, viewer, null); } public static void addContextMenu(final IWorkbenchSite workbenchSite, final Viewer viewer, final IMenuListener menuListener) { MenuManager menuMgr = createContextMenu(workbenchSite, viewer, menuListener); if (workbenchSite instanceof IWorkbenchPartSite) { ((IWorkbenchPartSite)workbenchSite).registerContextMenu(menuMgr, viewer); } else if (workbenchSite instanceof IPageSite) { ((IPageSite)workbenchSite).registerContextMenu("navigatorMenu", menuMgr, viewer); } } public static MenuManager createContextMenu(final IWorkbenchSite workbenchSite, final Viewer viewer, final IMenuListener menuListener) { final Control control = viewer.getControl(); final MenuManager menuMgr = new MenuManager(); Menu menu = menuMgr.createContextMenu(control); menu.addMenuListener(new MenuListener() { @Override public void menuHidden(MenuEvent e) { } @Override public void menuShown(MenuEvent e) { Menu m = (Menu)e.widget; DBNNode node = getSelectedNode(viewer.getSelection()); if (node != null && !node.isLocked() && node.allowsOpen()) { String commandID = NavigatorUtils.getNodeActionCommand(DBXTreeNodeHandler.Action.open, node, CoreCommands.CMD_OBJECT_OPEN); // Dirty hack // Get contribution item from menu item and check it's ID for (MenuItem item : m.getItems()) { Object itemData = item.getData(); if (itemData instanceof IContributionItem) { String contribId = ((IContributionItem)itemData).getId(); if (contribId != null && contribId.equals(commandID)) { m.setDefaultItem(item); } } } } } }); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(final IMenuManager manager) { ViewerColumnController columnController = ViewerColumnController.getFromControl(control); if (columnController != null && columnController.isClickOnHeader()) { columnController.fillConfigMenu(manager); manager.add(new Separator()); return; } manager.add(new GroupMarker(MB_NAVIGATOR_ADDITIONS)); final IStructuredSelection selection = (IStructuredSelection)viewer.getSelection(); final DBNNode selectedNode = getSelectedNode(viewer); if (selectedNode != null && !selectedNode.isLocked() && workbenchSite != null) { // Add "Set active object" menu if (selectedNode.isPersisted() && selectedNode instanceof DBNDatabaseNode && !(selectedNode instanceof DBNDatabaseFolder) && ((DBNDatabaseNode)selectedNode).getObject() != null) { final DBSObjectSelector activeContainer = DBUtils.getParentAdapter( DBSObjectSelector.class, ((DBNDatabaseNode) selectedNode).getObject()); if (activeContainer != null && activeContainer.supportsDefaultChange()) { DBSObject activeChild; activeChild = activeContainer.getDefaultObject(); if (activeChild != ((DBNDatabaseNode)selectedNode).getObject()) { DBNDatabaseNode databaseNode = (DBNDatabaseNode)selectedNode; if (databaseNode.getObject() != null && (activeChild == null || activeChild.getClass() == databaseNode.getObject().getClass())) { String text = "Set Active ";// + databaseNode.getNodeType(); // Fill context menu IAction action = ActionUtils.makeAction(new NavigatorActionSetActiveObject(), workbenchSite, selection, text, null, null); manager.add(action); } } } } manager.add(new Separator()); manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); manager.add(new GroupMarker(IActionConstants.MB_ADDITIONS_END)); // Add properties button if (PreferencesUtil.hasPropertiesContributors(selection.getFirstElement())) { manager.add(ActionUtils.makeCommandContribution(workbenchSite, IWorkbenchCommandConstants.FILE_PROPERTIES)); } if (selectedNode.isPersisted()) { // Add refresh button manager.add(ActionUtils.makeCommandContribution(workbenchSite, IWorkbenchCommandConstants.FILE_REFRESH)); } } manager.add(new GroupMarker(CoreCommands.GROUP_TOOLS)); if (menuListener != null) { menuListener.menuAboutToShow(manager); } } }); menuMgr.setRemoveAllWhenShown(true); control.setMenu(menu); control.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { menuMgr.dispose(); } }); return menuMgr; } public static void executeNodeAction(DBXTreeNodeHandler.Action action, Object node, IServiceLocator serviceLocator) { String defCommandId = null; if (action == DBXTreeNodeHandler.Action.open) { defCommandId = CoreCommands.CMD_OBJECT_OPEN; } String actionCommand = getNodeActionCommand(action, node, defCommandId); if (actionCommand != null) { ActionUtils.runCommand(actionCommand, new StructuredSelection(node), serviceLocator); } else { // do nothing // TODO: implement some other behavior } } public static String getNodeActionCommand(DBXTreeNodeHandler.Action action, Object node, String defCommand) { if (node instanceof DBNDatabaseNode) { DBXTreeNodeHandler handler = ((DBNDatabaseNode) node).getMeta().getHandler(action); if (handler != null && handler.getPerform() == DBXTreeNodeHandler.Perform.command && !CommonUtils.isEmpty(handler.getCommand())) { return handler.getCommand(); } } return defCommand; } public static void addDragAndDropSupport(final Viewer viewer) { Transfer[] types = new Transfer[] {TextTransfer.getInstance(), TreeNodeTransfer.getInstance(), DatabaseObjectTransfer.getInstance()}; int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK; final DragSource source = new DragSource(viewer.getControl(), operations); source.setTransfer(types); source.addDragListener (new DragSourceListener() { private IStructuredSelection selection; @Override public void dragStart(DragSourceEvent event) { selection = (IStructuredSelection) viewer.getSelection(); } @Override public void dragSetData (DragSourceEvent event) { if (!selection.isEmpty()) { List<DBNNode> nodes = new ArrayList<>(); List<DBPNamedObject> objects = new ArrayList<>(); String lineSeparator = CommonUtils.getLineSeparator(); StringBuilder buf = new StringBuilder(); for (Iterator<?> i = selection.iterator(); i.hasNext(); ) { Object nextSelected = i.next(); if (!(nextSelected instanceof DBNNode)) { continue; } nodes.add((DBNNode)nextSelected); String nodeName; if (nextSelected instanceof DBNDatabaseNode && !(nextSelected instanceof DBNDataSource)) { DBSObject object = ((DBNDatabaseNode)nextSelected).getObject(); if (object == null) { continue; } nodeName = DBUtils.getObjectFullName(object, DBPEvaluationContext.UI); objects.add(object); } else { nodeName = ((DBNNode)nextSelected).getNodeName(); } if (buf.length() > 0) { buf.append(lineSeparator); } buf.append(nodeName); } if (TreeNodeTransfer.getInstance().isSupportedType(event.dataType)) { event.data = nodes; } else if (DatabaseObjectTransfer.getInstance().isSupportedType(event.dataType)) { event.data = objects; } else if (TextTransfer.getInstance().isSupportedType(event.dataType)) { event.data = buf.toString(); } } else { if (TreeNodeTransfer.getInstance().isSupportedType(event.dataType)) { event.data = Collections.emptyList(); } else if (DatabaseObjectTransfer.getInstance().isSupportedType(event.dataType)) { event.data = Collections.emptyList(); } else if (TextTransfer.getInstance().isSupportedType(event.dataType)) { event.data = ""; } } } @Override public void dragFinished(DragSourceEvent event) { } }); DropTarget dropTarget = new DropTarget(viewer.getControl(), DND.DROP_MOVE); dropTarget.setTransfer(new Transfer[] {TreeNodeTransfer.getInstance()}); dropTarget.addDropListener(new DropTargetListener() { @Override public void dragEnter(DropTargetEvent event) { handleDragEvent(event); } @Override public void dragLeave(DropTargetEvent event) { handleDragEvent(event); } @Override public void dragOperationChanged(DropTargetEvent event) { handleDragEvent(event); } @Override public void dragOver(DropTargetEvent event) { handleDragEvent(event); } @Override public void drop(DropTargetEvent event) { handleDragEvent(event); if (event.detail == DND.DROP_MOVE) { moveNodes(event); } } @Override public void dropAccept(DropTargetEvent event) { handleDragEvent(event); } private void handleDragEvent(DropTargetEvent event) { event.detail = isDropSupported(event) ? DND.DROP_MOVE : DND.DROP_NONE; event.feedback = DND.FEEDBACK_SELECT; } private boolean isDropSupported(DropTargetEvent event) { if (TreeNodeTransfer.getInstance().isSupportedType(event.currentDataType) && event.item instanceof TreeItem) { TreeItem treeItem = (TreeItem)event.item; Object curObject = treeItem.getData(); if (curObject instanceof DBNNode) { @SuppressWarnings("unchecked") Collection<DBNNode> nodesToDrop = (Collection<DBNNode>) event.data; if (!CommonUtils.isEmpty(nodesToDrop)) { for (DBNNode node : nodesToDrop) { if (!((DBNNode)curObject).supportsDrop(node)) { return false; } } return true; } else { return ((DBNNode)curObject).supportsDrop(null); } } } return false; } private void moveNodes(DropTargetEvent event) { if (TreeNodeTransfer.getInstance().isSupportedType(event.currentDataType) && event.item instanceof TreeItem) { TreeItem treeItem = (TreeItem)event.item; Object curObject = treeItem.getData(); if (curObject instanceof DBNNode) { Collection<DBNNode> nodesToDrop = TreeNodeTransfer.getInstance().getObject(); try { ((DBNNode)curObject).dropNodes(nodesToDrop); } catch (DBException e) { UIUtils.showErrorDialog(viewer.getControl().getShell(), "Drop error", "Can't drop node", e); } } } } }); } public static boolean isDefaultElement(Object element) { if (element instanceof DBSWrapper) { DBSObject object = ((DBSWrapper) element).getObject(); DBSObjectSelector activeContainer = DBUtils.getParentAdapter( DBSObjectSelector.class, object); if (activeContainer != null) { return activeContainer.getDefaultObject() == object; } } else if (element instanceof DBNProject) { if (((DBNProject)element).getProject() == DBeaverCore.getInstance().getProjectRegistry().getActiveProject()) { return true; } } return false; } public static NavigatorViewBase getActiveNavigatorView(ExecutionEvent event) { IWorkbenchPart activePart = HandlerUtil.getActivePart(event); if (activePart instanceof NavigatorViewBase) { return (NavigatorViewBase) activePart; } final IWorkbenchPage activePage = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); activePart = activePage.findView(DatabaseNavigatorView.VIEW_ID); if (activePart instanceof NavigatorViewBase && activePage.isPartVisible(activePart)) { return (NavigatorViewBase) activePart; } activePart = activePage.findView(ProjectNavigatorView.VIEW_ID); if (activePart instanceof NavigatorViewBase && activePage.isPartVisible(activePart)) { return (NavigatorViewBase) activePart; } return null; } public static void filterSelection(final ISelection selection, boolean exclude) { if (selection instanceof IStructuredSelection) { Map<DBNDatabaseFolder, DBSObjectFilter> folders = new HashMap<>(); for (Object item : ((IStructuredSelection)selection).toArray()) { if (item instanceof DBNNode) { final DBNNode node = (DBNNode) item; DBNDatabaseFolder folder = (DBNDatabaseFolder) node.getParentNode(); DBSObjectFilter nodeFilter = folders.get(folder); if (nodeFilter == null) { nodeFilter = folder.getNodeFilter(folder.getItemsMeta(), true); if (nodeFilter == null) { nodeFilter = new DBSObjectFilter(); } folders.put(folder, nodeFilter); } if (exclude) { nodeFilter.addExclude(node.getNodeName()); } else { nodeFilter.addInclude(node.getNodeName()); } nodeFilter.setEnabled(true); } } // Save folders for (Map.Entry<DBNDatabaseFolder, DBSObjectFilter> entry : folders.entrySet()) { entry.getKey().setNodeFilter(entry.getKey().getItemsMeta(), entry.getValue()); } // Refresh all folders NavigatorHandlerRefresh.refreshNavigator(folders.keySet()); } } public static boolean syncEditorWithNavigator(INavigatorModelView navigatorView, IEditorPart activeEditor) { if (!(activeEditor instanceof IDataSourceContainerProviderEx)) { return false; } IDataSourceContainerProviderEx dsProvider = (IDataSourceContainerProviderEx) activeEditor; Viewer navigatorViewer = navigatorView.getNavigatorViewer(); if (navigatorViewer == null) { return false; } DBNNode selectedNode = getSelectedNode(navigatorViewer.getSelection()); if (!(selectedNode instanceof DBNDatabaseNode)) { return false; } final DBPDataSourceContainer ds = ((DBNDatabaseNode) selectedNode).getDataSourceContainer(); if (ds == null) { return false; } if (dsProvider.getDataSourceContainer() != ds) { dsProvider.setDataSourceContainer(ds); } // Now check if we can change default object DBSObject dbObject = ((DBNDatabaseNode) selectedNode).getObject(); if (dbObject != null && dbObject.getParentObject() != null) { DBPObject parentObject = DBUtils.getPublicObject(dbObject.getParentObject()); if (parentObject instanceof DBSObjectSelector) { DBSObjectSelector selector = (DBSObjectSelector) parentObject; DBSObject curDefaultObject = selector.getDefaultObject(); if (curDefaultObject != dbObject) { if (curDefaultObject != null && curDefaultObject.getClass() != dbObject.getClass()) { // Wrong object type return true; } try { selector.setDefaultObject(new VoidProgressMonitor(), dbObject); } catch (Throwable e) { log.debug(e); } } } } return true; } public static DBNDataSource getDataSourceNode(DBNNode node) { for (DBNNode pn = node; pn != null; pn = pn.getParentNode()) { if (pn instanceof DBNDataSource) { return (DBNDataSource) pn; } } return null; } public static DBNNode[] getNodeChildrenFiltered(DBRProgressMonitor monitor, DBNNode node, boolean forTree) throws DBException { DBNNode[] children = node.getChildren(monitor); if (children != null && children.length > 0) { children = filterNavigableChildren(children, forTree); } return children; } public static DBNNode[] filterNavigableChildren(DBNNode[] children, boolean forTree) { if (ArrayUtils.isEmpty(children)) { return children; } List<DBNNode> filtered = null; if (forTree) { for (int i = 0; i < children.length; i++) { DBNNode node = children[i]; if (node instanceof DBNDatabaseNode && !((DBNDatabaseNode) node).getMeta().isNavigable()) { if (filtered == null) { filtered = new ArrayList<>(children.length); for (int k = 0; k < i; k++) { filtered.add(children[k]); } } } else if (filtered != null) { filtered.add(node); } } } DBNNode[] result = filtered == null ? children : filtered.toArray(new DBNNode[filtered.size()]); sortNodes(result); return result; } private static void sortNodes(DBNNode[] children) { final DBPPreferenceStore prefStore = DBeaverCore.getGlobalPreferenceStore(); // Sort children is we have this feature on in preferences // and if children are not folders if (children.length > 0 && prefStore.getBoolean(DBeaverPreferences.NAVIGATOR_SORT_ALPHABETICALLY)) { if (!(children[0] instanceof DBNContainer)) { Arrays.sort(children, NodeNameComparator.INSTANCE); } } if (children.length > 0 && prefStore.getBoolean(DBeaverPreferences.NAVIGATOR_SORT_FOLDERS_FIRST)) { Arrays.sort(children, NodeFolderComparator.INSTANCE); } } public static void openNavigatorNode(Object node, IWorkbenchWindow window) { if (node instanceof DBNResource) { NavigatorHandlerObjectOpen.openResource( ((DBNResource) node).getResource(), window); } else if (node instanceof DBNNode && ((DBNNode) node).allowsOpen()) { NavigatorHandlerObjectOpen.openEntityEditor( (DBNNode) node, null, window); } } public static void refreshNavigatorResource(@NotNull IResource resource, Object source) { final DBNProject projectNode = DBeaverCore.getInstance().getNavigatorModel().getRoot().getProject(resource.getProject()); if (projectNode != null) { final DBNResource fileNode = projectNode.findResource(resource); if (fileNode != null) { fileNode.refreshResourceState(source); } } } private static class NodeNameComparator implements Comparator<DBNNode> { static NodeNameComparator INSTANCE = new NodeNameComparator(); @Override public int compare(DBNNode node1, DBNNode node2) { return node1.getNodeName().compareToIgnoreCase(node2.getNodeName()); } } private static class NodeFolderComparator implements Comparator<DBNNode> { static NodeFolderComparator INSTANCE = new NodeFolderComparator(); @Override public int compare(DBNNode node1, DBNNode node2) { int first = node1 instanceof DBNContainer ? -1 : 1; int second = node2 instanceof DBNContainer ? -1 : 1; return first - second; } } }