tesseract
3.03
|
00001 // Copyright 2007 Google Inc. All Rights Reserved. 00002 // 00003 // Licensed under the Apache License, Version 2.0 (the "License"); You may not 00004 // use this file except in compliance with the License. You may obtain a copy of 00005 // the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by 00006 // applicable law or agreed to in writing, software distributed under the 00007 // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 00008 // OF ANY KIND, either express or implied. See the License for the specific 00009 // language governing permissions and limitations under the License. 00010 00011 package com.google.scrollview.ui; 00012 00013 import com.google.scrollview.ScrollView; 00014 import com.google.scrollview.events.SVEvent; 00015 import com.google.scrollview.events.SVEventHandler; 00016 import com.google.scrollview.events.SVEventType; 00017 import com.google.scrollview.ui.SVMenuBar; 00018 import com.google.scrollview.ui.SVPopupMenu; 00019 00020 import org.piccolo2d.PCamera; 00021 import org.piccolo2d.PCanvas; 00022 import org.piccolo2d.PLayer; 00023 import org.piccolo2d.extras.swing.PScrollPane; 00024 import org.piccolo2d.nodes.PImage; 00025 import org.piccolo2d.nodes.PPath; 00026 import org.piccolo2d.nodes.PText; 00027 import org.piccolo2d.util.PPaintContext; 00028 00029 import java.awt.BasicStroke; 00030 import java.awt.BorderLayout; 00031 import java.awt.Color; 00032 import java.awt.Font; 00033 import java.awt.GraphicsEnvironment; 00034 import java.awt.Rectangle; 00035 import java.awt.TextArea; 00036 import java.awt.geom.IllegalPathStateException; 00037 import java.util.regex.Matcher; 00038 import java.util.regex.Pattern; 00039 00040 import javax.swing.JFrame; 00041 import javax.swing.JOptionPane; 00042 import javax.swing.SwingUtilities; 00043 import javax.swing.WindowConstants; 00044 00052 public class SVWindow extends JFrame { 00056 private static final int MAX_WINDOW_X = 1000; 00057 private static final int MAX_WINDOW_Y = 800; 00058 00059 /* Constant defining the (approx) height of the default message box*/ 00060 private static final int DEF_MESSAGEBOX_HEIGHT = 200; 00061 00063 public static final double SCALING_FACTOR = 2; 00064 00066 PLayer layer; 00067 00069 Color currentPenColor; 00070 00075 Color currentBrushColor; 00076 00079 Font currentFont; 00080 00082 // This really needs to be a fixed width stroke as the basic stroke is 00083 // anti-aliased and gets too faint, but the piccolo fixed width stroke 00084 // is too buggy and generates missing initial moveto in path definition 00085 // errors with a IllegalPathStateException that cannot be caught because 00086 // it is in the automatic repaint function. If we can fix the exceptions 00087 // in piccolo, then we can use the following instead of BasicStroke: 00088 // import edu.umd.cs.piccolox.util.PFixedWidthStroke; 00089 // PFixedWidthStroke stroke = new PFixedWidthStroke(0.5f); 00090 // Instead we use the BasicStroke and turn off anti-aliasing. 00091 BasicStroke stroke = new BasicStroke(0.5f); 00092 00097 public int hash; 00098 00103 public static int nrWindows = 0; 00104 00109 private SVEventHandler svEventHandler = null; 00110 private SVMenuBar svMenuBar = null; 00111 private TextArea ta = null; 00112 public SVPopupMenu svPuMenu = null; 00113 public PCanvas canvas; 00114 private int winSizeX; 00115 private int winSizeY; 00116 00118 public void brush(int red, int green, int blue) { 00119 brush(red, green, blue, 255); 00120 } 00121 00123 public void brush(int red, int green, int blue, int alpha) { 00124 // If alpha is zero, use a null brush to save rendering time. 00125 if (alpha == 0) { 00126 currentBrushColor = null; 00127 } else { 00128 currentBrushColor = new Color(red, green, blue, alpha); 00129 } 00130 } 00131 00133 public void clear() { 00134 // Manipulation of Piccolo's scene graph should be done from Swings 00135 // event dispatch thread since Piccolo is not thread safe. This code calls 00136 // removeAllChildren() from that thread and releases the latch. 00137 final java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1); 00138 SwingUtilities.invokeLater(new Runnable() { 00139 public void run() { 00140 layer.removeAllChildren(); 00141 repaint(); 00142 latch.countDown(); 00143 } 00144 }); 00145 try { 00146 latch.await(); 00147 } catch (InterruptedException e) { 00148 } 00149 } 00150 00157 public void createPolyline(int length) { 00158 ScrollView.polylineXCoords = new float[length]; 00159 ScrollView.polylineYCoords = new float[length]; 00160 ScrollView.polylineSize = length; 00161 ScrollView.polylineScanned = 0; 00162 } 00163 00167 public void drawPolyline() { 00168 int numCoords = ScrollView.polylineXCoords.length; 00169 if (numCoords < 2) { 00170 return; 00171 } 00172 PPath pn = PPath.createLine(ScrollView.polylineXCoords[0], 00173 ScrollView.polylineYCoords[0], 00174 ScrollView.polylineXCoords[1], 00175 ScrollView.polylineYCoords[1]); 00176 pn.reset(); 00177 pn.moveTo(ScrollView.polylineXCoords[0], ScrollView.polylineYCoords[0]); 00178 for (int p = 1; p < numCoords; ++p) { 00179 pn.lineTo(ScrollView.polylineXCoords[p], ScrollView.polylineYCoords[p]); 00180 } 00181 pn.closePath(); 00182 ScrollView.polylineSize = 0; 00183 pn.setStrokePaint(currentPenColor); 00184 pn.setPaint(null); // Don't fill the polygon - this is just a polyline. 00185 pn.setStroke(stroke); 00186 layer.addChild(pn); 00187 } 00188 00202 public SVWindow(String name, int hash, int posX, int posY, int sizeX, 00203 int sizeY, int canvasSizeX, int canvasSizeY) { 00204 super(name); 00205 00206 // Provide defaults for sizes. 00207 if (sizeX == 0) sizeX = canvasSizeX; 00208 if (sizeY == 0) sizeY = canvasSizeY; 00209 if (canvasSizeX == 0) canvasSizeX = sizeX; 00210 if (canvasSizeY == 0) canvasSizeY = sizeY; 00211 00212 // Initialize variables 00213 nrWindows++; 00214 this.hash = hash; 00215 this.svEventHandler = new SVEventHandler(this); 00216 this.currentPenColor = Color.BLACK; 00217 this.currentBrushColor = Color.BLACK; 00218 this.currentFont = new Font("Times New Roman", Font.PLAIN, 12); 00219 00220 // Determine the initial size and zoom factor of the window. 00221 // If the window is too big, rescale it and zoom out. 00222 int shrinkfactor = 1; 00223 00224 if (sizeX > MAX_WINDOW_X) { 00225 shrinkfactor = (sizeX + MAX_WINDOW_X - 1) / MAX_WINDOW_X; 00226 } 00227 if (sizeY / shrinkfactor > MAX_WINDOW_Y) { 00228 shrinkfactor = (sizeY + MAX_WINDOW_Y - 1) / MAX_WINDOW_Y; 00229 } 00230 winSizeX = sizeX / shrinkfactor; 00231 winSizeY = sizeY / shrinkfactor; 00232 double initialScalingfactor = 1.0 / shrinkfactor; 00233 if (winSizeX > canvasSizeX || winSizeY > canvasSizeY) { 00234 initialScalingfactor = Math.min(1.0 * winSizeX / canvasSizeX, 00235 1.0 * winSizeY / canvasSizeY); 00236 } 00237 00238 // Setup the actual window (its size, camera, title, etc.) 00239 if (canvas == null) { 00240 canvas = new PCanvas(); 00241 getContentPane().add(canvas, BorderLayout.CENTER); 00242 } 00243 00244 layer = canvas.getLayer(); 00245 canvas.setBackground(Color.BLACK); 00246 00247 // Disable anitaliasing to make the lines more visible. 00248 canvas.setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING); 00249 00250 setLayout(new BorderLayout()); 00251 00252 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 00253 00254 validate(); 00255 canvas.requestFocus(); 00256 00257 // Manipulation of Piccolo's scene graph should be done from Swings 00258 // event dispatch thread since Piccolo is not thread safe. This code calls 00259 // initialize() from that thread once the PFrame is initialized, so you are 00260 // safe to start working with Piccolo in the initialize() method. 00261 SwingUtilities.invokeLater(new Runnable() { 00262 public void run() { 00263 repaint(); 00264 } 00265 }); 00266 00267 setSize(winSizeX, winSizeY); 00268 setLocation(posX, posY); 00269 setTitle(name); 00270 00271 // Add a Scrollpane to be able to scroll within the canvas 00272 PScrollPane scrollPane = new PScrollPane(canvas); 00273 getContentPane().add(scrollPane); 00274 scrollPane.setWheelScrollingEnabled(false); 00275 PCamera lc = canvas.getCamera(); 00276 lc.scaleViewAboutPoint(initialScalingfactor, 0, 0); 00277 00278 // Disable the default event handlers and add our own. 00279 addWindowListener(svEventHandler); 00280 canvas.removeInputEventListener(canvas.getPanEventHandler()); 00281 canvas.removeInputEventListener(canvas.getZoomEventHandler()); 00282 canvas.addInputEventListener(svEventHandler); 00283 canvas.addKeyListener(svEventHandler); 00284 00285 // Make the window visible. 00286 validate(); 00287 setVisible(true); 00288 00289 } 00290 00295 public void addMessageBox() { 00296 if (ta == null) { 00297 ta = new TextArea(); 00298 ta.setEditable(false); 00299 getContentPane().add(ta, BorderLayout.SOUTH); 00300 } 00301 // We need to make the window bigger to accomodate the message box. 00302 winSizeY += DEF_MESSAGEBOX_HEIGHT; 00303 setSize(winSizeX, winSizeY); 00304 } 00305 00311 public void setStrokeWidth(float width) { 00312 // If this worked we wouldn't need the antialiased rendering off. 00313 // stroke = new PFixedWidthStroke(width); 00314 stroke = new BasicStroke(width); 00315 } 00316 00322 public void drawEllipse(int x, int y, int width, int height) { 00323 PPath pn = PPath.createEllipse(x, y, width, height); 00324 pn.setStrokePaint(currentPenColor); 00325 pn.setStroke(stroke); 00326 pn.setPaint(currentBrushColor); 00327 layer.addChild(pn); 00328 } 00329 00335 public void drawImage(PImage img, int xPos, int yPos) { 00336 img.setX(xPos); 00337 img.setY(yPos); 00338 layer.addChild(img); 00339 } 00340 00344 public void drawLine(int x1, int y1, int x2, int y2) { 00345 PPath pn = PPath.createLine(x1, y1, x2, y2); 00346 pn.setStrokePaint(currentPenColor); 00347 pn.setPaint(null); // Null paint may render faster than the default. 00348 pn.setStroke(stroke); 00349 pn.moveTo(x1, y1); 00350 pn.lineTo(x2, y2); 00351 layer.addChild(pn); 00352 } 00353 00359 public void drawRectangle(int x1, int y1, int x2, int y2) { 00360 00361 if (x1 > x2) { 00362 int t = x1; 00363 x1 = x2; 00364 x2 = t; 00365 } 00366 if (y1 > y2) { 00367 int t = y1; 00368 y1 = y2; 00369 y2 = t; 00370 } 00371 00372 PPath pn = PPath.createRectangle(x1, y1, x2 - x1, y2 - y1); 00373 pn.setStrokePaint(currentPenColor); 00374 pn.setStroke(stroke); 00375 pn.setPaint(currentBrushColor); 00376 layer.addChild(pn); 00377 } 00378 00386 public void drawText(int x, int y, String text) { 00387 int unreadableCharAt = -1; 00388 char[] chars = text.toCharArray(); 00389 PText pt = new PText(text); 00390 pt.setTextPaint(currentPenColor); 00391 pt.setFont(currentFont); 00392 00393 // Check to see if every character can be displayed by the current font. 00394 for (int i = 0; i < chars.length; i++) { 00395 if (!currentFont.canDisplay(chars[i])) { 00396 // Set to the first not displayable character. 00397 unreadableCharAt = i; 00398 break; 00399 } 00400 } 00401 00402 // Have to find some working font and use it for this text entry. 00403 if (unreadableCharAt != -1) { 00404 Font[] allfonts = 00405 GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); 00406 for (int j = 0; j < allfonts.length; j++) { 00407 if (allfonts[j].canDisplay(chars[unreadableCharAt])) { 00408 Font tempFont = 00409 new Font(allfonts[j].getFontName(), currentFont.getStyle(), 00410 currentFont.getSize()); 00411 pt.setFont(tempFont); 00412 break; 00413 } 00414 } 00415 } 00416 00417 pt.setX(x); 00418 pt.setY(y); 00419 layer.addChild(pt); 00420 } 00421 00423 public void pen(int red, int green, int blue) { 00424 pen(red, green, blue, 255); 00425 } 00426 00428 public void pen(int red, int green, int blue, int alpha) { 00429 currentPenColor = new Color(red, green, blue, alpha); 00430 } 00431 00435 public void textAttributes(String font, int pixelSize, boolean bold, 00436 boolean italic, boolean underlined) { 00437 00438 // For legacy reasons convert "Times" to "Times New Roman" 00439 if (font.equals("Times")) { 00440 font = "Times New Roman"; 00441 } 00442 00443 int style = Font.PLAIN; 00444 if (bold) { 00445 style += Font.BOLD; 00446 } 00447 if (italic) { 00448 style += Font.ITALIC; 00449 } 00450 currentFont = new Font(font, style, pixelSize); 00451 } 00452 00457 public void zoomRectangle(int x1, int y1, int x2, int y2) { 00458 if (x2 > x1 && y2 > y1) { 00459 winSizeX = getWidth(); 00460 winSizeY = getHeight(); 00461 int width = x2 - x1; 00462 int height = y2 - y1; 00463 // Since piccolo doesn't do this well either, pad with a margin 00464 // all the way around. 00465 int wmargin = width / 2; 00466 int hmargin = height / 2; 00467 double scalefactor = Math.min(winSizeX / (2.0 * wmargin + width), 00468 winSizeY / (2.0 * hmargin + height)); 00469 PCamera lc = canvas.getCamera(); 00470 lc.scaleView(scalefactor / lc.getViewScale()); 00471 lc.animateViewToPanToBounds(new Rectangle(x1 - hmargin, y1 - hmargin, 00472 2 * wmargin + width, 00473 2 * hmargin + height), 0); 00474 } 00475 } 00476 00483 public void update() { 00484 // TODO(rays) fix bugs in piccolo or use something else. 00485 // The repaint function generates many 00486 // exceptions for no good reason. We catch and ignore as many as we 00487 // can here, but most of them are generated by the system repaints 00488 // caused by resizing/exposing parts of the window etc, and they 00489 // generate unwanted stack traces that have to be piped to /dev/null 00490 // (on linux). 00491 try { 00492 repaint(); 00493 } catch (NullPointerException e) { 00494 // Do nothing so the output isn't full of stack traces. 00495 } catch (IllegalPathStateException e) { 00496 // Do nothing so the output isn't full of stack traces. 00497 } 00498 } 00499 00501 public void addMenuBarItem(String parent, String name, int id, 00502 boolean checked) { 00503 svMenuBar.add(parent, name, id, checked); 00504 } 00505 00507 public void addMenuBarItem(String parent, String name) { 00508 addMenuBarItem(parent, name, -1); 00509 } 00510 00512 public void addMenuBarItem(String parent, String name, int id) { 00513 if (svMenuBar == null) { 00514 svMenuBar = new SVMenuBar(this); 00515 00516 } 00517 svMenuBar.add(parent, name, id); 00518 } 00519 00521 public void addMessage(String message) { 00522 if (ta != null) { 00523 ta.append(message + "\n"); 00524 } else { 00525 System.out.println(message + "\n"); 00526 } 00527 } 00528 00539 private static String convertIntegerStringToUnicodeString(String input) { 00540 StringBuffer sb = new StringBuffer(input); 00541 Pattern numbers = Pattern.compile("0x[0-9a-fA-F]{4}"); 00542 Matcher matcher = numbers.matcher(sb); 00543 00544 while (matcher.find()) { 00545 // Find the next match which resembles a hexadecimal value and convert it 00546 // to 00547 // its char value 00548 char a = (char) (Integer.decode(matcher.group()).intValue()); 00549 00550 // Replace the original with the new character 00551 sb.replace(matcher.start(), matcher.end(), String.valueOf(a)); 00552 00553 // Start again, since our positions have switched 00554 matcher.reset(); 00555 } 00556 return sb.toString(); 00557 } 00558 00569 public void showInputDialog(String msg, String def, int id, 00570 SVEventType evtype) { 00571 svEventHandler.timer.stop(); 00572 String tmp = 00573 (String) JOptionPane.showInputDialog(this, msg, "", 00574 JOptionPane.QUESTION_MESSAGE, null, null, def); 00575 00576 if (tmp != null) { 00577 tmp = convertIntegerStringToUnicodeString(tmp); 00578 SVEvent res = new SVEvent(evtype, this, id, tmp); 00579 ScrollView.addMessage(res); 00580 } 00581 svEventHandler.timer.restart(); 00582 } 00583 00584 00591 public void showInputDialog(String msg) { 00592 showInputDialog(msg, null, -1, SVEventType.SVET_INPUT); 00593 } 00594 00601 public void showYesNoDialog(String msg) { 00602 // res returns 0 on yes, 1 on no. Seems to be a bit counterintuitive 00603 int res = 00604 JOptionPane.showOptionDialog(this, msg, "", JOptionPane.YES_NO_OPTION, 00605 JOptionPane.QUESTION_MESSAGE, null, null, null); 00606 SVEvent e = null; 00607 00608 if (res == 0) { 00609 e = new SVEvent(SVEventType.SVET_INPUT, this, 0, 0, 0, 0, "y"); 00610 } else if (res == 1) { 00611 e = new SVEvent(SVEventType.SVET_INPUT, this, 0, 0, 0, 0, "n"); 00612 } 00613 ScrollView.addMessage(e); 00614 } 00615 00617 public void addPopupMenuItem(String parent, String name) { 00618 if (svPuMenu == null) { 00619 svPuMenu = new SVPopupMenu(this); 00620 } 00621 svPuMenu.add(parent, name, -1); 00622 } 00623 00625 public void addPopupMenuItem(String parent, String name, int cmdEvent, 00626 String value, String desc) { 00627 if (svPuMenu == null) { 00628 svPuMenu = new SVPopupMenu(this); 00629 } 00630 svPuMenu.add(parent, name, cmdEvent, value, desc); 00631 } 00632 00634 public void destroy() { 00635 ScrollView.addMessage(new SVEvent(SVEventType.SVET_DESTROY, this, 0, 00636 "SVET_DESTROY")); 00637 setVisible(false); 00638 // dispose(); 00639 } 00640 }