Java tutorial
/******************************************************************************* * Copyright (c) 2005 Andrei Loskutov. * All rights reserved. This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * Contributor: Andrei Loskutov - initial API and implementation *******************************************************************************/ package de.loskutov.eclipseskins.ui; import java.util.Map; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlExtension; import org.eclipse.jface.text.IInformationControlExtension2; import org.eclipse.jface.text.IInformationControlExtension3; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; 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.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Monitor; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.IWindowListener; import org.eclipse.ui.IWorkbenchWindow; import de.loskutov.eclipseskins.PresentationPlugin; import de.loskutov.eclipseskins.ThemeConstants; import de.loskutov.eclipseskins.ThemeWrapper; public abstract class AbstractPartListControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension2, IInformationControlExtension3 { protected TabArea tabArea; protected ToolTipHandler tooltip; /** Border thickness in pixels. */ protected static final int BORDER = 1; /** Right margin in pixels. */ protected static final int RIGHT_MARGIN = 3; /** The control's shell */ protected Shell rootControl; /** The composite */ protected Composite fComposite; /** The control's text widget */ protected Text fFilterText; /** The control's table widget */ protected TableViewer fTableViewer; protected Label separator; protected Composite fViewMenuButtonComposite; protected IAction fShowViewMenuAction; protected MenuManager fViewMenuManager; protected ToolBar fToolBar; protected ToolItem viewMenuButton; protected ToolItem sortListButton; protected ThemeWrapper currentTheme; protected Map/* <String,Action> */ actionMap; protected Listener fDeactivateListener; protected Listener layoutListener; protected NamePatternFilter namePatternFilter; protected Rectangle fBounds; protected Rectangle fTrim; protected boolean showFullPath; protected boolean sortTabList; protected boolean isDeactivateListenerActive; protected boolean isEditorPartList; protected boolean isDisposed; protected boolean isPinned; protected boolean isVisible; protected boolean mainWindowActive; protected boolean isActive; protected boolean forcePosition; public boolean menuAboutToShow; public void setBackgroundColor(Color background) { if (PresentationPlugin.DEBUG) { System.out.println("backgr:" + background); } /* * TODO possible workaround for bug: * Depending on the GTK theme, setting the table color BEFORE or AFTER setting the * the color of table items, causes elements bg color be wrongly displayed * on GTK 2.20.1-0ubuntu2 / Eclipse 3.4/3.5/3.6 * As side effect of the workarownd, few pixels below/above table are * painted in the unexpected color, without the workaround, ALL table * elements are painted in wrong color */ //if(!UIUtils.isGtk) { fTableViewer.getTable().setBackground(background); //} fFilterText.setBackground(background); fComposite.setBackground(background); fViewMenuButtonComposite.setBackground(background); if (fToolBar != null && !fToolBar.isDisposed()) { fToolBar.setBackground(background); } } protected void showViewMenu() { menuAboutToShow = true; Menu aMenu = getViewMenuManager().createContextMenu(rootControl); Rectangle bounds = fToolBar.getBounds(); Point topLeft = new Point(bounds.x, bounds.y + bounds.height); topLeft = rootControl.toDisplay(topLeft); aMenu.setLocation(topLeft.x, topLeft.y); aMenu.setVisible(true); } protected MenuManager getViewMenuManager() { if (fViewMenuManager == null) { fViewMenuManager = new MenuManager(); fillViewMenu(fViewMenuManager); } return fViewMenuManager; } protected abstract void fillViewMenu(IMenuManager viewMenuManager); public boolean isFocusControl() { return fFilterText.isFocusControl() || fTableViewer.getControl().isFocusControl(); } public void setFocus() { rootControl.forceFocus(); fFilterText.setFocus(); } public void addFocusListener(FocusListener listener) { rootControl.addFocusListener(listener); } public void removeFocusListener(FocusListener listener) { rootControl.removeFocusListener(listener); } public boolean hasContents() { return fTableViewer != null && fTableViewer.getInput() != null; } public void setSizeConstraints(int maxWidth, int maxHeight) { //fMaxWidth= maxWidth; //fMaxHeight= maxHeight; } public Point computeSizeHint() { return rootControl.computeSize(SWT.DEFAULT, SWT.DEFAULT); } public void setLocation(Point location) { if (rootControl != null) { fTrim = rootControl.computeTrim(0, 0, 0, 0); Point textLocation = fComposite.getLocation(); location.x += fTrim.x - textLocation.x; location.y += fTrim.y - textLocation.y; rootControl.setLocation(location); } } public void setPosition(Point displayCoordinates, boolean forcePosition) { this.forcePosition = forcePosition; if (!forcePosition && restoresLocation()) { restorePosition(displayCoordinates); return; } setLocation(fixLocation(displayCoordinates.x, displayCoordinates.y)); } protected final Point fixLocation(int x, int y) { Monitor mon = tabArea.getMonitor(); Rectangle bounds = mon.getClientArea(); Point size = computeSizeHint(); if (x + size.x > bounds.x + bounds.width) { x = bounds.x + bounds.width - size.x; } if (y + size.y > bounds.y + bounds.height) { y = bounds.y + bounds.height - size.y; } return new Point(x, y); } public boolean restoresSize() { return restoresLocation(); } public Rectangle computeTrim() { if (fTrim != null) { return fTrim; } return new Rectangle(0, 0, 0, 0); } public void setSize(int width, int height) { rootControl.setSize(width, height); } public Rectangle getBounds() { return fBounds; } protected void setSortEnabled(boolean enabled) { sortTabList = enabled; setInput(null); setBooleanToTheme(ThemeConstants.TAB_LIST_SORT, sortTabList); } public void setInformation(String information) { // no-op } protected IPreferenceStore getStore() { return PresentationPlugin.getDefault().getPreferenceStore(); } protected boolean getBooleanFromTheme(String id) { return currentTheme.getBoolean(id); } protected void setBooleanToTheme(String id, boolean value) { currentTheme.setBoolean(id, value); PresentationPlugin.getDefault().getPreferenceStore().setValue(id, value); } protected void setInfoSystemColor() { Display display = rootControl.getDisplay(); setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); } public void setInput(Object information) { if (fTableViewer == null) { return; } int tableHeight = 0; int tableWidth = 0; if (information instanceof Rectangle) { Rectangle new_size = (Rectangle) information; tableHeight = new_size.height; tableWidth = new_size.width; } else { fFilterText.setText(""); //$NON-NLS-1$ fTableViewer.setInput(tabArea); } // Resize the table's height accordingly to the new input Table viewerTable = fTableViewer.getTable(); Point tableSize; if (tableWidth != 0 && tableHeight != 0) { tableSize = new Point(tableWidth, tableHeight); } else { tableSize = viewerTable.computeSize(SWT.DEFAULT, SWT.DEFAULT); } Rectangle displayBounds = fComposite.getDisplay().getBounds(); int tableMaxHeight = displayBounds.height / 2; int tableMaxWidth = displayBounds.width / 2; // removes padding if necessary int itemHeight = viewerTable.getItemHeight(); tableHeight = (tableSize.y <= tableMaxHeight) ? tableSize.y - itemHeight - itemHeight / 2 : tableMaxHeight; TableItem[] tableItems = viewerTable.getItems(); for (int i = 0; i < tableItems.length; i++) { Rectangle bounds = tableItems[i].getBounds(0); tableMaxWidth = Math.max(bounds.width, tableMaxWidth); } tableWidth = (tableSize.x <= tableMaxWidth) ? tableSize.x : tableMaxWidth; GridData layoutData = (GridData) viewerTable.getLayoutData(); layoutData.heightHint = tableHeight; layoutData.widthHint = tableWidth; Point fCompSize = fComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT); fComposite.setSize(fCompSize); Shell shell = fComposite.getShell(); shell.setSize(fCompSize); shell.layout(); } protected Text createFilterText(Composite parent) { Text filterText = new Text(parent, SWT.NONE); filterText.setToolTipText("Press <Esc> to hide tab list"); GridData data = new GridData(GridData.FILL_HORIZONTAL); GC gc = new GC(parent); gc.setFont(parent.getFont()); FontMetrics fontMetrics = gc.getFontMetrics(); gc.dispose(); data.heightHint = Dialog.convertHeightInCharsToPixels(fontMetrics, 1); data.horizontalAlignment = GridData.FILL; data.verticalAlignment = GridData.CENTER; filterText.setLayoutData(data); filterText.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { if (e.keyCode == 0x0D) { // return gotoSelectedElement(); return; } Table table = fTableViewer.getTable(); if (e.keyCode == SWT.ARROW_DOWN) { table.setFocus(); table.setSelection(0); } if (e.keyCode == SWT.ARROW_UP) { table.setFocus(); table.setSelection(table.getItemCount() - 1); } if (e.character == 0x1B) { // ESC dispose(); } } public void keyReleased(KeyEvent e) { // do nothing } }); return filterText; } protected void installFilter() { fFilterText.setText(""); //$NON-NLS-1$ fFilterText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { String text = ((Text) e.widget).getText(); int length = text.length(); if (length > 0 && text.charAt(length - 1) != '*') { text = text + ".*"; } setMatcherString(text); } }); } /** * Sets the patterns to filter out for the receiver. * <p> * The following characters have special meaning: ? => any character * => * any string * </p> */ protected void setMatcherString(String pattern) { namePatternFilter.setPattern(pattern); // refresh viewer to refilter fTableViewer.getControl().setRedraw(false); fTableViewer.refresh(); selectFirstMatch(); fTableViewer.getControl().setRedraw(true); } protected TableViewer createTableViewer(Composite parent, int style) { Table table = new Table(parent, SWT.SINGLE | (style & ~SWT.MULTI)); table.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); TableViewer tableViewer = new TableViewer(table) { protected void internalRefresh(Object element) { boolean usingMotif = "motif".equals(SWT.getPlatform()); //$NON-NLS-1$ try { // This avoids a "graphic is disposed" error on Motif by not letting // it redraw while we remove entries. Some items in this table are // being removed and may have icons which may have already been // disposed elsewhere. if (usingMotif) { getTable().setRedraw(false); } super.internalRefresh(element); } finally { if (usingMotif) { getTable().setRedraw(true); } } } }; namePatternFilter = new NamePatternFilter(); tableViewer.addFilter(namePatternFilter); configureTableViewer(tableViewer); return tableViewer; } protected abstract void configureTableViewer(TableViewer tableViewer); protected Object getSelectedElement() { return ((IStructuredSelection) fTableViewer.getSelection()).getFirstElement(); } protected IStructuredSelection getSelectedElements() { return (IStructuredSelection) fTableViewer.getSelection(); } /** * Selects the first element in the table which matches the current filter * pattern. */ protected void selectFirstMatch() { Table table = fTableViewer.getTable(); Object element = namePatternFilter.findElement(table.getItems(), (ILabelProvider) fTableViewer.getLabelProvider()); if (element != null) { fTableViewer.setSelection(new StructuredSelection(element), true); } else { fTableViewer.setSelection(StructuredSelection.EMPTY); } } public void setVisible(boolean visible) { this.isVisible = visible; if (PresentationPlugin.DEBUG) { System.out.println("set list visible: " + visible); } if (rootControl != null && !rootControl.isDisposed()) { if (!visible && isPinned && isActive) { if (PresentationPlugin.DEBUG) { System.out.println("set list inVisible cancelled"); } } else { rootControl.setVisible(visible); } } else { if (PresentationPlugin.DEBUG) { System.out.println("set list visible: " + visible + ", shell is already disposed!!!"); } } } protected abstract void restorePosition(Point displayCoordinates); protected abstract void gotoSelectedElement(); public void addDisposeListener(DisposeListener listener) { rootControl.addDisposeListener(listener); } public void removeDisposeListener(DisposeListener listener) { rootControl.removeDisposeListener(listener); } public void setForegroundColor(Color foreground) { if (PresentationPlugin.DEBUG) { System.out.println("foregr:" + foreground); } fTableViewer.getTable().setForeground(foreground); fFilterText.setForeground(foreground); fComposite.setForeground(foreground); fViewMenuButtonComposite.setForeground(foreground); if (fToolBar != null) { fToolBar.setForeground(foreground); } } final class ToggleShowFullPathAction extends Action { public ToggleShowFullPathAction() { super(); setId("showFullPath"); showFullPath = getBooleanFromTheme(ThemeConstants.TAB_LIST_SHOW_FULL_PATH); setChecked(showFullPath); } public void run() { showFullPath = isChecked(); setInput(null); // re-set position setPosition(rootControl.getLocation(), true); setBooleanToTheme(ThemeConstants.TAB_LIST_SHOW_FULL_PATH, showFullPath); } } final class WindowListener implements IWindowListener { private final IWorkbenchWindow myWindow; WindowListener(IWorkbenchWindow activeWorkbenchWindow) { this.myWindow = activeWorkbenchWindow; } public void windowActivated(IWorkbenchWindow window) { if (myWindow != window) { return; } if (PresentationPlugin.DEBUG) { System.out.println("window activate"); } mainWindowActive = true; } public void windowDeactivated(IWorkbenchWindow window) { if (myWindow != window) { return; } if (PresentationPlugin.DEBUG) { System.out.println("window deactivate"); } mainWindowActive = false; if (isPinned && isVisible && isDeactivateListenerActive && rootControl != null && !rootControl.isDisposed()) { rootControl.getDisplay().timerExec(50, new Runnable() { public void run() { if (!isActive) { if (PresentationPlugin.DEBUG) { System.out.println("re-call from deactivate window"); } setVisible(false); dispose(); } } }); } } public void windowClosed(IWorkbenchWindow window) { // no-op } public void windowOpened(IWorkbenchWindow window) { // no-op } } final class MyMouseMoveListener implements MouseMoveListener { private final int count; private TableItem fLastItem; private int lastY; private int divCount; MyMouseMoveListener(int count) { super(); this.count = count; } public void mouseMove(MouseEvent e) { Table table = fTableViewer.getTable(); int itemHeightdiv4 = table.getItemHeight() / 4; int tableHeight = table.getBounds().height; Point tableLoc = table.toDisplay(0, 0); if (divCount == count) { divCount = 0; } divCount++; if (table.equals(e.getSource()) && divCount == count) { TableItem o = table.getItem(new Point(e.x, e.y)); if (o == null) { return; } if (lastY != e.y) { lastY = e.y; if (!o.equals(fLastItem)) { fLastItem = o; table.setSelection(new TableItem[] { fLastItem }); } else if (e.y < itemHeightdiv4) { // Scroll up Item item = fTableViewer.scrollUp(e.x + tableLoc.x, e.y + tableLoc.y); if (item instanceof TableItem) { fLastItem = (TableItem) item; table.setSelection(new TableItem[] { fLastItem }); } } else if (e.y > tableHeight - itemHeightdiv4) { // Scroll down Item item = fTableViewer.scrollDown(e.x + tableLoc.x, e.y + tableLoc.y); if (item instanceof TableItem) { fLastItem = (TableItem) item; table.setSelection(new TableItem[] { fLastItem }); } } } } } } final class DeactivateListener implements Listener { protected DeactivateListener() { super(); } public void shellActivate(Event e) { if (PresentationPlugin.DEBUG) { System.out.println("activate list"); } isActive = true; if (rootControl != null) { if (e.widget == rootControl && rootControl.getShells().length == 0) { isDeactivateListenerActive = true; } } if (isPinned && !rootControl.isVisible()) { setVisible(true); } } public void shellDeactivate(Event e) { if (PresentationPlugin.DEBUG) { System.out.println("deactivate list:\n" + "\t visible: " + isVisible + ", pinned: " + isPinned + ", window active: " + mainWindowActive + ", list active: " + isActive); } // WORKAROUND for disposing control too early on menu events on gtk (linux) if (menuAboutToShow && UIUtils.isGtk) { if (PresentationPlugin.DEBUG) { System.out.println("shellDeactivate on gtk ignored!!!"); } menuAboutToShow = false; return; } isActive = false; if (isPinned) { if (isVisible && mainWindowActive) { setVisible(false); dispose(); } else if (isVisible && isDeactivateListenerActive && rootControl != null && !rootControl.isDisposed()) { rootControl.getDisplay().timerExec(50, new Runnable() { public void run() { if (!mainWindowActive) { if (PresentationPlugin.DEBUG) { System.out.println("re-call from deactivate list"); } setVisible(false); dispose(); } } }); } } else if (!isEditorPartList || isDeactivateListenerActive) { setVisible(false); dispose(); } } public void handleEvent(Event event) { switch (event.type) { case SWT.Activate: shellActivate(event); break; case SWT.Deactivate: shellDeactivate(event); break; default: break; } } } }