/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.ui.editors;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.ViewForm;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.PageBook;
import org.teiid.designer.ui.PluginConstants;
import org.teiid.designer.ui.UiConstants;
import org.teiid.designer.ui.UiPlugin;
import org.teiid.designer.ui.common.actions.ActionService;
/**
* ModelObjectEditorPanel is a container for ModelObjectEditor controls to be placed beneath the ModelEditor.
*
* @since 8.0
*/
public class ModelObjectEditorPanel implements IAdaptable, FocusListener, IPropertyListener {
static final String RESTORE = UiConstants.Util.getString("ModelObjectEditorPanel.restore"); //$NON-NLS-1$
static final String MAXIMIZE = UiConstants.Util.getString("ModelObjectEditorPanel.maximize"); //$NON-NLS-1$
static final String CLOSE = UiConstants.Util.getString("ModelObjectEditorPanel.close"); //$NON-NLS-1$
private static final char DIRTY_CHAR = '*';
private MultiPageModelEditor modelEditor;
CLabel titleLabel;
ToolBar toolBar;
private ToolBarManager toolBarMgr;
private MenuManager menuMgr;
boolean isZoomed = false;
private Action closeAction;
private ViewForm control;
private ArrayList editorList = new ArrayList();
// private ToolBarManager toolBarManager;
private PageBook pageBook;
ModelObjectEditorPage activeEditor;
private List focusListeners = new ArrayList();
public ModelObjectEditorPanel( MultiPageModelEditor modelEditor,
Composite parent,
int style ) {
this.modelEditor = modelEditor;
createControl(parent);
modelEditor.addPropertyListener(this);
}
/**
* creates the content of this panel
*/
public void createControl( Composite parent ) {
if (control != null && !control.isDisposed()) return;
// Create the ViewForm, which is the outer control of this panel
// to hold the title label, the toolbar, and the pagebook
control = new ViewForm(parent, SWT.BORDER);
control.setLayoutData(new GridData(GridData.FILL_BOTH));
control.marginWidth = 0;
control.marginHeight = 0;
// Create a title bar
createTitleBar();
// Create a tool bar
createToolBar();
// Create a PageBook for holding controls
createPageBook();
// Only include the ISV toolbar and the content in the tab list.
// All actions on the System toolbar should be accessible on the pane menu.
if (control.getContent() == null) {
// content can be null if view creation failed
control.setTabList(new Control[] {toolBar});
} else {
control.setTabList(new Control[] {toolBar, control.getContent()});
}
}
protected void createPageBook() {
pageBook = new PageBook(control, SWT.NULL);
// pageBook.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
control.setContent(pageBook);
setVisible(false);
}
private void addModelObjectEditor( ModelObjectEditorPage editor ) {
if (!editorList.contains(editor)) {
editor.createControl(pageBook);
editorList.add(editor);
}
}
/**
* Show the specified ModelObjectEditorPage in this panel, editing the specified object. The MultiPageModelEditor has already
* determined that the page can edit the object.
*
* @param editor
* @param object
*/
public void activateModelObjectEditor( ModelObjectEditorPage editor,
Object object ) {
// first, make sure the current editor is willing to deactivate
if (deactivate(false)) {
// see if we are just sending a new object to the same editor
if (editor == activeEditor) {
activeEditor.edit(object);
toolBarMgr.update(true);
updateTitles();
} else {
// we need to close the existing editor and activate a new one
addModelObjectEditor(editor);
editor.edit(object);
activeEditor = editor;
pageBook.showPage(editor.getControl());
editor.contributeToolbarActions(toolBarMgr);
if (editor.canClose()) {
toolBarMgr.add(closeAction);
}
toolBarMgr.update(true);
updateTitles();
activeEditor.addPropertyListener(this);
}
}
setVisible(true);
}
/**
* Adds the given FocusListener to our list of listeners.
*
* @param listener the IFontChangeListener to be added
*/
public void addFocusListener( FocusListener listener ) {
// System.out.println("[MOEP.addFocusListener] adding listener: " + listener ); //$NON-NLS-1$
focusListeners.add(listener);
}
/**
* Removes the given FocusListener from our list of listeners.
*
* @param listener the IFontChangeListener to be removed
*/
public void removeFocusListener( FocusListener listener ) {
// System.out.println("[MOEP.removeFocusListener] removing listener: " + listener ); //$NON-NLS-1$
focusListeners.remove(listener);
}
/**
* Notifies listeners that the zoom level has changed.
*/
public void firefocusGained( FocusEvent fe ) {
// System.out.println("[MOEP.fireFocusGained] focus list: " + focusListeners ); //$NON-NLS-1$
Iterator iter = focusListeners.iterator();
while (iter.hasNext())
((FocusListener)iter.next()).focusGained(fe);
updateForFocus();
}
/**
* Notifies listeners that the zoom level has changed.
*/
public void firefocusLost( FocusEvent fe ) {
// System.out.println("[MOEP.firefocusLost] focus list: " + focusListeners ); //$NON-NLS-1$
Iterator iter = focusListeners.iterator();
while (iter.hasNext())
((FocusListener)iter.next()).focusLost(fe);
}
public boolean hasFocus() {
return hasFocus(this.control);
}
private boolean hasFocus( Control control ) {
if (control.isFocusControl()) {
return true;
}
if (control instanceof Composite) {
Control[] kids = ((Composite)control).getChildren();
for (int i = 0; i < kids.length; ++i) {
if (hasFocus(kids[i])) {
return true;
}
}
}
return false;
}
@Override
public void focusLost( FocusEvent fe ) {
// System.out.println("[ModelObjectEditorPanel.focusLost]"); //$NON-NLS-1$
firefocusLost(fe);
}
@Override
public void focusGained( FocusEvent fe ) {
// System.out.println("[ModelObjectEditorPanel.focusGained]"); //$NON-NLS-1$
firefocusGained(fe);
}
public void updateForFocus() {
if (getActiveEditor() != null) {
getActiveEditor().updateReadOnlyState();
}
}
/**
* Added here to provide IFindReplaceTarget adapter.
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter( Class key ) {
Object oResult = null;
if (key.equals(IFindReplaceTarget.class)) {
/*
* 1. get current page from 'ModelEditor'. if it is the diagram editor, a. if current page is the 'Source' tab and it
* has focus, return the Source Tab's TextViewer ELSE b. if the optional TransformationObjectEditorPage is the
* 'activeEditor', if this editor's TextViewer has focus return the TOEP's TextViewer
*/
// 2. If we did not find one yet, try looking at the TransformationObjectEditorPage
if (activeEditor != null && activeEditor instanceof IAdaptable) {
oResult = ((IAdaptable)activeEditor).getAdapter(IFindReplaceTarget.class);
}
// 1. Check for an IFindReplaceTarget in the current editor
if (oResult == null) {
if (modelEditor != null && modelEditor instanceof ModelEditor) {
IEditorPart editorPage = ((ModelEditor)modelEditor).getCurrentPage();
if (editorPage != null) {
/*
* PROBLEM: We only want the following code to successfully return an adapter when the Editor involved is
* VISIBLE, and HAS FOCUS.
*/
Object oAdapter = editorPage.getAdapter(IFindReplaceTarget.class);
if (oAdapter != null && oAdapter instanceof IFindReplaceTarget) {
oResult = oAdapter;
}
}
}
}
}
return oResult;
}
/**
* @since 4.0
*/
public ActionService getActionService() {
IEditorPart editorPage = ((ModelEditor)modelEditor).getCurrentPage();
if (editorPage != null) {
return UiPlugin.getDefault().getActionService(editorPage.getSite().getPage());
}
return null;
}
/**
* Callback from ModelEditor that a doSave is about to occur and any active ModelObjectEditor needs to save it's state to the
* model.
*
* @param isClosing true if the ModelEditor is closing, false if this is a simple save.
*/
public void saveEditorState( boolean isClosing ) {
if (activeEditor != null && activeEditor.isDirty()) {
activeEditor.doSave(isClosing);
}
}
/**
* Request that the current ModelObjectEditor be deactivated and the panel be closed. Since client controls can veto this
* request, the method returns true if the panel actually closed, false if it did not.
*
* @param closePanel
* @return
*/
public boolean close() {
return deactivate(true);
}
/**
* Request that the current ModelObjectEditor be deactivated, and the panel optionally closed. Since client controls can veto
* this request, the method returns true if the panel actually closed, false if it did not.
*
* @param closePanel true if the panel should be closed, false if the editor is only being deactivated so that a new editor
* can replace it.
* @return
*/
boolean deactivate( boolean closePanel ) {
boolean result = false;
if (activeEditor != null) {
if (activeEditor.deactivate()) {
result = true;
toolBarMgr.removeAll();
if (closePanel) {
setVisible(false);
}
if( activeEditor != null ) {
activeEditor.removePropertyListener(this);
}
activeEditor = null;
}
} else {
result = true;
if (closePanel) {
setVisible(false);
}
}
return result;
}
/**
* Create a title bar for the pane. - the view icon and title to the far left - the view toolbar appears in the middle. - the
* view pulldown menu, pin button, and close button to the far right.
*/
protected void createTitleBar() {
// Only do this once.
if (titleLabel != null) return;
titleLabel = new CLabel(control, SWT.SHADOW_NONE);
titleLabel.setAlignment(SWT.LEFT);
titleLabel.setBackground(null, null);
titleLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown( MouseEvent e ) {
// the PaneMenu is a popup menu over the title bar of the view. it shows the following actions:
// Restore, Move (View, TabGroup), Size (Left, Right, Top, Bottom), FastView, Maximize, and Close
if (e.button == 3) {
showPaneMenu(titleLabel, new Point(e.x, e.y));
} else if ((e.button == 1) && overImage(e.x)) {
showPaneMenu();
}
}
@Override
public void mouseDoubleClick( MouseEvent event ) {
doZoom();
}
});
updateTitles();
control.setTopLeft(titleLabel);
}
/**
* Shows the pane menu (system menu) for this pane.
*/
public void showPaneMenu() {
Rectangle bounds = titleLabel.getBounds();
showPaneMenu(titleLabel, new Point(0, bounds.height));
}
protected void showPaneMenu( Control parent,
Point point ) {
if (menuMgr == null) {
menuMgr = new MenuManager();
menuMgr.add(new PaneContribution());
}
Menu aMenu = menuMgr.createContextMenu(parent);
// open menu
point = parent.toDisplay(point);
aMenu.setLocation(point.x, point.y);
aMenu.setVisible(true);
}
protected void createToolBar() {
// View toolbar
toolBar = new ToolBar(control, SWT.FLAT | SWT.WRAP);
control.setTopRight(toolBar);
toolBar.addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick( MouseEvent event ) {
// 1GD0ISU: ITPUI:ALL - Dbl click on view tool cause zoom
if (toolBar.getItem(new Point(event.x, event.y)) == null) doZoom();
}
});
toolBarMgr = new PaneToolBarManager(toolBar);
closeAction = new Action() {
@Override
public void run() {
deactivate(true);
}
};
closeAction.setImageDescriptor(UiPlugin.getDefault().getImageDescriptor(PluginConstants.Images.CLOSE_ICON));
closeAction.setToolTipText(CLOSE);
}
public void dispose() {
deactivate(true);
if (toolBarMgr != null) toolBarMgr.dispose();
if (menuMgr != null) menuMgr.dispose();
}
/**
* Change the zoom state of this panel
*/
public void doZoom() {
isZoomed = !isZoomed;
modelEditor.zoomObjectEditor(isZoomed);
}
/**
* Shows the receiver if <code>visible</code> is true otherwise hide it.
*/
private void setVisible( boolean makeVisible ) {
if (control != null && !control.isDisposed()) {
if (makeVisible) {
isZoomed = false;
control.setVisible(true);
} else {
modelEditor.zoomObjectEditor(false);
isZoomed = false;
control.setVisible(false);
}
control.getParent().layout();
}
}
/**
* Update the title attributes.
*/
public void updateTitles() {
String text = null;
if (activeEditor != null) {
text = activeEditor.getTitle();
Image image = activeEditor.getTitleImage();
// only update and relayout if text or image has changed
if (!text.equals(titleLabel.getText()) || image != titleLabel.getImage()) {
titleLabel.setText(text);
titleLabel.setImage(image);
control.layout();
}
titleLabel.setToolTipText(activeEditor.getTitleToolTip());
}
titleLabel.setText(text);
// XXX: Workaround for 1GCGA89: SWT:ALL - CLabel tool tip does not always update properly
titleLabel.update();
}
/**
* Return true if <code>x</code> is over the label image.
*/
boolean overImage( int x ) {
if (titleLabel.getImage() == null) return false;
return x < titleLabel.getImage().getBounds().width;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.IPropertyListener#propertyChanged(java.lang.Object, int)
*/
@Override
public void propertyChanged( Object source,
int propId ) {
if (propId == IEditorPart.PROP_DIRTY) {
markDirty();
} else if (propId == IWorkbenchPart.PROP_TITLE) {
markDirty();
}
}
private void markDirty() {
if (titleLabel != null && !titleLabel.isDisposed()) {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (titleLabel != null && !titleLabel.isDisposed()) {
String title = PluginConstants.EMPTY_STRING;
if (activeEditor != null) {
title = activeEditor.getTitle();
if (activeEditor.isDirty()) {
title = DIRTY_CHAR + title;
}
}
titleLabel.setText(title);
}
}
};
if (Display.getDefault().getThread() == Thread.currentThread()) {
Display.getCurrent().syncExec(runnable);
} else {
Display.getDefault().asyncExec(runnable);
}
}
}
/**
* @return
*/
public ModelObjectEditorPage getActiveEditor() {
return activeEditor;
}
/**
* @return
*/
public MultiPageModelEditor getModelEditor() {
return modelEditor;
}
/**
* @param page
*/
public void setActiveEditor( ModelObjectEditorPage page ) {
activeEditor = page;
}
/**
* Toolbar manager for theis panel's toolbar.
*/
class PaneToolBarManager extends ToolBarManager {
public PaneToolBarManager( ToolBar paneToolBar ) {
super(paneToolBar);
}
@Override
protected void relayout( ToolBar toolBar,
int oldCount,
int newCount ) {
toolBar.layout();
Composite parent = toolBar.getParent();
parent.layout();
if (parent.getParent() != null) parent.getParent().layout();
}
}
class PaneContribution extends ContributionItem {
@Override
public boolean isDynamic() {
return true;
}
@Override
public void fill( Menu menu,
int index ) {
MenuItem item;
// add restore item
item = new MenuItem(menu, SWT.NONE);
item.setText(RESTORE);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e ) {
if (isZoomed) doZoom();
}
});
item.setEnabled(isZoomed);
// add maximize item
item = new MenuItem(menu, SWT.NONE);
item.setText(MAXIMIZE);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e ) {
doZoom();
}
});
item.setEnabled(!isZoomed);
new MenuItem(menu, SWT.SEPARATOR);
// add close item
item = new MenuItem(menu, SWT.NONE);
item.setText(CLOSE);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected( SelectionEvent e ) {
close();
}
});
}
}
}