/* * Copyright (C) 2006 Davy Vanherbergen dvanherbergen@users.sourceforge.net * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package net.sourceforge.sqlexplorer.plugin.views; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sourceforge.sqlexplorer.IConstants; import net.sourceforge.sqlexplorer.Messages; import net.sourceforge.sqlexplorer.SQLCannotConnectException; import net.sourceforge.sqlexplorer.dbproduct.MetaDataSession; import net.sourceforge.sqlexplorer.dbproduct.Session; import net.sourceforge.sqlexplorer.dbproduct.User; import net.sourceforge.sqlexplorer.dbstructure.DBTreeActionGroup; import net.sourceforge.sqlexplorer.dbstructure.DBTreeContentProvider; import net.sourceforge.sqlexplorer.dbstructure.DBTreeLabelProvider; import net.sourceforge.sqlexplorer.dbstructure.actions.FilterStructureAction; import net.sourceforge.sqlexplorer.dbstructure.nodes.ColumnNode; import net.sourceforge.sqlexplorer.dbstructure.nodes.DatabaseNode; import net.sourceforge.sqlexplorer.dbstructure.nodes.INode; import net.sourceforge.sqlexplorer.dbstructure.nodes.TableNode; import net.sourceforge.sqlexplorer.plugin.SQLExplorerPlugin; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeViewerListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeExpansionEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabFolder2Adapter; import org.eclipse.swt.custom.CTabFolderEvent; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; /** * Database Structure View. Shows the database outline. Selections made in this view are shown in the * DatabaseDetailView. * * @author Davy Vanherbergen */ public class DatabaseStructureView extends ViewPart { /* * Contains state data for each tab */ private static class TabData { private TreeViewer treeViewer; private MetaDataSession session; } private Map<MetaDataSession, ISelection> sessionSelectionMap = new HashMap<MetaDataSession, ISelection>(); private FilterStructureAction _filterAction; private Composite _parent; /** We use one tab for every session */ private CTabFolder _tabFolder; private List<MetaDataSession> _allSessions = new ArrayList<MetaDataSession>(); public DatabaseStructureView() { super(); SQLExplorerPlugin.getDefault().setDatabaseStructureView(this); } /** * Adds a new user * * @param user */ public void addUser(final User user) throws SQLCannotConnectException { // Make sure we list each user only once for (Session session : _allSessions) { if (session.getUser() == user) { return; } } MetaDataSession session = user.getMetaDataSession(); if (session != null) { addSession(user.getMetaDataSession()); } } /** * Add a new session to the database structure view. This will create a new tab for the session. * * @param session */ private void addSession(final MetaDataSession session) throws SQLCannotConnectException { if (_allSessions.contains(session)) { return; } try { session.getMetaData(); session.setAutoCommit(true); } catch (SQLCannotConnectException e) { SQLExplorerPlugin.error(e); throw e; } catch (SQLException e) { SQLExplorerPlugin.error(e); MessageDialog.openError(getSite().getShell(), "Cannot connect", e.getMessage()); } DatabaseNode rootNode = session.getRoot(); if (rootNode == null) { return; } _allSessions.add(session); if (_filterAction != null) { _filterAction.setEnabled(true); } if (_tabFolder == null || _tabFolder.isDisposed()) { clearParent(); // create tab folder for different sessions _tabFolder = new CTabFolder(_parent, SWT.TOP | SWT.CLOSE); // add listener to keep both views on the same active tab _tabFolder.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // set the selected node in the detail view. DatabaseDetailView detailView = (DatabaseDetailView) getSite().getPage().findView( SqlexplorerViewConstants.SQLEXPLORER_DBDETAIL); synchronizeDetailView(detailView); } }); // Set up a gradient background for the selected tab Display display = getSite().getShell().getDisplay(); _tabFolder.setSelectionBackground(new Color[] { display.getSystemColor(SWT.COLOR_WHITE), new Color(null, 211, 225, 250), new Color(null, 175, 201, 246), IConstants.TAB_BORDER_COLOR }, new int[] { 25, 50, 75 }, true); // Add a listener to handle the close button on each tab _tabFolder.addCTabFolder2Listener(new CTabFolder2Adapter() { @Override public void close(CTabFolderEvent event) { CTabItem tabItem = (CTabItem) event.item; TabData tabData = (TabData) tabItem.getData(); _allSessions.remove(tabData.session); event.doit = true; } }); _parent.layout(); _parent.redraw(); } // create tab final CTabItem tabItem = new CTabItem(_tabFolder, SWT.NULL); TabData tabData = new TabData(); tabItem.setData(tabData); tabData.session = session; // set tab text String labelText = session.getUser().getDescription(); tabItem.setText(labelText); // create composite for our outline Composite composite = new Composite(_tabFolder, SWT.NULL); composite.setLayout(new FillLayout()); tabItem.setControl(composite); // create outline final TreeViewer treeViewer = new TreeViewer(composite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER); tabData.treeViewer = treeViewer; // add drag support // TODO improve drag support options Transfer[] transfers = new Transfer[] { TableNodeTransfer.getInstance() }; treeViewer.addDragSupport(DND.DROP_COPY, transfers, new DragSourceListener() { public void dragFinished(DragSourceEvent event) { System.out.println("$drag finished"); TableNodeTransfer.getInstance().setSelection(null); } public void dragSetData(DragSourceEvent event) { Object sel = ((IStructuredSelection) treeViewer.getSelection()).getFirstElement(); event.data = sel; } public void dragStart(DragSourceEvent event) { event.doit = !treeViewer.getSelection().isEmpty(); if (event.doit) { Object sel = ((IStructuredSelection) treeViewer.getSelection()).getFirstElement(); if (!(sel instanceof TableNode)) { event.doit = false; } else { TableNode tn = (TableNode) sel; TableNodeTransfer.getInstance().setSelection(tn); if (!tn.isTable()) { event.doit = false; } } } } }); // use hash lookup to improve performance treeViewer.setUseHashlookup(true); // add content and label provider treeViewer.setContentProvider(new DBTreeContentProvider()); treeViewer.setLabelProvider(new DBTreeLabelProvider()); // set input session treeViewer.setInput(rootNode); // add selection change listener, so we can update detail view as // required. treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent ev) { // set the selected node in the detail view. DatabaseDetailView detailView = (DatabaseDetailView) getSite().getPage().findView( SqlexplorerViewConstants.SQLEXPLORER_DBDETAIL); synchronizeDetailView(detailView); } }); // bring detail to front on doubleclick of node treeViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { try { // find view DatabaseDetailView detailView = (DatabaseDetailView) getSite().getPage().findView( SqlexplorerViewConstants.SQLEXPLORER_DBDETAIL); if (detailView == null) { getSite().getPage().showView(SqlexplorerViewConstants.SQLEXPLORER_DBDETAIL); } getSite().getPage().bringToTop(detailView); synchronizeDetailView(detailView); } catch (Exception e) { // fail silent } } }); // add expand/collapse listener treeViewer.addTreeListener(new ITreeViewerListener() { public void treeCollapsed(TreeExpansionEvent event) { // refresh the node to change image INode node = (INode) event.getElement(); node.setExpanded(false); TreeViewer viewer = (TreeViewer) event.getSource(); viewer.update(node, null); } public void treeExpanded(TreeExpansionEvent event) { // refresh the node to change image INode node = (INode) event.getElement(); node.setExpanded(true); TreeViewer viewer = (TreeViewer) event.getSource(); viewer.update(node, null); } }); // set new tab as the active one _tabFolder.setSelection(_tabFolder.getItemCount() - 1); // update detail view DatabaseDetailView detailView = (DatabaseDetailView) getSite().getPage().findView( SqlexplorerViewConstants.SQLEXPLORER_DBDETAIL); if (detailView != null) { // synchronze detail view with new session synchronizeDetailView(detailView); // bring detail to top of the view stack getSite().getPage().bringToTop(detailView); } // refresh view composite.layout(); _tabFolder.layout(); _tabFolder.redraw(); // bring this view to top of the view stack, above detail if needed.. getSite().getPage().bringToTop(this); // add context menu final DBTreeActionGroup actionGroup = new DBTreeActionGroup(treeViewer); MenuManager menuManager = new MenuManager("DBTreeContextMenu"); menuManager.setRemoveAllWhenShown(true); Menu contextMenu = menuManager.createContextMenu(treeViewer.getTree()); treeViewer.getTree().setMenu(contextMenu); menuManager.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { actionGroup.fillContextMenu(manager); } }); // if (sessionSelectionMap.containsKey(tabData.session)) { // tabData.treeViewer.setSelection(sessionSelectionMap.get(tabData.session)); // sessionSelectionMap.remove(tabData.session); // _allSessions.remove(tabData.session); // } } /** * DOC bZhou Comment method "setSessionSelectionNode". * * @param metadataSession * @param selection */ public void setSessionSelectionNode(MetaDataSession session, ISelection selection) { // MOD qiongli bug 13093,delete the condition :'if (_tabFolder == null)' try { addSession(session); } catch (SQLCannotConnectException e) { e.printStackTrace(); } CTabItem item = _tabFolder.getSelection(); if (item != null) { TabData tabData = (TabData) item.getData(); if (tabData != null) { // MOD qiongli bug 13093 ,2010-7-2 if (tabData.session.getUser() != session.getUser()) { CTabItem items[] = _tabFolder.getItems(); for (CTabItem it : items) { tabData = (TabData) it.getData(); if (tabData.session.getUser() == session.getUser()) { _tabFolder.setSelection(it); break; } } } // ~ tabData.treeViewer.setSelection(selection); } } } /** * Remove all items from parent */ private void clearParent() { Control[] children = _parent.getChildren(); if (children != null) { for (Control element : children) { element.dispose(); } } } /** * Initializes the view and creates the root tabfolder that holds all the sessions. * * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl(Composite parent) { PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, SQLExplorerPlugin.PLUGIN_ID + ".DatabaseStructureView"); _parent = parent; // load all open sessions /* * for (Alias alias : SQLExplorerPlugin.getDefault().getAliasManager().getAliases()) for (User user: * alias.getUsers()) { MetaDataSession session = user.getMetaDataSession(); if (session != null) * addSession(session); } */ // set default message if (_allSessions.isEmpty()) { setDefaultMessage(); } _filterAction = new FilterStructureAction(); _filterAction.setEnabled(!_allSessions.isEmpty()); IToolBarManager toolBarMgr = getViewSite().getActionBars().getToolBarManager(); toolBarMgr.add(_filterAction); } /** * Cleanup and reset detail view. * * @see org.eclipse.ui.IWorkbenchPart#dispose() */ @Override public void dispose() { // refresh detail view DatabaseDetailView detailView = (DatabaseDetailView) getSite().getPage().findView( SqlexplorerViewConstants.SQLEXPLORER_DBDETAIL); if (detailView != null) { detailView.setSelectedNode(null); } // clear session _allSessions.clear(); if (_tabFolder != null && !_tabFolder.isDisposed()) { _tabFolder.dispose(); } SQLExplorerPlugin.getDefault().setDatabaseStructureView(null); } public MetaDataSession getSession() { if (_tabFolder == null || _tabFolder.getSelectionIndex() < 0) { return null; } CTabItem item = _tabFolder.getItem(_tabFolder.getSelectionIndex()); TabData tabData = (TabData) item.getData(); return tabData.session; } /** * Loop through all tabs and refresh trees for sessions with session */ public void refreshSessionTrees(Session session) { if (_tabFolder == null || _tabFolder.getSelectionIndex() < 0) { return; } CTabItem[] items = _tabFolder.getItems(); if (items != null) { for (CTabItem item : items) { TabData tabData = (TabData) item.getData(); if (tabData.session.getUser() == session.getUser()) { tabData.session.getRoot().refresh(); tabData.treeViewer.refresh(); } } } } /** * Set a default message, this method is called when no sessions are available for viewing. */ private void setDefaultMessage() { clearParent(); // add message String message = Messages.getString("DatabaseStructureView.NoSession"); Label label = new Label(_parent, SWT.FILL); label.setText(message); label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); _parent.layout(); _parent.redraw(); } /** * Set focus on our database structure view.. * * @see org.eclipse.ui.IWorkbenchPart#setFocus() */ @Override public void setFocus() { // we don't need to do anything here.. } /** * Update the detail view with the selection in the active treeviewer. */ public void synchronizeDetailView(final DatabaseDetailView detailView) { BusyIndicator.showWhile(Display.getCurrent(), new Runnable() { public void run() { if (detailView == null) { return; } // MOD qiongli 2010-10-27,bug 16394.add the condition of '_tabFolder.isDisposed()' if (_tabFolder == null || _tabFolder.getItemCount() == 0 || _tabFolder.getSelectionIndex() < 0 || _tabFolder.isDisposed()) { return; } TabData tabData = (TabData) _tabFolder.getItem(_tabFolder.getSelectionIndex()).getData(); INode selectedNode = null; if (tabData.treeViewer != null) { // find our target node.. IStructuredSelection selection = (IStructuredSelection) tabData.treeViewer.getSelection(); // check if we have a valid selection if (selection != null && (selection.getFirstElement() instanceof INode)) { selectedNode = (INode) selection.getFirstElement(); // if the selected node is a column node, we want to // show it's parent instead // in the detail view. if (selectedNode instanceof ColumnNode) { selectedNode = selectedNode.getParent(); } } } detailView.setSelectedNode(selectedNode); } }); } public boolean isConnectedToUser(User user) { for (Session session : _allSessions) { if (session.getUser().compareTo(user) == 0) { return true; } } return false; } /** * * DOC qiongli Comment method "closeCurrentCabItem".bug 16394. */ public void closeCurrentCabItem(String conName) { if (_tabFolder == null || _tabFolder.isDisposed() || conName == null) { return; } CTabItem items[] = _tabFolder.getItems(); for (CTabItem item : items) { if (!item.isDisposed() && item.getText().startsWith(conName + "/")) { TabData tabData = (TabData) item.getData(); _allSessions.remove(tabData.session); _filterAction.setEnabled(!_allSessions.isEmpty()); _tabFolder.layout(); _tabFolder.redraw(); item.dispose(); this.dispose(); break; } } } }