]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / gallery / GalleryViewer.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.gallery;\r
13 \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
32 \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
106 \r
107 /**\r
108  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
109  * @author Tuukka Lehtonen\r
110  */\r
111 public class GalleryViewer extends ContentViewer {\r
112 \r
113     /**\r
114      * A hint key for storing a GalleryViewer within the hint stack of\r
115      * {@link GalleryViewer}'s {@link ICanvasContext}.\r
116      */\r
117     public static final Key              KEY_VIEWER      = new KeyOf(GalleryViewer.class, "GALLERY_VIEWER");\r
118 \r
119     public static final ShadowParameters SHADOW          = new ShadowParameters(0.5, Color.BLACK, 5);\r
120 \r
121     ViewerFilter                         filter;\r
122     IThreadWorkQueue                     swtThread;\r
123     Display                              display;\r
124     SWTChassis                           chassis;\r
125     Component                            awtComponent;\r
126     CanvasContext                        ctx;\r
127     Selection                            selection;\r
128     GalleryItemPainter                   itemPainter;\r
129     IDiagram                             diagram;\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
134     int                                  hMargin     = 5;\r
135     int                                  vMargin     = 5;\r
136     ElementClass                         itemClass;\r
137     FontRegistry                         fontRegistry;\r
138     Font                                 currentItemFont;\r
139 \r
140     GalleryTooltipProvider               tooltipProvider = new GalleryTooltipProvider();\r
141 \r
142     /** Background paint */\r
143     public static final Paint BG_PAINT;\r
144     static {\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
152     }\r
153 \r
154     public GalleryViewer(Composite composite) {\r
155         this(composite, 0);\r
156     }\r
157 \r
158     public GalleryViewer(Composite composite, int style) {\r
159         super();\r
160         display = composite.getDisplay();\r
161         swtThread = SWTThread.getThreadAccess(composite);\r
162         chassis = new SWTChassis(composite, style) {\r
163             @Override\r
164             public Point computeSize(int wHint, int hHint, boolean changed) {\r
165 //              System.out.println("chassis compute size: " + wHint + ", " + hHint + ", " + changed);\r
166 \r
167                 if (diagram == null)\r
168                     return super.computeSize(wHint, hHint, changed);\r
169 \r
170                 Rectangle2D rect;\r
171 //                if (!changed) {\r
172 //                    rect = ElementUtils.getSurroundingElementBoundsOnDiagram(diagram.getSnapshot());\r
173 //                }\r
174 //                else\r
175                 {\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
179                 }\r
180                 return new Point((int)rect.getMaxX()+hMargin*2, (int)rect.getMaxY()+vMargin*2);\r
181             }\r
182         };\r
183 \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
190 \r
191         // Create canvas context here in SWT thread but do not initialize it yet.\r
192         this.ctx = new CanvasContext(AWTThread.getThreadAccess());\r
193 \r
194         chassis.populate(parameter -> {\r
195             awtComponent = parameter.getAWTComponent();\r
196 \r
197             // Initialize the canvas context\r
198             ITask task = ThreadLogger.getInstance().begin("createCanvas");\r
199             initializeCanvasContext(ctx);\r
200             task.finish();\r
201 \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
205 \r
206             // Set IDiagram for canvas context\r
207             hintCtx.setHint(DiagramHints.KEY_DIAGRAM, diagram);\r
208 \r
209             // Force layout\r
210             ThreadUtils.asyncExec(swtThread, new Runnable() {\r
211                 @Override\r
212                 public void run() {\r
213                     resized(false);\r
214                 }\r
215             });\r
216         });\r
217 \r
218         chassis.addControlListener(new ControlListener() {\r
219             @Override\r
220             public void controlMoved(ControlEvent e) {}\r
221             @Override\r
222             public void controlResized(ControlEvent e) {\r
223                 resized();\r
224             }});\r
225 \r
226         ITask task2 = ThreadLogger.getInstance().begin("fonts");\r
227         try {\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
235         }\r
236         task2.finish();\r
237 \r
238 \r
239         itemClass = ElementClass.compile(\r
240                 DefaultTransform.INSTANCE,\r
241                 TextImpl.INSTANCE,\r
242                 //TextAsTooltip.INSTANCE,\r
243                 Clickable.INSTANCE,\r
244                 Resizeable.UNCONSTRICTED,\r
245                 new GalleryItemSGNode(currentItemFont)\r
246         );\r
247 \r
248         chassis.addListener(SWT.Dispose, new Listener() {\r
249             @Override\r
250             public void handleEvent(Event event) {\r
251                 if (fontRegistry != null)\r
252                     fontRegistry.removeListener(fontRegistryListener);\r
253 \r
254                 // Prevent memory leaks.\r
255                 ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() {\r
256                     @Override\r
257                     public void run() {\r
258                         chassis.getAWTComponent().setCanvasContext(null);\r
259                         ctx.dispose();\r
260                     }\r
261                 });\r
262             }\r
263         });\r
264     }\r
265 \r
266     IPropertyChangeListener fontRegistryListener = new IPropertyChangeListener() {\r
267         @Override\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
275                 @Override\r
276                 public void run() {\r
277                     resized(true);\r
278                 }\r
279             });\r
280         }\r
281     };\r
282 \r
283     /**\r
284      * Set alignment of elements (left, right, center, fill)\r
285      * @param align\r
286      */\r
287     public void setAlign(FlowLayout.Align align) {\r
288         diagram.setHint(FlowLayout.ALIGN, align);\r
289         resized();\r
290     }\r
291 \r
292     /**\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
296      * replaced.\r
297      *\r
298      * <p>\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
301      * </p>\r
302      *\r
303      * @param filter the new filter\r
304      */\r
305     public void setFilter(ViewerFilter filter) {\r
306         if (filter == this.filter || (filter != null && filter.equals(this.filter)))\r
307             return;\r
308 \r
309         this.filter = filter;\r
310     }\r
311 \r
312     public ViewerFilter getFilter() {\r
313         return filter;\r
314     }\r
315 \r
316     private void resized() {\r
317         resized(false);\r
318     }\r
319 \r
320     private void resized(final boolean refreshElementSizes) {\r
321         //System.out.println(this + ".resized(" + refreshElementSizes + ")");\r
322         if (chassis.isDisposed())\r
323             return;\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
327             @Override\r
328             public void run() {\r
329                 if (ctx.isDisposed())\r
330                     return;\r
331                 if (diagram == null)\r
332                     return;\r
333                 //System.out.println(this + ".resized(" + refreshElementSizes + ") AWT update");\r
334                 if (refreshElementSizes)\r
335                     refreshElementSizes();\r
336                 fl.layout(diagram, bounds);\r
337 \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
343             }});\r
344     }\r
345 \r
346     /**\r
347      * Invoke only from AWT thread.\r
348      * \r
349      * @param thread\r
350      * @return\r
351      */\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
355 \r
356         // Support & Util Participants\r
357         canvasContext.add( new TransformUtil() );\r
358 \r
359         canvasContext.add( new MouseUtil() );\r
360         canvasContext.add( new KeyUtil() );\r
361         canvasContext.add( new SymbolUtil() );\r
362 \r
363         // Grid & Ruler & Background\r
364         h.setHint(Hints.KEY_BACKGROUND_PAINT, BG_PAINT);\r
365         canvasContext.add( new BackgroundPainter() );\r
366 \r
367         // Key bindings\r
368         canvasContext.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) );\r
369 \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
379 \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
383 \r
384         // Adds DragInteractor & DropInteractor\r
385 \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
391 \r
392         swtThread.asyncExec(new Runnable() {\r
393             @Override\r
394             public void run() {\r
395                 if (!chassis.isDisposed())\r
396                     chassis.setCanvasContext(canvasContext);\r
397             }\r
398         });\r
399 \r
400         canvasContext.assertParticipantDependencies();\r
401     }\r
402 \r
403     @Override\r
404     public Control getControl() {\r
405         return chassis;\r
406     }\r
407 \r
408     @Override\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
412         //refresh();\r
413     }\r
414 \r
415     @Override\r
416     public ISelection getSelection() {\r
417         Set<IElement> sel = selection.getSelection(0);\r
418         if (sel.isEmpty())\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
424     }\r
425 \r
426     @SuppressWarnings("unchecked")\r
427     @Override\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
432         }\r
433 \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
438 \r
439         this.selection.setSelection(0, selectionElements);\r
440     }\r
441 \r
442     /**\r
443      * Refreshes this viewer completely with information freshly obtained from this\r
444      * viewer's model.\r
445      */\r
446     @Override\r
447     public void refresh() {\r
448         refresh(null);\r
449     }\r
450 \r
451     public Object[] getFilteredElements() {\r
452         IStructuredContentProvider cp = (IStructuredContentProvider) getContentProvider();\r
453         if (cp == null)\r
454             return new Object[0];\r
455 \r
456         Object[] elements = cp.getElements( getInput() );\r
457         Object[] filtered = filter( elements );\r
458         return filtered;\r
459     }\r
460 \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
467                     continue;\r
468                 filtered.add(items[i]);\r
469             }\r
470             return filtered.toArray();\r
471         }\r
472         return items;\r
473     }\r
474 \r
475     /**\r
476      * @param contentCallback a callback for receiving the final filtered\r
477      *        elements left visible\r
478      */\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
485         }\r
486     }\r
487 \r
488     /**\r
489      * Set viewer contents to the specified objects.\r
490      * This method must be invoked from the SWT thread.\r
491      * \r
492      * @param objects\r
493      */\r
494     public void refreshWithContent(final Object[] objects) {\r
495         if (!swtThread.currentThreadAccess())\r
496             throw new IllegalStateException("Not invoked from SWT thread");\r
497 \r
498         //System.out.println(this + ".refreshWithContent(" + Arrays.toString(objects) + ")");\r
499 \r
500         final Semaphore barrier = new Semaphore(0);\r
501         IThreadWorkQueue t = ctx.getThreadAccess();\r
502         ThreadUtils.asyncExec(t, new Runnable() {\r
503             @Override\r
504             public void run() {\r
505                 try {\r
506                     perform();\r
507                 } finally {\r
508                     barrier.release();\r
509                 }\r
510             }\r
511 \r
512             public void perform() {\r
513                 // $AWT-Thread-Begin$\r
514                 //System.out.println(this + ".refreshWithContent(" + Arrays.toString(objects) + ") AWT WORK");\r
515 \r
516                 Object[] objectsCopy = Arrays.copyOf(objects, objects.length);\r
517 \r
518                 Set<Object> objs = new HashSet<Object>();\r
519                 for (Object o : objectsCopy)\r
520                     objs.add(o);\r
521 \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
528                     }\r
529                 }\r
530                 for (int i = 0; i < objectsCopy.length; i++)\r
531                     if (!objs.contains(objectsCopy[i]))\r
532                         objectsCopy[i] = null;\r
533 \r
534                 // 2. Add new elements\r
535                 for (Object o : objectsCopy) {\r
536                     if (o == null)\r
537                         continue;\r
538 \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
547 \r
548                     // tooltips\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
557                     }\r
558                     diagram.addElement(e);\r
559 \r
560                     e.getElementClass().getSingleItem(GalleryItemSGNode.class).update(e);\r
561 \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
565 \r
566                     ElementUtils.setText(e, lp.getText(o));\r
567                     //System.out.println("Added: " + e);\r
568                 }\r
569 \r
570                 // 3. Calculate maximum vertical space needed by current diagram element texts\r
571                 refreshElementSizes();\r
572 \r
573                 ThreadUtils.asyncExec(swtThread, new Runnable() {\r
574                     @Override\r
575                     public void run() {\r
576                         resized(false);\r
577                     }\r
578                 });\r
579                 // $AWT-Thread-End$\r
580             }\r
581         });\r
582 \r
583         boolean done = false;\r
584         while (!done) {\r
585             try {\r
586                 done = barrier.tryAcquire(10, TimeUnit.MILLISECONDS);\r
587                 while (!done && display.readAndDispatch()) {\r
588                     done = barrier.tryAcquire();\r
589                 }\r
590             } catch (InterruptedException e) {\r
591                 done = true;\r
592             }\r
593         }\r
594     }\r
595 \r
596     /**\r
597      * Invoke from canvas context thread only.\r
598      */\r
599     void refreshElementSizes() {\r
600         if (awtComponent == null) {\r
601             System.err.println("GalleryViewer.refreshElementSizes: awtComponent is null");\r
602             return;\r
603         }\r
604 \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
621 \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
629 \r
630             el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el);\r
631         }\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
635     }\r
636 \r
637     ImageListener imageListener = new ImageListener() {\r
638         @Override\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
644                 if (image != i)\r
645                     continue;\r
646 \r
647                 ctx.getThreadAccess().asyncExec(new Runnable() {\r
648                     @Override\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
654                     }\r
655                 });\r
656                 break;\r
657             }\r
658         }\r
659     };\r
660 \r
661     public void addDropSupport(final IDropTargetParticipant p) {\r
662         if (ctx.getThreadAccess().currentThreadAccess()) {\r
663             ctx.add(p);\r
664         } else {\r
665             ctx.getThreadAccess().asyncExec(new Runnable() {\r
666                 @Override\r
667                 public void run() {\r
668                     if (!ctx.isDisposed())\r
669                         ctx.add(p);\r
670                 }\r
671             });\r
672         }\r
673     }\r
674 \r
675     public void addDragSupport(final IDragSourceParticipant p) {\r
676         if (ctx.getThreadAccess().currentThreadAccess()) {\r
677             ctx.add(p);\r
678         } else {\r
679             ctx.getThreadAccess().asyncExec(new Runnable() {\r
680                 @Override\r
681                 public void run() {\r
682                     if (!ctx.isDisposed())\r
683                         ctx.add(p);\r
684                 }\r
685             });\r
686         }\r
687     }\r
688 \r
689     public CanvasContext getCanvasContext() {\r
690         return ctx;\r
691     }\r
692 \r
693     public IDiagram getDiagram() {\r
694         return diagram;\r
695     }\r
696 \r
697     //private final static Color BG_COLOR = new Color(0.3f, 0.3f, 1.0f, 0.35f);\r
698 \r
699     static class GalleryItemPainter extends ElementPainter {\r
700         @Override\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
705 \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
710 \r
711             // Paint selection before anything else in elementNode\r
712             selectionNode.setZIndex(Integer.MIN_VALUE);\r
713         }\r
714     }\r
715 \r
716 }\r