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.beans.PropertyChangeEvent;
|
14
|
import java.util.Iterator;
|
15
|
|
16
|
import org.eclipse.swt.events.MenuEvent;
|
17
|
import org.eclipse.swt.events.MenuListener;
|
18
|
import org.eclipse.swt.graphics.Point;
|
19
|
import org.eclipse.swt.widgets.Display;
|
20
|
import org.eclipse.swt.widgets.Menu;
|
21
|
|
22
|
import org.eclipse.jface.action.MenuManager;
|
23
|
|
24
|
import org.eclipse.draw2d.ActionEvent;
|
25
|
import org.eclipse.draw2d.ActionListener;
|
26
|
import org.eclipse.draw2d.Border;
|
27
|
import org.eclipse.draw2d.BorderLayout;
|
28
|
import org.eclipse.draw2d.ButtonBorder;
|
29
|
import org.eclipse.draw2d.ButtonModel;
|
30
|
import org.eclipse.draw2d.ChangeEvent;
|
31
|
import org.eclipse.draw2d.ChangeListener;
|
32
|
import org.eclipse.draw2d.Clickable;
|
33
|
import org.eclipse.draw2d.ColorConstants;
|
34
|
import org.eclipse.draw2d.Figure;
|
35
|
import org.eclipse.draw2d.Graphics;
|
36
|
import org.eclipse.draw2d.IFigure;
|
37
|
import org.eclipse.draw2d.StackLayout;
|
38
|
import org.eclipse.draw2d.geometry.Dimension;
|
39
|
import org.eclipse.draw2d.geometry.Rectangle;
|
40
|
|
41
|
import org.eclipse.gef.GraphicalEditPart;
|
42
|
import org.eclipse.gef.Request;
|
43
|
import org.eclipse.gef.RequestConstants;
|
44
|
import org.eclipse.gef.palette.PaletteEntry;
|
45
|
import org.eclipse.gef.palette.PaletteListener;
|
46
|
import org.eclipse.gef.palette.PaletteStack;
|
47
|
import org.eclipse.gef.palette.ToolEntry;
|
48
|
import org.eclipse.gef.ui.actions.SetActivePaletteToolAction;
|
49
|
import org.eclipse.gef.ui.palette.PaletteViewer;
|
50
|
import org.eclipse.gef.ui.palette.editparts.PaletteEditPart;
|
51
|
|
52
|
/**
|
53
|
* The EditPart for a PaletteStack to be used on the toolbar.
|
54
|
*
|
55
|
* @author Whitney Sorenson
|
56
|
* @since 3.0
|
57
|
*/
|
58
|
public class PaletteStackEditPart extends PaletteEditPart implements
|
59
|
IPaletteStackEditPart {
|
60
|
|
61
|
private static final Dimension EMPTY_DIMENSION = new Dimension(0, 0);
|
62
|
|
63
|
// listen to changes of clickable tool figure
|
64
|
private ChangeListener clickableListener = new ChangeListener() {
|
65
|
public void handleStateChanged(ChangeEvent event) {
|
66
|
if (event.getPropertyName().equals(ButtonModel.MOUSEOVER_PROPERTY))
|
67
|
arrowFigure.getModel().setMouseOver(
|
68
|
activeFigure.getModel().isMouseOver());
|
69
|
else if (event.getPropertyName().equals(ButtonModel.ARMED_PROPERTY))
|
70
|
arrowFigure.getModel().setArmed(
|
71
|
activeFigure.getModel().isArmed());
|
72
|
}
|
73
|
};
|
74
|
|
75
|
// listen to changes of arrow figure
|
76
|
private ChangeListener clickableArrowListener = new ChangeListener() {
|
77
|
public void handleStateChanged(ChangeEvent event) {
|
78
|
if (event.getPropertyName().equals(ButtonModel.MOUSEOVER_PROPERTY))
|
79
|
activeFigure.getModel().setMouseOver(
|
80
|
arrowFigure.getModel().isMouseOver());
|
81
|
if (event.getPropertyName().equals(ButtonModel.ARMED_PROPERTY))
|
82
|
activeFigure.getModel().setArmed(
|
83
|
arrowFigure.getModel().isArmed());
|
84
|
}
|
85
|
};
|
86
|
|
87
|
// listen to see if arrow is pressed
|
88
|
private ActionListener actionListener = new ActionListener() {
|
89
|
public void actionPerformed(ActionEvent event) {
|
90
|
openMenu();
|
91
|
}
|
92
|
};
|
93
|
|
94
|
// listen to see if active tool is changed in palette
|
95
|
private PaletteListener paletteListener = new PaletteListener() {
|
96
|
public void activeToolChanged(PaletteViewer palette, ToolEntry tool) {
|
97
|
if (getStack().getChildren().contains(tool)) {
|
98
|
if (!arrowFigure.getModel().isSelected())
|
99
|
arrowFigure.getModel().setSelected(true);
|
100
|
if (!getStack().getActiveEntry().equals(tool))
|
101
|
getStack().setActiveEntry(tool);
|
102
|
} else
|
103
|
arrowFigure.getModel().setSelected(false);
|
104
|
}
|
105
|
};
|
106
|
|
107
|
private Clickable activeFigure;
|
108
|
private RolloverArrow arrowFigure;
|
109
|
private Figure contentsFigure;
|
110
|
private Menu menu;
|
111
|
|
112
|
/**
|
113
|
* Creates a new PaletteStackEditPart with the given PaletteStack as its
|
114
|
* model.
|
115
|
*
|
116
|
* @param model
|
117
|
* the PaletteStack to associate with this EditPart.
|
118
|
*/
|
119
|
public PaletteStackEditPart(PaletteStack model) {
|
120
|
super(model);
|
121
|
}
|
122
|
|
123
|
/**
|
124
|
* @see org.eclipse.gef.EditPart#activate()
|
125
|
*/
|
126
|
public void activate() {
|
127
|
// in case the model is out of sync
|
128
|
checkActiveEntrySync();
|
129
|
getPaletteViewer().addPaletteListener(paletteListener);
|
130
|
super.activate();
|
131
|
}
|
132
|
|
133
|
/**
|
134
|
* Called when the active entry has changed.
|
135
|
*
|
136
|
* @param oldValue
|
137
|
* the old model value (can be null)
|
138
|
* @param newValue
|
139
|
* the new model value (can be null)
|
140
|
*/
|
141
|
private void activeEntryChanged(Object oldValue, Object newValue) {
|
142
|
GraphicalEditPart part = null;
|
143
|
Clickable clickable = null;
|
144
|
|
145
|
if (newValue != null) {
|
146
|
part = (GraphicalEditPart) getViewer().getEditPartRegistry().get(
|
147
|
newValue);
|
148
|
clickable = (Clickable) part.getFigure();
|
149
|
clickable.setVisible(true);
|
150
|
clickable.addChangeListener(clickableListener);
|
151
|
activeFigure = clickable;
|
152
|
} else {
|
153
|
activeFigure = null;
|
154
|
}
|
155
|
|
156
|
if (oldValue != null) {
|
157
|
part = (GraphicalEditPart) getViewer().getEditPartRegistry().get(
|
158
|
oldValue);
|
159
|
// if part is null, its no longer a child.
|
160
|
if (part != null) {
|
161
|
clickable = (Clickable) part.getFigure();
|
162
|
clickable.setVisible(false);
|
163
|
clickable.removeChangeListener(clickableListener);
|
164
|
}
|
165
|
}
|
166
|
}
|
167
|
|
168
|
private void checkActiveEntrySync() {
|
169
|
if (activeFigure == null)
|
170
|
activeEntryChanged(null, getStack().getActiveEntry());
|
171
|
}
|
172
|
|
173
|
/**
|
174
|
* @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
|
175
|
*/
|
176
|
public IFigure createFigure() {
|
177
|
|
178
|
Figure figure = new Figure() {
|
179
|
public Dimension getPreferredSize(int wHint, int hHint) {
|
180
|
if (PaletteStackEditPart.this.getChildren().isEmpty())
|
181
|
return EMPTY_DIMENSION;
|
182
|
return super.getPreferredSize(wHint, hHint);
|
183
|
}
|
184
|
};
|
185
|
figure.setLayoutManager(new BorderLayout());
|
186
|
|
187
|
contentsFigure = new Figure();
|
188
|
StackLayout stackLayout = new StackLayout();
|
189
|
// make it so the stack layout does not allow the invisible figures to
|
190
|
// contribute
|
191
|
// to its bounds
|
192
|
stackLayout.setObserveVisibility(true);
|
193
|
contentsFigure.setLayoutManager(stackLayout);
|
194
|
figure.add(contentsFigure, BorderLayout.CENTER);
|
195
|
|
196
|
arrowFigure = new RolloverArrow();
|
197
|
arrowFigure.addChangeListener(clickableArrowListener);
|
198
|
arrowFigure.addActionListener(actionListener);
|
199
|
figure.add(arrowFigure, BorderLayout.RIGHT);
|
200
|
|
201
|
return figure;
|
202
|
}
|
203
|
|
204
|
/**
|
205
|
* @see org.eclipse.gef.EditPart#deactivate()
|
206
|
*/
|
207
|
public void deactivate() {
|
208
|
if (activeFigure != null)
|
209
|
activeFigure.removeChangeListener(clickableListener);
|
210
|
arrowFigure.removeActionListener(actionListener);
|
211
|
arrowFigure.removeChangeListener(clickableArrowListener);
|
212
|
getPaletteViewer().removePaletteListener(paletteListener);
|
213
|
super.deactivate();
|
214
|
}
|
215
|
|
216
|
/**
|
217
|
* @see org.eclipse.gef.EditPart#eraseTargetFeedback(org.eclipse.gef.Request)
|
218
|
*/
|
219
|
public void eraseTargetFeedback(Request request) {
|
220
|
Iterator children = getChildren().iterator();
|
221
|
|
222
|
while (children.hasNext()) {
|
223
|
PaletteEditPart part = (PaletteEditPart) children.next();
|
224
|
part.eraseTargetFeedback(request);
|
225
|
}
|
226
|
super.eraseTargetFeedback(request);
|
227
|
}
|
228
|
|
229
|
/**
|
230
|
* @see org.eclipse.gef.GraphicalEditPart#getContentPane()
|
231
|
*/
|
232
|
public IFigure getContentPane() {
|
233
|
return contentsFigure;
|
234
|
}
|
235
|
|
236
|
private PaletteStack getStack() {
|
237
|
return (PaletteStack) getModel();
|
238
|
}
|
239
|
|
240
|
/**
|
241
|
* Opens the menu to display the choices for the active entry.
|
242
|
*/
|
243
|
public void openMenu() {
|
244
|
MenuManager menuManager = new MenuManager();
|
245
|
|
246
|
Iterator children = getChildren().iterator();
|
247
|
PaletteEditPart part = null;
|
248
|
PaletteEntry entry = null;
|
249
|
while (children.hasNext()) {
|
250
|
part = (PaletteEditPart) children.next();
|
251
|
entry = (PaletteEntry) part.getModel();
|
252
|
|
253
|
menuManager
|
254
|
.add(new SetActivePaletteToolAction(getPaletteViewer(),
|
255
|
entry.getLabel(), entry.getSmallIcon(), getStack()
|
256
|
.getActiveEntry().equals(entry),
|
257
|
(ToolEntry) entry));
|
258
|
}
|
259
|
|
260
|
menu = menuManager.createContextMenu(getPaletteViewer().getControl());
|
261
|
|
262
|
// make the menu open below the figure
|
263
|
Rectangle figureBounds = getFigure().getBounds().getCopy();
|
264
|
getFigure().translateToAbsolute(figureBounds);
|
265
|
|
266
|
Point menuLocation = getPaletteViewer().getControl().toDisplay(
|
267
|
figureBounds.getBottomLeft().x, figureBounds.getBottomLeft().y);
|
268
|
|
269
|
// remove feedback from the arrow Figure and children figures
|
270
|
arrowFigure.getModel().setMouseOver(false);
|
271
|
eraseTargetFeedback(new Request(RequestConstants.REQ_SELECTION));
|
272
|
|
273
|
menu.setLocation(menuLocation);
|
274
|
menu.addMenuListener(new StackMenuListener(menu, getViewer()
|
275
|
.getControl().getDisplay()));
|
276
|
menu.setVisible(true);
|
277
|
}
|
278
|
|
279
|
/**
|
280
|
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
|
281
|
*/
|
282
|
public void propertyChange(PropertyChangeEvent event) {
|
283
|
if (event.getPropertyName().equals(PaletteStack.PROPERTY_ACTIVE_ENTRY))
|
284
|
activeEntryChanged(event.getOldValue(), event.getNewValue());
|
285
|
else
|
286
|
super.propertyChange(event);
|
287
|
}
|
288
|
|
289
|
/**
|
290
|
* @see org.eclipse.gef.editparts.AbstractEditPart#refreshChildren()
|
291
|
*/
|
292
|
protected void refreshChildren() {
|
293
|
super.refreshChildren();
|
294
|
checkActiveEntrySync();
|
295
|
Iterator children = getChildren().iterator();
|
296
|
while (children.hasNext()) {
|
297
|
PaletteEditPart editPart = (PaletteEditPart) children.next();
|
298
|
if (!editPart.getFigure().equals(activeFigure))
|
299
|
editPart.getFigure().setVisible(false);
|
300
|
}
|
301
|
}
|
302
|
|
303
|
/**
|
304
|
* @see org.eclipse.gef.EditPart#showTargetFeedback(org.eclipse.gef.Request)
|
305
|
*/
|
306
|
public void showTargetFeedback(Request request) {
|
307
|
// if menu is showing, don't show feedback. this is a fix
|
308
|
// for the occasion when show is called after forced erase
|
309
|
if (menu != null && !menu.isDisposed() && menu.isVisible())
|
310
|
return;
|
311
|
|
312
|
Iterator children = getChildren().iterator();
|
313
|
while (children.hasNext()) {
|
314
|
PaletteEditPart part = (PaletteEditPart) children.next();
|
315
|
part.showTargetFeedback(request);
|
316
|
}
|
317
|
|
318
|
super.showTargetFeedback(request);
|
319
|
}
|
320
|
|
321
|
public PaletteEditPart getActiveEntry() {
|
322
|
return (PaletteEditPart) getViewer().getEditPartRegistry().get(
|
323
|
getStack().getActiveEntry());
|
324
|
}
|
325
|
|
326
|
}
|
327
|
|
328
|
class StackMenuListener implements MenuListener {
|
329
|
|
330
|
private Menu menu;
|
331
|
private Display d;
|
332
|
|
333
|
/**
|
334
|
* Creates a new listener to listen to the menu that it used to select the
|
335
|
* active tool on a stack. Disposes the stack with an asyncExec after hidden
|
336
|
* is called.
|
337
|
*/
|
338
|
StackMenuListener(Menu menu, Display d) {
|
339
|
this.menu = menu;
|
340
|
this.d = d;
|
341
|
}
|
342
|
|
343
|
/**
|
344
|
* @see org.eclipse.swt.events.MenuListener#menuShown(org.eclipse.swt.events.MenuEvent)
|
345
|
*/
|
346
|
public void menuShown(MenuEvent e) {
|
347
|
}
|
348
|
|
349
|
/**
|
350
|
* @see org.eclipse.swt.events.MenuListener#menuHidden(org.eclipse.swt.events.MenuEvent)
|
351
|
*/
|
352
|
public void menuHidden(MenuEvent e) {
|
353
|
d.asyncExec(new Runnable() {
|
354
|
public void run() {
|
355
|
if (menu != null) {
|
356
|
if (!menu.isDisposed())
|
357
|
menu.dispose();
|
358
|
menu = null;
|
359
|
}
|
360
|
}
|
361
|
});
|
362
|
}
|
363
|
|
364
|
}
|
365
|
|
366
|
class RolloverArrow extends Clickable {
|
367
|
|
368
|
private static final Border BORDER_TOGGLE = new ButtonBorder(
|
369
|
ButtonBorder.SCHEMES.TOOLBAR);
|
370
|
|
371
|
/**
|
372
|
* Creates a new Clickable that paints a triangle figure.
|
373
|
*/
|
374
|
RolloverArrow() {
|
375
|
super();
|
376
|
setRolloverEnabled(true);
|
377
|
setBorder(BORDER_TOGGLE);
|
378
|
setBackgroundColor(ColorConstants.black);
|
379
|
setOpaque(false);
|
380
|
setPreferredSize(11, -1);
|
381
|
}
|
382
|
|
383
|
/**
|
384
|
* @return false so that the focus rectangle is not drawn.
|
385
|
*/
|
386
|
public boolean hasFocus() {
|
387
|
return false;
|
388
|
}
|
389
|
|
390
|
public void paintFigure(Graphics graphics) {
|
391
|
Rectangle rect = getClientArea();
|
392
|
|
393
|
graphics.translate(getLocation());
|
394
|
|
395
|
// fill the arrow
|
396
|
int[] points = new int[8];
|
397
|
|
398
|
points[0] = 3;
|
399
|
points[1] = rect.height / 2;
|
400
|
points[2] = 8;
|
401
|
points[3] = rect.height / 2;
|
402
|
points[4] = 5;
|
403
|
points[5] = 3 + rect.height / 2;
|
404
|
points[6] = 3;
|
405
|
points[7] = rect.height / 2;
|
406
|
|
407
|
graphics.fillPolygon(points);
|
408
|
|
409
|
graphics.translate(getLocation().getNegated());
|
410
|
}
|
411
|
|
412
|
}
|