1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.diyefi.openlogviewer.graphing;
24
25 import java.awt.Dimension;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.awt.event.ComponentEvent;
29 import java.awt.event.ComponentListener;
30 import java.awt.event.InputEvent;
31 import java.awt.event.KeyEvent;
32 import java.awt.event.KeyListener;
33 import java.awt.event.MouseEvent;
34 import java.awt.event.MouseListener;
35 import java.awt.event.MouseMotionListener;
36 import java.awt.event.MouseWheelEvent;
37 import java.awt.event.MouseWheelListener;
38
39 import javax.swing.JPanel;
40 import javax.swing.Timer;
41
42 import org.diyefi.openlogviewer.OpenLogViewer;
43 import org.diyefi.openlogviewer.genericlog.GenericLog;
44
45 public class EntireGraphingPanel extends JPanel implements ActionListener, MouseMotionListener, MouseListener, MouseWheelListener, KeyListener, ComponentListener {
46 private static final long serialVersionUID = 1L;
47 public static final int LEFT_OFFSCREEN_POINTS_ZOOMED_IN = 0;
48 public static final int RIGHT_OFFSCREEN_POINTS_ZOOMED_IN = 3;
49 public static final int LEFT_OFFSCREEN_POINTS_ZOOMED_OUT = 2;
50 public static final int RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT = 2;
51
52 private MultiGraphLayeredPane multiGraph;
53 private GraphPositionPanel graphPositionPanel;
54 private double graphPosition;
55 private int graphSize;
56 private boolean playing;
57 private boolean wasPlaying;
58 private Timer playTimer;
59 private Timer flingTimer;
60 private boolean dragging;
61 private boolean flinging;
62 private long thePastMouseDragged;
63 private long thePastLeftArrow;
64 private long thePastRightArrow;
65 private int scrollAcceleration;
66 private int prevDragXCoord;
67 private int flingInertia;
68 private int zoom;
69 private boolean zoomedOutBeyondOneToOne;
70 private int oldComponentWidth;
71
72 public EntireGraphingPanel() {
73 super();
74 init();
75 }
76
77 private void init() {
78 this.setName("Graphing Panel");
79 this.setLayout(new java.awt.BorderLayout());
80 multiGraph = new MultiGraphLayeredPane();
81 multiGraph.setPreferredSize(new Dimension(600, 400));
82 this.add(multiGraph, java.awt.BorderLayout.CENTER);
83 graphPositionPanel = new GraphPositionPanel();
84 graphPositionPanel.setPreferredSize(new Dimension(600, 20));
85 this.add(graphPositionPanel, java.awt.BorderLayout.SOUTH);
86 zoom = 1;
87 zoomedOutBeyondOneToOne = false;
88 oldComponentWidth = this.getWidth();
89 resetGraphPosition();
90 setGraphSize(0);
91 playing = false;
92 wasPlaying = false;
93 playTimer = new Timer(10, this);
94 playTimer.setInitialDelay(0);
95 flingTimer = new Timer(10, this);
96 flingTimer.setInitialDelay(0);
97 addMouseListener(this);
98 addMouseMotionListener(this);
99 addMouseWheelListener(this);
100 addMouseListener(multiGraph.getInfoPanel());
101 addMouseMotionListener(multiGraph.getInfoPanel());
102 stopDragging();
103 stopFlinging();
104 thePastMouseDragged = System.currentTimeMillis();
105 thePastLeftArrow = System.currentTimeMillis();
106 thePastRightArrow = System.currentTimeMillis();
107 scrollAcceleration = 0;
108 }
109
110 public final void actionPerformed(final ActionEvent e) {
111 if (playing && graphPosition < getGraphPositionMax()) {
112 if (zoomedOutBeyondOneToOne) {
113 moveGraphPosition(zoom);
114 } else {
115 moveGraphPosition(1);
116 }
117 } else if ((flinging && graphPosition < getGraphPositionMax()) && (graphPosition > getGraphPositionMin())) {
118 if (flingInertia == 0) {
119 stopFlinging();
120 } else {
121 moveEntireGraphingPanel(flingInertia);
122 if (flingInertia > 0) {
123 flingInertia--;
124 } else {
125 flingInertia++;
126 }
127 }
128 }
129 }
130
131 public final MultiGraphLayeredPane getMultiGraphLayeredPane() {
132 return multiGraph;
133 }
134
135 public final GraphPositionPanel getGraphPositionPanel() {
136 return graphPositionPanel;
137 }
138
139 public final void setLog(final GenericLog genLog) {
140 playing = false;
141 resetGraphPosition();
142 multiGraph.setLog(genLog);
143 graphPositionPanel.setLog(genLog);
144 }
145
146
147
148
149
150 private int getTightestZoom() {
151 return this.getWidth() - 1;
152 }
153
154
155
156
157 private int getWidestZoom() {
158 return (multiGraph.graphSize() / 2);
159 }
160
161
162
163
164
165
166
167 public final void zoomIn() {
168 final double graphWidth = this.getWidth();
169 double move = 0;
170
171 if (zoomedOutBeyondOneToOne) {
172 if (zoom == 2) {
173 zoomedOutBeyondOneToOne = false;
174 }
175 zoom--;
176 move = graphWidth / (double) (zoom * 2);
177 } else if (zoom < getTightestZoom()) {
178 move = graphWidth / (double) (zoom * 2);
179 zoom++;
180 }
181
182 moveEntireGraphingPanel(move);
183 }
184
185
186
187
188
189
190
191 public final void zoomInCoarse() {
192 final int zoomAmount = (int) Math.sqrt(zoom);
193 for (int i = 0; i < zoomAmount; i++) {
194 zoomIn();
195 }
196 }
197
198
199
200
201
202
203
204 public final void zoomOut() {
205 final double graphWidth = this.getWidth();
206 double move = 0;
207
208 if (!zoomedOutBeyondOneToOne) {
209 if (zoom == 1) {
210 zoomedOutBeyondOneToOne = true;
211 zoom = 2;
212 move = graphWidth / (double) (zoom * 2);
213 } else {
214 move = graphWidth / (double) (zoom * 2);
215 zoom--;
216 }
217 } else if (zoom < getWidestZoom()) {
218 zoom++;
219 move = graphWidth / (double) (zoom * 2);
220 }
221
222 moveEntireGraphingPanel(-move);
223 }
224
225
226
227
228
229
230
231 public final void zoomOutCoarse() {
232 final int zoomAmount = (int) Math.sqrt(zoom);
233 for (int i = 0; i < zoomAmount; i++) {
234 zoomOut();
235 }
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 private void zoomGraphToFit(final int dataPointsToFit) {
252 final int graphWindowWidth = this.getWidth() - 8;
253 int dataPointsThatFitInDisplay = 0;
254 if (zoomedOutBeyondOneToOne) {
255 dataPointsThatFitInDisplay = graphWindowWidth * zoom;
256 } else {
257 dataPointsThatFitInDisplay = graphWindowWidth / zoom;
258 }
259
260
261 while (dataPointsToFit < dataPointsThatFitInDisplay && zoom != getTightestZoom()) {
262 zoomIn();
263 if (zoomedOutBeyondOneToOne) {
264 dataPointsThatFitInDisplay = graphWindowWidth * zoom;
265 } else {
266 dataPointsThatFitInDisplay = graphWindowWidth / zoom;
267 }
268 }
269
270
271 while (dataPointsToFit > dataPointsThatFitInDisplay && zoom != getWidestZoom()) {
272 zoomOut();
273 if (zoomedOutBeyondOneToOne) {
274 dataPointsThatFitInDisplay = graphWindowWidth * zoom;
275 } else {
276 dataPointsThatFitInDisplay = graphWindowWidth / zoom;
277 }
278 }
279 }
280
281 public boolean isZoomedOutBeyondOneToOne() {
282 return zoomedOutBeyondOneToOne;
283 }
284
285 public final void play() {
286 if (playing) {
287 pause();
288 } else {
289 playing = true;
290 stopDragging();
291 stopFlinging();
292 playTimer.start();
293 }
294 }
295
296 public final void pause() {
297 playing = false;
298 playTimer.stop();
299 stopDragging();
300 stopFlinging();
301 }
302
303
304
305
306 public final void fastForward() {
307 final int currentDelay = playTimer.getDelay();
308 if (currentDelay > 0) {
309 playTimer.setDelay(currentDelay - 1);
310 }
311 }
312
313 public final void eject() {
314 resetGraphPosition();
315 }
316
317 public final void stop() {
318 playing = false;
319 wasPlaying = false;
320 playTimer.stop();
321 resetGraphPosition();
322 }
323
324
325
326
327 public final void slowDown() {
328 final int currentDelay = playTimer.getDelay();
329 playTimer.setDelay(currentDelay + 1);
330 }
331
332 public final void fling() {
333 flinging = true;
334 flingTimer.start();
335 }
336
337 private double getGraphPositionMin() {
338 double min = 0.0;
339 if (zoomedOutBeyondOneToOne) {
340 min = -((this.getWidth() - 1) * zoom);
341 } else {
342 min = -(((double) this.getWidth() - 1.0) / (double) zoom);
343 }
344 return min;
345 }
346
347 public final double getGraphPosition() {
348 return graphPosition;
349 }
350
351 private int getGraphPositionMax() {
352 if (zoom == getWidestZoom()) {
353 int size = graphSize - (LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom);
354 if (size < 0) {
355 size = 0;
356 }
357 return size;
358 }
359 return graphSize;
360 }
361
362 public final void setGraphPosition(final double newPos) {
363 graphPosition = newPos;
364 repaint();
365 }
366
367
368
369
370 public final void setGraphSize(final int newGraphSize) {
371 graphSize = newGraphSize;
372 if (graphSize > 0) {
373 centerGraphPosition(0, graphSize);
374 zoomGraphToFit(graphSize);
375 }
376 }
377
378
379
380
381
382 private void resetGraphPosition() {
383 setGraphPosition(getGraphPositionMin());
384 }
385
386
387
388
389
390
391
392
393 private void centerGraphPosition(final int beginPosition, final int endPosition) {
394 final int halfScreen = this.getWidth() / 2;
395 double pointsThatFitInHalfScreen = 0;
396 if (zoomedOutBeyondOneToOne) {
397 pointsThatFitInHalfScreen = halfScreen * zoom;
398 } else {
399 pointsThatFitInHalfScreen = halfScreen / zoom;
400 }
401 final int distanceBetweenPositions = endPosition - beginPosition;
402 final double halfwayBetweenTwoPositions = distanceBetweenPositions / 2;
403 final double centerPosition = (beginPosition + halfwayBetweenTwoPositions) - pointsThatFitInHalfScreen;
404 setGraphPosition(centerPosition);
405 }
406
407
408
409
410
411 private void goToLastGraphPosition() {
412 setGraphPosition(getGraphPositionMax());
413 }
414
415 public final boolean isPlaying() {
416 return playing;
417 }
418
419 public final int getZoom() {
420 return zoom;
421 }
422
423
424
425
426 private void moveGraphPosition(final double amount) {
427 final double newPos = graphPosition + amount;
428 if (newPos > getGraphPositionMax()) {
429 goToLastGraphPosition();
430 } else if (newPos < getGraphPositionMin()) {
431 resetGraphPosition();
432 } else {
433 setGraphPosition(newPos);
434 }
435 }
436
437
438
439
440
441 private void moveEntireGraphingPanel(final double newPosition) {
442 double move = -1.0;
443 if (zoomedOutBeyondOneToOne) {
444 move = newPosition * zoom;
445 } else {
446 move = newPosition / zoom;
447 }
448 if (graphPosition + move < getGraphPositionMax()) {
449 if (graphPosition + move < getGraphPositionMin()) {
450 resetGraphPosition();
451 } else {
452 moveGraphPosition(move);
453 }
454 } else {
455 goToLastGraphPosition();
456 }
457 }
458
459 private void stopDragging() {
460 dragging = false;
461 prevDragXCoord = -1;
462 }
463
464 private void stopFlinging() {
465 flinging = false;
466 flingInertia = 0;
467 }
468
469
470 @Override
471 public final void mouseClicked(final MouseEvent e) {
472 if (!dragging) {
473 final int half = this.getWidth() / 2;
474 moveEntireGraphingPanel(e.getX() - half);
475 } else {
476 stopDragging();
477 stopFlinging();
478 }
479 }
480
481 @Override
482 public final void mouseDragged(final MouseEvent e) {
483 dragging = true;
484 final int xMouseCoord = e.getX();
485 if ((prevDragXCoord > 0) && (prevDragXCoord != xMouseCoord)) {
486 moveEntireGraphingPanel(prevDragXCoord - xMouseCoord);
487 flingInertia = ((prevDragXCoord - xMouseCoord) * 2);
488 thePastMouseDragged = System.currentTimeMillis();
489 }
490 prevDragXCoord = xMouseCoord;
491 }
492
493 @Override
494 public final void mouseMoved(final MouseEvent e) {
495
496
497
498 }
499
500 @Override
501 public final void mouseEntered(final MouseEvent e) {
502
503
504
505 }
506
507 @Override
508 public final void mouseExited(final MouseEvent e) {
509
510
511
512 }
513
514 @Override
515 public final void mousePressed(final MouseEvent e) {
516 wasPlaying = playing;
517 if (playing) {
518 pause();
519 }
520
521 stopDragging();
522 stopFlinging();
523 }
524
525 @Override
526 public final void mouseReleased(final MouseEvent e) {
527 stopDragging();
528
529 final long now = System.currentTimeMillis();
530 if ((now - thePastMouseDragged) > 50) {
531 stopFlinging();
532 }
533
534 if (flingInertia != 0) {
535 fling();
536 }
537
538 if (wasPlaying) {
539 play();
540 }
541 }
542
543 @Override
544 public final void mouseWheelMoved(final MouseWheelEvent e) {
545 final int xMouseCoord = e.getX();
546 final double center = this.getWidth() / 2.0;
547 final int notches = e.getWheelRotation();
548 double move = 0;
549 final int zoomAmount = (int)Math.sqrt(zoom);
550 if (notches < 0) {
551 for (int i = 0; i < zoomAmount; i++) {
552 if (zoomedOutBeyondOneToOne) {
553 move = (xMouseCoord - center) / (zoom - 1.0);
554 } else {
555 move = (xMouseCoord - center) / zoom;
556 }
557 if (!(!zoomedOutBeyondOneToOne && zoom == getTightestZoom())) {
558 zoomIn();
559 moveEntireGraphingPanel(move);
560 }
561 }
562 } else {
563 for (int i = 0; i < zoomAmount; i++) {
564 if (zoomedOutBeyondOneToOne || zoom == 1) {
565 move = -(xMouseCoord - center) / (zoom + 1.0);
566 } else {
567 move = -(xMouseCoord - center) / zoom;
568 }
569 if (!(zoomedOutBeyondOneToOne && zoom == getWidestZoom())) {
570 zoomOut();
571 moveEntireGraphingPanel(move);
572 }
573 }
574 }
575 }
576
577
578 @Override
579 public final void keyPressed(final KeyEvent e) {
580 switch (e.getKeyCode()) {
581
582 case KeyEvent.VK_SPACE: {
583 play();
584 break;
585 }
586
587
588 case KeyEvent.VK_ENTER: {
589 if (e.getModifiers() == InputEvent.ALT_MASK
590 && e.getKeyLocation() == KeyEvent.KEY_LOCATION_STANDARD) {
591 OpenLogViewer.getInstance().enterFullScreen();
592 }
593 break;
594 }
595
596
597 case KeyEvent.VK_ESCAPE: {
598 OpenLogViewer.getInstance().exitFullScreen();
599 break;
600 }
601
602
603 case KeyEvent.VK_F11: {
604 OpenLogViewer.getInstance().toggleFullScreen();
605 break;
606 }
607
608
609 case KeyEvent.VK_HOME: {
610 resetGraphPosition();
611 break;
612 }
613
614
615 case KeyEvent.VK_END: {
616 goToLastGraphPosition();
617 break;
618 }
619
620
621 case KeyEvent.VK_PAGE_UP: {
622
623 moveEntireGraphingPanel(-(this.getWidth() * 0.75));
624 break;
625 }
626
627 case KeyEvent.VK_LEFT:
628 case KeyEvent.VK_KP_LEFT: {
629 int localZoom = zoom;
630 if(zoomedOutBeyondOneToOne){
631 localZoom = 1;
632 }
633 if (e.getModifiers() == InputEvent.CTRL_MASK) {
634
635 moveEntireGraphingPanel(-(this.getWidth() * 0.75));
636 } else {
637 final long now = System.currentTimeMillis();
638 final long delay = now - thePastLeftArrow;
639 if (delay < 50) {
640 scrollAcceleration++;
641 moveEntireGraphingPanel(-localZoom - (scrollAcceleration * localZoom));
642 } else {
643 scrollAcceleration = 0;
644 moveEntireGraphingPanel(-localZoom);
645 }
646 thePastLeftArrow = System.currentTimeMillis();
647 }
648 break;
649 }
650
651
652 case KeyEvent.VK_PAGE_DOWN: {
653
654 moveEntireGraphingPanel(this.getWidth() * 0.75);
655 break;
656 }
657
658 case KeyEvent.VK_RIGHT:
659 case KeyEvent.VK_KP_RIGHT: {
660 int localZoom = zoom;
661 if(zoomedOutBeyondOneToOne){
662 localZoom = 1;
663 }
664 if (e.getModifiers() == InputEvent.CTRL_MASK) {
665
666 moveEntireGraphingPanel(this.getWidth() * 0.75);
667 } else {
668 final long now = System.currentTimeMillis();
669 final long delay = now - thePastRightArrow;
670 if (delay < 50) {
671 scrollAcceleration++;
672 moveEntireGraphingPanel(localZoom + (scrollAcceleration * localZoom));
673 } else {
674 scrollAcceleration = 0;
675 moveEntireGraphingPanel(localZoom);
676 }
677 thePastRightArrow = System.currentTimeMillis();
678 }
679 break;
680 }
681
682
683 case KeyEvent.VK_UP:
684 case KeyEvent.VK_KP_UP: {
685 zoomInCoarse();
686 break;
687 }
688
689 case KeyEvent.VK_ADD: {
690 if (e.getModifiers() == InputEvent.CTRL_MASK) {
691 zoomInCoarse();
692 }
693 break;
694 }
695
696
697 case KeyEvent.VK_DOWN:
698 case KeyEvent.VK_KP_DOWN: {
699 zoomOutCoarse();
700 break;
701 }
702
703 case KeyEvent.VK_SUBTRACT: {
704 if (e.getModifiers() == InputEvent.CTRL_MASK) {
705 zoomOutCoarse();
706 }
707 break;
708 }
709 }
710 }
711
712 @Override
713 public final void keyReleased(final KeyEvent e) {
714
715
716
717 }
718
719 @Override
720 public final void keyTyped(final KeyEvent e) {
721
722
723
724 }
725
726 @Override
727 public void componentHidden(final ComponentEvent e) {
728
729
730 }
731
732
733 @Override
734 public void componentMoved(final ComponentEvent e) {
735 componentResized(e);
736 }
737
738 @Override
739 public void componentResized(final ComponentEvent e) {
740 int newWidth = this.getWidth();
741 if (newWidth != oldComponentWidth) {
742 double move = 0.0;
743 int amount = newWidth - oldComponentWidth;
744 if (zoomedOutBeyondOneToOne) {
745 move = -(amount * zoom);
746 } else {
747 move = -((double) amount / (double) zoom);
748 }
749 move /= 2.0;
750 oldComponentWidth = newWidth;
751 moveGraphPosition(move);
752 }
753 }
754
755 @Override
756 public void componentShown(final ComponentEvent e) {
757
758
759 }
760 }