SVWindow.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  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. package com.google.scrollview.ui;
  11. import com.google.scrollview.ScrollView;
  12. import com.google.scrollview.events.SVEvent;
  13. import com.google.scrollview.events.SVEventHandler;
  14. import com.google.scrollview.events.SVEventType;
  15. import com.google.scrollview.ui.SVMenuBar;
  16. import com.google.scrollview.ui.SVPopupMenu;
  17. import org.piccolo2d.PCamera;
  18. import org.piccolo2d.PCanvas;
  19. import org.piccolo2d.PLayer;
  20. import org.piccolo2d.extras.swing.PScrollPane;
  21. import org.piccolo2d.nodes.PImage;
  22. import org.piccolo2d.nodes.PPath;
  23. import org.piccolo2d.nodes.PText;
  24. import org.piccolo2d.util.PPaintContext;
  25. import java.awt.BasicStroke;
  26. import java.awt.BorderLayout;
  27. import java.awt.Color;
  28. import java.awt.Font;
  29. import java.awt.GraphicsEnvironment;
  30. import java.awt.Rectangle;
  31. import java.awt.TextArea;
  32. import java.awt.geom.IllegalPathStateException;
  33. import java.util.regex.Matcher;
  34. import java.util.regex.Pattern;
  35. import javax.swing.JFrame;
  36. import javax.swing.JOptionPane;
  37. import javax.swing.SwingUtilities;
  38. import javax.swing.WindowConstants;
  39. /**
  40. * The SVWindow is the top-level ui class. It should get instantiated whenever
  41. * the user intends to create a new window. It contains helper functions to draw
  42. * on the canvas, add new menu items, show modal dialogs etc.
  43. *
  44. * @author wanke@google.com
  45. */
  46. public class SVWindow extends JFrame {
  47. /**
  48. * Constants defining the maximum initial size of the window.
  49. */
  50. private static final int MAX_WINDOW_X = 1000;
  51. private static final int MAX_WINDOW_Y = 800;
  52. /* Constant defining the (approx) height of the default message box*/
  53. private static final int DEF_MESSAGEBOX_HEIGHT = 200;
  54. /** Constant defining the "speed" at which to zoom in and out. */
  55. public static final double SCALING_FACTOR = 2;
  56. /** The top level layer we add our PNodes to (root node). */
  57. PLayer layer;
  58. /** The current color of the pen. It is used to draw edges, text, etc. */
  59. Color currentPenColor;
  60. /**
  61. * The current color of the brush. It is used to draw the interior of
  62. * primitives.
  63. */
  64. Color currentBrushColor;
  65. /** The system name of the current font we are using (e.g.
  66. * "Times New Roman"). */
  67. Font currentFont;
  68. /** The stroke width to be used. */
  69. // This really needs to be a fixed width stroke as the basic stroke is
  70. // anti-aliased and gets too faint, but the piccolo fixed width stroke
  71. // is too buggy and generates missing initial moveto in path definition
  72. // errors with an IllegalPathStateException that cannot be caught because
  73. // it is in the automatic repaint function. If we can fix the exceptions
  74. // in piccolo, then we can use the following instead of BasicStroke:
  75. // import edu.umd.cs.piccolox.util.PFixedWidthStroke;
  76. // PFixedWidthStroke stroke = new PFixedWidthStroke(0.5f);
  77. // Instead we use the BasicStroke and turn off anti-aliasing.
  78. BasicStroke stroke = new BasicStroke(0.5f);
  79. /**
  80. * A unique representation for the window, also known by the client. It is
  81. * used when sending messages from server to client to identify him.
  82. */
  83. public int hash;
  84. /**
  85. * The total number of created Windows. If this ever reaches 0 (apart from the
  86. * beginning), quit the server.
  87. */
  88. public static int nrWindows = 0;
  89. /**
  90. * The Canvas, MessageBox, EventHandler, Menubar and Popupmenu associated with
  91. * this window.
  92. */
  93. private SVEventHandler svEventHandler = null;
  94. private SVMenuBar svMenuBar = null;
  95. private TextArea ta = null;
  96. public SVPopupMenu svPuMenu = null;
  97. public PCanvas canvas;
  98. private int winSizeX;
  99. private int winSizeY;
  100. /** Set the brush to an RGB color */
  101. public void brush(int red, int green, int blue) {
  102. brush(red, green, blue, 255);
  103. }
  104. /** Set the brush to an RGBA color */
  105. public void brush(int red, int green, int blue, int alpha) {
  106. // If alpha is zero, use a null brush to save rendering time.
  107. if (alpha == 0) {
  108. currentBrushColor = null;
  109. } else {
  110. currentBrushColor = new Color(red, green, blue, alpha);
  111. }
  112. }
  113. /** Erase all content from the window, but do not destroy it. */
  114. public void clear() {
  115. // Manipulation of Piccolo's scene graph should be done from Swings
  116. // event dispatch thread since Piccolo is not thread safe. This code calls
  117. // removeAllChildren() from that thread and releases the latch.
  118. final java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1);
  119. SwingUtilities.invokeLater(new Runnable() {
  120. public void run() {
  121. layer.removeAllChildren();
  122. repaint();
  123. latch.countDown();
  124. }
  125. });
  126. try {
  127. latch.await();
  128. } catch (InterruptedException e) {
  129. }
  130. }
  131. /**
  132. * Start setting up a new polyline. The server will now expect
  133. * polyline data until the polyline is complete.
  134. *
  135. * @param length number of coordinate pairs
  136. */
  137. public void createPolyline(int length) {
  138. ScrollView.polylineXCoords = new float[length];
  139. ScrollView.polylineYCoords = new float[length];
  140. ScrollView.polylineSize = length;
  141. ScrollView.polylineScanned = 0;
  142. }
  143. /**
  144. * Draw the now complete polyline.
  145. */
  146. public void drawPolyline() {
  147. int numCoords = ScrollView.polylineXCoords.length;
  148. if (numCoords < 2) {
  149. return;
  150. }
  151. PPath pn = PPath.createLine(ScrollView.polylineXCoords[0],
  152. ScrollView.polylineYCoords[0],
  153. ScrollView.polylineXCoords[1],
  154. ScrollView.polylineYCoords[1]);
  155. pn.reset();
  156. pn.moveTo(ScrollView.polylineXCoords[0], ScrollView.polylineYCoords[0]);
  157. for (int p = 1; p < numCoords; ++p) {
  158. pn.lineTo(ScrollView.polylineXCoords[p], ScrollView.polylineYCoords[p]);
  159. }
  160. pn.closePath();
  161. ScrollView.polylineSize = 0;
  162. pn.setStrokePaint(currentPenColor);
  163. pn.setPaint(null); // Don't fill the polygon - this is just a polyline.
  164. pn.setStroke(stroke);
  165. layer.addChild(pn);
  166. }
  167. /**
  168. * Construct a new SVWindow and set it visible.
  169. *
  170. * @param name Title of the window.
  171. * @param hash Unique internal representation. This has to be the same as
  172. * defined by the client, as they use this to refer to the windows.
  173. * @param posX X position of where to draw the window (upper left).
  174. * @param posY Y position of where to draw the window (upper left).
  175. * @param sizeX The width of the window.
  176. * @param sizeY The height of the window.
  177. * @param canvasSizeX The canvas width of the window.
  178. * @param canvasSizeY The canvas height of the window.
  179. */
  180. public SVWindow(String name, int hash, int posX, int posY, int sizeX,
  181. int sizeY, int canvasSizeX, int canvasSizeY) {
  182. super(name);
  183. // Provide defaults for sizes.
  184. if (sizeX <= 0) sizeX = canvasSizeX;
  185. if (sizeY <= 0) sizeY = canvasSizeY;
  186. if (canvasSizeX <= 0) canvasSizeX = sizeX;
  187. if (canvasSizeY <= 0) canvasSizeY = sizeY;
  188. // Avoid later division by zero.
  189. if (sizeX <= 0) {
  190. sizeX = 1;
  191. canvasSizeX = sizeX;
  192. }
  193. if (sizeY <= 0) {
  194. sizeY = 1;
  195. canvasSizeY = sizeY;
  196. }
  197. // Initialize variables
  198. nrWindows++;
  199. this.hash = hash;
  200. this.svEventHandler = new SVEventHandler(this);
  201. this.currentPenColor = Color.BLACK;
  202. this.currentBrushColor = Color.BLACK;
  203. this.currentFont = new Font("Times New Roman", Font.PLAIN, 12);
  204. // Determine the initial size and zoom factor of the window.
  205. // If the window is too big, rescale it and zoom out.
  206. int shrinkfactor = 1;
  207. if (sizeX > MAX_WINDOW_X) {
  208. shrinkfactor = (sizeX + MAX_WINDOW_X - 1) / MAX_WINDOW_X;
  209. }
  210. if (sizeY / shrinkfactor > MAX_WINDOW_Y) {
  211. shrinkfactor = (sizeY + MAX_WINDOW_Y - 1) / MAX_WINDOW_Y;
  212. }
  213. winSizeX = sizeX / shrinkfactor;
  214. winSizeY = sizeY / shrinkfactor;
  215. double initialScalingfactor = 1.0 / shrinkfactor;
  216. if (winSizeX > canvasSizeX || winSizeY > canvasSizeY) {
  217. initialScalingfactor = Math.min(1.0 * winSizeX / canvasSizeX,
  218. 1.0 * winSizeY / canvasSizeY);
  219. }
  220. // Setup the actual window (its size, camera, title, etc.)
  221. if (canvas == null) {
  222. canvas = new PCanvas();
  223. getContentPane().add(canvas, BorderLayout.CENTER);
  224. }
  225. layer = canvas.getLayer();
  226. canvas.setBackground(Color.BLACK);
  227. // Disable antialiasing to make the lines more visible.
  228. canvas.setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
  229. setLayout(new BorderLayout());
  230. setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  231. validate();
  232. canvas.requestFocus();
  233. // Manipulation of Piccolo's scene graph should be done from Swings
  234. // event dispatch thread since Piccolo is not thread safe. This code calls
  235. // initialize() from that thread once the PFrame is initialized, so you are
  236. // safe to start working with Piccolo in the initialize() method.
  237. SwingUtilities.invokeLater(new Runnable() {
  238. public void run() {
  239. repaint();
  240. }
  241. });
  242. setSize(winSizeX, winSizeY);
  243. setLocation(posX, posY);
  244. setTitle(name);
  245. // Add a Scrollpane to be able to scroll within the canvas
  246. PScrollPane scrollPane = new PScrollPane(canvas);
  247. getContentPane().add(scrollPane);
  248. scrollPane.setWheelScrollingEnabled(false);
  249. PCamera lc = canvas.getCamera();
  250. lc.scaleViewAboutPoint(initialScalingfactor, 0, 0);
  251. // Disable the default event handlers and add our own.
  252. addWindowListener(svEventHandler);
  253. canvas.removeInputEventListener(canvas.getPanEventHandler());
  254. canvas.removeInputEventListener(canvas.getZoomEventHandler());
  255. canvas.addInputEventListener(svEventHandler);
  256. canvas.addKeyListener(svEventHandler);
  257. // Make the window visible.
  258. validate();
  259. setVisible(true);
  260. }
  261. /**
  262. * Convenience function to add a message box to the window which can be used
  263. * to output debug information.
  264. */
  265. public void addMessageBox() {
  266. if (ta == null) {
  267. ta = new TextArea();
  268. ta.setEditable(false);
  269. getContentPane().add(ta, BorderLayout.SOUTH);
  270. }
  271. // We need to make the window bigger to accommodate the message box.
  272. winSizeY += DEF_MESSAGEBOX_HEIGHT;
  273. setSize(winSizeX, winSizeY);
  274. }
  275. /**
  276. * Allows you to specify the thickness with which to draw lines, recantgles
  277. * and ellipses.
  278. * @param width The new thickness.
  279. */
  280. public void setStrokeWidth(float width) {
  281. // If this worked we wouldn't need the antialiased rendering off.
  282. // stroke = new PFixedWidthStroke(width);
  283. stroke = new BasicStroke(width);
  284. }
  285. /**
  286. * Draw an ellipse at (x,y) with given width and height, using the
  287. * current stroke, the current brush color to fill it and the
  288. * current pen color for the outline.
  289. */
  290. public void drawEllipse(int x, int y, int width, int height) {
  291. PPath pn = PPath.createEllipse(x, y, width, height);
  292. pn.setStrokePaint(currentPenColor);
  293. pn.setStroke(stroke);
  294. pn.setPaint(currentBrushColor);
  295. layer.addChild(pn);
  296. }
  297. /**
  298. * Draw the image with the given name at (x,y). Any image loaded stays in
  299. * memory, so if you intend to redraw an image, you do not have to use
  300. * createImage again.
  301. */
  302. public void drawImage(PImage img, int xPos, int yPos) {
  303. img.setX(xPos);
  304. img.setY(yPos);
  305. layer.addChild(img);
  306. }
  307. /**
  308. * Draw a line from (x1,y1) to (x2,y2) using the current pen color and stroke.
  309. */
  310. public void drawLine(int x1, int y1, int x2, int y2) {
  311. PPath pn = PPath.createLine(x1, y1, x2, y2);
  312. pn.setStrokePaint(currentPenColor);
  313. pn.setPaint(null); // Null paint may render faster than the default.
  314. pn.setStroke(stroke);
  315. pn.moveTo(x1, y1);
  316. pn.lineTo(x2, y2);
  317. layer.addChild(pn);
  318. }
  319. /**
  320. * Draw a rectangle given the two points (x1,y1) and (x2,y2) using the current
  321. * stroke, pen color for the border and the brush to fill the
  322. * interior.
  323. */
  324. public void drawRectangle(int x1, int y1, int x2, int y2) {
  325. if (x1 > x2) {
  326. int t = x1;
  327. x1 = x2;
  328. x2 = t;
  329. }
  330. if (y1 > y2) {
  331. int t = y1;
  332. y1 = y2;
  333. y2 = t;
  334. }
  335. PPath pn = PPath.createRectangle(x1, y1, x2 - x1, y2 - y1);
  336. pn.setStrokePaint(currentPenColor);
  337. pn.setStroke(stroke);
  338. pn.setPaint(currentBrushColor);
  339. layer.addChild(pn);
  340. }
  341. /**
  342. * Draw some text at (x,y) using the current pen color and text attributes. If
  343. * the current font does NOT support at least one character, it tries to find
  344. * a font which is capable of displaying it and use that to render the text.
  345. * Note: If the font says it can render a glyph, but in reality it turns out
  346. * to be crap, there is nothing we can do about it.
  347. */
  348. public void drawText(int x, int y, String text) {
  349. int unreadableCharAt = -1;
  350. char[] chars = text.toCharArray();
  351. PText pt = new PText(text);
  352. pt.setTextPaint(currentPenColor);
  353. pt.setFont(currentFont);
  354. // Check to see if every character can be displayed by the current font.
  355. for (int i = 0; i < chars.length; i++) {
  356. if (!currentFont.canDisplay(chars[i])) {
  357. // Set to the first not displayable character.
  358. unreadableCharAt = i;
  359. break;
  360. }
  361. }
  362. // Have to find some working font and use it for this text entry.
  363. if (unreadableCharAt != -1) {
  364. Font[] allfonts =
  365. GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
  366. for (int j = 0; j < allfonts.length; j++) {
  367. if (allfonts[j].canDisplay(chars[unreadableCharAt])) {
  368. Font tempFont =
  369. new Font(allfonts[j].getFontName(), currentFont.getStyle(),
  370. currentFont.getSize());
  371. pt.setFont(tempFont);
  372. break;
  373. }
  374. }
  375. }
  376. pt.setX(x);
  377. pt.setY(y);
  378. layer.addChild(pt);
  379. }
  380. /** Set the pen color to an RGB value */
  381. public void pen(int red, int green, int blue) {
  382. pen(red, green, blue, 255);
  383. }
  384. /** Set the pen color to an RGBA value */
  385. public void pen(int red, int green, int blue, int alpha) {
  386. currentPenColor = new Color(red, green, blue, alpha);
  387. }
  388. /**
  389. * Define how to display text. Note: underlined is not currently not supported
  390. */
  391. public void textAttributes(String font, int pixelSize, boolean bold,
  392. boolean italic, boolean underlined) {
  393. // For legacy reasons convert "Times" to "Times New Roman"
  394. if (font.equals("Times")) {
  395. font = "Times New Roman";
  396. }
  397. int style = Font.PLAIN;
  398. if (bold) {
  399. style += Font.BOLD;
  400. }
  401. if (italic) {
  402. style += Font.ITALIC;
  403. }
  404. currentFont = new Font(font, style, pixelSize);
  405. }
  406. /**
  407. * Zoom the window to the rectangle given the two points (x1,y1)
  408. * and (x2,y2), which must be greater than (x1,y1).
  409. */
  410. public void zoomRectangle(int x1, int y1, int x2, int y2) {
  411. if (x2 > x1 && y2 > y1) {
  412. winSizeX = getWidth();
  413. winSizeY = getHeight();
  414. int width = x2 - x1;
  415. int height = y2 - y1;
  416. // Since piccolo doesn't do this well either, pad with a margin
  417. // all the way around.
  418. int wmargin = width / 2;
  419. int hmargin = height / 2;
  420. double scalefactor = Math.min(winSizeX / (2.0 * wmargin + width),
  421. winSizeY / (2.0 * hmargin + height));
  422. PCamera lc = canvas.getCamera();
  423. lc.scaleView(scalefactor / lc.getViewScale());
  424. lc.animateViewToPanToBounds(new Rectangle(x1 - hmargin, y1 - hmargin,
  425. 2 * wmargin + width,
  426. 2 * hmargin + height), 0);
  427. }
  428. }
  429. /**
  430. * Flush buffers and update display.
  431. *
  432. * Only actually reacts if there are no more messages in the stack, to prevent
  433. * the canvas from flickering.
  434. */
  435. public void update() {
  436. // TODO(rays) fix bugs in piccolo or use something else.
  437. // The repaint function generates many
  438. // exceptions for no good reason. We catch and ignore as many as we
  439. // can here, but most of them are generated by the system repaints
  440. // caused by resizing/exposing parts of the window etc, and they
  441. // generate unwanted stack traces that have to be piped to /dev/null
  442. // (on linux).
  443. try {
  444. repaint();
  445. } catch (NullPointerException e) {
  446. // Do nothing so the output isn't full of stack traces.
  447. } catch (IllegalPathStateException e) {
  448. // Do nothing so the output isn't full of stack traces.
  449. }
  450. }
  451. /** Adds a checkbox entry to the menubar, c.f. SVMenubar.add(...) */
  452. public void addMenuBarItem(String parent, String name, int id,
  453. boolean checked) {
  454. svMenuBar.add(parent, name, id, checked);
  455. }
  456. /** Adds a submenu to the menubar, c.f. SVMenubar.add(...) */
  457. public void addMenuBarItem(String parent, String name) {
  458. addMenuBarItem(parent, name, -1);
  459. }
  460. /** Adds a new entry to the menubar, c.f. SVMenubar.add(...) */
  461. public void addMenuBarItem(String parent, String name, int id) {
  462. if (svMenuBar == null) {
  463. svMenuBar = new SVMenuBar(this);
  464. }
  465. svMenuBar.add(parent, name, id);
  466. }
  467. /** Add a message to the message box. */
  468. public void addMessage(String message) {
  469. if (ta != null) {
  470. ta.append(message + "\n");
  471. } else {
  472. System.out.println(message + "\n");
  473. }
  474. }
  475. /**
  476. * This method converts a string which might contain hexadecimal values to a
  477. * string which contains the respective unicode counterparts.
  478. *
  479. * For example, Hall0x0094chen returns Hall<o umlaut>chen
  480. * encoded as utf8.
  481. *
  482. * @param input The original string, containing 0x values
  483. * @return The converted string which has the replaced unicode symbols
  484. */
  485. private static String convertIntegerStringToUnicodeString(String input) {
  486. StringBuffer sb = new StringBuffer(input);
  487. Pattern numbers = Pattern.compile("0x[0-9a-fA-F]{4}");
  488. Matcher matcher = numbers.matcher(sb);
  489. while (matcher.find()) {
  490. // Find the next match which resembles a hexadecimal value and convert it
  491. // to
  492. // its char value
  493. char a = (char) (Integer.decode(matcher.group()).intValue());
  494. // Replace the original with the new character
  495. sb.replace(matcher.start(), matcher.end(), String.valueOf(a));
  496. // Start again, since our positions have switched
  497. matcher.reset();
  498. }
  499. return sb.toString();
  500. }
  501. /**
  502. * Show a modal input dialog. The answer by the dialog is then send to the
  503. * client, together with the associated menu id, as SVET_POPUP
  504. *
  505. * @param msg The text that is displayed in the dialog.
  506. * @param def The default value of the dialog.
  507. * @param id The associated commandId
  508. * @param evtype The event this is associated with (usually SVET_MENU
  509. * or SVET_POPUP)
  510. */
  511. public void showInputDialog(String msg, String def, int id,
  512. SVEventType evtype) {
  513. svEventHandler.timer.stop();
  514. String tmp =
  515. (String) JOptionPane.showInputDialog(this, msg, "",
  516. JOptionPane.QUESTION_MESSAGE, null, null, def);
  517. if (tmp != null) {
  518. tmp = convertIntegerStringToUnicodeString(tmp);
  519. SVEvent res = new SVEvent(evtype, this, id, tmp);
  520. ScrollView.addMessage(res);
  521. }
  522. svEventHandler.timer.restart();
  523. }
  524. /**
  525. * Shows a modal input dialog to the user. The return value is automatically
  526. * sent to the client as SVET_INPUT event (with command id -1).
  527. *
  528. * @param msg The text of the dialog.
  529. */
  530. public void showInputDialog(String msg) {
  531. showInputDialog(msg, null, -1, SVEventType.SVET_INPUT);
  532. }
  533. /**
  534. * Shows a dialog presenting "Yes" and "No" as answers and returns either a
  535. * "y" or "n" to the client.
  536. *
  537. * Closing the dialog without answering is handled like "No".
  538. *
  539. * @param msg The text that is displayed in the dialog.
  540. */
  541. public void showYesNoDialog(String msg) {
  542. // res returns 0 on yes, 1 on no. Seems to be a bit counterintuitive
  543. int res =
  544. JOptionPane.showOptionDialog(this, msg, "", JOptionPane.YES_NO_OPTION,
  545. JOptionPane.QUESTION_MESSAGE, null, null, null);
  546. SVEvent e = new SVEvent(SVEventType.SVET_INPUT, this, 0, 0, 0, 0,
  547. res == 0 ? "y" : "n");
  548. ScrollView.addMessage(e);
  549. }
  550. /** Adds a submenu to the popup menu, c.f. SVPopupMenu.add(...) */
  551. public void addPopupMenuItem(String parent, String name) {
  552. if (svPuMenu == null) {
  553. svPuMenu = new SVPopupMenu(this);
  554. }
  555. svPuMenu.add(parent, name, -1);
  556. }
  557. /** Adds a new menu entry to the popup menu, c.f. SVPopupMenu.add(...) */
  558. public void addPopupMenuItem(String parent, String name, int cmdEvent,
  559. String value, String desc) {
  560. if (svPuMenu == null) {
  561. svPuMenu = new SVPopupMenu(this);
  562. }
  563. svPuMenu.add(parent, name, cmdEvent, value, desc);
  564. }
  565. /** Destroys a window. */
  566. public void destroy() {
  567. ScrollView.addMessage(new SVEvent(SVEventType.SVET_DESTROY, this, 0,
  568. "SVET_DESTROY"));
  569. setVisible(false);
  570. // dispose();
  571. }
  572. }