Tesseract  3.02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups 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 
13 import com.google.scrollview.ScrollView;
14 import com.google.scrollview.events.SVEvent;
15 import com.google.scrollview.events.SVEventHandler;
16 import com.google.scrollview.events.SVEventType;
17 import com.google.scrollview.ui.SVImageHandler;
18 import com.google.scrollview.ui.SVMenuBar;
19 import com.google.scrollview.ui.SVPopupMenu;
20 
21 import edu.umd.cs.piccolo.PCamera;
22 import edu.umd.cs.piccolo.PCanvas;
23 import edu.umd.cs.piccolo.PLayer;
24 
25 import edu.umd.cs.piccolo.nodes.PImage;
26 import edu.umd.cs.piccolo.nodes.PPath;
27 import edu.umd.cs.piccolo.nodes.PText;
28 import edu.umd.cs.piccolo.util.PPaintContext;
29 import edu.umd.cs.piccolox.swing.PScrollPane;
30 
31 import java.awt.BasicStroke;
32 import java.awt.BorderLayout;
33 import java.awt.Color;
34 import java.awt.Font;
35 import java.awt.GraphicsEnvironment;
36 import java.awt.geom.IllegalPathStateException;
37 import java.awt.Rectangle;
38 import java.awt.TextArea;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41 
42 import javax.swing.JFrame;
43 import javax.swing.JOptionPane;
44 import javax.swing.SwingUtilities;
45 import javax.swing.WindowConstants;
46 
54 public class SVWindow extends JFrame {
58  private static final int MAX_WINDOW_X = 1000;
59  private static final int MAX_WINDOW_Y = 800;
60 
61  /* Constant defining the (approx) height of the default message box*/
62  private static final int DEF_MESSAGEBOX_HEIGHT = 200;
63 
65  public static final double SCALING_FACTOR = 2;
66 
68  PLayer layer;
69 
71  Color currentPenColor;
72 
77  Color currentBrushColor;
78 
81  Font currentFont;
82 
84  // This really needs to be a fixed width stroke as the basic stroke is
85  // anti-aliased and gets too faint, but the piccolo fixed width stroke
86  // is too buggy and generates missing initial moveto in path definition
87  // errors with a IllegalPathStateException that cannot be caught because
88  // it is in the automatic repaint function. If we can fix the exceptions
89  // in piccolo, then we can use the following instead of BasicStroke:
90  // import edu.umd.cs.piccolox.util.PFixedWidthStroke;
91  // PFixedWidthStroke stroke = new PFixedWidthStroke(0.5f);
92  // Instead we use the BasicStroke and turn off anti-aliasing.
93  BasicStroke stroke = new BasicStroke(0.5f);
94 
99  public int hash;
100 
105  public static int nrWindows = 0;
106 
111  private SVEventHandler svEventHandler = null;
112  private SVMenuBar svMenuBar = null;
113  private TextArea ta = null;
114  public SVPopupMenu svPuMenu = null;
115  public PCanvas canvas;
116  private int winSizeX;
117  private int winSizeY;
118 
120  public void brush(int red, int green, int blue) {
121  brush(red, green, blue, 255);
122  }
123 
125  public void brush(int red, int green, int blue, int alpha) {
126  // If alpha is zero, use a null brush to save rendering time.
127  if (alpha == 0) {
128  currentBrushColor = null;
129  } else {
130  currentBrushColor = new Color(red, green, blue, alpha);
131  }
132  }
133 
135  public void clear() {
136  layer.removeAllChildren();
137  }
138 
149  public void createImage(String internalName, int width, int height,
150  int bitsPerPixel) {
151  SVImageHandler.createImage(internalName, width, height, bitsPerPixel);
152  }
153 
160  public void createPolyline(int length) {
161  ScrollView.polylineXCoords = new float[length];
162  ScrollView.polylineYCoords = new float[length];
163  ScrollView.polylineSize = length;
165  }
166 
170  public void drawPolyline() {
171  PPath pn = PPath.createPolyline(ScrollView.polylineXCoords,
174  pn.setStrokePaint(currentPenColor);
175  pn.setPaint(null); // Don't fill the polygon - this is just a polyline.
176  pn.setStroke(stroke);
177  layer.addChild(pn);
178  }
179 
193  public SVWindow(String name, int hash, int posX, int posY, int sizeX,
194  int sizeY, int canvasSizeX, int canvasSizeY) {
195  super(name);
196 
197  // Provide defaults for sizes.
198  if (sizeX == 0) sizeX = canvasSizeX;
199  if (sizeY == 0) sizeY = canvasSizeY;
200  if (canvasSizeX == 0) canvasSizeX = sizeX;
201  if (canvasSizeY == 0) canvasSizeY = sizeY;
202 
203  // Initialize variables
204  nrWindows++;
205  this.hash = hash;
206  this.svEventHandler = new SVEventHandler(this);
207  this.currentPenColor = Color.BLACK;
208  this.currentBrushColor = Color.BLACK;
209  this.currentFont = new Font("Times New Roman", Font.PLAIN, 12);
210 
211  // Determine the initial size and zoom factor of the window.
212  // If the window is too big, rescale it and zoom out.
213  int shrinkfactor = 1;
214 
215  if (sizeX > MAX_WINDOW_X) {
216  shrinkfactor = (sizeX + MAX_WINDOW_X - 1) / MAX_WINDOW_X;
217  }
218  if (sizeY / shrinkfactor > MAX_WINDOW_Y) {
219  shrinkfactor = (sizeY + MAX_WINDOW_Y - 1) / MAX_WINDOW_Y;
220  }
221  winSizeX = sizeX / shrinkfactor;
222  winSizeY = sizeY / shrinkfactor;
223  double initialScalingfactor = 1.0 / shrinkfactor;
224  if (winSizeX > canvasSizeX || winSizeY > canvasSizeY) {
225  initialScalingfactor = Math.min(1.0 * winSizeX / canvasSizeX,
226  1.0 * winSizeY / canvasSizeY);
227  }
228 
229  // Setup the actual window (its size, camera, title, etc.)
230  if (canvas == null) {
231  canvas = new PCanvas();
232  getContentPane().add(canvas, BorderLayout.CENTER);
233  }
234 
235  layer = canvas.getLayer();
236  canvas.setBackground(Color.BLACK);
237 
238  // Disable anitaliasing to make the lines more visible.
239  canvas.setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
240 
241  setLayout(new BorderLayout());
242 
243  setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
244 
245  validate();
246  canvas.requestFocus();
247 
248  // Manipulation of Piccolo's scene graph should be done from Swings
249  // event dispatch thread since Piccolo is not thread safe. This code calls
250  // initialize() from that thread once the PFrame is initialized, so you are
251  // safe to start working with Piccolo in the initialize() method.
252  SwingUtilities.invokeLater(new Runnable() {
253  public void run() {
254  repaint();
255  }
256  });
257 
258  setSize(winSizeX, winSizeY);
259  setLocation(posX, posY);
260  setTitle(name);
261 
262  // Add a Scrollpane to be able to scroll within the canvas
263  PScrollPane scrollPane = new PScrollPane(canvas);
264  getContentPane().add(scrollPane);
265  scrollPane.setWheelScrollingEnabled(false);
266  PCamera lc = canvas.getCamera();
267  lc.scaleViewAboutPoint(initialScalingfactor, 0, 0);
268 
269  // Disable the default event handlers and add our own.
270  addWindowListener(svEventHandler);
271  canvas.removeInputEventListener(canvas.getPanEventHandler());
272  canvas.removeInputEventListener(canvas.getZoomEventHandler());
273  canvas.addInputEventListener(svEventHandler);
274  canvas.addKeyListener(svEventHandler);
275 
276  // Make the window visible.
277  validate();
278  setVisible(true);
279 
280  }
281 
286  public void addMessageBox() {
287  if (ta == null) {
288  ta = new TextArea();
289  ta.setEditable(false);
290  getContentPane().add(ta, BorderLayout.SOUTH);
291  }
292  // We need to make the window bigger to accomodate the message box.
293  winSizeY += DEF_MESSAGEBOX_HEIGHT;
294  setSize(winSizeX, winSizeY);
295  }
296 
302  public void setStrokeWidth(float width) {
303  // If this worked we wouldn't need the antialiased rendering off.
304  // stroke = new PFixedWidthStroke(width);
305  stroke = new BasicStroke(width);
306  }
307 
313  public void drawEllipse(int x, int y, int width, int height) {
314  PPath pn = PPath.createEllipse(x, y, width, height);
315  pn.setStrokePaint(currentPenColor);
316  pn.setStroke(stroke);
317  pn.setPaint(currentBrushColor);
318  layer.addChild(pn);
319  }
320 
326  public void drawImage(String internalName, int x_pos, int y_pos) {
327  PImage img = SVImageHandler.getImage(internalName);
328  img.setX(x_pos);
329  img.setY(y_pos);
330  layer.addChild(img);
331  }
332 
336  public void drawLine(int x1, int y1, int x2, int y2) {
337  PPath pn = PPath.createLine(x1, y1, x2, y2);
338  pn.setStrokePaint(currentPenColor);
339  pn.setPaint(null); // Null paint may render faster than the default.
340  pn.setStroke(stroke);
341  pn.moveTo(x1, y1);
342  pn.lineTo(x2, y2);
343  layer.addChild(pn);
344  }
345 
351  public void drawRectangle(int x1, int y1, int x2, int y2) {
352 
353  if (x1 > x2) {
354  int t = x1;
355  x1 = x2;
356  x2 = t;
357  }
358  if (y1 > y2) {
359  int t = y1;
360  y1 = y2;
361  y2 = t;
362  }
363 
364  PPath pn = PPath.createRectangle(x1, y1, x2 - x1, y2 - y1);
365  pn.setStrokePaint(currentPenColor);
366  pn.setStroke(stroke);
367  pn.setPaint(currentBrushColor);
368  layer.addChild(pn);
369  }
370 
378  public void drawText(int x, int y, String text) {
379  int unreadableCharAt = -1;
380  char[] chars = text.toCharArray();
381  PText pt = new PText(text);
382  pt.setTextPaint(currentPenColor);
383  pt.setFont(currentFont);
384 
385  // Check to see if every character can be displayed by the current font.
386  for (int i = 0; i < chars.length; i++) {
387  if (!currentFont.canDisplay(chars[i])) {
388  // Set to the first not displayable character.
389  unreadableCharAt = i;
390  break;
391  }
392  }
393 
394  // Have to find some working font and use it for this text entry.
395  if (unreadableCharAt != -1) {
396  Font[] allfonts =
397  GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
398  for (int j = 0; j < allfonts.length; j++) {
399  if (allfonts[j].canDisplay(chars[unreadableCharAt])) {
400  Font tempFont =
401  new Font(allfonts[j].getFontName(), currentFont.getStyle(),
402  currentFont.getSize());
403  pt.setFont(tempFont);
404  break;
405  }
406  }
407  }
408 
409  pt.setX(x);
410  pt.setY(y);
411  layer.addChild(pt);
412  }
413 
415  public void pen(int red, int green, int blue) {
416  pen(red, green, blue, 255);
417  }
418 
420  public void pen(int red, int green, int blue, int alpha) {
421  currentPenColor = new Color(red, green, blue, alpha);
422  }
423 
427  public void textAttributes(String font, int pixelSize, boolean bold,
428  boolean italic, boolean underlined) {
429 
430  // For legacy reasons convert "Times" to "Times New Roman"
431  if (font.equals("Times")) {
432  font = "Times New Roman";
433  }
434 
435  int style = Font.PLAIN;
436  if (bold) {
437  style += Font.BOLD;
438  }
439  if (italic) {
440  style += Font.ITALIC;
441  }
442  currentFont = new Font(font, style, pixelSize);
443  }
444 
449  public void zoomRectangle(int x1, int y1, int x2, int y2) {
450  if (x2 > x1 && y2 > y1) {
451  winSizeX = getWidth();
452  winSizeY = getHeight();
453  int width = x2 - x1;
454  int height = y2 - y1;
455  // Since piccolo doesn't do this well either, pad with a margin
456  // all the way around.
457  int wmargin = width / 2;
458  int hmargin = height / 2;
459  double scalefactor = Math.min(winSizeX / (2.0 * wmargin + width),
460  winSizeY / (2.0 * hmargin + height));
461  PCamera lc = canvas.getCamera();
462  lc.scaleView(scalefactor / lc.getViewScale());
463  lc.animateViewToPanToBounds(new Rectangle(x1 - hmargin, y1 - hmargin,
464  2 * wmargin + width,
465  2 * hmargin + height), 0);
466  }
467  }
468 
475  public void update() {
476  // TODO(rays) fix bugs in piccolo or use something else.
477  // The repaint function generates many
478  // exceptions for no good reason. We catch and ignore as many as we
479  // can here, but most of them are generated by the system repaints
480  // caused by resizing/exposing parts of the window etc, and they
481  // generate unwanted stack traces that have to be piped to /dev/null
482  // (on linux).
483  try {
484  repaint();
485  } catch (NullPointerException e) {
486  // Do nothing so the output isn't full of stack traces.
487  } catch (IllegalPathStateException e) {
488  // Do nothing so the output isn't full of stack traces.
489  }
490  }
491 
493  public void addMenuBarItem(String parent, String name, int id,
494  boolean checked) {
495  svMenuBar.add(parent, name, id, checked);
496  }
497 
499  public void addMenuBarItem(String parent, String name) {
500  addMenuBarItem(parent, name, -1);
501  }
502 
504  public void addMenuBarItem(String parent, String name, int id) {
505  if (svMenuBar == null) {
506  svMenuBar = new SVMenuBar(this);
507 
508  }
509  svMenuBar.add(parent, name, id);
510  }
511 
513  public void addMessage(String message) {
514  if (ta != null) {
515  ta.append(message + "\n");
516  } else {
517  System.out.println(message + "\n");
518  }
519  }
520 
531  private static String convertIntegerStringToUnicodeString(String input) {
532  StringBuffer sb = new StringBuffer(input);
533  Pattern numbers = Pattern.compile("0x[0-9a-fA-F]{4}");
534  Matcher matcher = numbers.matcher(sb);
535 
536  while (matcher.find()) {
537  // Find the next match which resembles a hexadecimal value and convert it
538  // to
539  // its char value
540  char a = (char) (Integer.decode(matcher.group()).intValue());
541 
542  // Replace the original with the new character
543  sb.replace(matcher.start(), matcher.end(), String.valueOf(a));
544 
545  // Start again, since our positions have switched
546  matcher.reset();
547  }
548  return sb.toString();
549  }
550 
561  public void showInputDialog(String msg, String def, int id,
562  SVEventType evtype) {
563  svEventHandler.timer.stop();
564  String tmp =
565  (String) JOptionPane.showInputDialog(this, msg, "",
566  JOptionPane.QUESTION_MESSAGE, null, null, def);
567 
568  if (tmp != null) {
569  tmp = convertIntegerStringToUnicodeString(tmp);
570  SVEvent res = new SVEvent(evtype, this, id, tmp);
571  ScrollView.addMessage(res);
572  }
573  svEventHandler.timer.restart();
574  }
575 
576 
583  public void showInputDialog(String msg) {
584  showInputDialog(msg, null, -1, SVEventType.SVET_INPUT);
585  }
586 
593  public void showYesNoDialog(String msg) {
594  // res returns 0 on yes, 1 on no. Seems to be a bit counterintuitive
595  int res =
596  JOptionPane.showOptionDialog(this, msg, "", JOptionPane.YES_NO_OPTION,
597  JOptionPane.QUESTION_MESSAGE, null, null, null);
598  SVEvent e = null;
599 
600  if (res == 0) {
601  e = new SVEvent(SVEventType.SVET_INPUT, this, 0, 0, 0, 0, "y");
602  } else if (res == 1) {
603  e = new SVEvent(SVEventType.SVET_INPUT, this, 0, 0, 0, 0, "n");
604  }
606  }
607 
609  public void addPopupMenuItem(String parent, String name) {
610  if (svPuMenu == null) {
611  svPuMenu = new SVPopupMenu(this);
612  }
613  svPuMenu.add(parent, name, -1);
614  }
615 
617  public void addPopupMenuItem(String parent, String name, int cmdEvent,
618  String value, String desc) {
619  if (svPuMenu == null) {
620  svPuMenu = new SVPopupMenu(this);
621  }
622  svPuMenu.add(parent, name, cmdEvent, value, desc);
623  }
624 
626  public void destroy() {
628  "SVET_DESTROY"));
629  setVisible(false);
630  // dispose();
631  }
632 
639  public void openImage(String filename) {
640  SVImageHandler.openImage(filename);
641  }
642 
643 }