All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
SVWindow.java
Go to the documentation of this file.
1 // Copyright 2007 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); You may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
6 // applicable law or agreed to in writing, software distributed under the
7 // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
8 // OF ANY KIND, either express or implied. See the License for the specific
9 // language governing permissions and limitations under the License.
10 
11 package com.google.scrollview.ui;
12 
19 
20 import org.piccolo2d.PCamera;
21 import org.piccolo2d.PCanvas;
22 import org.piccolo2d.PLayer;
23 import org.piccolo2d.extras.swing.PScrollPane;
24 import org.piccolo2d.nodes.PImage;
25 import org.piccolo2d.nodes.PPath;
26 import org.piccolo2d.nodes.PText;
27 import org.piccolo2d.util.PPaintContext;
28 
29 import java.awt.BasicStroke;
30 import java.awt.BorderLayout;
31 import java.awt.Color;
32 import java.awt.Font;
33 import java.awt.GraphicsEnvironment;
34 import java.awt.Rectangle;
35 import java.awt.TextArea;
36 import java.awt.geom.IllegalPathStateException;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 
40 import javax.swing.JFrame;
41 import javax.swing.JOptionPane;
42 import javax.swing.SwingUtilities;
43 import javax.swing.WindowConstants;
44 
52 public class SVWindow extends JFrame {
56  private static final int MAX_WINDOW_X = 1000;
57  private static final int MAX_WINDOW_Y = 800;
58 
59  /* Constant defining the (approx) height of the default message box*/
60  private static final int DEF_MESSAGEBOX_HEIGHT = 200;
61 
63  public static final double SCALING_FACTOR = 2;
64 
66  PLayer layer;
67 
69  Color currentPenColor;
70 
75  Color currentBrushColor;
76 
79  Font currentFont;
80 
82  // This really needs to be a fixed width stroke as the basic stroke is
83  // anti-aliased and gets too faint, but the piccolo fixed width stroke
84  // is too buggy and generates missing initial moveto in path definition
85  // errors with a IllegalPathStateException that cannot be caught because
86  // it is in the automatic repaint function. If we can fix the exceptions
87  // in piccolo, then we can use the following instead of BasicStroke:
88  // import edu.umd.cs.piccolox.util.PFixedWidthStroke;
89  // PFixedWidthStroke stroke = new PFixedWidthStroke(0.5f);
90  // Instead we use the BasicStroke and turn off anti-aliasing.
91  BasicStroke stroke = new BasicStroke(0.5f);
92 
97  public int hash;
98 
103  public static int nrWindows = 0;
104 
109  private SVEventHandler svEventHandler = null;
110  private SVMenuBar svMenuBar = null;
111  private TextArea ta = null;
112  public SVPopupMenu svPuMenu = null;
113  public PCanvas canvas;
114  private int winSizeX;
115  private int winSizeY;
116 
118  public void brush(int red, int green, int blue) {
119  brush(red, green, blue, 255);
120  }
121 
123  public void brush(int red, int green, int blue, int alpha) {
124  // If alpha is zero, use a null brush to save rendering time.
125  if (alpha == 0) {
126  currentBrushColor = null;
127  } else {
128  currentBrushColor = new Color(red, green, blue, alpha);
129  }
130  }
131 
133  public void clear() {
134  // Manipulation of Piccolo's scene graph should be done from Swings
135  // event dispatch thread since Piccolo is not thread safe. This code calls
136  // removeAllChildren() from that thread and releases the latch.
137  final java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1);
138  SwingUtilities.invokeLater(new Runnable() {
139  public void run() {
140  layer.removeAllChildren();
141  repaint();
142  latch.countDown();
143  }
144  });
145  try {
146  latch.await();
147  } catch (InterruptedException e) {
148  }
149  }
150 
157  public void createPolyline(int length) {
158  ScrollView.polylineXCoords = new float[length];
159  ScrollView.polylineYCoords = new float[length];
160  ScrollView.polylineSize = length;
162  }
163 
167  public void drawPolyline() {
168  int numCoords = ScrollView.polylineXCoords.length;
169  if (numCoords < 2) {
170  return;
171  }
172  PPath pn = PPath.createLine(ScrollView.polylineXCoords[0],
176  pn.reset();
178  for (int p = 1; p < numCoords; ++p) {
180  }
181  pn.closePath();
183  pn.setStrokePaint(currentPenColor);
184  pn.setPaint(null); // Don't fill the polygon - this is just a polyline.
185  pn.setStroke(stroke);
186  layer.addChild(pn);
187  }
188 
202  public SVWindow(String name, int hash, int posX, int posY, int sizeX,
203  int sizeY, int canvasSizeX, int canvasSizeY) {
204  super(name);
205 
206  // Provide defaults for sizes.
207  if (sizeX == 0) sizeX = canvasSizeX;
208  if (sizeY == 0) sizeY = canvasSizeY;
209  if (canvasSizeX == 0) canvasSizeX = sizeX;
210  if (canvasSizeY == 0) canvasSizeY = sizeY;
211 
212  // Initialize variables
213  nrWindows++;
214  this.hash = hash;
215  this.svEventHandler = new SVEventHandler(this);
216  this.currentPenColor = Color.BLACK;
217  this.currentBrushColor = Color.BLACK;
218  this.currentFont = new Font("Times New Roman", Font.PLAIN, 12);
219 
220  // Determine the initial size and zoom factor of the window.
221  // If the window is too big, rescale it and zoom out.
222  int shrinkfactor = 1;
223 
224  if (sizeX > MAX_WINDOW_X) {
225  shrinkfactor = (sizeX + MAX_WINDOW_X - 1) / MAX_WINDOW_X;
226  }
227  if (sizeY / shrinkfactor > MAX_WINDOW_Y) {
228  shrinkfactor = (sizeY + MAX_WINDOW_Y - 1) / MAX_WINDOW_Y;
229  }
230  winSizeX = sizeX / shrinkfactor;
231  winSizeY = sizeY / shrinkfactor;
232  double initialScalingfactor = 1.0 / shrinkfactor;
233  if (winSizeX > canvasSizeX || winSizeY > canvasSizeY) {
234  initialScalingfactor = Math.min(1.0 * winSizeX / canvasSizeX,
235  1.0 * winSizeY / canvasSizeY);
236  }
237 
238  // Setup the actual window (its size, camera, title, etc.)
239  if (canvas == null) {
240  canvas = new PCanvas();
241  getContentPane().add(canvas, BorderLayout.CENTER);
242  }
243 
244  layer = canvas.getLayer();
245  canvas.setBackground(Color.BLACK);
246 
247  // Disable anitaliasing to make the lines more visible.
248  canvas.setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
249 
250  setLayout(new BorderLayout());
251 
252  setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
253 
254  validate();
255  canvas.requestFocus();
256 
257  // Manipulation of Piccolo's scene graph should be done from Swings
258  // event dispatch thread since Piccolo is not thread safe. This code calls
259  // initialize() from that thread once the PFrame is initialized, so you are
260  // safe to start working with Piccolo in the initialize() method.
261  SwingUtilities.invokeLater(new Runnable() {
262  public void run() {
263  repaint();
264  }
265  });
266 
267  setSize(winSizeX, winSizeY);
268  setLocation(posX, posY);
269  setTitle(name);
270 
271  // Add a Scrollpane to be able to scroll within the canvas
272  PScrollPane scrollPane = new PScrollPane(canvas);
273  getContentPane().add(scrollPane);
274  scrollPane.setWheelScrollingEnabled(false);
275  PCamera lc = canvas.getCamera();
276  lc.scaleViewAboutPoint(initialScalingfactor, 0, 0);
277 
278  // Disable the default event handlers and add our own.
279  addWindowListener(svEventHandler);
280  canvas.removeInputEventListener(canvas.getPanEventHandler());
281  canvas.removeInputEventListener(canvas.getZoomEventHandler());
282  canvas.addInputEventListener(svEventHandler);
283  canvas.addKeyListener(svEventHandler);
284 
285  // Make the window visible.
286  validate();
287  setVisible(true);
288 
289  }
290 
295  public void addMessageBox() {
296  if (ta == null) {
297  ta = new TextArea();
298  ta.setEditable(false);
299  getContentPane().add(ta, BorderLayout.SOUTH);
300  }
301  // We need to make the window bigger to accomodate the message box.
302  winSizeY += DEF_MESSAGEBOX_HEIGHT;
303  setSize(winSizeX, winSizeY);
304  }
305 
311  public void setStrokeWidth(float width) {
312  // If this worked we wouldn't need the antialiased rendering off.
313  // stroke = new PFixedWidthStroke(width);
314  stroke = new BasicStroke(width);
315  }
316 
322  public void drawEllipse(int x, int y, int width, int height) {
323  PPath pn = PPath.createEllipse(x, y, width, height);
324  pn.setStrokePaint(currentPenColor);
325  pn.setStroke(stroke);
326  pn.setPaint(currentBrushColor);
327  layer.addChild(pn);
328  }
329 
335  public void drawImage(PImage img, int xPos, int yPos) {
336  img.setX(xPos);
337  img.setY(yPos);
338  layer.addChild(img);
339  }
340 
344  public void drawLine(int x1, int y1, int x2, int y2) {
345  PPath pn = PPath.createLine(x1, y1, x2, y2);
346  pn.setStrokePaint(currentPenColor);
347  pn.setPaint(null); // Null paint may render faster than the default.
348  pn.setStroke(stroke);
349  pn.moveTo(x1, y1);
350  pn.lineTo(x2, y2);
351  layer.addChild(pn);
352  }
353 
359  public void drawRectangle(int x1, int y1, int x2, int y2) {
360 
361  if (x1 > x2) {
362  int t = x1;
363  x1 = x2;
364  x2 = t;
365  }
366  if (y1 > y2) {
367  int t = y1;
368  y1 = y2;
369  y2 = t;
370  }
371 
372  PPath pn = PPath.createRectangle(x1, y1, x2 - x1, y2 - y1);
373  pn.setStrokePaint(currentPenColor);
374  pn.setStroke(stroke);
375  pn.setPaint(currentBrushColor);
376  layer.addChild(pn);
377  }
378 
386  public void drawText(int x, int y, String text) {
387  int unreadableCharAt = -1;
388  char[] chars = text.toCharArray();
389  PText pt = new PText(text);
390  pt.setTextPaint(currentPenColor);
391  pt.setFont(currentFont);
392 
393  // Check to see if every character can be displayed by the current font.
394  for (int i = 0; i < chars.length; i++) {
395  if (!currentFont.canDisplay(chars[i])) {
396  // Set to the first not displayable character.
397  unreadableCharAt = i;
398  break;
399  }
400  }
401 
402  // Have to find some working font and use it for this text entry.
403  if (unreadableCharAt != -1) {
404  Font[] allfonts =
405  GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
406  for (int j = 0; j < allfonts.length; j++) {
407  if (allfonts[j].canDisplay(chars[unreadableCharAt])) {
408  Font tempFont =
409  new Font(allfonts[j].getFontName(), currentFont.getStyle(),
410  currentFont.getSize());
411  pt.setFont(tempFont);
412  break;
413  }
414  }
415  }
416 
417  pt.setX(x);
418  pt.setY(y);
419  layer.addChild(pt);
420  }
421 
423  public void pen(int red, int green, int blue) {
424  pen(red, green, blue, 255);
425  }
426 
428  public void pen(int red, int green, int blue, int alpha) {
429  currentPenColor = new Color(red, green, blue, alpha);
430  }
431 
435  public void textAttributes(String font, int pixelSize, boolean bold,
436  boolean italic, boolean underlined) {
437 
438  // For legacy reasons convert "Times" to "Times New Roman"
439  if (font.equals("Times")) {
440  font = "Times New Roman";
441  }
442 
443  int style = Font.PLAIN;
444  if (bold) {
445  style += Font.BOLD;
446  }
447  if (italic) {
448  style += Font.ITALIC;
449  }
450  currentFont = new Font(font, style, pixelSize);
451  }
452 
457  public void zoomRectangle(int x1, int y1, int x2, int y2) {
458  if (x2 > x1 && y2 > y1) {
459  winSizeX = getWidth();
460  winSizeY = getHeight();
461  int width = x2 - x1;
462  int height = y2 - y1;
463  // Since piccolo doesn't do this well either, pad with a margin
464  // all the way around.
465  int wmargin = width / 2;
466  int hmargin = height / 2;
467  double scalefactor = Math.min(winSizeX / (2.0 * wmargin + width),
468  winSizeY / (2.0 * hmargin + height));
469  PCamera lc = canvas.getCamera();
470  lc.scaleView(scalefactor / lc.getViewScale());
471  lc.animateViewToPanToBounds(new Rectangle(x1 - hmargin, y1 - hmargin,
472  2 * wmargin + width,
473  2 * hmargin + height), 0);
474  }
475  }
476 
483  public void update() {
484  // TODO(rays) fix bugs in piccolo or use something else.
485  // The repaint function generates many
486  // exceptions for no good reason. We catch and ignore as many as we
487  // can here, but most of them are generated by the system repaints
488  // caused by resizing/exposing parts of the window etc, and they
489  // generate unwanted stack traces that have to be piped to /dev/null
490  // (on linux).
491  try {
492  repaint();
493  } catch (NullPointerException e) {
494  // Do nothing so the output isn't full of stack traces.
495  } catch (IllegalPathStateException e) {
496  // Do nothing so the output isn't full of stack traces.
497  }
498  }
499 
501  public void addMenuBarItem(String parent, String name, int id,
502  boolean checked) {
503  svMenuBar.add(parent, name, id, checked);
504  }
505 
507  public void addMenuBarItem(String parent, String name) {
508  addMenuBarItem(parent, name, -1);
509  }
510 
512  public void addMenuBarItem(String parent, String name, int id) {
513  if (svMenuBar == null) {
514  svMenuBar = new SVMenuBar(this);
515 
516  }
517  svMenuBar.add(parent, name, id);
518  }
519 
521  public void addMessage(String message) {
522  if (ta != null) {
523  ta.append(message + "\n");
524  } else {
525  System.out.println(message + "\n");
526  }
527  }
528 
539  private static String convertIntegerStringToUnicodeString(String input) {
540  StringBuffer sb = new StringBuffer(input);
541  Pattern numbers = Pattern.compile("0x[0-9a-fA-F]{4}");
542  Matcher matcher = numbers.matcher(sb);
543 
544  while (matcher.find()) {
545  // Find the next match which resembles a hexadecimal value and convert it
546  // to
547  // its char value
548  char a = (char) (Integer.decode(matcher.group()).intValue());
549 
550  // Replace the original with the new character
551  sb.replace(matcher.start(), matcher.end(), String.valueOf(a));
552 
553  // Start again, since our positions have switched
554  matcher.reset();
555  }
556  return sb.toString();
557  }
558 
569  public void showInputDialog(String msg, String def, int id,
570  SVEventType evtype) {
571  svEventHandler.timer.stop();
572  String tmp =
573  (String) JOptionPane.showInputDialog(this, msg, "",
574  JOptionPane.QUESTION_MESSAGE, null, null, def);
575 
576  if (tmp != null) {
577  tmp = convertIntegerStringToUnicodeString(tmp);
578  SVEvent res = new SVEvent(evtype, this, id, tmp);
579  ScrollView.addMessage(res);
580  }
581  svEventHandler.timer.restart();
582  }
583 
584 
591  public void showInputDialog(String msg) {
592  showInputDialog(msg, null, -1, SVEventType.SVET_INPUT);
593  }
594 
601  public void showYesNoDialog(String msg) {
602  // res returns 0 on yes, 1 on no. Seems to be a bit counterintuitive
603  int res =
604  JOptionPane.showOptionDialog(this, msg, "", JOptionPane.YES_NO_OPTION,
605  JOptionPane.QUESTION_MESSAGE, null, null, null);
606  SVEvent e = null;
607 
608  if (res == 0) {
609  e = new SVEvent(SVEventType.SVET_INPUT, this, 0, 0, 0, 0, "y");
610  } else if (res == 1) {
611  e = new SVEvent(SVEventType.SVET_INPUT, this, 0, 0, 0, 0, "n");
612  }
614  }
615 
617  public void addPopupMenuItem(String parent, String name) {
618  if (svPuMenu == null) {
619  svPuMenu = new SVPopupMenu(this);
620  }
621  svPuMenu.add(parent, name, -1);
622  }
623 
625  public void addPopupMenuItem(String parent, String name, int cmdEvent,
626  String value, String desc) {
627  if (svPuMenu == null) {
628  svPuMenu = new SVPopupMenu(this);
629  }
630  svPuMenu.add(parent, name, cmdEvent, value, desc);
631  }
632 
634  public void destroy() {
636  "SVET_DESTROY"));
637  setVisible(false);
638  // dispose();
639  }
640 }
void addMessage(String message)
Definition: SVWindow.java:521
void addMenuBarItem(String parent, String name, int id)
Definition: SVWindow.java:512
void textAttributes(String font, int pixelSize, boolean bold, boolean italic, boolean underlined)
Definition: SVWindow.java:435
void zoomRectangle(int x1, int y1, int x2, int y2)
Definition: SVWindow.java:457
void addMenuBarItem(String parent, String name, int id, boolean checked)
Definition: SVWindow.java:501
void brush(int red, int green, int blue)
Definition: SVWindow.java:118
void drawRectangle(int x1, int y1, int x2, int y2)
Definition: SVWindow.java:359
void drawImage(PImage img, int xPos, int yPos)
Definition: SVWindow.java:335
void addMenuBarItem(String parent, String name)
Definition: SVWindow.java:507
void drawLine(int x1, int y1, int x2, int y2)
Definition: SVWindow.java:344
void showInputDialog(String msg)
Definition: SVWindow.java:591
void add(String parent, String name, int id)
void drawText(int x, int y, String text)
Definition: SVWindow.java:386
void pen(int red, int green, int blue, int alpha)
Definition: SVWindow.java:428
void showYesNoDialog(String msg)
Definition: SVWindow.java:601
name_table name
void setStrokeWidth(float width)
Definition: SVWindow.java:311
static void addMessage(SVEvent e)
Definition: ScrollView.java:60
void pen(int red, int green, int blue)
Definition: SVWindow.java:423
static final double SCALING_FACTOR
Definition: SVWindow.java:63
void addPopupMenuItem(String parent, String name)
Definition: SVWindow.java:617
SVWindow(String name, int hash, int posX, int posY, int sizeX, int sizeY, int canvasSizeX, int canvasSizeY)
Definition: SVWindow.java:202
void showInputDialog(String msg, String def, int id, SVEventType evtype)
Definition: SVWindow.java:569
void add(String parent, String name, int id)
Definition: SVMenuBar.java:73
void addPopupMenuItem(String parent, String name, int cmdEvent, String value, String desc)
Definition: SVWindow.java:625
void drawEllipse(int x, int y, int width, int height)
Definition: SVWindow.java:322
void brush(int red, int green, int blue, int alpha)
Definition: SVWindow.java:123