Project

General

Profile

Download (16 KB) Statistics
| Branch: | Tag: | Revision:
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2010 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.gef.internal.ui.palette.editparts;
12

    
13
import java.util.Iterator;
14
import java.util.List;
15

    
16
import org.eclipse.swt.graphics.Color;
17
import org.eclipse.swt.graphics.Image;
18
import org.eclipse.swt.widgets.Control;
19
import org.eclipse.swt.widgets.Display;
20

    
21
import org.eclipse.draw2d.Animation;
22
import org.eclipse.draw2d.Border;
23
import org.eclipse.draw2d.BorderLayout;
24
import org.eclipse.draw2d.ButtonModel;
25
import org.eclipse.draw2d.ChangeEvent;
26
import org.eclipse.draw2d.ChangeListener;
27
import org.eclipse.draw2d.Clickable;
28
import org.eclipse.draw2d.ColorConstants;
29
import org.eclipse.draw2d.CompoundBorder;
30
import org.eclipse.draw2d.Figure;
31
import org.eclipse.draw2d.FigureUtilities;
32
import org.eclipse.draw2d.Graphics;
33
import org.eclipse.draw2d.IFigure;
34
import org.eclipse.draw2d.Label;
35
import org.eclipse.draw2d.LayoutManager;
36
import org.eclipse.draw2d.MarginBorder;
37
import org.eclipse.draw2d.MouseEvent;
38
import org.eclipse.draw2d.MouseListener;
39
import org.eclipse.draw2d.MouseMotionListener;
40
import org.eclipse.draw2d.SchemeBorder;
41
import org.eclipse.draw2d.ScrollPane;
42
import org.eclipse.draw2d.Toggle;
43
import org.eclipse.draw2d.ToolbarLayout;
44
import org.eclipse.draw2d.geometry.Dimension;
45
import org.eclipse.draw2d.geometry.Insets;
46
import org.eclipse.draw2d.geometry.Rectangle;
47

    
48
import org.eclipse.gef.internal.ui.palette.PaletteColorUtil;
49
import org.eclipse.gef.ui.palette.PaletteViewerPreferences;
50
import org.eclipse.gef.ui.palette.editparts.PaletteToolbarLayout;
51

    
52
/**
53
 * @author Pratik Shah
54
 */
