/*
* 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;
}
}
}
}