Mercurial > projects > dwt-addons
diff dwtx/jface/text/source/CompositeRuler.d @ 129:eb30df5ca28b
Added JFace Text sources
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 23 Aug 2008 19:10:48 +0200 |
parents | |
children | c4fb132a086c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/text/source/CompositeRuler.d Sat Aug 23 19:10:48 2008 +0200 @@ -0,0 +1,881 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.text.source.CompositeRuler; + +import dwt.dwthelper.utils; + + +import java.util.ArrayList; +import java.util.EventListener; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import dwt.DWT; +import dwt.custom.StyledText; +import dwt.events.ControlListener; +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.events.FocusListener; +import dwt.events.HelpListener; +import dwt.events.KeyListener; +import dwt.events.MouseListener; +import dwt.events.MouseMoveListener; +import dwt.events.MouseTrackListener; +import dwt.events.PaintListener; +import dwt.events.TraverseListener; +import dwt.graphics.Font; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Canvas; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Event; +import dwt.widgets.Layout; +import dwt.widgets.Listener; +import dwt.widgets.Menu; +import dwtx.core.runtime.Assert; +import dwtx.jface.text.BadLocationException; +import dwtx.jface.text.IDocument; +import dwtx.jface.text.IRegion; +import dwtx.jface.text.ITextViewer; +import dwtx.jface.text.ITextViewerExtension; +import dwtx.jface.text.ITextViewerExtension5; + + +/** + * Standard implementation of + * {@link dwtx.jface.text.source.IVerticalRuler}. + * <p> + * This ruler does not have a a visual representation of its own. The + * presentation comes from the configurable list of vertical ruler columns. Such + * columns must implement the + * {@link dwtx.jface.text.source.IVerticalRulerColumn}. interface.</p> + * <p> + * Clients may instantiate and configure this class.</p> + * + * @see dwtx.jface.text.source.IVerticalRulerColumn + * @see dwtx.jface.text.ITextViewer + * @since 2.0 + */ +public class CompositeRuler : IVerticalRuler, IVerticalRulerExtension, IVerticalRulerInfoExtension { + + + /** + * Layout of the composite vertical ruler. Arranges the list of columns. + */ + class RulerLayout : Layout { + + /** + * Creates the new ruler layout. + */ + protected RulerLayout() { + } + + /* + * @see Layout#computeSize(Composite, int, int, bool) + */ + protected Point computeSize(Composite composite, int wHint, int hHint, bool flushCache) { + Control[] children= composite.getChildren(); + Point size= new Point(0, 0); + for (int i= 0; i < children.length; i++) { + Point s= children[i].computeSize(DWT.DEFAULT, DWT.DEFAULT, flushCache); + size.x += s.x; + size.y= Math.max(size.y, s.y); + } + size.x += (Math.max(0, children.length -1) * fGap); + return size; + } + + /* + * @see Layout#layout(Composite, bool) + */ + protected void layout(Composite composite, bool flushCache) { + Rectangle clArea= composite.getClientArea(); + int rulerHeight= clArea.height; + + int x= 0; + Iterator e= fDecorators.iterator(); + while (e.hasNext()) { + IVerticalRulerColumn column= (IVerticalRulerColumn) e.next(); + int columnWidth= column.getWidth(); + column.getControl().setBounds(x, 0, columnWidth, rulerHeight); + x += (columnWidth + fGap); + } + } + } + + /** + * A canvas that adds listeners to all its children. Used by the implementation of the + * vertical ruler to propagate listener additions and removals to the ruler's columns. + */ + static class CompositeRulerCanvas : Canvas { + + /** + * Keeps the information for which event type a listener object has been added. + */ + static class ListenerInfo { + Class fClass; + EventListener fListener; + } + + /** The list of listeners added to this canvas. */ + private List fCachedListeners= new ArrayList(); + /** + * Internal listener for opening the context menu. + * @since 3.0 + */ + private Listener fMenuDetectListener; + + /** + * Creates a new composite ruler canvas. + * + * @param parent the parent composite + * @param style the DWT styles + */ + public CompositeRulerCanvas(Composite parent, int style) { + super(parent, style); + fMenuDetectListener= new Listener() { + public void handleEvent(Event event) { + if (event.type is DWT.MenuDetect) { + Menu menu= getMenu(); + if (menu !is null) { + menu.setLocation(event.x, event.y); + menu.setVisible(true); + } + } + } + }; + super.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + if (fCachedListeners !is null) { + fCachedListeners.clear(); + fCachedListeners= null; + } + } + }); + } + + /** + * Adds the given listener object as listener of the given type (<code>clazz</code>) to + * the given control. + * + * @param clazz the listener type + * @param control the control to add the listener to + * @param listener the listener to be added + */ + private void addListener(Class clazz, Control control, EventListener listener) { + if (ControlListener.class.equals(clazz)) { + control. addControlListener((ControlListener) listener); + return; + } + if (FocusListener.class.equals(clazz)) { + control. addFocusListener((FocusListener) listener); + return; + } + if (HelpListener.class.equals(clazz)) { + control. addHelpListener((HelpListener) listener); + return; + } + if (KeyListener.class.equals(clazz)) { + control. addKeyListener((KeyListener) listener); + return; + } + if (MouseListener.class.equals(clazz)) { + control. addMouseListener((MouseListener) listener); + return; + } + if (MouseMoveListener.class.equals(clazz)) { + control. addMouseMoveListener((MouseMoveListener) listener); + return; + } + if (MouseTrackListener.class.equals(clazz)) { + control. addMouseTrackListener((MouseTrackListener) listener); + return; + } + if (PaintListener.class.equals(clazz)) { + control. addPaintListener((PaintListener) listener); + return; + } + if (TraverseListener.class.equals(clazz)) { + control. addTraverseListener((TraverseListener) listener); + return; + } + if (DisposeListener.class.equals(clazz)) { + control. addDisposeListener((DisposeListener) listener); + return; + } + } + + /** + * Removes the given listener object as listener of the given type (<code>clazz</code>) from + * the given control. + * + * @param clazz the listener type + * @param control the control to remove the listener from + * @param listener the listener to be removed + */ + private void removeListener(Class clazz, Control control, EventListener listener) { + if (ControlListener.class.equals(clazz)) { + control. removeControlListener((ControlListener) listener); + return; + } + if (FocusListener.class.equals(clazz)) { + control. removeFocusListener((FocusListener) listener); + return; + } + if (HelpListener.class.equals(clazz)) { + control. removeHelpListener((HelpListener) listener); + return; + } + if (KeyListener.class.equals(clazz)) { + control. removeKeyListener((KeyListener) listener); + return; + } + if (MouseListener.class.equals(clazz)) { + control. removeMouseListener((MouseListener) listener); + return; + } + if (MouseMoveListener.class.equals(clazz)) { + control. removeMouseMoveListener((MouseMoveListener) listener); + return; + } + if (MouseTrackListener.class.equals(clazz)) { + control. removeMouseTrackListener((MouseTrackListener) listener); + return; + } + if (PaintListener.class.equals(clazz)) { + control. removePaintListener((PaintListener) listener); + return; + } + if (TraverseListener.class.equals(clazz)) { + control. removeTraverseListener((TraverseListener) listener); + return; + } + if (DisposeListener.class.equals(clazz)) { + control. removeDisposeListener((DisposeListener) listener); + return; + } + } + + /** + * Adds the given listener object to the internal book keeping under + * the given listener type (<code>clazz</code>). + * + * @param clazz the listener type + * @param listener the listener object + */ + private void addListener(Class clazz, EventListener listener) { + Control[] children= getChildren(); + for (int i= 0; i < children.length; i++) { + if (children[i] !is null && !children[i].isDisposed()) + addListener(clazz, children[i], listener); + } + + ListenerInfo info= new ListenerInfo(); + info.fClass= clazz; + info.fListener= listener; + fCachedListeners.add(info); + } + + /** + * Removes the given listener object from the internal book keeping under + * the given listener type (<code>clazz</code>). + * + * @param clazz the listener type + * @param listener the listener object + */ + private void removeListener(Class clazz, EventListener listener) { + int length= fCachedListeners.size(); + for (int i= 0; i < length; i++) { + ListenerInfo info= (ListenerInfo) fCachedListeners.get(i); + if (listener is info.fListener && clazz.equals(info.fClass)) { + fCachedListeners.remove(i); + break; + } + } + + Control[] children= getChildren(); + for (int i= 0; i < children.length; i++) { + if (children[i] !is null && !children[i].isDisposed()) + removeListener(clazz, children[i], listener); + } + } + + /** + * Tells this canvas that a child has been added. + * + * @param child the child + */ + public void childAdded(Control child) { + if (child !is null && !child.isDisposed()) { + int length= fCachedListeners.size(); + for (int i= 0; i < length; i++) { + ListenerInfo info= (ListenerInfo) fCachedListeners.get(i); + addListener(info.fClass, child, info.fListener); + } + child.addListener(DWT.MenuDetect, fMenuDetectListener); + } + } + + /** + * Tells this canvas that a child has been removed. + * + * @param child the child + */ + public void childRemoved(Control child) { + if (child !is null && !child.isDisposed()) { + int length= fCachedListeners.size(); + for (int i= 0; i < length; i++) { + ListenerInfo info= (ListenerInfo) fCachedListeners.get(i); + removeListener(info.fClass, child, info.fListener); + } + child.removeListener(DWT.MenuDetect, fMenuDetectListener); + } + } + + /* + * @see Control#removeControlListener(ControlListener) + */ + public void removeControlListener(ControlListener listener) { + removeListener(ControlListener.class, listener); + super.removeControlListener(listener); + } + + /* + * @see Control#removeFocusListener(FocusListener) + */ + public void removeFocusListener(FocusListener listener) { + removeListener(FocusListener.class, listener); + super.removeFocusListener(listener); + } + + /* + * @see Control#removeHelpListener(HelpListener) + */ + public void removeHelpListener(HelpListener listener) { + removeListener(HelpListener.class, listener); + super.removeHelpListener(listener); + } + + /* + * @see Control#removeKeyListener(KeyListener) + */ + public void removeKeyListener(KeyListener listener) { + removeListener(KeyListener.class, listener); + super.removeKeyListener(listener); + } + + /* + * @see Control#removeMouseListener(MouseListener) + */ + public void removeMouseListener(MouseListener listener) { + removeListener(MouseListener.class, listener); + super.removeMouseListener(listener); + } + + /* + * @see Control#removeMouseMoveListener(MouseMoveListener) + */ + public void removeMouseMoveListener(MouseMoveListener listener) { + removeListener(MouseMoveListener.class, listener); + super.removeMouseMoveListener(listener); + } + + /* + * @see Control#removeMouseTrackListener(MouseTrackListener) + */ + public void removeMouseTrackListener(MouseTrackListener listener) { + removeListener(MouseTrackListener.class, listener); + super.removeMouseTrackListener(listener); + } + + /* + * @see Control#removePaintListener(PaintListener) + */ + public void removePaintListener(PaintListener listener) { + removeListener(PaintListener.class, listener); + super.removePaintListener(listener); + } + + /* + * @see Control#removeTraverseListener(TraverseListener) + */ + public void removeTraverseListener(TraverseListener listener) { + removeListener(TraverseListener.class, listener); + super.removeTraverseListener(listener); + } + + /* + * @see Widget#removeDisposeListener(DisposeListener) + */ + public void removeDisposeListener(DisposeListener listener) { + removeListener(DisposeListener.class, listener); + super.removeDisposeListener(listener); + } + + /* + * @seeControl#addControlListener(ControlListener) + */ + public void addControlListener(ControlListener listener) { + super.addControlListener(listener); + addListener(ControlListener.class, listener); + } + + /* + * @see Control#addFocusListener(FocusListener) + */ + public void addFocusListener(FocusListener listener) { + super.addFocusListener(listener); + addListener(FocusListener.class, listener); + } + + /* + * @see Control#addHelpListener(HelpListener) + */ + public void addHelpListener(HelpListener listener) { + super.addHelpListener(listener); + addListener(HelpListener.class, listener); + } + + /* + * @see Control#addKeyListener(KeyListener) + */ + public void addKeyListener(KeyListener listener) { + super.addKeyListener(listener); + addListener(KeyListener.class, listener); + } + + /* + * @see Control#addMouseListener(MouseListener) + */ + public void addMouseListener(MouseListener listener) { + super.addMouseListener(listener); + addListener(MouseListener.class, listener); + } + + /* + * @see Control#addMouseMoveListener(MouseMoveListener) + */ + public void addMouseMoveListener(MouseMoveListener listener) { + super.addMouseMoveListener(listener); + addListener(MouseMoveListener.class, listener); + } + + /* + * @see Control#addMouseTrackListener(MouseTrackListener) + */ + public void addMouseTrackListener(MouseTrackListener listener) { + super.addMouseTrackListener(listener); + addListener(MouseTrackListener.class, listener); + } + + /* + * @seeControl#addPaintListener(PaintListener) + */ + public void addPaintListener(PaintListener listener) { + super.addPaintListener(listener); + addListener(PaintListener.class, listener); + } + + /* + * @see Control#addTraverseListener(TraverseListener) + */ + public void addTraverseListener(TraverseListener listener) { + super.addTraverseListener(listener); + addListener(TraverseListener.class, listener); + } + + /* + * @see Widget#addDisposeListener(DisposeListener) + */ + public void addDisposeListener(DisposeListener listener) { + super.addDisposeListener(listener); + addListener(DisposeListener.class, listener); + } + } + + /** The ruler's viewer */ + private ITextViewer fTextViewer; + /** The ruler's canvas to which to add the ruler columns */ + private CompositeRulerCanvas fComposite; + /** The ruler's annotation model */ + private IAnnotationModel fModel; + /** The list of columns */ + private List fDecorators= new ArrayList(2); + /** The cached location of the last mouse button activity */ + private Point fLocation= new Point(-1, -1); + /** The cached line of the list mouse button activity */ + private int fLastMouseButtonActivityLine= -1; + /** The gap between the individual columns of this composite ruler */ + private int fGap; + /** + * The set of annotation listeners. + * @since 3.0 + */ + private Set fAnnotationListeners= new HashSet(); + + + /** + * Constructs a new composite vertical ruler. + */ + public CompositeRuler() { + this(0); + } + + /** + * Constructs a new composite ruler with the given gap between its columns. + * + * @param gap + */ + public CompositeRuler(int gap) { + fGap= gap; + } + + /** + * Inserts the given column at the specified slot to this composite ruler. + * Columns are counted from left to right. + * + * @param index the index + * @param rulerColumn the decorator to be inserted + */ + public void addDecorator(int index, IVerticalRulerColumn rulerColumn) { + rulerColumn.setModel(getModel()); + + if (index > fDecorators.size()) + fDecorators.add(rulerColumn); + else + fDecorators.add(index, rulerColumn); + + if (fComposite !is null && !fComposite.isDisposed()) { + rulerColumn.createControl(this, fComposite); + fComposite.childAdded(rulerColumn.getControl()); + layoutTextViewer(); + } + } + + /** + * Removes the decorator in the specified slot from this composite ruler. + * + * @param index the index + */ + public void removeDecorator(int index) { + IVerticalRulerColumn rulerColumn= (IVerticalRulerColumn) fDecorators.get(index); + removeDecorator(rulerColumn); + } + + /** + * Removes the given decorator from the composite ruler. + * + * @param rulerColumn the ruler column to be removed + * @since 3.0 + */ + public void removeDecorator(IVerticalRulerColumn rulerColumn) { + fDecorators.remove(rulerColumn); + if (rulerColumn !is null) { + Control cc= rulerColumn.getControl(); + if (cc !is null && !cc.isDisposed()) { + fComposite.childRemoved(cc); + cc.dispose(); + } + } + layoutTextViewer(); + } + + /** + * Layouts the text viewer. This also causes this ruler to get + * be layouted. + */ + private void layoutTextViewer() { + + Control parent= fTextViewer.getTextWidget(); + + if (fTextViewer instanceof ITextViewerExtension) { + ITextViewerExtension extension= (ITextViewerExtension) fTextViewer; + parent= extension.getControl(); + } + + if (parent instanceof Composite && !parent.isDisposed()) + ((Composite) parent).layout(true); + } + + /* + * @see IVerticalRuler#getControl() + */ + public Control getControl() { + return fComposite; + } + + /* + * @see IVerticalRuler#createControl(Composite, ITextViewer) + */ + public Control createControl(Composite parent, ITextViewer textViewer) { + + fTextViewer= textViewer; + + fComposite= new CompositeRulerCanvas(parent, DWT.NONE); + fComposite.setLayout(new RulerLayout()); + + Iterator iter= fDecorators.iterator(); + while (iter.hasNext()) { + IVerticalRulerColumn column= (IVerticalRulerColumn) iter.next(); + column.createControl(this, fComposite); + fComposite.childAdded(column.getControl()); + } + + return fComposite; + } + + /* + * @see IVerticalRuler#setModel(IAnnotationModel) + */ + public void setModel(IAnnotationModel model) { + + fModel= model; + + Iterator e= fDecorators.iterator(); + while (e.hasNext()) { + IVerticalRulerColumn column= (IVerticalRulerColumn) e.next(); + column.setModel(model); + } + } + + /* + * @see IVerticalRuler#getModel() + */ + public IAnnotationModel getModel() { + return fModel; + } + + /* + * @see IVerticalRuler#update() + */ + public void update() { + if (fComposite !is null && !fComposite.isDisposed()) { + Display d= fComposite.getDisplay(); + if (d !is null) { + d.asyncExec(new Runnable() { + public void run() { + immediateUpdate(); + } + }); + } + } + } + + /** + * Immediately redraws the entire ruler (without asynchronous posting). + * + * @since 3.2 + */ + public void immediateUpdate() { + Iterator e= fDecorators.iterator(); + while (e.hasNext()) { + IVerticalRulerColumn column= (IVerticalRulerColumn) e.next(); + column.redraw(); + } + } + + /* + * @see IVerticalRulerExtension#setFont(Font) + */ + public void setFont(Font font) { + Iterator e= fDecorators.iterator(); + while (e.hasNext()) { + IVerticalRulerColumn column= (IVerticalRulerColumn) e.next(); + column.setFont(font); + } + } + + /* + * @see IVerticalRulerInfo#getWidth() + */ + public int getWidth() { + int width= 0; + Iterator e= fDecorators.iterator(); + while (e.hasNext()) { + IVerticalRulerColumn column= (IVerticalRulerColumn) e.next(); + width += (column.getWidth() + fGap); + } + return Math.max(0, width - fGap); + } + + /* + * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity() + */ + public int getLineOfLastMouseButtonActivity() { + if (fLastMouseButtonActivityLine is -1) + fLastMouseButtonActivityLine= toDocumentLineNumber(fLocation.y); + else if (fTextViewer.getDocument() is null || fLastMouseButtonActivityLine >= fTextViewer.getDocument().getNumberOfLines()) + fLastMouseButtonActivityLine= -1; + return fLastMouseButtonActivityLine; + } + + /* + * @see IVerticalRulerInfo#toDocumentLineNumber(int) + */ + public int toDocumentLineNumber(int y_coordinate) { + if (fTextViewer is null || y_coordinate is -1) + return -1; + + StyledText text= fTextViewer.getTextWidget(); + int line= text.getLineIndex(y_coordinate); + + if (line is text.getLineCount() - 1) { + // check whether y_coordinate exceeds last line + if (y_coordinate > text.getLinePixel(line + 1)) + return -1; + } + + return widgetLine2ModelLine(fTextViewer, line); + } + + /** + * Returns the line in the given viewer's document that correspond to the given + * line of the viewer's widget. + * + * @param viewer the viewer + * @param widgetLine the widget line + * @return the corresponding line the viewer's document + * @since 2.1 + */ + protected final static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) { + + if (viewer instanceof ITextViewerExtension5) { + ITextViewerExtension5 extension= (ITextViewerExtension5) viewer; + return extension.widgetLine2ModelLine(widgetLine); + } + + try { + IRegion r= viewer.getVisibleRegion(); + IDocument d= viewer.getDocument(); + return widgetLine += d.getLineOfOffset(r.getOffset()); + } catch (BadLocationException x) { + } + return widgetLine; + } + + /** + * Returns this ruler's text viewer. + * + * @return this ruler's text viewer + */ + public ITextViewer getTextViewer() { + return fTextViewer; + } + + /* + * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int) + */ + public void setLocationOfLastMouseButtonActivity(int x, int y) { + fLocation.x= x; + fLocation.y= y; + fLastMouseButtonActivityLine= -1; + } + + /** + * Returns an iterator over the <code>IVerticalRulerColumns</code> that make up this + * composite column. + * + * @return an iterator over the contained columns. + * @since 3.0 + */ + public Iterator getDecoratorIterator() { + Assert.isNotNull(fDecorators, "fDecorators must be initialized"); //$NON-NLS-1$ + return fDecorators.iterator(); + } + + /* + * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#getHover() + * @since 3.0 + */ + public IAnnotationHover getHover() { + return null; + } + + /* + * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(dwtx.jface.text.source.IVerticalRulerListener) + * @since 3.0 + */ + public void addVerticalRulerListener(IVerticalRulerListener listener) { + fAnnotationListeners.add(listener); + } + + /* + * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(dwtx.jface.text.source.IVerticalRulerListener) + * @since 3.0 + */ + public void removeVerticalRulerListener(IVerticalRulerListener listener) { + fAnnotationListeners.remove(listener); + } + + /** + * Fires the annotation selected event to all registered vertical ruler + * listeners. + * TODO use robust iterators + * + * @param event the event to fire + * @since 3.0 + */ + public void fireAnnotationSelected(VerticalRulerEvent event) { + // forward to listeners + for (Iterator it= fAnnotationListeners.iterator(); it.hasNext();) { + IVerticalRulerListener listener= (IVerticalRulerListener) it.next(); + listener.annotationSelected(event); + } + } + + /** + * Fires the annotation default selected event to all registered vertical + * ruler listeners. + * TODO use robust iterators + * + * @param event the event to fire + * @since 3.0 + */ + public void fireAnnotationDefaultSelected(VerticalRulerEvent event) { + // forward to listeners + for (Iterator it= fAnnotationListeners.iterator(); it.hasNext();) { + IVerticalRulerListener listener= (IVerticalRulerListener) it.next(); + listener.annotationDefaultSelected(event); + } + } + + /** + * Informs all registered vertical ruler listeners that the content menu on a selected annotation\ + * is about to be shown. + * TODO use robust iterators + * + * @param event the event to fire + * @param menu the menu that is about to be shown + * @since 3.0 + */ + public void fireAnnotationContextMenuAboutToShow(VerticalRulerEvent event, Menu menu) { + // forward to listeners + for (Iterator it= fAnnotationListeners.iterator(); it.hasNext();) { + IVerticalRulerListener listener= (IVerticalRulerListener) it.next(); + listener.annotationContextMenuAboutToShow(event, menu); + } + } + + /** + * Relayouts the receiver. + * + * @since 3.3 + */ + public void relayout() { + layoutTextViewer(); + } +}