1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
\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.trend.impl;
\r
14 import java.awt.Cursor;
\r
15 import java.awt.Toolkit;
\r
16 import java.awt.datatransfer.Clipboard;
\r
17 import java.awt.datatransfer.StringSelection;
\r
18 import java.awt.geom.Point2D;
\r
19 import java.awt.geom.Rectangle2D;
\r
20 import java.awt.print.PrinterException;
\r
21 import java.io.File;
\r
22 import java.io.IOException;
\r
24 import org.simantics.databoard.accessor.error.AccessorException;
\r
25 import org.simantics.databoard.util.Bean;
\r
26 import org.simantics.g2d.canvas.Hints;
\r
27 import org.simantics.g2d.canvas.ICanvasContext;
\r
28 import org.simantics.g2d.canvas.IMouseCursorHandle;
\r
29 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
\r
30 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
\r
31 import org.simantics.g2d.canvas.impl.HintReflection.HintListener;
\r
32 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
\r
33 import org.simantics.g2d.chassis.ITooltipProvider;
\r
34 import org.simantics.g2d.participant.TimeParticipant;
\r
35 import org.simantics.history.HistoryException;
\r
36 import org.simantics.history.csv.CSVFormatter;
\r
37 import org.simantics.history.util.ProgressMonitor;
\r
38 import org.simantics.scenegraph.g2d.IG2DNode;
\r
39 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
\r
40 import org.simantics.scenegraph.g2d.events.MouseEvent;
\r
41 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
\r
42 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
\r
43 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
\r
44 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
\r
45 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
\r
46 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;
\r
47 import org.simantics.scenegraph.g2d.events.TimeEvent;
\r
48 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
\r
49 import org.simantics.scenegraph.g2d.events.command.Commands;
\r
50 import org.simantics.trend.configuration.ItemPlacement;
\r
51 import org.simantics.trend.configuration.TrendItem.Renderer;
\r
52 import org.simantics.trend.util.PrintUtil;
\r
53 import org.simantics.utils.datastructures.hints.IHintContext.Key;
\r
54 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
\r
55 import org.simantics.utils.datastructures.hints.IHintObservable;
\r
57 public class TrendParticipant extends AbstractCanvasParticipant {
\r
60 * Key for storing chart value tip box relative position. Changed by
\r
61 * {@link TrendParticipant} after user finishes dragging this class around.
\r
63 public static final Key KEY_VALUE_TIP_BOX_RELATIVE_POS = new KeyOf(Point2D.class);
\r
65 /** Key for chart redraw interval in milliseconds (drawn only if dirty) */
\r
66 public static final Key KEY_TREND_DRAW_INTERVAL = new KeyOf(Long.class);
\r
68 /** The time code when trend was last redrawn */
\r
69 public static final Key KEY_TREND_SHAPE_LAST = new KeyOf(Long.class);
\r
71 /** Key for interval time of value scale */
\r
72 public static final Key KEY_TREND_AUTOSCALE_INTERVAL = new KeyOf(Long.class);
\r
74 /** Key for time of last autoscale */
\r
75 public static final Key KEY_TREND_AUTOSCALE_LAST = new KeyOf(Long.class);
\r
77 public static final double KEY_MOVE = 0.33;
\r
79 /** Cursor when panning */
\r
80 public MouseCursors cursors = new MouseCursors();
\r
82 @HintListener(Class=Hints.class, Field="KEY_CONTROL_BOUNDS")
\r
83 public void selectionChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
\r
84 trend.shapedirty = true;
\r
90 @Dependency TimeParticipant time;
\r
94 // The single item on which the mouse hovers over is set to this field. (Binary items are selectable).
\r
95 public ItemNode hoveringItem;
\r
98 public void addedToContext(ICanvasContext ctx) {
\r
99 super.addedToContext(ctx);
\r
100 time.registerForEvents(getClass());
\r
103 public void setTrend(TrendNode node) {
\r
107 public TrendNode getTrend() {
\r
112 // public void initSG(G2DParentNode parent) {
\r
113 // Set<TrendNode> nodes = NodeUtil.collectNodes(parent, TrendNode.class);
\r
114 // trends.addAll(nodes);
\r
118 public void cleanupSG() {
\r
121 protected void updateNode() {
\r
122 // titleNode.setText(text)
\r
123 // node.setEnabled(isPaintingEnabled());
\r
124 // node.setGridColor(getGridColor());
\r
125 // node.setGridSize(getGridSize());
\r
128 @EventHandler(priority = 0)
\r
129 public boolean handleTimeEvent(TimeEvent e) {
\r
130 //System.out.println("(" + isRemoved() + ") time event: " + e.time + " (" + e.interval + ")");
\r
133 time.unregisterForEvents(getClass());
\r
137 long currentTime = e.time;
\r
139 Long drawInterval = getHint(KEY_TREND_DRAW_INTERVAL);
\r
140 if (drawInterval == null) drawInterval = 200L;
\r
141 Long lastDrawTime = getHint(KEY_TREND_SHAPE_LAST);
\r
142 boolean drawIntervalElapsed = lastDrawTime == null || (currentTime>=lastDrawTime+drawInterval);
\r
143 if (drawIntervalElapsed) {
\r
144 // System.out.println("elapsed="+drawIntervalElapsed+" currentTime="+currentTime+" lastDraw="+lastDrawTime+", interval="+drawInterval+", nextDrawTime="+(lastDrawTime+drawInterval));
\r
145 // System.out.println("elapsed="+drawIntervalElapsed+" currentTime="+currentTime+" lastDraw="+lastDrawTime+", interval="+drawInterval);
\r
146 setHint(KEY_TREND_SHAPE_LAST, currentTime);
\r
147 trend.shapedirty |= trend.datadirty;
\r
148 trend.datadirty = false;
\r
152 Long autoscaleInterval = getHint(KEY_TREND_AUTOSCALE_INTERVAL);
\r
153 if (autoscaleInterval == null) autoscaleInterval = 1000L;
\r
154 Long autoscaleTime = getHint(KEY_TREND_AUTOSCALE_LAST);
\r
155 boolean autoscale = autoscaleTime==null || (currentTime>=autoscaleTime+autoscaleInterval);
\r
157 boolean l = trend.autoscale(trend.autoscaletime, autoscale);
\r
158 if (!draggingValueTip(grab)) {
\r
159 trend.updateValueTipTime();
\r
165 setHint(KEY_TREND_AUTOSCALE_LAST, currentTime);
\r
169 if (trend.shapedirty) {
\r
170 // System.out.println(currentTime+": Draw time!");
\r
177 @EventHandler(priority = 0)
\r
178 public boolean mouseClick(MouseClickEvent me) {
\r
179 IG2DNode node = trend.pickNode( me.controlPosition );
\r
181 if (me.clickCount==2 && node!=null) {
\r
182 trend.valueTipTime = trend.prevValueTipTime;
\r
183 if (node instanceof Plot) {
\r
184 boolean binaryArea = me.controlPosition.getY()>trend.plot.getY()+trend.plot.analogAreaHeight;
\r
186 trend.horizRuler.zoomOut();
\r
192 } else if (node instanceof HorizRuler) {
\r
193 HorizRuler hr = (HorizRuler) node;
\r
197 } else if (node instanceof VertRuler) {
\r
198 VertRuler vr = (VertRuler) node;
\r
206 if (me.clickCount==1 && me.button==MouseEvent.LEFT_BUTTON && node!=null && node instanceof Plot) {
\r
207 trend.prevValueTipTime = trend.valueTipTime;
\r
208 if (trend.valueTipTime == null) {
\r
209 trend.valueTipTime = Double.isNaN(trend.mouseHoverTime)?null:trend.mouseHoverTime;
\r
210 trend.valueTipHover = true;
\r
212 double valueTipX = trend.horizRuler.toX( trend.valueTipTime );
\r
213 double x = me.controlPosition.getX() - trend.horizRuler.getBounds().getX();
\r
214 boolean hit = x>=valueTipX-TrendLayout.VALUE_TIP_HOVER_SENSITIVITY && x<=valueTipX+TrendLayout.VALUE_TIP_HOVER_SENSITIVITY;
\r
215 if (hit) trend.valueTipTime = null;
\r
221 // Right click removes value tip
\r
222 if (me.clickCount==1 && me.button==MouseEvent.RIGHT_BUTTON) {
\r
223 if (trend.valueTipTime != null) {
\r
224 trend.valueTipTime = null;
\r
229 if (me.clickCount==1 && me.button==MouseEvent.LEFT_BUTTON) {
\r
230 if (node instanceof VertRuler) {
\r
231 VertRuler vr = (VertRuler) node;
\r
232 trend.selectVertRuler( trend.vertRulers.indexOf(vr) );
\r
234 } else if (node == null || node instanceof TextNode) {
\r
235 Plot p = trend.plot;
\r
236 double x = me.controlPosition.getX() - p.getX();
\r
237 double y = me.controlPosition.getY() - p.getY();
\r
238 if ( x>=0 && x<=p.getWidth() && y<=0 && y>=-Plot.DIAMOND_SIZE*2) {
\r
239 // Click hits milestone area
\r
240 milestoneSearch: for ( Milestone ms : trend.milestones.milestones ) {
\r
241 double mx = trend.horizRuler.toX( ms.time );
\r
242 if ( x>=mx-Plot.DIAMOND_SIZE && x<=mx+Plot.DIAMOND_SIZE ) {
\r
243 ITooltipProvider tp = getContext().getTooltipProvider();
\r
245 tp.show(ms.label, ms.description);
\r
247 break milestoneSearch;
\r
258 @EventHandler(priority = 0)
\r
259 public boolean mouseDown(MouseButtonPressedEvent me) {
\r
261 if (me.button==MouseEvent.LEFT_BUTTON) {
\r
262 IG2DNode node = trend.pickNode( me.controlPosition );
\r
263 if (node == null || node instanceof Plot) {
\r
265 // Start value box grab
\r
267 double x = me.controlPosition.getX() - trend.horizRuler.getBounds().getX();
\r
268 double y = me.controlPosition.getY() - trend.plot.getBounds().getY();
\r
269 if (trend.plot.valueTipBoxBounds.contains(x, y)) {
\r
270 //System.out.println("grabbed value box @ " + x + ", " + y);
\r
272 grab.valueBoxRelPos.setLocation(trend.spec.viewProfile.valueViewPositionX, trend.spec.viewProfile.valueViewPositionY);
\r
273 grab.valueBoxPos.setFrame(trend.plot.valueTipBoxBounds);
\r
274 grab.mousepos = new Point2D.Double( me.controlPosition.getX(), me.controlPosition.getY() );
\r
275 grab.valueBoxGrab = true;
\r
280 // Start Value-Tip grab
\r
281 if (trend.valueTipTime!=null) {
\r
282 double valueTipX = trend.horizRuler.toX( trend.valueTipTime );
\r
283 double x = me.controlPosition.getX() - trend.horizRuler.getBounds().getX();
\r
284 boolean hit = x>=valueTipX-TrendLayout.VALUE_TIP_HOVER_SENSITIVITY && x<=valueTipX+TrendLayout.VALUE_TIP_HOVER_SENSITIVITY;
\r
287 grab.mousepos = new Point2D.Double( me.controlPosition.getX(), me.controlPosition.getY() );
\r
288 grab.sx = trend.horizRuler.unitsPerPixel();
\r
289 grab.sy = new double[ trend.vertRulers.size() ];
\r
290 grab.valueTipGrab = true;
\r
296 boolean binaryArea = me.controlPosition.getY()>trend.plot.getY()+trend.plot.analogAreaHeight;
\r
297 boolean timeZoom = (me.stateMask & MouseEvent.SHIFT_MASK)>0 || binaryArea;
\r
298 if (trend.selection==null) {
\r
299 trend.selection = trend.addNode("Selection", SelectionNode.class);
\r
300 trend.selection.setZIndex( 10 );
\r
301 trend.selection.start( me.controlPosition, binaryArea, timeZoom );
\r
308 IG2DNode node = trend.pickNode( me.controlPosition );
\r
309 if ( (me.button==MouseEvent.MIDDLE_BUTTON) &&
\r
310 node!=null && node instanceof Plot) {
\r
311 //Plot p = (Plot) node;
\r
312 TrendNode trend = (TrendNode) node.getParent();
\r
313 boolean shift = (me.stateMask & MouseEvent.SHIFT_MASK)>0;
\r
314 boolean alt = (me.stateMask & MouseEvent.ALT_MASK)>0;
\r
315 //boolean analogArea = me.controlPosition.getY() < p.getY()+p.analogAreaHeight;
\r
316 //boolean binaryArea = me.controlPosition.getY() >= p.getY()+p.analogAreaHeight;
\r
318 grab.mousepos = new Point2D.Double( me.controlPosition.getX(), me.controlPosition.getY() );
\r
319 grab.sx = trend.horizRuler.unitsPerPixel();
\r
320 grab.sy = new double[ trend.vertRulers.size() ];
\r
321 grab.horiz = !shift || alt;
\r
322 grab.vert = shift || alt;
\r
324 for (int i=0; i<trend.vertRulers.size(); i++)
\r
325 grab.sy[i] = trend.vertRulers.get(i).unitsPerPixel();
\r
327 Cursor c = grab.horiz ? (grab.vert ? cursors.grab : cursors.grab_horiz) : (grab.vert ? cursors.grab_vert : cursors.grab);
\r
328 grab.cursor = getContext().getMouseCursorContext().setCursor(me.mouseId, c);
\r
329 grab.mouseButton = me.button;
\r
331 trend.horizRuler.translate(0);
\r
332 setHoverTime(null);
\r
336 if ( (me.button==MouseEvent.LEFT_BUTTON||me.button==MouseEvent.MIDDLE_BUTTON) &&
\r
337 node!=null && node instanceof HorizRuler) {
\r
338 HorizRuler hr = (HorizRuler) node;
\r
340 grab.cursor = getContext().getMouseCursorContext().setCursor(me.mouseId, cursors.grab_horiz);
\r
341 grab.mousepos = new Point2D.Double();
\r
342 grab.mousepos.setLocation( me.controlPosition );
\r
343 grab.sx = hr.unitsPerPixel();
\r
344 grab.horizRuler = true;
\r
345 grab.mouseButton = me.button;
\r
347 trend.horizRuler.translate(0);
\r
351 if ( (me.button==MouseEvent.LEFT_BUTTON||me.button==MouseEvent.MIDDLE_BUTTON) &&
\r
352 node!=null && node instanceof VertRuler) {
\r
353 //VertRuler vr = (VertRuler) node;
\r
355 grab.cursor = getContext().getMouseCursorContext().setCursor(me.mouseId, cursors.grab_vert);
\r
356 grab.mousepos = new Point2D.Double();
\r
357 grab.mousepos.setLocation( me.controlPosition );
\r
358 grab.sy = new double[ trend.vertRulers.size() ];
\r
359 for (int i=0; i<trend.vertRulers.size(); i++)
\r
360 grab.sy[i] = trend.vertRulers.get(i).unitsPerPixel();
\r
361 grab.mouseButton = me.button;
\r
362 grab.vertRuler = trend.vertRulers.indexOf(node);
\r
363 trend.selectVertRuler( grab.vertRuler );
\r
371 @EventHandler(priority = 0)
\r
372 public boolean mouseUp(MouseButtonReleasedEvent me) {
\r
374 // Release value box grab
\r
375 if (me.button==1 && draggingValueBox(grab)) {
\r
376 setHint(KEY_VALUE_TIP_BOX_RELATIVE_POS, new Point2D.Double(
\r
377 trend.spec.viewProfile.valueViewPositionX,
\r
378 trend.spec.viewProfile.valueViewPositionY));
\r
382 // Release value tip grab
\r
383 if (me.button==1 && draggingValueTip(grab)) {
\r
387 // Release Box-Zoom
\r
388 if (me.button==1 && trend.selection!=null) {
\r
389 Rectangle2D rect = trend.selection.rect;
\r
391 if (rect.getWidth()>1 && rect.getHeight()>1) {
\r
392 if (trend.selection.timeZoom) {
\r
393 trend.horizRuler.zoomIn( rect.getX()-trend.horizRuler.getX(), rect.getWidth() );
\r
396 rect.getX()-trend.plot.getX(),
\r
397 rect.getY()-trend.plot.getY(),
\r
398 rect.getWidth(), rect.getHeight(), true, true );
\r
401 trend.shapedirty = true;
\r
404 trend.selection.delete();
\r
405 trend.selection = null;
\r
410 if (grab!=null && grab.mouseButton==me.button) {
\r
411 grab.cursor.remove();
\r
417 @EventHandler(priority = 0)
\r
418 public boolean mouseExit(MouseExitEvent me) {
\r
419 trend.valueTipHover = false;
\r
420 setHoverTime( null );
\r
424 void setHoverTime(Double time) {
\r
425 if ( time==null && Double.isNaN(trend.mouseHoverTime) ) return;
\r
426 if ( time!=null && trend.mouseHoverTime==time ) return;
\r
427 if ( time==null ) {
\r
428 trend.mouseHoverTime = Double.NaN;
\r
430 trend.mouseHoverTime = time;
\r
431 trend.lastMouseHoverTime = time;
\r
436 // @EventHandler(priority = 0)
\r
437 // public boolean mouseEnter(MouseExitEvent me) {
\r
438 // //double x = me.controlPosition.getX() - trend.horizRuler.getBounds().getX();
\r
439 // //double time = trend.horizRuler.toTime( x ) - trend.horizRuler.basetime;
\r
443 @SuppressWarnings("unused")
\r
444 @EventHandler(priority = 0)
\r
445 public boolean mouseMoved(MouseMovedEvent me) {
\r
446 // ITooltipProvider tt = getContext().getTooltipProvider();
\r
448 // tt.setTooltipText("Hello");
\r
451 IG2DNode pick = trend.pickNode( me.controlPosition );
\r
453 TrackMouseMovement: {
\r
454 if ( pick == trend.plot ) {
\r
455 double x = me.controlPosition.getX() - trend.horizRuler.getBounds().getX();
\r
456 double time = trend.horizRuler.toTime( x );
\r
457 double sx = (trend.horizRuler.end-trend.horizRuler.from) / trend.horizRuler.getWidth();
\r
458 double timeSnapTolerance = sx * 7.0;
\r
459 boolean shift = (me.stateMask & MouseEvent.SHIFT_MASK)>0;
\r
461 // SNAP - When shift is held
\r
463 Double snappedTime = null;
\r
465 snappedTime = trend.snapToValue( time, timeSnapTolerance );
\r
466 if (snappedTime != null) time = snappedTime;
\r
467 } catch (HistoryException e) {
\r
468 } catch (AccessorException e) {
\r
472 setHoverTime(time);
\r
476 // Implement value box moving while dragging.
\r
477 if (draggingValueBox(grab)) {
\r
478 Rectangle2D plotBox = trend.plot.getBounds();
\r
479 Rectangle2D valueBoxPos = grab.valueBoxPos;
\r
480 Rectangle2D valueBox = trend.plot.valueTipBoxBounds;
\r
481 double dx = me.controlPosition.getX() - grab.mousepos.getX();
\r
482 double dy = me.controlPosition.getY() - grab.mousepos.getY();
\r
483 double margin = Plot.VALUE_TIP_BOX_PLOT_MARGIN;
\r
484 double maxX = plotBox.getWidth() - margin - valueBox.getWidth();
\r
485 double maxY = plotBox.getHeight() - margin - valueBox.getHeight();
\r
486 double boxX = valueBoxPos.getX() + dx;
\r
487 double boxY = valueBoxPos.getY() + dy;
\r
488 double pw = plotBox.getWidth() - 2 * margin;
\r
489 double ph = plotBox.getHeight() - 2 * margin;
\r
490 double w = pw - valueBox.getWidth();
\r
491 double h = ph - valueBox.getHeight();
\r
492 if (w > 0 && h > 0) {
\r
493 double rx = (boxX - margin) / w;
\r
494 double ry = (boxY - margin) / h;
\r
495 rx = Math.max(0, Math.min(rx, 1));
\r
496 ry = Math.max(0, Math.min(ry, 1));
\r
497 trend.spec.viewProfile.valueViewPositionX = rx;
\r
498 trend.spec.viewProfile.valueViewPositionY = ry;
\r
504 ValueToolTip: if ( pick == trend.plot) {
\r
505 if (draggingValueTip(grab)) {
\r
506 // Move grabbed value-tip
\r
507 trend.valueTipTime = Double.isNaN(trend.mouseHoverTime) ? null : trend.mouseHoverTime;
\r
510 // Track hover color
\r
511 if (trend.valueTipTime!=null) {
\r
512 double valueTipX = trend.horizRuler.toX( trend.valueTipTime );
\r
513 double x = me.controlPosition.getX() - trend.horizRuler.getBounds().getX();
\r
514 boolean hit = x>=valueTipX-TrendLayout.VALUE_TIP_HOVER_SENSITIVITY && x<=valueTipX+TrendLayout.VALUE_TIP_HOVER_SENSITIVITY;
\r
515 trend.valueTipHover = hit;
\r
522 if ( pick instanceof VertRuler ) {
\r
523 VertRuler vr = (VertRuler) pick;
\r
524 hoveringItem = null;
\r
525 for (ItemNode item : trend.analogItems) {
\r
526 if (item.item.renderer != Renderer.Analog || item.ruler==vr) {
\r
527 if ( hoveringItem != null) break;
\r
528 hoveringItem = item;
\r
531 } else if ( pick == null || pick instanceof Plot ) {
\r
532 hoveringItem = trend.plot.pickItem( me.controlPosition );
\r
534 hoveringItem = null;
\r
539 if (trend.selection!=null) {
\r
540 trend.selection.setEndPoint( me.controlPosition );
\r
541 setHoverTime(null);
\r
547 if (grab != null) {
\r
548 double dx = me.controlPosition.getX() - grab.mousepos.x;
\r
549 double dy = me.controlPosition.getY() - grab.mousepos.y;
\r
550 grab.mousepos.setLocation(me.controlPosition);
\r
554 trend.horizRuler.translate(-dx*grab.sx);
\r
557 for (int i=0; i<grab.sy.length; i++) {
\r
558 if (i>=trend.vertRulers.size()) break;
\r
559 trend.vertRulers.get(i).translate(dy*grab.sy[i]);
\r
562 } else if (grab.horizRuler) {
\r
563 trend.horizRuler.translate(-dx*grab.sx);
\r
564 } else if (grab.vertRuler>=0) {
\r
565 if (grab.vertRuler<=trend.vertRulers.size()) {
\r
566 VertRuler vr = trend.vertRulers.get( grab.vertRuler );
\r
567 trend.selectVertRuler( grab.vertRuler );
\r
568 vr.translate(dy*grab.sy[ grab.vertRuler ]);
\r
572 trend.shapedirty = true;
\r
579 @EventHandler(priority = 0)
\r
580 public boolean mouseWheel(MouseWheelMovedEvent me) {
\r
581 IG2DNode node = trend.pickNode( me.controlPosition );
\r
582 if (node instanceof Plot || node instanceof HorizRuler || node instanceof VertRuler) {
\r
583 Point2D pt = node.parentToLocal( me.controlPosition );
\r
584 boolean shift = (me.stateMask & MouseEvent.SHIFT_MASK)>0;
\r
585 boolean alt = (me.stateMask & MouseEvent.ALT_MASK)>0;
\r
586 double pw = trend.plot.getWidth();
\r
587 double ph = trend.plot.analogAreaHeight;
\r
589 double r = Math.pow(0.9, me.wheelRotation);
\r
591 double w = r * pw;
\r
593 double rx = pt.getX() / pw;
\r
594 double ry = pt.getY() / ph;
\r
596 double zx = (pw-w)*rx;
\r
597 double zy = (ph-h)*ry;
\r
599 if (node instanceof Plot) {
\r
600 TrendNode trend = (TrendNode) node.getParent();
\r
602 for (VertRuler vr : trend.vertRulers) {
\r
607 trend.horizRuler.zoomIn(zx, w);
\r
611 if (node instanceof HorizRuler) {
\r
612 //HorizRuler hr = (HorizRuler) node;
\r
613 trend.horizRuler.zoomIn(zx, w);
\r
616 if (node instanceof VertRuler) {
\r
617 VertRuler vr = (VertRuler) node;
\r
618 trend.selectVertRuler( trend.vertRulers.indexOf(vr) );
\r
621 trend.shapedirty = true;
\r
629 @EventHandler(priority = 0)
\r
630 public boolean handleCommandEvent(CommandEvent e) {
\r
632 if (e.command == Commands.CANCEL) {
\r
633 if (trend.selection != null) {
\r
634 trend.selection.delete();
\r
635 trend.selection = null;
\r
641 if (draggingValueBox(grab)) {
\r
642 trend.spec.viewProfile.valueViewPositionX = grab.valueBoxRelPos.getX();
\r
643 trend.spec.viewProfile.valueViewPositionY = grab.valueBoxRelPos.getY();
\r
645 if (grab.cursor!=null) grab.cursor.remove();
\r
651 if (trend.valueTipTime != null) {
\r
652 trend.valueTipTime = null;
\r
659 if (e.command == Commands.PAN_LEFT) {
\r
660 double pw = trend.plot.getWidth();
\r
661 double ph = trend.plot.analogAreaHeight;
\r
662 double zx = -pw * KEY_MOVE;
\r
664 trend.zoomIn(zx, zy, pw, ph, true, true);
\r
669 if (e.command == Commands.PAN_RIGHT) {
\r
670 double pw = trend.plot.getWidth();
\r
671 double ph = trend.plot.analogAreaHeight;
\r
672 double zx = +pw * KEY_MOVE;
\r
674 trend.zoomIn(zx, zy, pw, ph, true, true);
\r
675 trend.horizRuler.autoscroll = false;
\r
680 if (e.command == Commands.PAN_UP) {
\r
681 double pw = trend.plot.getWidth();
\r
682 double ph = trend.plot.analogAreaHeight;
\r
684 double zy = -ph * KEY_MOVE;
\r
685 trend.zoomIn(zx, zy, pw, ph, true, true);
\r
686 trend.horizRuler.autoscroll = false;
\r
691 if (e.command == Commands.PAN_DOWN) {
\r
692 double pw = trend.plot.getWidth();
\r
693 double ph = trend.plot.analogAreaHeight;
\r
695 double zy = +ph * KEY_MOVE;
\r
696 trend.zoomIn(zx, zy, pw, ph, true, true);
\r
697 trend.horizRuler.autoscroll = false;
\r
702 // Zoom out to time window settings (VK_4)
\r
703 if (e.command.equals(Commands.AUTOSCALE)) {
\r
704 trend.horizRuler.autoscroll = true;
\r
705 for (VertRuler vertRuler : trend.vertRulers) vertRuler.autoscroll = true;
\r
711 // Fit all visible (VK_1)
\r
712 if (e.command.equals(Commands.ZOOM_TO_FIT)) {
\r
713 trend.readMinMaxFromEnd();
\r
714 trend.horizRuler.setFromEnd(trend.horizRuler.iFrom, trend.horizRuler.iEnd);
\r
715 trend.horizRuler.fireListener();
\r
716 int c = trend.vertRulers.size();
\r
717 for (int i=0; i<c; i++) {
\r
718 VertRuler vr = trend.vertRulers.get(c-i-1);
\r
719 double nMin = vr.iMin;
\r
720 double nMax = vr.iMax;
\r
722 double diff = nMax - nMin;
\r
726 diff = nMax - nMin;
\r
728 double margin = diff*0.02;
\r
730 if (trend.itemPlacement == ItemPlacement.Stacked) {
\r
731 nMin = nMin - (diff)*i - margin;
\r
732 nMax = nMax + (diff)*(c-i-1) + margin;
\r
735 nMin = vr.iMin - margin;
\r
736 nMax = vr.iMax + margin;
\r
739 vr.zoomTo(nMin, nMax);
\r
741 trend.horizRuler.autoscroll = false;
\r
747 // Fit horiz (VK_2)
\r
748 if (e.command.equals(Commands.ZOOM_TO_FIT_HORIZ)) {
\r
749 trend.readMinMaxFromEnd();
\r
750 trend.horizRuler.setFromEnd(trend.horizRuler.iFrom, trend.horizRuler.iEnd);
\r
752 trend.horizRuler.autoscroll = false;
\r
758 if (e.command.equals(Commands.ZOOM_TO_FIT_VERT)) {
\r
759 trend.readMinMaxFromEnd();
\r
760 int c = trend.vertRulers.size();
\r
761 for (int i=0; i<c; i++) {
\r
762 VertRuler vr = trend.vertRulers.get(c-i-1);
\r
763 double nMin = vr.iMin;
\r
764 double nMax = vr.iMax;
\r
766 double diff = nMax - nMin;
\r
770 diff = nMax - nMin;
\r
772 double margin = diff*0.02;
\r
774 if (trend.itemPlacement == ItemPlacement.Stacked) {
\r
775 nMin = nMin - (diff)*i - margin;
\r
776 nMax = nMax + (diff)*(c-i-1) + margin;
\r
779 nMin = vr.iMin - margin;
\r
780 nMax = vr.iMax + margin;
\r
783 vr.zoomTo(nMin, nMax);
\r
791 if (e.command == Commands.ZOOM_IN) {
\r
792 double pw = trend.plot.getWidth();
\r
793 double ph = trend.plot.analogAreaHeight;
\r
794 double za = 3; // Zoom amount
\r
795 double w = (1. - (0.1*za)) * pw;
\r
796 double h = (1. - (0.1*za)) * ph;
\r
798 double zx = (pw-w)/2;
\r
799 double zy = (ph-h)/2;
\r
801 trend.zoomIn(zx, zy, w, h, true, true);
\r
802 trend.horizRuler.autoscroll = false;
\r
809 if (e.command == Commands.ZOOM_OUT) {
\r
810 double pw = trend.plot.getWidth();
\r
811 double ph = trend.plot.analogAreaHeight;
\r
812 double za = -3; // Zoom amount
\r
813 double w = (1. - (0.1*za)) * pw;
\r
814 double h = (1. - (0.1*za)) * ph;
\r
816 double zx = (pw-w)/2;
\r
817 double zy = (ph-h)/2;
\r
819 trend.zoomIn(zx, zy, w, h, true, true);
\r
820 trend.horizRuler.autoscroll = false;
\r
826 // Print to printer
\r
827 if (e.command == Commands.PRINT) {
\r
829 PrintUtil pu = new PrintUtil();
\r
830 pu.addTrendPage(trend);
\r
832 } catch (PrinterException e1) {
\r
833 e1.printStackTrace();
\r
839 if (e.command == Commands.PDFPRINT) {
\r
840 PrintUtil pu = new PrintUtil();
\r
841 pu.addTrendPage(trend);
\r
843 File f = File.createTempFile("Trend", ".pdf");
\r
845 System.out.println("Printed Trend to "+f);
\r
846 } catch (IOException e1) {
\r
847 e1.printStackTrace();
\r
852 // Read visible values into CSV
\r
853 if (e.command == Commands.COPY || e.command == Commands.EXPORT) {
\r
854 StringBuilder sb = new StringBuilder(64*1024);
\r
856 // TODO: fix to use preferences.
\r
857 CSVFormatter formatter = new CSVFormatter();
\r
858 formatter.setTimeRange(trend.horizRuler.from, trend.horizRuler.end);
\r
859 for (ItemNode i : trend.allItems)
\r
861 if (i.item.hidden) continue;
\r
862 if (i.historyItems==null || i.historyItems.length==0) continue;
\r
863 Bean bestQualityStream = i.historyItems[0];
\r
864 String historyItemId = (String) bestQualityStream.getFieldUnchecked("id");
\r
865 formatter.addItem( trend.historian, historyItemId, i.item.simpleLabel, i.item.variableReference, i.item.unit);
\r
869 // TODO: Use a proper user-cancelable progressmonitor
\r
870 formatter.formulate2(new ProgressMonitor.Stub(), sb);
\r
871 } catch (IOException e1) {
\r
872 // sb cannot throw ioexception
\r
874 } catch (HistoryException e1) {
\r
877 Toolkit toolkit = Toolkit.getDefaultToolkit();
\r
878 Clipboard clipboard = toolkit.getSystemClipboard();
\r
879 StringSelection strSel = new StringSelection(sb.toString());
\r
880 clipboard.setContents(strSel, null);
\r
885 static class Grab {
\r
887 IMouseCursorHandle cursor;
\r
888 Point2D.Double mousepos = new Point2D.Double();
\r
889 boolean plot = false;
\r
890 boolean horizRuler = false;
\r
891 boolean vert = false;
\r
892 boolean horiz = false;
\r
893 int vertRuler = -1;
\r
896 boolean valueTipGrab = false;
\r
897 boolean valueBoxGrab = false;
\r
898 Point2D valueBoxRelPos = new Point2D.Double();
\r
899 Rectangle2D valueBoxPos = new Rectangle2D.Double();
\r
902 private static boolean draggingValueTip(Grab g) {
\r
903 return g != null && g.valueTipGrab;
\r
906 private static boolean draggingValueBox(Grab g) {
\r
907 return g != null && g.valueBoxGrab;
\r