55
public class DrawerFigure extends Figure {
56

    
57
	/** Foreground color constant **/
58
	protected static final Color FG_COLOR = FigureUtilities.mixColors(
59
			PaletteColorUtil.WIDGET_NORMAL_SHADOW,
60
			PaletteColorUtil.WIDGET_BACKGROUND);
61

    
62
	/** Scrollpane border constant for icon and column layout mode **/
63
	protected static final Border SCROLL_PANE_BORDER = new MarginBorder(2, 2,
64
			2, 2);
65
	/** Scrollpane border constant for list and details layout mode **/
66
	protected static final Border SCROLL_PANE_LIST_BORDER = new MarginBorder(2,
67
			0, 2, 0);
68
	/** Title margin border constant **/
69
	protected static final Border TITLE_MARGIN_BORDER = new MarginBorder(4, 2,
70
			2, 2);
71
	/** Toggle button border constant **/
72
	protected static final Border TOGGLE_BUTTON_BORDER = new RaisedBorder();
73
	/** Tooltip border constant **/
74
	protected static final Border TOOLTIP_BORDER = new CompoundBorder(
75
			new SchemeBorder(SchemeBorder.SCHEMES.RAISED), new MarginBorder(1));
76
	private Toggle collapseToggle;
77
	private Label drawerLabel, tipLabel;
78

    
79
	private boolean addedScrollpane = false;
80
	private int layoutMode = -1;
81
	private PinFigure pinFigure;
82
	private ScrollPane scrollpane;
83
	private boolean showPin = true, skipNextEvent;
84
	private EditPartTipHelper tipHelper;
85

    
86
	/**
87
	 * This is the figure for the entire drawer label button.
88
	 */
89
	private class CollapseToggle extends Toggle {
90

    
91
		public CollapseToggle(IFigure contents) {
92
			super(contents);
93
			setSelected(true);
94
			setRequestFocusEnabled(true);
95
			addChangeListener(new ChangeListener() {
96

    
97
				public void handleStateChanged(ChangeEvent e) {
98
					if (e.getPropertyName().equals(
99
							ButtonModel.SELECTED_PROPERTY)) {
100
						Animation.markBegin();
101
						handleExpandStateChanged();
102
						Animation.run(150);
103
					} else if (e.getPropertyName().equals(
104
							ButtonModel.MOUSEOVER_PROPERTY)) {
105
						repaint();
106
					}
107
				}
108
			});
109
		}
110

    
111
		public IFigure getToolTip() {
112
			return buildTooltip();
113
		}
114

    
115
		protected void paintFigure(Graphics g) {
116
			super.paintFigure(g);
117
			Rectangle r = Rectangle.SINGLETON;
118
			r.setBounds(getBounds());
119

    
120
			// draw top border of drawer figure
121
			g.setForegroundColor(PaletteColorUtil.WIDGET_NORMAL_SHADOW);
122
			g.drawLine(r.getTopLeft(), r.getTopRight());
123
			g.setForegroundColor(ColorConstants.white);
124
			g.drawLine(r.getTopLeft().getTranslated(0, 1), r.getTopRight()
125
					.getTranslated(0, 1));
126
			r.crop(new Insets(2, 0, 0, 0));
127
			if (isExpanded()) {
128
				g.setForegroundColor(PaletteColorUtil.WIDGET_BACKGROUND_NORMAL_SHADOW_65);
129
				g.drawLine(r.getLocation(), r.getTopRight());
130
				r.crop(new Insets(1, 0, 0, 0));
131
			}
132

    
133
			// draw bottom border of drawer figure
134
			if (!isExpanded()) {
135
				g.setForegroundColor(ColorConstants.white);
136
				g.drawLine(r.getBottomLeft().getTranslated(0, -1), r
137
						.getBottomRight().getTranslated(0, -1));
138
				r.crop(new Insets(0, 0, 1, 0));
139
			}
140

    
141
			paintToggleGradient(g, r);
142

    
143
		}
144
	}
145

    
146
	/**
147
	 * Constructor
148
	 * 
149
	 * @param control
150
	 *            The Control of the LWS to which this Figure belongs (it is
151
	 *            used to display the drawer header with an EditPartTipHelper,
152
	 *            if the header is not completely visible). It can be
153
	 *            <code>null</code> (the tip won't be displayed).
154
	 */
155
	public DrawerFigure(final Control control) {
156
		/*
157
		 * A PaletteToolbarLayout is being used here instead of a ToolbarLayout
158
		 * so that the ScrollPane can be stretched to take up vertical space.
159
		 * This affects selection and appearance (background color).
160
		 */
161
		setLayoutManager(new PaletteToolbarLayout() {
162
			protected boolean isChildGrowing(IFigure child) {
163
				int wHint = child.getBounds().width;
164
				return child.getPreferredSize(wHint, -1).height != child
165
						.getMinimumSize(wHint, -1).height;
166
			}
167
		});
168

    
169
		Figure title = new Figure();
170
		title.setBorder(TITLE_MARGIN_BORDER);
171
		BorderLayout borderLayout = new BorderLayout();
172
		borderLayout.setHorizontalSpacing(2);
173
		title.setLayoutManager(borderLayout);
174

    
175
		drawerLabel = new Label();
176
		drawerLabel.setLabelAlignment(Label.LEFT);
177

    
178
		pinFigure = new PinFigure();
179

    
180
		title.add(pinFigure, BorderLayout.RIGHT);
181
		title.add(drawerLabel, BorderLayout.CENTER);
182

    
183
		collapseToggle = new CollapseToggle(title);
184

    
185
		/*
186
		 * @TODO:Pratik
187
		 * 
188
		 * There is a bug here. Right-click on the name pop-up for the header of
189
		 * a drawer figure in the palette. This will hide the pop-up.
190
		 * Right-click again, this time on the collapse toggle, to bring up the
191
		 * drawer's context menu. Now, left-click on the collapse toggle. The
192
		 * context menu will disappear, the name pop-up will re-appear, and the
193
		 * drawer will collapse/expand. If the drawer was in such a position,
194
		 * that collapsing/expanding it will cause its header to move, the name
195
		 * pop-up will now be floating over where the collapse toggle used to
196
		 * be, but is not anymore. To fix this, you can detect the left mouse
197
		 * click on the collapse toggle and hide the name pop-up then. The
198
		 * problem is that when you click on the collapseToggle after the
199
		 * context menu has been brought up, it does not fire a mouse pressed
200
		 * event. The listener below, that is commented out for now, is never
201
		 * notified.
202
		 */
203
		// collapseToggle.addMouseListener(new MouseListener.Stub(){
204
		// public void mousePressed(MouseEvent me) {
205
		// System.out.println("AAA");
206
		// }
207
		// });
208

    
209
		add(collapseToggle);
210
		createScrollpane();
211
		createHoverHelp(control);
212
	}
213

    
214
	/**
215
	 * Paints the background gradient on the drawer toggle figure.
216
	 * 
217
	 * @param g
218
	 *            the graphics object
219
	 * @param rect
220
	 *            the rectangle which the background gradient should cover
221
	 */
222
	private void paintToggleGradient(Graphics g, Rectangle rect) {
223
		if (isExpanded()) {
224
			g.setBackgroundColor(PaletteColorUtil.WIDGET_BACKGROUND_LIST_BACKGROUND_85);
225
			g.fillRectangle(rect);
226
		} else if (collapseToggle.getModel().isMouseOver()) {
227
			Color color1 = PaletteColorUtil.WIDGET_BACKGROUND_LIST_BACKGROUND_60;
228
			Color color2 = PaletteColorUtil.WIDGET_BACKGROUND_NORMAL_SHADOW_90;
229
			Color color3 = PaletteColorUtil.WIDGET_BACKGROUND_NORMAL_SHADOW_95;
230
			Color color4 = PaletteColorUtil.WIDGET_BACKGROUND_LIST_BACKGROUND_90;
231

    
232
			g.setForegroundColor(color1);
233
			g.setBackgroundColor(color2);
234
			g.fillGradient(rect.x, rect.y, rect.width, rect.height - 4, true);
235

    
236
			g.setForegroundColor(color2);
237
			g.setBackgroundColor(color3);
238
			g.fillGradient(rect.x, rect.bottom() - 4, rect.width, 2, true);
239

    
240
			g.setForegroundColor(color3);
241
			g.setBackgroundColor(color4);
242
			g.fillGradient(rect.x, rect.bottom() - 2, rect.width, 2, true);
243
		} else {
244
			g.setForegroundColor(PaletteColorUtil.WIDGET_BACKGROUND_LIST_BACKGROUND_85);
245
			g.setBackgroundColor(PaletteColorUtil.WIDGET_BACKGROUND_NORMAL_SHADOW_45);
246
			g.fillGradient(rect, true);
247
		}
248
	}
249

    
250
	private void createHoverHelp(final Control control) {
251
		if (control == null) {
252
			return;
253
		}
254
		// If a control was provided, create the tipLabel -- if the text in the
255
		// header is
256
		// truncated, it will display it as a tooltip.
257
		tipLabel = new Label() {
258
			/**
259
			 * @see org.eclipse.draw2d.Figure#getToolTip()
260
			 */
261
			public IFigure getToolTip() {
262
				return buildTooltip();
263
			}
264

    
265
			protected void paintFigure(Graphics graphics) {
266
				Rectangle r = Rectangle.SINGLETON;
267
				r.setBounds(getBounds());
268
				graphics.pushState();
269
				paintToggleGradient(graphics, getBounds());
270
				graphics.popState();
271
				super.paintFigure(graphics);
272
			}
273
		};
274
		tipLabel.setOpaque(false);
275
		tipLabel.setBorder(TOOLTIP_BORDER);
276
		collapseToggle.addMouseMotionListener(new MouseMotionListener.Stub() {
277
			public void mouseMoved(MouseEvent e) {
278
				if (!drawerLabel.getBounds().contains(e.getLocation()))
279
					return;
280
				if (skipNextEvent) {
281
					skipNextEvent = false;
282
					return;
283
				}
284
				if (drawerLabel.isTextTruncated()
285
						&& !EditPartTipHelper.isCurrent(tipHelper)) {
286
					tipLabel.setText(drawerLabel.getText());
287
					tipLabel.setIcon(drawerLabel.getIcon());
288
					tipLabel.setFont(drawerLabel.getFont());
289
					tipHelper = new EditPartTipHelper(control);
290
					Rectangle bounds = drawerLabel.getBounds()
291
							.getExpanded(2, 2);
292
					drawerLabel.translateToAbsolute(bounds);
293
					org.eclipse.swt.graphics.Rectangle loc = new org.eclipse.swt.graphics.Rectangle(
294
							bounds.x, bounds.y, bounds.width, bounds.height);
295
					loc = Display.getCurrent().map(control, null, loc);
296
					tipHelper.displayToolTipAt(tipLabel, loc.x, loc.y);
297
				}
298
			}
299
		});
300
		tipLabel.addMouseListener(new MouseListener.Stub() {
301
			public void mousePressed(MouseEvent e) {
302
				if (e.button == 1) {
303
					Rectangle original = getCollapseToggle().getBounds()
304
							.getCopy();
305
					getCollapseToggle().requestFocus();
306
					setExpanded(!isExpanded());
307
					// Hide the tip if expanding the drawer causes the collapse
308
					// toggle to move
309
					if (!original.equals(getCollapseToggle().getBounds())) {
310
						tipHelper.hide();
311
					}
312
				} else {
313
					tipHelper.hide();
314
					if (e.button == 3) {
315
						skipNextEvent = true;
316
					}
317
				}
318
			}
319
		});
320
	}
321

    
322
	private void createScrollpane() {
323
		scrollpane = new ScrollPane();
324
		scrollpane.getViewport().setContentsTracksWidth(true);
325
		scrollpane.setMinimumSize(new Dimension(0, 0));
326
		scrollpane.setHorizontalScrollBarVisibility(ScrollPane.NEVER);
327
		scrollpane.setVerticalScrollBar(new PaletteScrollBar());
328
		scrollpane.getVerticalScrollBar().setStepIncrement(20);
329
		scrollpane.setLayoutManager(new OverlayScrollPaneLayout());
330
		scrollpane.setContents(new Figure());
331
		scrollpane.getContents().setOpaque(true);
332
		scrollpane.getContents().setBackgroundColor(
333
				PaletteColorUtil.WIDGET_LIST_BACKGROUND);
334
	}
335

    
336
	IFigure buildTooltip() {
337
		return null;
338
	}
339

    
340
	/**
341
	 * @return The Clickable that is used to expand/collapse the drawer.
342
	 */
343
	public Clickable getCollapseToggle() {
344
		return collapseToggle;
345
	}
346

    
347
	/**
348
	 * @return The content pane for this figure, i.e. the Figure to which
349
	 *         children can be added.
350
	 */
351
	public IFigure getContentPane() {
352
		return scrollpane.getContents();
353
	}
354

    
355
	/**
356
	 * @see Figure#getMinimumSize(int, int)
357
	 */
358
	public Dimension getMinimumSize(int wHint, int hHint) {
359
		/*
360
		 * Fix related to Bug #35176 The figure returns a minimum size that is
361
		 * of at least a certain height, so as to prevent each drawer from
362
		 * getting too small (in which case, the scrollbars cover up the entire
363
		 * available space).
364
		 */
365
		if (isExpanded()) {
366
			List children = getContentPane().getChildren();
367
			if (!children.isEmpty()) {
368
				Dimension result = collapseToggle
369
						.getPreferredSize(wHint, hHint).getCopy();
370
				result.height += getContentPane().getInsets().getHeight();
371
				IFigure child = (IFigure) children.get(0);
372
				result.height += Math.min(80,
373
						child.getPreferredSize(wHint, -1).height + 9);
374
				return result.intersect(getPreferredSize(wHint, hHint));
375
			}
376
		}
377

    
378
		return super.getMinimumSize(wHint, hHint);
379
	}
380

    
381
	/**
382
	 * Returns the ScrollPane associated with this DrawerFigure
383
	 * 
384
	 * @return the ScrollPane
385
	 */
386
	public ScrollPane getScrollpane() {
387
		return scrollpane;
388
	}
389

    
390
	protected void handleExpandStateChanged() {
391
		if (isExpanded()) {
392
			if (scrollpane.getParent() != this)
393
				add(scrollpane);
394
		} else {
395
			if (scrollpane.getParent() == this)
396
				remove(scrollpane);
397

    
398
			// collapse all pinnable palette stack children that aren't pinned
399
			for (Iterator iterator = getContentPane().getChildren().iterator(); iterator
400
					.hasNext();) {
401
				Object child = iterator.next();
402
				if (child instanceof PinnablePaletteStackFigure
403
						&& !((PinnablePaletteStackFigure) child).isPinnedOpen()) {
404
					((PinnablePaletteStackFigure) child).setExpanded(false);
405
				}
406
			}
407

    
408
		}
409

    
410
		if (pinFigure != null) {
411
			pinFigure.setVisible(isExpanded() && showPin);
412
		}
413
	}
414

    
415
	/**
416
	 * @return <code>true</code> if the drawer is expanded
417
	 */
418
	public boolean isExpanded() {
419
		return collapseToggle.isSelected();
420
	}
421

    
422
	/**
423
	 * @return <code>true</code> if the drawer is expanded and is pinned (i.e.,
424
	 *         it can't be automatically collapsed)
425
	 */
426
	public boolean isPinnedOpen() {
427
		return isExpanded() && pinFigure.isVisible() && pinFigure.isSelected();
428
	}
429

    
430
	/**
431
	 * @return <code>true</code> if the drawer is expanded and its pin is
432
	 *         showing
433
	 */
434
	public boolean isPinShowing() {
435
		return isExpanded() && showPin;
436
	}
437

    
438
	public void setAnimating(boolean isAnimating) {
439
		if (isAnimating) {
440
			if (scrollpane.getParent() != this) {
441
				addedScrollpane = true;
442
				add(scrollpane);
443
			}
444
			scrollpane.setVerticalScrollBarVisibility(ScrollPane.NEVER);
445
		} else {
446
			scrollpane.setVerticalScrollBarVisibility(ScrollPane.AUTOMATIC);
447
			if (addedScrollpane) {
448
				remove(scrollpane);
449
				addedScrollpane = false;
450
			}
451
		}
452
	}
453

    
454
	public void setExpanded(boolean value) {
455
		collapseToggle.setSelected(value);
456
	}
457

    
458
	public void setLayoutMode(int layoutMode) {
459
		if (this.layoutMode == layoutMode) {
460
			return;
461
		}
462

    
463
		this.layoutMode = layoutMode;
464

    
465
		LayoutManager manager;
466
		if (layoutMode == PaletteViewerPreferences.LAYOUT_COLUMNS) {
467
			manager = new ColumnsLayout();
468
			getContentPane().setBorder(SCROLL_PANE_BORDER);
469
		} else if (layoutMode == PaletteViewerPreferences.LAYOUT_ICONS) {
470
			PaletteContainerFlowLayout fl = new PaletteContainerFlowLayout();
471
			fl.setMinorSpacing(0);
472
			fl.setMajorSpacing(0);
473
			manager = fl;
474
			getContentPane().setBorder(SCROLL_PANE_BORDER);
475
		} else {
476
			manager = new ToolbarLayout();
477
			getContentPane().setBorder(SCROLL_PANE_LIST_BORDER);
478
		}
479
		getContentPane().setLayoutManager(manager);
480
	}
481

    
482
	/**
483
	 * Pins or unpins the drawer. The drawer can be pinned open only when it is
484
	 * expanded. Attempts to pin it when it is collapsed will do nothing.
485
	 * 
486
	 * @param pinned
487
	 *            <code>true</code> if the drawer is to be pinned
488
	 */
489
	public void setPinned(boolean pinned) {
490
		if (!isExpanded() || !showPin) {
491
			return;
492
		}
493

    
494
		pinFigure.setSelected(pinned);
495
	}
496

    
497
	/**
498
	 * Displays the given text in the drawer's header as its title.
499
	 * 
500
	 * @param s
501
	 *            The title of the drawer
502
	 */
503
	public void setTitle(String s) {
504
		drawerLabel.setText(s);
505
	}
506

    
507
	/**
508
	 * Displays the given image in the header as the drawer's icon.
509
	 * 
510
	 * @param icon
511
	 *            The icon for this drawer.
512
	 */
513
	public void setTitleIcon(Image icon) {
514
		drawerLabel.setIcon(icon);
515
	}
516

    
517
	public void showPin(boolean show) {
518
		showPin = show;
519
		handleExpandStateChanged();
520
	}
521

    
522
}
(4-4/22)