001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.forms.widgets;
011:
012: import org.eclipse.jface.action.IMenuManager;
013: import org.eclipse.jface.action.MenuManager;
014: import org.eclipse.swt.SWT;
015: import org.eclipse.swt.dnd.DragSource;
016: import org.eclipse.swt.dnd.DragSourceEffect;
017: import org.eclipse.swt.dnd.DragSourceEvent;
018: import org.eclipse.swt.dnd.DragSourceListener;
019: import org.eclipse.swt.dnd.DropTarget;
020: import org.eclipse.swt.dnd.DropTargetListener;
021: import org.eclipse.swt.dnd.Transfer;
022: import org.eclipse.swt.events.MouseEvent;
023: import org.eclipse.swt.events.MouseMoveListener;
024: import org.eclipse.swt.events.MouseTrackListener;
025: import org.eclipse.swt.events.PaintEvent;
026: import org.eclipse.swt.events.PaintListener;
027: import org.eclipse.swt.graphics.Color;
028: import org.eclipse.swt.graphics.Font;
029: import org.eclipse.swt.graphics.FontMetrics;
030: import org.eclipse.swt.graphics.GC;
031: import org.eclipse.swt.graphics.Image;
032: import org.eclipse.swt.graphics.Point;
033: import org.eclipse.swt.graphics.Rectangle;
034: import org.eclipse.swt.widgets.Canvas;
035: import org.eclipse.swt.widgets.Composite;
036: import org.eclipse.swt.widgets.Control;
037: import org.eclipse.swt.widgets.Event;
038: import org.eclipse.swt.widgets.Label;
039: import org.eclipse.swt.widgets.Layout;
040: import org.eclipse.swt.widgets.Listener;
041: import org.eclipse.swt.widgets.Menu;
042: import org.eclipse.ui.forms.IFormColors;
043: import org.eclipse.ui.forms.widgets.ILayoutExtension;
044: import org.eclipse.ui.forms.widgets.SizeCache;
045: import org.eclipse.ui.forms.widgets.Twistie;
046: import org.eclipse.ui.internal.forms.IMessageToolTipManager;
047:
048: /**
049: * Form heading title.
050: */
051: public class TitleRegion extends Canvas {
052: public static final int STATE_NORMAL = 0;
053: public static final int STATE_HOVER_LIGHT = 1;
054: public static final int STATE_HOVER_FULL = 2;
055: private int hoverState;
056: private static final int HMARGIN = 1;
057: private static final int VMARGIN = 5;
058: private static final int SPACING = 5;
059: private static final int ARC_WIDTH = 20;
060: private static final int ARC_HEIGHT = 20;
061: private Image image;
062: private BusyIndicator busyLabel;
063: private Label titleLabel;
064: private SizeCache titleCache;
065: private int fontHeight = -1;
066: private int fontBaselineHeight = -1;
067: private MenuHyperlink menuHyperlink;
068: private MenuManager menuManager;
069: private boolean dragSupport;
070: private int dragOperations;
071: private Transfer[] dragTransferTypes;
072: private DragSourceListener dragListener;
073: private DragSource dragSource;
074: private Image dragImage;
075:
076: private class HoverListener implements MouseTrackListener,
077: MouseMoveListener {
078:
079: public void mouseEnter(MouseEvent e) {
080: setHoverState(STATE_HOVER_FULL);
081: }
082:
083: public void mouseExit(MouseEvent e) {
084: setHoverState(STATE_NORMAL);
085: }
086:
087: public void mouseHover(MouseEvent e) {
088: }
089:
090: public void mouseMove(MouseEvent e) {
091: if (e.button > 0)
092: setHoverState(STATE_NORMAL);
093: else
094: setHoverState(STATE_HOVER_FULL);
095: }
096: }
097:
098: private class MenuHyperlink extends Twistie {
099: private boolean firstTime = true;
100:
101: public MenuHyperlink(Composite parent, int style) {
102: super (parent, style);
103: setExpanded(true);
104: }
105:
106: public void setExpanded(boolean expanded) {
107: if (firstTime) {
108: super .setExpanded(expanded);
109: firstTime = false;
110: } else {
111: Menu menu = menuManager
112: .createContextMenu(menuHyperlink);
113: menu.setVisible(true);
114: }
115: }
116: }
117:
118: private class TitleRegionLayout extends Layout implements
119: ILayoutExtension {
120:
121: protected Point computeSize(Composite composite, int wHint,
122: int hHint, boolean flushCache) {
123: return layout(composite, false, 0, 0, wHint, hHint,
124: flushCache);
125: }
126:
127: protected void layout(Composite composite, boolean flushCache) {
128: Rectangle carea = composite.getClientArea();
129: layout(composite, true, carea.x, carea.y, carea.width,
130: carea.height, flushCache);
131: }
132:
133: private Point layout(Composite composite, boolean move, int x,
134: int y, int width, int height, boolean flushCache) {
135: int iwidth = width == SWT.DEFAULT ? SWT.DEFAULT : width
136: - HMARGIN * 2;
137: Point bsize = null;
138: Point tsize = null;
139: Point msize = null;
140:
141: if (busyLabel != null) {
142: bsize = busyLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
143: }
144: if (menuManager != null) {
145: menuHyperlink.setVisible(!menuManager.isEmpty()
146: && titleLabel.getVisible());
147: if (menuHyperlink.getVisible())
148: msize = menuHyperlink.computeSize(SWT.DEFAULT,
149: SWT.DEFAULT);
150: }
151: if (flushCache)
152: titleCache.flush();
153: titleCache.setControl(titleLabel);
154: int twidth = iwidth == SWT.DEFAULT ? iwidth : iwidth
155: - SPACING * 2;
156: if (bsize != null && twidth != SWT.DEFAULT)
157: twidth -= bsize.x + SPACING;
158: if (msize != null && twidth != SWT.DEFAULT)
159: twidth -= msize.x + SPACING;
160: if (titleLabel.getVisible()) {
161: tsize = titleCache.computeSize(twidth, SWT.DEFAULT);
162: if (twidth != SWT.DEFAULT) {
163: // correct for the case when width hint is larger
164: // than the maximum width - this is when the text
165: // can be rendered on one line with width to spare
166: int maxWidth = titleCache.computeSize(SWT.DEFAULT,
167: SWT.DEFAULT).x;
168: tsize.x = Math.min(tsize.x, maxWidth);
169: // System.out.println("twidth="+twidth+",
170: // tsize.x="+tsize.x); //$NON-NLS-1$//$NON-NLS-2$
171: }
172: } else
173: tsize = new Point(0, 0);
174: Point size = new Point(width, height);
175: if (!move) {
176: // compute size
177: size.x = tsize.x > 0 ? HMARGIN * 2 + SPACING * 2
178: + tsize.x : 0;
179: size.y = tsize.y;
180: if (bsize != null) {
181: size.x += bsize.x + SPACING;
182: size.y = Math.max(size.y, bsize.y);
183: }
184: if (msize != null) {
185: size.x += msize.x + SPACING;
186: size.y = Math.max(size.y, msize.y);
187: }
188: if (size.y > 0)
189: size.y += VMARGIN * 2;
190: // System.out.println("Compute size: width="+width+",
191: // size.x="+size.x); //$NON-NLS-1$ //$NON-NLS-2$
192: } else {
193: // position controls
194: int xloc = x + HMARGIN + SPACING;
195: int yloc = y + VMARGIN;
196: if (bsize != null) {
197: busyLabel.setBounds(xloc,
198: // yloc + height / 2 - bsize.y / 2,
199: yloc + getFontHeight() - 1 - bsize.y,
200: bsize.x, bsize.y);
201: xloc += bsize.x + SPACING;
202: }
203: if (titleLabel.getVisible()) {
204: int tw = width - HMARGIN * 2 - SPACING * 2;
205: if (bsize != null)
206: tw -= bsize.x + SPACING;
207: if (msize != null)
208: tw -= msize.x + SPACING;
209: titleLabel.setBounds(xloc,
210: // yloc + height / 2 - tsize.y / 2,
211: yloc, tw, tsize.y);
212: // System.out.println("tw="+tw); //$NON-NLS-1$
213: xloc += tw + SPACING;
214: }
215: if (msize != null) {
216: menuHyperlink.setBounds(xloc, yloc
217: + getFontHeight() / 2 - msize.y / 2,
218: msize.x, msize.y);
219: }
220: }
221: return size;
222: }
223:
224: public int computeMaximumWidth(Composite parent, boolean changed) {
225: return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT,
226: changed).x;
227: }
228:
229: public int computeMinimumWidth(Composite parent, boolean changed) {
230: return computeSize(parent, 0, SWT.DEFAULT, changed).x;
231: }
232: }
233:
234: public TitleRegion(Composite parent) {
235: super (parent, SWT.NULL);
236: titleLabel = new Label(this , SWT.WRAP);
237: titleLabel.setVisible(false);
238: titleCache = new SizeCache();
239: super .setLayout(new TitleRegionLayout());
240: hookHoverListeners();
241: addListener(SWT.Dispose, new Listener() {
242: public void handleEvent(Event e) {
243: if (dragImage != null) {
244: dragImage.dispose();
245: dragImage = null;
246: }
247: }
248: });
249: }
250:
251: private Color getColor(String key) {
252: return (Color) ((FormHeading) getParent()).colors.get(key);
253: }
254:
255: private void hookHoverListeners() {
256: HoverListener listener = new HoverListener();
257: addMouseTrackListener(listener);
258: addMouseMoveListener(listener);
259: titleLabel.addMouseTrackListener(listener);
260: titleLabel.addMouseMoveListener(listener);
261: addPaintListener(new PaintListener() {
262: public void paintControl(PaintEvent e) {
263: onPaint(e);
264: }
265: });
266: }
267:
268: private void onPaint(PaintEvent e) {
269: if (hoverState == STATE_NORMAL)
270: return;
271: GC gc = e.gc;
272: Rectangle carea = getClientArea();
273: gc.setBackground(getHoverBackground());
274: int savedAntialias = gc.getAntialias();
275: FormUtil.setAntialias(gc, SWT.ON);
276: gc.fillRoundRectangle(carea.x + HMARGIN, carea.y + 2,
277: carea.width - HMARGIN * 2, carea.height - 4, ARC_WIDTH,
278: ARC_HEIGHT);
279: FormUtil.setAntialias(gc, savedAntialias);
280: }
281:
282: private Color getHoverBackground() {
283: if (hoverState == STATE_NORMAL)
284: return null;
285: Color color = getColor(hoverState == STATE_HOVER_FULL ? IFormColors.H_HOVER_FULL
286: : IFormColors.H_HOVER_LIGHT);
287: if (color == null)
288: color = getDisplay()
289: .getSystemColor(
290: hoverState == STATE_HOVER_FULL ? SWT.COLOR_WIDGET_BACKGROUND
291: : SWT.COLOR_WIDGET_LIGHT_SHADOW);
292: return color;
293: }
294:
295: public void setHoverState(int state) {
296: if (dragSource == null || this .hoverState == state)
297: return;
298: this .hoverState = state;
299: Color color = getHoverBackground();
300: titleLabel.setBackground(color != null ? color
301: : getColor(FormHeading.COLOR_BASE_BG));
302: if (busyLabel != null)
303: busyLabel.setBackground(color != null ? color
304: : getColor(FormHeading.COLOR_BASE_BG));
305: if (menuHyperlink != null)
306: menuHyperlink.setBackground(color != null ? color
307: : getColor(FormHeading.COLOR_BASE_BG));
308: redraw();
309: }
310:
311: /**
312: * Fully delegates the size computation to the internal layout manager.
313: */
314: public final Point computeSize(int wHint, int hHint, boolean changed) {
315: return ((TitleRegionLayout) getLayout()).computeSize(this ,
316: wHint, hHint, changed);
317: }
318:
319: public final void setLayout(Layout layout) {
320: // do nothing
321: }
322:
323: public Image getImage() {
324: return image;
325: }
326:
327: public void setImage(Image image) {
328: this .image = image;
329: }
330:
331: public void updateImage(Image newImage, boolean doLayout) {
332: Image theImage = newImage != null ? newImage : this .image;
333:
334: if (theImage != null) {
335: ensureBusyLabelExists();
336: } else if (busyLabel != null) {
337: if (!busyLabel.isBusy()) {
338: busyLabel.dispose();
339: busyLabel = null;
340: }
341: }
342: if (busyLabel != null) {
343: busyLabel.setImage(theImage);
344: }
345: if (doLayout)
346: layout();
347: }
348:
349: public void updateToolTip(String toolTip) {
350: if (busyLabel != null)
351: busyLabel.setToolTipText(toolTip);
352: }
353:
354: public void setBackground(Color bg) {
355: super .setBackground(bg);
356: titleLabel.setBackground(bg);
357: if (busyLabel != null)
358: busyLabel.setBackground(bg);
359: if (menuHyperlink != null)
360: menuHyperlink.setBackground(bg);
361: }
362:
363: public void setForeground(Color fg) {
364: super .setForeground(fg);
365: titleLabel.setForeground(fg);
366: if (menuHyperlink != null)
367: menuHyperlink.setForeground(fg);
368: }
369:
370: public void setText(String text) {
371: if (text != null)
372: titleLabel.setText(text);
373: titleLabel.setVisible(text != null);
374: layout();
375: redraw();
376: }
377:
378: public String getText() {
379: return titleLabel.getText();
380: }
381:
382: public void setFont(Font font) {
383: super .setFont(font);
384: titleLabel.setFont(font);
385: fontHeight = -1;
386: fontBaselineHeight = -1;
387: layout();
388: }
389:
390: private void ensureBusyLabelExists() {
391: if (busyLabel == null) {
392: busyLabel = new BusyIndicator(this , SWT.NULL);
393: busyLabel
394: .setBackground(getColor(FormHeading.COLOR_BASE_BG));
395: HoverListener listener = new HoverListener();
396: busyLabel.addMouseTrackListener(listener);
397: busyLabel.addMouseMoveListener(listener);
398: if (menuManager != null)
399: busyLabel.setMenu(menuManager.createContextMenu(this ));
400: if (dragSupport)
401: addDragSupport(busyLabel, dragOperations,
402: dragTransferTypes, dragListener);
403: IMessageToolTipManager mng = ((FormHeading) getParent())
404: .getMessageToolTipManager();
405: if (mng != null)
406: mng.createToolTip(busyLabel, true);
407: }
408: }
409:
410: private void createMenuHyperlink() {
411: menuHyperlink = new MenuHyperlink(this , SWT.NULL);
412: menuHyperlink
413: .setBackground(getColor(FormHeading.COLOR_BASE_BG));
414: menuHyperlink.setDecorationColor(getForeground());
415: menuHyperlink.setHoverDecorationColor(getDisplay()
416: .getSystemColor(SWT.COLOR_LIST_FOREGROUND));
417: HoverListener listener = new HoverListener();
418: menuHyperlink.addMouseTrackListener(listener);
419: menuHyperlink.addMouseMoveListener(listener);
420: if (dragSupport)
421: addDragSupport(menuHyperlink, dragOperations,
422: dragTransferTypes, dragListener);
423: }
424:
425: /**
426: * Sets the form's busy state. Busy form will display 'busy' animation in
427: * the area of the title image.
428: *
429: * @param busy
430: * the form's busy state
431: */
432:
433: public boolean setBusy(boolean busy) {
434: if (busy)
435: ensureBusyLabelExists();
436: else if (busyLabel == null)
437: return false;
438: if (busy == busyLabel.isBusy())
439: return false;
440: busyLabel.setBusy(busy);
441: if (busyLabel.getImage() == null) {
442: layout();
443: return true;
444: }
445: return false;
446: }
447:
448: public boolean isBusy() {
449: return busyLabel != null && busyLabel.isBusy();
450: }
451:
452: /*
453: * Returns the complete height of the font.
454: */
455: public int getFontHeight() {
456: if (fontHeight == -1) {
457: Font font = getFont();
458: GC gc = new GC(getDisplay());
459: gc.setFont(font);
460: FontMetrics fm = gc.getFontMetrics();
461: fontHeight = fm.getHeight();
462: gc.dispose();
463: }
464: return fontHeight;
465: }
466:
467: /*
468: * Returns the height of the font starting at the baseline,
469: * i.e. without the descent.
470: */
471: public int getFontBaselineHeight() {
472: if (fontBaselineHeight == -1) {
473: Font font = getFont();
474: GC gc = new GC(getDisplay());
475: gc.setFont(font);
476: FontMetrics fm = gc.getFontMetrics();
477: fontBaselineHeight = fm.getHeight() - fm.getDescent();
478: gc.dispose();
479: }
480: return fontBaselineHeight;
481: }
482:
483: public IMenuManager getMenuManager() {
484: if (menuManager == null) {
485: menuManager = new MenuManager();
486: Menu menu = menuManager.createContextMenu(this );
487: setMenu(menu);
488: titleLabel.setMenu(menu);
489: if (busyLabel != null)
490: busyLabel.setMenu(menu);
491: createMenuHyperlink();
492: }
493: return menuManager;
494: }
495:
496: public void addDragSupport(int operations,
497: Transfer[] transferTypes, DragSourceListener listener) {
498: dragSupport = true;
499: dragOperations = operations;
500: dragTransferTypes = transferTypes;
501: dragListener = listener;
502: dragSource = addDragSupport(titleLabel, operations,
503: transferTypes, listener);
504: addDragSupport(this , operations, transferTypes, listener);
505: if (busyLabel != null)
506: addDragSupport(busyLabel, operations, transferTypes,
507: listener);
508: if (menuHyperlink != null)
509: addDragSupport(menuHyperlink, operations, transferTypes,
510: listener);
511: }
512:
513: private DragSource addDragSupport(Control control, int operations,
514: Transfer[] transferTypes, DragSourceListener listener) {
515: DragSource source = new DragSource(control, operations);
516: source.setTransfer(transferTypes);
517: source.addDragListener(listener);
518: source.setDragSourceEffect(new DragSourceEffect(control) {
519: public void dragStart(DragSourceEvent event) {
520: event.image = createDragEffectImage();
521: }
522: });
523: return source;
524: }
525:
526: private Image createDragEffectImage() {
527: /*
528: * if (dragImage != null) { dragImage.dispose(); } GC gc = new GC(this);
529: * Point size = getSize(); dragImage = new Image(getDisplay(), size.x,
530: * size.y); gc.copyArea(dragImage, 0, 0); gc.dispose(); return
531: * dragImage;
532: */
533: return null;
534: }
535:
536: public void addDropSupport(int operations,
537: Transfer[] transferTypes, DropTargetListener listener) {
538: final DropTarget target = new DropTarget(this, operations);
539: target.setTransfer(transferTypes);
540: target.addDropListener(listener);
541: }
542: }
|