1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.gallery;
\r
14 import java.awt.Color;
\r
15 import java.awt.Component;
\r
16 import java.awt.Font;
\r
17 import java.awt.FontMetrics;
\r
18 import java.awt.Paint;
\r
19 import java.awt.Shape;
\r
20 import java.awt.TexturePaint;
\r
21 import java.awt.geom.Rectangle2D;
\r
22 import java.awt.image.BufferedImage;
\r
23 import java.util.ArrayList;
\r
24 import java.util.Arrays;
\r
25 import java.util.Collections;
\r
26 import java.util.HashSet;
\r
27 import java.util.List;
\r
28 import java.util.Set;
\r
29 import java.util.concurrent.Semaphore;
\r
30 import java.util.concurrent.TimeUnit;
\r
31 import java.util.function.Consumer;
\r
33 import org.eclipse.jface.resource.FontRegistry;
\r
34 import org.eclipse.jface.util.IPropertyChangeListener;
\r
35 import org.eclipse.jface.util.PropertyChangeEvent;
\r
36 import org.eclipse.jface.viewers.ContentViewer;
\r
37 import org.eclipse.jface.viewers.ISelection;
\r
38 import org.eclipse.jface.viewers.IStructuredContentProvider;
\r
39 import org.eclipse.jface.viewers.IStructuredSelection;
\r
40 import org.eclipse.jface.viewers.StructuredSelection;
\r
41 import org.eclipse.jface.viewers.ViewerFilter;
\r
42 import org.eclipse.swt.SWT;
\r
43 import org.eclipse.swt.events.ControlEvent;
\r
44 import org.eclipse.swt.events.ControlListener;
\r
45 import org.eclipse.swt.graphics.FontData;
\r
46 import org.eclipse.swt.graphics.Point;
\r
47 import org.eclipse.swt.widgets.Composite;
\r
48 import org.eclipse.swt.widgets.Control;
\r
49 import org.eclipse.swt.widgets.Display;
\r
50 import org.eclipse.swt.widgets.Event;
\r
51 import org.eclipse.swt.widgets.Listener;
\r
52 import org.simantics.g2d.canvas.Hints;
\r
53 import org.simantics.g2d.canvas.ICanvasContext;
\r
54 import org.simantics.g2d.canvas.impl.CanvasContext;
\r
55 import org.simantics.g2d.chassis.SWTChassis;
\r
56 import org.simantics.g2d.diagram.DiagramClass;
\r
57 import org.simantics.g2d.diagram.DiagramHints;
\r
58 import org.simantics.g2d.diagram.IDiagram;
\r
59 import org.simantics.g2d.diagram.handler.DataElementMap;
\r
60 import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;
\r
61 import org.simantics.g2d.diagram.handler.layout.FlowLayout;
\r
62 import org.simantics.g2d.diagram.impl.Diagram;
\r
63 import org.simantics.g2d.diagram.participant.DiagramParticipant;
\r
64 import org.simantics.g2d.diagram.participant.ElementInteractor;
\r
65 import org.simantics.g2d.diagram.participant.ElementPainter;
\r
66 import org.simantics.g2d.diagram.participant.Selection;
\r
67 import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;
\r
68 import org.simantics.g2d.dnd.IDragSourceParticipant;
\r
69 import org.simantics.g2d.dnd.IDropTargetParticipant;
\r
70 import org.simantics.g2d.element.ElementClass;
\r
71 import org.simantics.g2d.element.ElementHints;
\r
72 import org.simantics.g2d.element.ElementUtils;
\r
73 import org.simantics.g2d.element.IElement;
\r
74 import org.simantics.g2d.element.handler.Clickable;
\r
75 import org.simantics.g2d.element.handler.Resize;
\r
76 import org.simantics.g2d.element.handler.impl.DefaultTransform;
\r
77 import org.simantics.g2d.element.handler.impl.Resizeable;
\r
78 import org.simantics.g2d.element.handler.impl.TextImpl;
\r
79 import org.simantics.g2d.element.impl.Element;
\r
80 import org.simantics.g2d.image.Image;
\r
81 import org.simantics.g2d.image.Image.Feature;
\r
82 import org.simantics.g2d.image.Image.ImageListener;
\r
83 import org.simantics.g2d.image.impl.Shadow.ShadowParameters;
\r
84 import org.simantics.g2d.participant.BackgroundPainter;
\r
85 import org.simantics.g2d.participant.KeyToCommand;
\r
86 import org.simantics.g2d.participant.KeyUtil;
\r
87 import org.simantics.g2d.participant.MouseUtil;
\r
88 import org.simantics.g2d.participant.SymbolUtil;
\r
89 import org.simantics.g2d.participant.TransformUtil;
\r
90 import org.simantics.g2d.tooltip.TooltipParticipant;
\r
91 import org.simantics.g2d.utils.FontHelper;
\r
92 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
93 import org.simantics.scenegraph.g2d.events.command.CommandKeyBinding;
\r
94 import org.simantics.scenegraph.g2d.nodes.ShapeNode;
\r
95 import org.simantics.scenegraph.utils.GeometryUtils;
\r
96 import org.simantics.scenegraph.utils.TextUtil;
\r
97 import org.simantics.utils.datastructures.hints.IHintContext;
\r
98 import org.simantics.utils.datastructures.hints.IHintContext.Key;
\r
99 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
\r
100 import org.simantics.utils.threads.AWTThread;
\r
101 import org.simantics.utils.threads.IThreadWorkQueue;
\r
102 import org.simantics.utils.threads.SWTThread;
\r
103 import org.simantics.utils.threads.ThreadUtils;
\r
104 import org.simantics.utils.threads.logger.ITask;
\r
105 import org.simantics.utils.threads.logger.ThreadLogger;
\r
108 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
\r
109 * @author Tuukka Lehtonen
\r
111 public class GalleryViewer extends ContentViewer {
\r
114 * A hint key for storing a GalleryViewer within the hint stack of
\r
115 * {@link GalleryViewer}'s {@link ICanvasContext}.
\r
117 public static final Key KEY_VIEWER = new KeyOf(GalleryViewer.class, "GALLERY_VIEWER");
\r
119 public static final ShadowParameters SHADOW = new ShadowParameters(0.5, Color.BLACK, 5);
\r
121 ViewerFilter filter;
\r
122 IThreadWorkQueue swtThread;
\r
124 SWTChassis chassis;
\r
125 Component awtComponent;
\r
127 Selection selection;
\r
128 GalleryItemPainter itemPainter;
\r
130 /** element size */
\r
131 Rectangle2D itemSize = new Rectangle2D.Double(0, 0, 50, 50);
\r
132 Rectangle2D maxItemSize = itemSize.getBounds2D();
\r
133 FlowLayout fl = new FlowLayout();
\r
136 ElementClass itemClass;
\r
137 FontRegistry fontRegistry;
\r
138 Font currentItemFont;
\r
140 GalleryTooltipProvider tooltipProvider = new GalleryTooltipProvider();
\r
142 /** Background paint */
\r
143 public static final Paint BG_PAINT;
\r
145 // Create checkerboard pattern paint with 5x5 square size
\r
146 BufferedImage bi = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
\r
147 for (int x = 0; x < 2; ++x)
\r
148 for (int y = 0; y < 2; ++y)
\r
149 bi.setRGB(x, y, (((x ^ y) & 1) == 0) ? 0xffffffff : 0xfffdfdfd);
\r
150 BG_PAINT = new TexturePaint(bi, new Rectangle2D.Double(0, 0, 10, 10));
\r
151 //BG_PAINT = Color.WHITE;
\r
154 public GalleryViewer(Composite composite) {
\r
155 this(composite, 0);
\r
158 public GalleryViewer(Composite composite, int style) {
\r
160 display = composite.getDisplay();
\r
161 swtThread = SWTThread.getThreadAccess(composite);
\r
162 chassis = new SWTChassis(composite, style) {
\r
164 public Point computeSize(int wHint, int hHint, boolean changed) {
\r
165 // System.out.println("chassis compute size: " + wHint + ", " + hHint + ", " + changed);
\r
167 if (diagram == null)
\r
168 return super.computeSize(wHint, hHint, changed);
\r
172 // rect = ElementUtils.getSurroundingElementBoundsOnDiagram(diagram.getSnapshot());
\r
176 Double wH = wHint==SWT.DEFAULT ? null : (double) wHint-vMargin-vMargin;
\r
177 Double hH = hHint==SWT.DEFAULT ? null : (double) hHint-hMargin-hMargin;
\r
178 rect = fl.computeSize(diagram, wH, hH);
\r
180 return new Point((int)rect.getMaxX()+hMargin*2, (int)rect.getMaxY()+vMargin*2);
\r
184 // Create IDiagram for gallery
\r
185 this.diagram = Diagram.spawnNew(DiagramClass.DEFAULT);
\r
186 diagram.setHint(FlowLayout.HGAP, 5.0);
\r
187 diagram.setHint(FlowLayout.VGAP, 5.0);
\r
188 diagram.setHint(DiagramHints.KEY_ELEMENT_RASTER_TARGET_SIZE,
\r
189 new java.awt.Point((int) itemSize.getWidth(), (int) itemSize.getHeight()));
\r
191 // Create canvas context here in SWT thread but do not initialize it yet.
\r
192 this.ctx = new CanvasContext(AWTThread.getThreadAccess());
\r
194 chassis.populate(parameter -> {
\r
195 awtComponent = parameter.getAWTComponent();
\r
197 // Initialize the canvas context
\r
198 ITask task = ThreadLogger.getInstance().begin("createCanvas");
\r
199 initializeCanvasContext(ctx);
\r
202 // Initialize canvas context hint context with KEY_VIEWER key.
\r
203 IHintContext hintCtx = ctx.getDefaultHintContext();
\r
204 hintCtx.setHint(KEY_VIEWER, GalleryViewer.this);
\r
206 // Set IDiagram for canvas context
\r
207 hintCtx.setHint(DiagramHints.KEY_DIAGRAM, diagram);
\r
210 ThreadUtils.asyncExec(swtThread, new Runnable() {
\r
212 public void run() {
\r
218 chassis.addControlListener(new ControlListener() {
\r
220 public void controlMoved(ControlEvent e) {}
\r
222 public void controlResized(ControlEvent e) {
\r
226 ITask task2 = ThreadLogger.getInstance().begin("fonts");
\r
228 this.fontRegistry = FontHelper.getCurrentThemeFontRegistry();
\r
229 fontRegistry.addListener(fontRegistryListener);
\r
230 currentItemFont = FontHelper.toAwt(fontRegistry, "org.simantics.gallery.itemfont");
\r
231 } catch (IllegalStateException e) {
\r
232 // No workbench available, use SWT control fonts for viewer items.
\r
233 org.eclipse.swt.graphics.Font f = chassis.getFont();
\r
234 currentItemFont = FontHelper.toAwt(f.getFontData()[0]);
\r
239 itemClass = ElementClass.compile(
\r
240 DefaultTransform.INSTANCE,
\r
242 //TextAsTooltip.INSTANCE,
\r
243 Clickable.INSTANCE,
\r
244 Resizeable.UNCONSTRICTED,
\r
245 new GalleryItemSGNode(currentItemFont)
\r
248 chassis.addListener(SWT.Dispose, new Listener() {
\r
250 public void handleEvent(Event event) {
\r
251 if (fontRegistry != null)
\r
252 fontRegistry.removeListener(fontRegistryListener);
\r
254 // Prevent memory leaks.
\r
255 ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() {
\r
257 public void run() {
\r
258 chassis.getAWTComponent().setCanvasContext(null);
\r
266 IPropertyChangeListener fontRegistryListener = new IPropertyChangeListener() {
\r
268 public void propertyChange(PropertyChangeEvent event) {
\r
269 FontData fdn = ((FontData[]) event.getNewValue())[0];
\r
270 //System.out.println(event.getSource() + ": font changed: " + event.getProperty() + ": " + fdn);
\r
271 currentItemFont = FontHelper.toAwt(fdn);
\r
272 itemClass.getSingleItem(GalleryItemSGNode.class).setFont(currentItemFont);
\r
273 // FIXME: a bug exists in this case. The group size will not be refreshed even though the sizes of the gallery items are recalculated and changed.
\r
274 ThreadUtils.asyncExec(swtThread, new Runnable() {
\r
276 public void run() {
\r
284 * Set alignment of elements (left, right, center, fill)
\r
287 public void setAlign(FlowLayout.Align align) {
\r
288 diagram.setHint(FlowLayout.ALIGN, align);
\r
293 * Sets the content filter of this viewer. The filter will be invoked after
\r
294 * getting input data elements from the content provider and before setting
\r
295 * the new content for the viewer. Any previously set filter will be
\r
299 * This method will not refresh the viewer, this needs to be done separately
\r
300 * using {@link #refresh()} or {@link #refresh(Consumer)}.
\r
303 * @param filter the new filter
\r
305 public void setFilter(ViewerFilter filter) {
\r
306 if (filter == this.filter || (filter != null && filter.equals(this.filter)))
\r
309 this.filter = filter;
\r
312 public ViewerFilter getFilter() {
\r
316 private void resized() {
\r
320 private void resized(final boolean refreshElementSizes) {
\r
321 //System.out.println(this + ".resized(" + refreshElementSizes + ")");
\r
322 if (chassis.isDisposed())
\r
324 org.eclipse.swt.graphics.Rectangle b = chassis.getBounds();
\r
325 final Rectangle2D bounds = new Rectangle2D.Double(hMargin, vMargin, b.width-hMargin*2, b.height-vMargin*2);
\r
326 ctx.getThreadAccess().asyncExec(new Runnable() {
\r
328 public void run() {
\r
329 if (ctx.isDisposed())
\r
331 if (diagram == null)
\r
333 //System.out.println(this + ".resized(" + refreshElementSizes + ") AWT update");
\r
334 if (refreshElementSizes)
\r
335 refreshElementSizes();
\r
336 fl.layout(diagram, bounds);
\r
338 // Makes sure RTreeNode is marked dirty and everything is
\r
339 // properly repainted.
\r
340 if (itemPainter != null)
\r
341 itemPainter.updateAll();
\r
342 ctx.getContentContext().setDirty();
\r
347 * Invoke only from AWT thread.
\r
352 private void initializeCanvasContext(final CanvasContext canvasContext) {
\r
353 // Create canvas context and a layer of interactors
\r
354 final IHintContext h = canvasContext.getDefaultHintContext();
\r
356 // Support & Util Participants
\r
357 canvasContext.add( new TransformUtil() );
\r
359 canvasContext.add( new MouseUtil() );
\r
360 canvasContext.add( new KeyUtil() );
\r
361 canvasContext.add( new SymbolUtil() );
\r
363 // Grid & Ruler & Background
\r
364 h.setHint(Hints.KEY_BACKGROUND_PAINT, BG_PAINT);
\r
365 canvasContext.add( new BackgroundPainter() );
\r
368 canvasContext.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) );
\r
370 ////// Diagram Participants //////
\r
371 PointerInteractor pi = new PointerInteractor(true, true, false, true, false, null);
\r
372 pi.setBoxSelectMode(PickPolicy.PICK_INTERSECTING_OBJECTS);
\r
373 canvasContext.add( pi );
\r
374 canvasContext.add( selection = new Selection() );
\r
375 canvasContext.add( new DiagramParticipant() );
\r
376 canvasContext.add( itemPainter = new GalleryItemPainter() );
\r
377 canvasContext.add( new ElementInteractor() );
\r
378 canvasContext.add( new TooltipParticipant());
\r
380 h.setHint(ElementPainter.KEY_SELECTION_FRAME_COLOR, Color.WHITE);
\r
381 h.setHint(ElementPainter.KEY_SELECTION_CONTENT_COLOR, new Color(0.7f, 0.7f, 1.f, 0.5f));
\r
382 h.setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
\r
384 // Adds DragInteractor & DropInteractor
\r
386 // therefore has to be done before assertParticipantDependencies.
\r
387 // Also, this must be invoked BEFORE SWTChassis chassis.setCanvasContext
\r
388 // because otherwise setCanvasContext would be ran in the
\r
389 // wrong thread (SWT) for AWTChassis.
\r
390 chassis.getAWTComponent().setCanvasContext(canvasContext);
\r
392 swtThread.asyncExec(new Runnable() {
\r
394 public void run() {
\r
395 if (!chassis.isDisposed())
\r
396 chassis.setCanvasContext(canvasContext);
\r
400 canvasContext.assertParticipantDependencies();
\r
404 public Control getControl() {
\r
409 protected void inputChanged(Object input, Object oldInput) {
\r
410 // Skip automatic refreshing at setInput to allow room for manual
\r
411 // optimization in the client.
\r
416 public ISelection getSelection() {
\r
417 Set<IElement> sel = selection.getSelection(0);
\r
419 return StructuredSelection.EMPTY;
\r
420 List<Object> elements = new ArrayList<Object>(sel.size());
\r
421 for (IElement e : sel)
\r
422 elements.add(e.getHint(ElementHints.KEY_OBJECT));
\r
423 return new StructuredSelection(elements);
\r
426 @SuppressWarnings("unchecked")
\r
428 public void setSelection(ISelection selection, boolean reveal) {
\r
429 List<Object> selectedObjects = Collections.EMPTY_LIST;
\r
430 if (selection instanceof IStructuredSelection) {
\r
431 selectedObjects = ((IStructuredSelection) selection).toList();
\r
434 DataElementMap map = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
435 List<IElement> selectionElements = new ArrayList<IElement>();
\r
436 for (Object o : selectedObjects)
\r
437 selectionElements.add( map.getElement(diagram, o) );
\r
439 this.selection.setSelection(0, selectionElements);
\r
443 * Refreshes this viewer completely with information freshly obtained from this
\r
447 public void refresh() {
\r
451 public Object[] getFilteredElements() {
\r
452 IStructuredContentProvider cp = (IStructuredContentProvider) getContentProvider();
\r
454 return new Object[0];
\r
456 Object[] elements = cp.getElements( getInput() );
\r
457 Object[] filtered = filter( elements );
\r
461 protected Object[] filter(Object[] items) {
\r
462 if (filter != null) {
\r
463 ArrayList<Object> filtered = new ArrayList<Object>(items.length);
\r
464 Object root = getInput();
\r
465 for (int i = 0; i < items.length; i++) {
\r
466 if (!filter.select(this, root, items[i]))
\r
468 filtered.add(items[i]);
\r
470 return filtered.toArray();
\r
476 * @param contentCallback a callback for receiving the final filtered
\r
477 * elements left visible
\r
479 public void refresh(Consumer<Object[]> contentCallback) {
\r
480 //System.out.println(this + ".refresh(" + contentCallback + ")");
\r
481 Object[] elements = getFilteredElements();
\r
482 refreshWithContent(elements);
\r
483 if (contentCallback != null) {
\r
484 contentCallback.accept(elements);
\r
489 * Set viewer contents to the specified objects.
\r
490 * This method must be invoked from the SWT thread.
\r
494 public void refreshWithContent(final Object[] objects) {
\r
495 if (!swtThread.currentThreadAccess())
\r
496 throw new IllegalStateException("Not invoked from SWT thread");
\r
498 //System.out.println(this + ".refreshWithContent(" + Arrays.toString(objects) + ")");
\r
500 final Semaphore barrier = new Semaphore(0);
\r
501 IThreadWorkQueue t = ctx.getThreadAccess();
\r
502 ThreadUtils.asyncExec(t, new Runnable() {
\r
504 public void run() {
\r
512 public void perform() {
\r
513 // $AWT-Thread-Begin$
\r
514 //System.out.println(this + ".refreshWithContent(" + Arrays.toString(objects) + ") AWT WORK");
\r
516 Object[] objectsCopy = Arrays.copyOf(objects, objects.length);
\r
518 Set<Object> objs = new HashSet<Object>();
\r
519 for (Object o : objectsCopy)
\r
522 // 1. Remove unused
\r
523 for (IElement e : diagram.getSnapshot()) {
\r
524 Object backendObject = e.getHint(ElementHints.KEY_OBJECT);
\r
525 if (!objs.remove(backendObject)) {
\r
526 //System.out.println("Removing " + e);
\r
527 diagram.removeElement(e);
\r
530 for (int i = 0; i < objectsCopy.length; i++)
\r
531 if (!objs.contains(objectsCopy[i]))
\r
532 objectsCopy[i] = null;
\r
534 // 2. Add new elements
\r
535 for (Object o : objectsCopy) {
\r
539 IElement e = Element.spawnNew(itemClass);
\r
540 e.setHint(ElementHints.KEY_OBJECT, o);
\r
541 // e.getElementClass().getSingleItem(Resize.class).resize(e, itemSize);
\r
542 ILabelProvider lp = (ILabelProvider) getLabelProvider();
\r
543 Image i = lp.getImage(o);
\r
544 if (i.getFeatures().contains(Feature.Volatile))
\r
545 i.addImageListener(imageListener);
\r
546 e.setHint(GalleryItemSGNode.KEY_IMAGE, i);
\r
549 String tooltipText = lp.getToolTipText(o);
\r
550 java.awt.Image tooltipImage = lp.getToolTipImage(o);
\r
551 if (tooltipText != null || tooltipImage != null) {
\r
552 e.setHint(TooltipParticipant.TOOLTIP_KEY, tooltipProvider);
\r
553 if (tooltipText != null)
\r
554 e.setHint(GalleryTooltipProvider.TOOLTIP_TEXT, tooltipText);
\r
555 if (tooltipImage != null)
\r
556 e.setHint(GalleryTooltipProvider.TOOLTIP_IMAGE, tooltipImage);
\r
558 diagram.addElement(e);
\r
560 e.getElementClass().getSingleItem(GalleryItemSGNode.class).update(e);
\r
562 // Image si = ImageUtils.createShadow(i, SHADOW, (int) itemSize.getWidth(), (int) itemSize.getHeight());
\r
563 // si = ImageUtils.createBuffer(si);
\r
564 // e.setHint(GalleryItemPainter.KEY_IMAGE_SHADOW, si);
\r
566 ElementUtils.setText(e, lp.getText(o));
\r
567 //System.out.println("Added: " + e);
\r
570 // 3. Calculate maximum vertical space needed by current diagram element texts
\r
571 refreshElementSizes();
\r
573 ThreadUtils.asyncExec(swtThread, new Runnable() {
\r
575 public void run() {
\r
579 // $AWT-Thread-End$
\r
583 boolean done = false;
\r
586 done = barrier.tryAcquire(10, TimeUnit.MILLISECONDS);
\r
587 while (!done && display.readAndDispatch()) {
\r
588 done = barrier.tryAcquire();
\r
590 } catch (InterruptedException e) {
\r
597 * Invoke from canvas context thread only.
\r
599 void refreshElementSizes() {
\r
600 if (awtComponent == null) {
\r
601 System.err.println("GalleryViewer.refreshElementSizes: awtComponent is null");
\r
605 //System.out.println(this + ".refreshElementSizes()");
\r
606 // Calculate maximum vertical space needed by current diagram element texts
\r
607 FontMetrics metrics = awtComponent.getFontMetrics(currentItemFont);
\r
608 int fontHeight = metrics.getHeight();
\r
609 int maxWidth = (int) itemSize.getWidth();
\r
610 Rectangle2D size = itemSize;
\r
611 java.awt.Point targetSize = new java.awt.Point((int) itemSize.getWidth(), (int) itemSize.getHeight());
\r
612 diagram.setHint(DiagramHints.KEY_ELEMENT_RASTER_TARGET_SIZE, targetSize);
\r
613 int maxLinesNeeded = 0;
\r
614 for (IElement el : diagram.getElements()) {
\r
615 // This makes it possible to give the gallery item a reference size
\r
616 // for caching rendered images in the correct size only.
\r
617 // NOTE: currently this is not used in GalleryItemPainter since the
\r
618 // target size is now propagated through the element class loading
\r
619 // process through the diagram hint KEY_ELEMENT_RASTER_REFERENCE_SIZE.
\r
620 el.setHint(GalleryItemSGNode.KEY_TARGET_IMAGE_SIZE, targetSize);
\r
622 String text = ElementUtils.getText(el);
\r
623 int linesNeeded = TextUtil.wordWrap(text, metrics, maxWidth).length;
\r
624 maxLinesNeeded = Math.max(maxLinesNeeded, linesNeeded);
\r
625 int textSpaceNeeded = fontHeight * linesNeeded + metrics.getMaxDescent();
\r
626 Rectangle2D s = new Rectangle2D.Double(0, 0, size.getWidth(), size.getHeight() + textSpaceNeeded);
\r
627 el.getElementClass().getSingleItem(Resize.class).resize(el, s);
\r
628 //System.out.println(this + " lines needed: " + linesNeeded + " = " + s);
\r
630 el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el);
\r
632 int maxTextSpaceNeeded = fontHeight * maxLinesNeeded + metrics.getMaxDescent();
\r
633 maxItemSize = new Rectangle2D.Double(0, 0, size.getWidth(), size.getHeight() + maxTextSpaceNeeded);
\r
634 //System.out.println(this + "[" + diagram.getElements().size() + "]: max lines needed: " + maxLinesNeeded + " = " + fontHeight*maxLinesNeeded + " pixels");
\r
637 ImageListener imageListener = new ImageListener() {
\r
639 public void onContentChangedNotification(Image image) {
\r
640 //System.out.println(Thread.currentThread() + ": contentChanged(" + image + ")");
\r
641 // Update the image of the element if the element is found.
\r
642 for (final IElement el : diagram.getSnapshot()) {
\r
643 Image i = GalleryItemSGNode.getImage(el);
\r
647 ctx.getThreadAccess().asyncExec(new Runnable() {
\r
649 public void run() {
\r
650 //System.out.println(Thread.currentThread() + ": update scene graph(" + el + ")");
\r
651 // Update scene graph and repaint.
\r
652 el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el);
\r
653 ctx.getContentContext().setDirty();
\r
661 public void addDropSupport(final IDropTargetParticipant p) {
\r
662 if (ctx.getThreadAccess().currentThreadAccess()) {
\r
665 ctx.getThreadAccess().asyncExec(new Runnable() {
\r
667 public void run() {
\r
668 if (!ctx.isDisposed())
\r
675 public void addDragSupport(final IDragSourceParticipant p) {
\r
676 if (ctx.getThreadAccess().currentThreadAccess()) {
\r
679 ctx.getThreadAccess().asyncExec(new Runnable() {
\r
681 public void run() {
\r
682 if (!ctx.isDisposed())
\r
689 public CanvasContext getCanvasContext() {
\r
693 public IDiagram getDiagram() {
\r
697 //private final static Color BG_COLOR = new Color(0.3f, 0.3f, 1.0f, 0.35f);
\r
699 static class GalleryItemPainter extends ElementPainter {
\r
701 public void paintSelectionFrame(G2DParentNode elementNode, G2DParentNode selectionNode, IElement e, Color color) {
\r
702 final Shape outline = ElementUtils.getElementBoundsOnDiagram(e);
\r
703 Rectangle2D bounds = outline.getBounds2D();
\r
704 GeometryUtils.expandRectangle(bounds, 2, 2, 2, 2);
\r
706 ShapeNode shapenode = selectionNode.getOrCreateNode("shape_"+e.hashCode(), ShapeNode.class);
\r
707 shapenode.setShape(bounds);
\r
708 shapenode.setColor(new Color(0.3f, 0.3f, 1.0f, 0.25f));
\r
709 shapenode.setFill(true);
\r
711 // Paint selection before anything else in elementNode
\r
712 selectionNode.setZIndex(Integer.MIN_VALUE);
\r