1 package prefuse; 2 3 import java.awt.Color ; 4 import java.awt.Dimension ; 5 import java.awt.Font ; 6 import java.awt.Graphics ; 7 import java.awt.Graphics2D ; 8 import java.awt.GraphicsEnvironment ; 9 import java.awt.Image ; 10 import java.awt.Point ; 11 import java.awt.Rectangle ; 12 import java.awt.RenderingHints ; 13 import java.awt.event.ActionEvent ; 14 import java.awt.event.ActionListener ; 15 import java.awt.event.KeyEvent ; 16 import java.awt.event.KeyListener ; 17 import java.awt.event.MouseEvent ; 18 import java.awt.event.MouseListener ; 19 import java.awt.event.MouseMotionListener ; 20 import java.awt.event.MouseWheelEvent ; 21 import java.awt.event.MouseWheelListener ; 22 import java.awt.geom.AffineTransform ; 23 import java.awt.geom.NoninvertibleTransformException ; 24 import java.awt.geom.Point2D ; 25 import java.awt.geom.Rectangle2D ; 26 import java.awt.image.BufferedImage ; 27 import java.io.OutputStream ; 28 import java.util.Iterator ; 29 import java.util.logging.Logger ; 30 31 import javax.imageio.ImageIO ; 32 import javax.swing.JComponent ; 33 import javax.swing.JTextArea ; 34 import javax.swing.JTextField ; 35 import javax.swing.JToolTip ; 36 import javax.swing.KeyStroke ; 37 import javax.swing.text.JTextComponent ; 38 39 import prefuse.activity.Activity; 40 import prefuse.activity.SlowInSlowOutPacer; 41 import prefuse.controls.Control; 42 import prefuse.data.expression.AndPredicate; 43 import prefuse.data.expression.BooleanLiteral; 44 import prefuse.data.expression.Predicate; 45 import prefuse.data.expression.parser.ExpressionParser; 46 import prefuse.render.Renderer; 47 import prefuse.util.ColorLib; 48 import prefuse.util.StringLib; 49 import prefuse.util.UpdateListener; 50 import prefuse.util.collections.CopyOnWriteArrayList; 51 import prefuse.util.display.BackgroundPainter; 52 import prefuse.util.display.Clip; 53 import prefuse.util.display.DebugStatsPainter; 54 import prefuse.util.display.ExportDisplayAction; 55 import prefuse.util.display.ItemBoundsListener; 56 import prefuse.util.display.PaintListener; 57 import prefuse.util.display.RenderingQueue; 58 import prefuse.visual.VisualItem; 59 import prefuse.visual.expression.VisiblePredicate; 60 import prefuse.visual.sort.ItemSorter; 61 62 63 105 public class Display extends JComponent { 106 107 private static final Logger s_logger 108 = Logger.getLogger(Display.class.getName()); 109 110 protected Visualization m_vis; 112 protected AndPredicate m_predicate = new AndPredicate(); 113 114 protected CopyOnWriteArrayList m_controls = new CopyOnWriteArrayList(); 116 protected CopyOnWriteArrayList m_painters; 117 protected CopyOnWriteArrayList m_bounders; 118 119 protected BufferedImage m_offscreen; 121 protected Clip m_clip = new Clip(); 122 protected Clip m_screen = new Clip(); 123 protected Clip m_bounds = new Clip(); 124 protected Rectangle2D m_rclip = new Rectangle2D.Double (); 125 protected boolean m_damageRedraw = true; 126 protected boolean m_highQuality = false; 127 128 protected BackgroundPainter m_bgpainter = null; 130 131 protected RenderingQueue m_queue = new RenderingQueue(); 133 protected int m_visibleCount = 0; 134 135 protected AffineTransform m_transform = new AffineTransform (); 137 protected AffineTransform m_itransform = new AffineTransform (); 138 protected TransformActivity m_transact = new TransformActivity(); 139 protected Point2D m_tmpPoint = new Point2D.Double (); 140 141 protected double frameRate; 143 protected int nframes = 0; 144 private int sampleInterval = 10; 145 private long mark = -1L; 146 147 148 protected JToolTip m_customToolTip = null; 149 150 private JTextComponent m_editor; 152 private boolean m_editing; 153 private VisualItem m_editItem; 154 private String m_editAttribute; 155 156 160 public Display() { 161 this(null); 162 } 163 164 170 public Display(Visualization visualization) { 171 this(visualization, (Predicate)null); 172 } 173 174 185 public Display(Visualization visualization, String predicate) { 186 this(visualization, 187 (Predicate)ExpressionParser.parse(predicate, true)); 188 } 189 190 197 public Display(Visualization visualization, Predicate predicate) { 198 setDoubleBuffered(false); 199 setBackground(Color.WHITE); 200 201 m_editing = false; 203 m_editor = new JTextField (); 204 m_editor.setBorder(null); 205 m_editor.setVisible(false); 206 this.add(m_editor); 207 208 InputEventCapturer iec = new InputEventCapturer(); 210 addMouseListener(iec); 211 addMouseMotionListener(iec); 212 addMouseWheelListener(iec); 213 addKeyListener(iec); 214 215 registerDefaultCommands(); 216 217 m_predicate.addExpressionListener(new UpdateListener() { 219 public void update(Object src) { damageReport(); } 220 }); 221 222 setVisualization(visualization); 223 setPredicate(predicate); 224 setSize(400,400); } 226 227 237 protected void registerDefaultCommands() { 238 registerKeyboardAction(new ActionListener () { 240 private PaintListener m_debug = null; 241 242 public void actionPerformed(ActionEvent e) { 243 if (m_debug == null) { 244 m_debug = new DebugStatsPainter(); 245 addPaintListener(m_debug); 246 } else { 247 removePaintListener(m_debug); 248 m_debug = null; 249 } 250 repaint(); 251 } 252 }, "debug info", KeyStroke.getKeyStroke("ctrl D"), WHEN_FOCUSED); 253 254 registerKeyboardAction(new ActionListener () { 256 public void actionPerformed(ActionEvent e) { 257 setHighQuality(!isHighQuality()); 258 repaint(); 259 } 260 }, "toggle high-quality drawing", KeyStroke.getKeyStroke("ctrl H"), 261 WHEN_FOCUSED); 262 263 try { 265 registerKeyboardAction(new ExportDisplayAction(this), 266 "export display", KeyStroke.getKeyStroke("ctrl E"), WHEN_FOCUSED); 267 } catch (SecurityException se) { 268 } 269 } 270 271 277 public void setSize(int width, int height) { 278 m_offscreen = null; 279 setPreferredSize(new Dimension (width, height)); 280 super.setSize(width, height); 281 } 282 283 288 public void setSize(Dimension d) { 289 m_offscreen = null; 290 setPreferredSize(d); 291 super.setSize(d); 292 } 293 294 299 public void invalidate() { 300 damageReport(); 301 super.invalidate(); 302 } 303 304 307 public void setBounds(int x, int y, int w, int h) { 308 m_offscreen = null; 309 super.setBounds(x,y,w,h); 310 } 311 312 317 public void setFont(Font f) { 318 super.setFont(f); 319 m_editor.setFont(f); 320 } 321 322 326 public double getFrameRate() { 327 return frameRate; 328 } 329 330 336 public void setHighQuality(boolean on) { 337 if ( m_highQuality != on ) 338 damageReport(); 339 m_highQuality = on; 340 } 341 342 347 public boolean isHighQuality() { 348 return m_highQuality; 349 } 350 351 355 public Visualization getVisualization() { 356 return m_vis; 357 } 358 359 366 public void setVisualization(Visualization vis) { 367 if ( m_vis == vis ) { 369 return; 371 } else if ( m_vis != null ) { 372 m_vis.removeDisplay(this); 374 } 375 m_vis = vis; 376 if ( m_vis != null ) 377 m_vis.addDisplay(this); 378 } 379 380 385 public Predicate getPredicate() { 386 if ( m_predicate.size() == 1 ) { 387 return BooleanLiteral.TRUE; 388 } else { 389 return m_predicate.get(0); 390 } 391 } 392 393 402 public void setPredicate(String expr) { 403 Predicate p = (Predicate)ExpressionParser.parse(expr, true); 404 setPredicate(p); 405 } 406 407 412 public synchronized void setPredicate(Predicate p) { 413 if ( p == null ) { 414 m_predicate.set(VisiblePredicate.TRUE); 415 } else { 416 m_predicate.set(new Predicate[] {p, VisiblePredicate.TRUE}); 417 } 418 } 419 420 426 public int getVisibleItemCount() { 427 return m_visibleCount; 428 } 429 430 436 public ItemSorter getItemSorter() { 437 return m_queue.sort; 438 } 439 440 446 public synchronized void setItemSorter(ItemSorter cmp) { 447 damageReport(); 448 m_queue.sort = cmp; 449 } 450 451 452 462 public synchronized void setBackgroundImage(Image image, 463 boolean fixed, boolean tileImage) 464 { 465 BackgroundPainter bg = null; 466 if ( image != null ) 467 bg = new BackgroundPainter(image, fixed, tileImage); 468 setBackgroundPainter(bg); 469 } 470 471 484 public synchronized void setBackgroundImage(String location, 485 boolean fixed, boolean tileImage) 486 { 487 BackgroundPainter bg = null; 488 if ( location != null ) 489 bg = new BackgroundPainter(location, fixed, tileImage); 490 setBackgroundPainter(bg); 491 } 492 493 private void setBackgroundPainter(BackgroundPainter bg) { 494 if ( m_bgpainter != null ) 495 removePaintListener(m_bgpainter); 496 m_bgpainter = bg; 497 if ( bg != null ) 498 addPaintListener(bg); 499 } 500 501 504 512 public JToolTip createToolTip() { 513 if ( m_customToolTip == null ) { 514 return super.createToolTip(); 515 } else { 516 return m_customToolTip; 517 } 518 } 519 520 532 public void setCustomToolTip(JToolTip tooltip) { 533 m_customToolTip = tooltip; 534 } 535 536 541 public JToolTip getCustomToolTip() { 542 return m_customToolTip; 543 } 544 545 548 567 public synchronized boolean isDamageRedraw() { 568 return m_damageRedraw; 569 } 570 571 590 public synchronized void setDamageRedraw(boolean b) { 591 m_damageRedraw = b; 592 m_clip.invalidate(); 593 } 594 595 599 public synchronized void damageReport(Rectangle2D region) { 600 if ( m_damageRedraw ) 601 m_clip.union(region); 602 } 603 604 607 public synchronized void damageReport() { 608 m_clip.invalidate(); 609 } 610 611 617 public synchronized void clearDamage() { 618 if ( m_damageRedraw ) 619 m_clip.reset(); 620 } 621 622 629 public synchronized Rectangle2D getItemBounds() { 630 return getItemBounds(new Rectangle2D.Double ()); 631 } 632 633 639 public synchronized Rectangle2D getItemBounds(Rectangle2D b) { 640 b.setFrameFromDiagonal(m_bounds.getMinX(), m_bounds.getMinY(), 641 m_bounds.getMaxX(), m_bounds.getMaxY()); 642 return b; 643 } 644 645 648 652 public BufferedImage getOffscreenBuffer() { 653 return m_offscreen; 654 } 655 656 659 protected BufferedImage getNewOffscreenBuffer(int width, int height) { 660 BufferedImage img = null; 661 if ( !GraphicsEnvironment.isHeadless() ) { 662 try { 663 img = (BufferedImage )createImage(width, height); 664 } catch ( Exception e ) { 665 img = null; 666 } 667 } 668 if ( img == null ) { 669 return new BufferedImage (width, height, 670 BufferedImage.TYPE_INT_RGB); 671 } 672 return img; 673 } 674 675 686 public boolean saveImage(OutputStream output, String format, double scale) 687 { 688 try { 689 Dimension d = new Dimension ((int)(scale*getWidth()), 691 (int)(scale*getHeight())); 692 BufferedImage img = getNewOffscreenBuffer(d.width, d.height); 693 Graphics2D g = (Graphics2D )img.getGraphics(); 694 695 Point2D p = new Point2D.Double (0,0); 697 zoom(p, scale); boolean q = isHighQuality(); 699 setHighQuality(true); 700 paintDisplay(g, d); 701 setHighQuality(q); 702 zoom(p, 1/scale); 704 ImageIO.write(img, format, output); 706 return true; 707 } catch ( Exception e ) { 708 e.printStackTrace(); 709 return false; 710 } 711 } 712 713 716 public void update(Graphics g) { 717 paint(g); 718 } 719 720 724 protected void paintBufferToScreen(Graphics g) { 725 synchronized ( this ) { 726 g.drawImage(m_offscreen, 0, 0, null); 727 } 728 } 729 730 734 public void repaintImmediate() { 735 Graphics g = this.getGraphics(); 736 if (g != null && m_offscreen != null) { 737 paintBufferToScreen(g); 738 } 739 } 740 741 746 protected void prepareGraphics(Graphics2D g) { 747 if ( m_transform != null ) 748 g.transform(m_transform); 749 setRenderingHints(g); 750 } 751 752 759 protected void setRenderingHints(Graphics2D g) { 760 if ( m_highQuality ) { 761 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 762 RenderingHints.VALUE_ANTIALIAS_ON); 763 } else { 764 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 765 RenderingHints.VALUE_ANTIALIAS_OFF); 766 } 767 g.setRenderingHint( 768 RenderingHints.KEY_RENDERING, 769 RenderingHints.VALUE_RENDER_QUALITY); 770 g.setRenderingHint( 771 RenderingHints.KEY_INTERPOLATION, 772 RenderingHints.VALUE_INTERPOLATION_BICUBIC); 773 } 774 775 776 779 public void paintComponent(Graphics g) { 780 if (m_offscreen == null) { 781 m_offscreen = getNewOffscreenBuffer(getWidth(), getHeight()); 782 damageReport(); 783 } 784 Graphics2D g2D = (Graphics2D )g; 785 Graphics2D buf_g2D = (Graphics2D ) m_offscreen.getGraphics(); 786 787 790 paintDisplay(buf_g2D, getSize()); 792 paintBufferToScreen(g2D); 793 794 firePostPaint(g2D); 796 797 buf_g2D.dispose(); 798 799 nframes++; 801 if ( mark < 0 ) { 802 mark = System.currentTimeMillis(); 803 nframes = 0; 804 } else if ( nframes == sampleInterval ){ 805 long t = System.currentTimeMillis(); 806 frameRate = (1000.0*nframes)/(t-mark); 807 mark = t; 808 nframes = 0; 809 } 810 } 811 812 817 public void paintDisplay(Graphics2D g2D, Dimension d) { 818 synchronized ( m_vis ) { 820 synchronized ( this ) { 821 822 if ( m_clip.isEmpty() ) 823 return; 825 m_screen.setClip(0, 0, d.width+1, d.height+1); 827 m_screen.transform(m_itransform); 828 829 double pixel = 1.0 + 1.0/getScale(); 833 834 if ( m_damageRedraw ) { 835 if ( m_clip.isInvalid() ) { 836 m_clip.setClip(m_screen); 838 } else { 839 m_clip.intersection(m_screen); 841 } 842 843 m_clip.expand(pixel); 845 846 prepareGraphics(g2D); 848 849 m_rclip.setFrameFromDiagonal( 851 m_clip.getMinX(), m_clip.getMinY(), 852 m_clip.getMaxX(), m_clip.getMaxY()); 853 g2D.setClip(m_rclip); 854 855 m_rclip.setFrameFromDiagonal( 860 m_clip.getMinX()-pixel, m_clip.getMinY()-pixel, 861 m_clip.getMaxX()+pixel, m_clip.getMaxY()+pixel); 862 863 } else { 864 m_rclip.setFrame(0, 0, getWidth(), getHeight()); 866 m_clip.setClip(m_screen); 868 prepareGraphics(g2D); 870 } 871 872 clearRegion(g2D, m_rclip); 874 875 878 getItemBounds(m_rclip); 880 m_bounds.reset(); 881 882 m_queue.clear(); Iterator items = m_vis.items(m_predicate); 885 for ( m_visibleCount=0; items.hasNext(); ++m_visibleCount ) { 886 VisualItem item = (VisualItem)items.next(); 887 Rectangle2D bounds = item.getBounds(); 888 m_bounds.union(bounds); 890 if ( m_clip.intersects(bounds, pixel) ) 891 m_queue.addToRenderQueue(item); 892 if ( item.isInteractive() ) 893 m_queue.addToPickingQueue(item); 894 } 895 896 m_queue.sortRenderQueue(); 898 899 for ( int i=0; i<m_queue.rsize; ++i ) { 901 m_queue.ritems[i].render(g2D); 902 } 903 904 if ( m_damageRedraw ) 906 m_clip.reset(); 907 908 checkItemBoundsChanged(m_rclip); 910 911 }} } 913 914 919 public void renderImmediate(VisualItem item) { 920 Graphics2D g2D = (Graphics2D )this.getGraphics(); 921 prepareGraphics(g2D); 922 item.render(g2D); 923 } 924 925 935 protected void printComponent(Graphics g) { 936 boolean wasHighQuality = m_highQuality; 937 try { 938 m_highQuality = true; 940 paintDisplay((Graphics2D ) g, getSize()); 942 } finally { 943 m_highQuality = wasHighQuality; 945 } 946 } 947 948 952 protected void clearRegion(Graphics2D g, Rectangle2D r) { 953 g.setColor(getBackground()); 954 g.fill(r); 955 firePrePaint(g); 957 } 958 959 962 969 public synchronized void setTransform(AffineTransform transform) 970 throws NoninvertibleTransformException 971 { 972 damageReport(); 973 m_transform = transform; 974 m_itransform = m_transform.createInverse(); 975 } 976 977 983 public AffineTransform getTransform() { 984 return m_transform; 985 } 986 987 993 public AffineTransform getInverseTransform() { 994 return m_itransform; 995 } 996 997 1007 public Point2D getAbsoluteCoordinate(Point2D screen, Point2D abs) { 1008 return m_itransform.transform(screen, abs); 1009 } 1010 1011 1017 public double getScale() { 1018 return m_transform.getScaleX(); 1019 } 1020 1021 1026 public double getDisplayX() { 1027 return -m_transform.getTranslateX(); 1028 } 1029 1030 1035 public double getDisplayY() { 1036 return -m_transform.getTranslateY(); 1037 } 1038 1039 1044 public synchronized void pan(double dx, double dy) { 1045 double panx = dx / m_transform.getScaleX(); 1046 double pany = dy / m_transform.getScaleY(); 1047 panAbs(panx,pany); 1048 } 1049 1050 1056 public synchronized void panAbs(double dx, double dy) { 1057 damageReport(); 1058 m_transform.translate(dx, dy); 1059 try { 1060 m_itransform = m_transform.createInverse(); 1061 } catch ( Exception e ) { } 1062 } 1063 1064 1069 public synchronized void panTo(Point2D p) { 1070 m_itransform.transform(p, m_tmpPoint); 1071 panToAbs(m_tmpPoint); 1072 } 1073 1074 1079 public synchronized void panToAbs(Point2D p) { 1080 double sx = m_transform.getScaleX(); 1081 double sy = m_transform.getScaleY(); 1082 double x = p.getX(); x = (Double.isNaN(x) ? 0 : x); 1083 double y = p.getY(); y = (Double.isNaN(y) ? 0 : y); 1084 x = getWidth() /(2*sx) - x; 1085 y = getHeight()/(2*sy) - y; 1086 1087 double dx = x-(m_transform.getTranslateX()/sx); 1088 double dy = y-(m_transform.getTranslateY()/sy); 1089 1090 damageReport(); 1091 m_transform.translate(dx, dy); 1092 try { 1093 m_itransform = m_transform.createInverse(); 1094 } catch ( Exception e ) { } 1095 } 1096 1097 1103 public synchronized void zoom(final Point2D p, double scale) { 1104 m_itransform.transform(p, m_tmpPoint); 1105 zoomAbs(m_tmpPoint, scale); 1106 } 1107 1108 1115 public synchronized void zoomAbs(final Point2D p, double scale) {; 1116 double zx = p.getX(), zy = p.getY(); 1117 damageReport(); 1118 m_transform.translate(zx, zy); 1119 m_transform.scale(scale,scale); 1120 m_transform.translate(-zx, -zy); 1121 try { 1122 m_itransform = m_transform.createInverse(); 1123 } catch ( Exception e ) { } 1124 } 1125 1126 1132 public synchronized void rotate(final Point2D p, double theta) { 1133 m_itransform.transform(p, m_tmpPoint); 1134 rotateAbs(m_tmpPoint, theta); 1135 } 1136 1137 1144 public synchronized void rotateAbs(final Point2D p, double theta) { 1145 double zx = p.getX(), zy = p.getY(); 1146 damageReport(); 1147 m_transform.translate(zx, zy); 1148 m_transform.rotate(theta); 1149 m_transform.translate(-zx, -zy); 1150 try { 1151 m_itransform = m_transform.createInverse(); 1152 } catch ( Exception e ) { } 1153 } 1154 1155 1162 public synchronized void animatePan(double dx, double dy, long duration) { 1163 double panx = dx / m_transform.getScaleX(); 1164 double pany = dy / m_transform.getScaleY(); 1165 animatePanAbs(panx,pany,duration); 1166 } 1167 1168 1175 public synchronized void animatePanAbs(double dx, double dy, long duration) { 1176 m_transact.pan(dx,dy,duration); 1177 } 1178 1179 1185 public synchronized void animatePanTo(Point2D p, long duration) { 1186 Point2D pp = new Point2D.Double (); 1187 m_itransform.transform(p,pp); 1188 animatePanToAbs(pp,duration); 1189 } 1190 1191 1197 public synchronized void animatePanToAbs(Point2D p, long duration) { 1198 m_tmpPoint.setLocation(0,0); 1199 m_itransform.transform(m_tmpPoint,m_tmpPoint); 1200 double x = p.getX(); x = (Double.isNaN(x) ? 0 : x); 1201 double y = p.getY(); y = (Double.isNaN(y) ? 0 : y); 1202 double w = getWidth() /(2*m_transform.getScaleX()); 1203 double h = getHeight()/(2*m_transform.getScaleY()); 1204 double dx = w-x+m_tmpPoint.getX(); 1205 double dy = h-y+m_tmpPoint.getY(); 1206 animatePanAbs(dx,dy,duration); 1207 } 1208 1209 1216 public synchronized void animateZoom(final Point2D p, double scale, long duration) { 1217 Point2D pp = new Point2D.Double (); 1218 m_itransform.transform(p,pp); 1219 animateZoomAbs(pp,scale,duration); 1220 } 1221 1222 1229 public synchronized void animateZoomAbs(final Point2D p, double scale, long duration) { 1230 m_transact.zoom(p,scale,duration); 1231 } 1232 1233 1240 public synchronized void animatePanAndZoomTo(final Point2D p, double scale, long duration) { 1241 Point2D pp = new Point2D.Double (); 1242 m_itransform.transform(p,pp); 1243 animatePanAndZoomToAbs(pp,scale,duration); 1244 } 1245 1246 1253 public synchronized void animatePanAndZoomToAbs(final Point2D p, double scale, long duration) { 1254 m_transact.panAndZoom(p,scale,duration); 1255 } 1256 1257 1261 public boolean isTranformInProgress() { 1262 return m_transact.isRunning(); 1263 } 1264 1265 1268 private class TransformActivity extends Activity { 1269 1273 private double[] src, dst; 1274 private AffineTransform m_at; 1275 public TransformActivity() { 1276 super(2000,20,0); 1277 src = new double[6]; 1278 dst = new double[6]; 1279 m_at = new AffineTransform (); 1280 setPacingFunction(new SlowInSlowOutPacer()); 1281 } 1282 private AffineTransform getTransform() { 1283 if ( this.isScheduled() ) 1284 m_at.setTransform(dst[0],dst[1],dst[2],dst[3],dst[4],dst[5]); 1285 else 1286 m_at.setTransform(m_transform); 1287 return m_at; 1288 } 1289 public void panAndZoom(final Point2D p, double scale, long duration) { 1290 AffineTransform at = getTransform(); 1291 this.cancel(); 1292 setDuration(duration); 1293 1294 m_tmpPoint.setLocation(0,0); 1295 m_itransform.transform(m_tmpPoint,m_tmpPoint); 1296 double x = p.getX(); x = (Double.isNaN(x) ? 0 : x); 1297 double y = p.getY(); y = (Double.isNaN(y) ? 0 : y); 1298 double w = getWidth() /(2*m_transform.getScaleX()); 1299 double h = getHeight()/(2*m_transform.getScaleY()); 1300 double dx = w-x+m_tmpPoint.getX(); 1301 double dy = h-y+m_tmpPoint.getY(); 1302 at.translate(dx,dy); 1303 1304 at.translate(p.getX(), p.getY()); 1305 at.scale(scale,scale); 1306 at.translate(-p.getX(), -p.getY()); 1307 1308 at.getMatrix(dst); 1309 m_transform.getMatrix(src); 1310 this.run(); 1311 } 1312 public void pan(double dx, double dy, long duration) { 1313 AffineTransform at = getTransform(); 1314 this.cancel(); 1315 setDuration(duration); 1316 at.translate(dx,dy); 1317 at.getMatrix(dst); 1318 m_transform.getMatrix(src); 1319 this.run(); 1320 } 1321 public void zoom(final Point2D p, double scale, long duration) { 1322 AffineTransform at = getTransform(); 1323 this.cancel(); 1324 setDuration(duration); 1325 double zx = p.getX(), zy = p.getY(); 1326 at.translate(zx, zy); 1327 at.scale(scale,scale); 1328 at.translate(-zx, -zy); 1329 at.getMatrix(dst); 1330 m_transform.getMatrix(src); 1331 this.run(); 1332 } 1333 protected void run(long elapsedTime) { 1334 double f = getPace(elapsedTime); 1335 damageReport(); 1336 m_transform.setTransform( 1337 src[0] + f*(dst[0]-src[0]), 1338 src[1] + f*(dst[1]-src[1]), 1339 src[2] + f*(dst[2]-src[2]), 1340 src[3] + f*(dst[3]-src[3]), 1341 src[4] + f*(dst[4]-src[4]), 1342 src[5] + f*(dst[5]-src[5]) 1343 ); 1344 try { 1345 m_itransform = m_transform.createInverse(); 1346 } catch ( Exception e ) { } 1347 repaint(); 1348 } 1349 } 1351 1354 1359 public void addPaintListener(PaintListener pl) { 1360 if ( m_painters == null ) 1361 m_painters = new CopyOnWriteArrayList(); 1362 m_painters.add(pl); 1363 } 1364 1365 1369 public void removePaintListener(PaintListener pl) { 1370 m_painters.remove(pl); 1371 } 1372 1373 1377 protected void firePrePaint(Graphics2D g) { 1378 if ( m_painters != null && m_painters.size() > 0 ) { 1379 Object [] lstnrs = m_painters.getArray(); 1380 for ( int i=0; i<lstnrs.length; ++i ) { 1381 try { 1382 ((PaintListener)lstnrs[i]).prePaint(this, g); 1383 } catch ( Exception e ) { 1384 s_logger.warning( 1385 "Exception thrown by PaintListener: " + e + "\n" + 1386 StringLib.getStackTrace(e)); 1387 } 1388 } 1389 } 1390 } 1391 1392 1396 protected void firePostPaint(Graphics2D g) { 1397 if ( m_painters != null && m_painters.size() > 0 ) { 1398 Object [] lstnrs = m_painters.getArray(); 1399 for ( int i=0; i<lstnrs.length; ++i ) { 1400 try { 1401 ((PaintListener)lstnrs[i]).postPaint(this, g); 1402 } catch ( Exception e ) { 1403 s_logger.warning( 1404 "Exception thrown by PaintListener: " + e + "\n" + 1405 StringLib.getStackTrace(e)); 1406 } 1407 } 1408 } 1409 } 1410 1411 1412 1415 1420 public void addItemBoundsListener(ItemBoundsListener ibl) { 1421 if ( m_bounders == null ) 1422 m_bounders = new CopyOnWriteArrayList(); 1423 m_bounders.add(ibl); 1424 } 1425 1426 1431 public void removeItemBoundsListener(ItemBoundsListener ibl) { 1432 m_bounders.remove(ibl); 1433 } 1434 1435 1439 protected void checkItemBoundsChanged(Rectangle2D prev) { 1440 if ( m_bounds.equals(prev) ) 1441 return; 1443 if ( m_bounders != null && m_bounders.size() > 0 ) { 1444 Object [] lstnrs = m_bounders.getArray(); 1445 for ( int i=0; i<lstnrs.length; ++i ) { 1446 try { 1447 ((ItemBoundsListener)lstnrs[i]).itemBoundsChanged(this); 1448 } catch ( Exception e ) { 1449 s_logger.warning( 1450 "Exception thrown by ItemBoundsListener: " + e + "\n" + 1451 StringLib.getStackTrace(e)); 1452 } 1453 } 1454 } 1455 } 1456 1457 1458 1461 1465 public void addControlListener(Control cl) { 1466 m_controls.add(cl); 1467 } 1468 1469 1473 public void removeControlListener(Control cl) { 1474 m_controls.remove(cl); 1475 } 1476 1477 1482 public synchronized VisualItem findItem(Point p) { 1483 Point2D p2 = (m_itransform==null ? p : 1485 m_itransform.transform(p, m_tmpPoint)); 1486 if ( !m_queue.psorted ) 1488 m_queue.sortPickingQueue(); 1489 for ( int i = m_queue.psize; --i >= 0; ) { 1491 VisualItem vi = m_queue.pitems[i]; 1492 if ( !vi.isValid() ) continue; Renderer r = vi.getRenderer(); 1494 if (r!=null && vi.isInteractive() && r.locatePoint(p2, vi)) { 1495 return vi; 1496 } 1497 } 1498 return null; 1499 } 1500 1501 1505 public class InputEventCapturer implements MouseMotionListener , 1506 MouseWheelListener , MouseListener , KeyListener 1507 { 1508 private VisualItem activeItem = null; 1509 private boolean mouseDown = false; 1510 1511 private boolean validityCheck() { 1512 if ( activeItem.isValid() ) 1513 return true; 1514 activeItem = null; 1515 return false; 1516 } 1517 1518 public void mouseDragged(MouseEvent e) { 1519 synchronized ( m_vis ) { 1520 if ( activeItem != null ) { 1521 if ( validityCheck() ) 1522 fireItemDragged(activeItem, e); 1523 } else { 1524 fireMouseDragged(e); 1525 } 1526 } 1527 } 1528 1529 public void mouseMoved(MouseEvent e) { 1530 synchronized ( m_vis ) { 1531 boolean earlyReturn = false; 1532 VisualItem vi = findItem(e.getPoint()); 1534 if ( activeItem != null && activeItem != vi ) { 1535 if ( validityCheck() ) 1536 fireItemExited(activeItem, e); 1537 earlyReturn = true; 1538 } 1539 if ( vi != null && vi != activeItem ) { 1540 fireItemEntered(vi, e); 1541 earlyReturn = true; 1542 } 1543 activeItem = vi; 1544 if ( earlyReturn ) return; 1545 1546 if ( vi != null && vi == activeItem ) { 1547 fireItemMoved(vi, e); 1548 } 1549 if ( vi == null ) { 1550 fireMouseMoved(e); 1551 } 1552 } 1553 } 1554 1555 public void mouseWheelMoved(MouseWheelEvent e) { 1556 synchronized ( m_vis ) { 1557 if ( activeItem != null ) { 1558 if ( validityCheck() ) 1559 fireItemWheelMoved(activeItem, e); 1560 } else { 1561 fireMouseWheelMoved(e); 1562 } 1563 } 1564 } 1565 1566 public void mouseClicked(MouseEvent e) { 1567 synchronized ( m_vis ) { 1568 if ( activeItem != null ) { 1569 if ( validityCheck() ) 1570 fireItemClicked(activeItem, e); 1571 } else { 1572 fireMouseClicked(e); 1573 } 1574 } 1575 } 1576 1577 1578 public void mousePressed(MouseEvent e) { 1579 synchronized ( m_vis ) { 1580 mouseDown = true; 1581 if ( activeItem != null ) { 1582 if ( validityCheck() ) 1583 fireItemPressed(activeItem, e); 1584 } else { 1585 fireMousePressed(e); 1586 } 1587 } 1588 } 1589 1590 public void mouseReleased(MouseEvent e) { 1591 synchronized ( m_vis ) { 1592 if ( activeItem != null ) { 1593 if ( validityCheck() ) 1594 fireItemReleased(activeItem, e); 1595 } else { 1596 fireMouseReleased(e); 1597 } 1598 if ( activeItem != null && mouseDown && isOffComponent(e) ) { 1599 fireItemExited(activeItem, e); 1602 activeItem = null; 1603 } 1604 mouseDown = false; 1605 } 1606 } 1607 1608 public void mouseEntered(MouseEvent e) { 1609 synchronized ( m_vis ) { 1610 fireMouseEntered(e); 1611 } 1612 } 1613 1614 public void mouseExited(MouseEvent e) { 1615 synchronized ( m_vis ) { 1616 if ( !mouseDown && activeItem != null ) { 1617 fireItemExited(activeItem, e); 1620 activeItem = null; 1621 } 1622 fireMouseExited(e); 1623 } 1624 } 1625 1626 public void keyPressed(KeyEvent e) { 1627 synchronized ( m_vis ) { 1628 if ( activeItem != null ) { 1629 if ( validityCheck() ) 1630 fireItemKeyPressed(activeItem, e); 1631 } else { 1632 fireKeyPressed(e); 1633 } 1634 } 1635 } 1636 1637 public void keyReleased(KeyEvent e) { 1638 synchronized ( m_vis ) { 1639 if ( activeItem != null ) { 1640 if ( validityCheck() ) 1641 fireItemKeyReleased(activeItem, e); 1642 } else { 1643 fireKeyReleased(e); 1644 } 1645 } 1646 } 1647 1648 public void keyTyped(KeyEvent e) { 1649 synchronized ( m_vis ) { 1650 if ( activeItem != null ) { 1651 if ( validityCheck() ) 1652 fireItemKeyTyped(activeItem, e); 1653 } else { 1654 fireKeyTyped(e); 1655 } 1656 } 1657 } 1658 1659 private boolean isOffComponent(MouseEvent e) { 1660 int x = e.getX(), y = e.getY(); 1661 return ( x<0 || x>getWidth() || y<0 || y>getHeight() ); 1662 } 1663 1664 1667 private void fireItemDragged(VisualItem item, MouseEvent e) { 1668 Object [] lstnrs = m_controls.getArray(); 1669 for (int i = 0; i < lstnrs.length; ++i) { 1670 Control ctrl = (Control) lstnrs[i]; 1671 if (ctrl.isEnabled()) 1672 try { 1673 ctrl.itemDragged(item, e); 1674 } catch ( Exception ex ) { 1675 s_logger.warning( 1676 "Exception thrown by Control: " + ex + "\n" + 1677 StringLib.getStackTrace(ex)); 1678 } 1679 } 1680 } 1681 1682 private void fireItemMoved(VisualItem item, MouseEvent e) { 1683 Object [] lstnrs = m_controls.getArray(); 1684 for (int i = 0; i < lstnrs.length; ++i) { 1685 Control ctrl = (Control) lstnrs[i]; 1686 if (ctrl.isEnabled()) 1687 try { 1688 ctrl.itemMoved(item, e); 1689 } catch ( Exception ex ) { 1690 s_logger.warning( 1691 "Exception thrown by Control: " + ex + "\n" + 1692 StringLib.getStackTrace(ex)); 1693 } 1694 } 1695 } 1696 1697 private void fireItemWheelMoved(VisualItem item, MouseWheelEvent e) { 1698 Object [] lstnrs = m_controls.getArray(); 1699 for (int i = 0; i < lstnrs.length; ++i) { 1700 Control ctrl = (Control) lstnrs[i]; 1701 if (ctrl.isEnabled()) 1702 try { 1703 ctrl.itemWheelMoved(item, e); 1704 } catch ( Exception ex ) { 1705 s_logger.warning( 1706 "Exception thrown by Control: " + ex + "\n" + 1707 StringLib.getStackTrace(ex)); 1708 } 1709 } 1710 } 1711 1712 private void fireItemClicked(VisualItem item, MouseEvent e) { 1713 Object [] lstnrs = m_controls.getArray(); 1714 for (int i = 0; i < lstnrs.length; ++i) { 1715 Control ctrl = (Control) lstnrs[i]; 1716 if (ctrl.isEnabled()) 1717 try { 1718 ctrl.itemClicked(item, e); 1719 } catch ( Exception ex ) { 1720 s_logger.warning( 1721 "Exception thrown by Control: " + ex + "\n" + 1722 StringLib.getStackTrace(ex)); 1723 } 1724 } 1725 } 1726 1727 private void fireItemPressed(VisualItem item, MouseEvent e) { 1728 Object [] lstnrs = m_controls.getArray(); 1729 for (int i = 0; i < lstnrs.length; ++i) { 1730 Control ctrl = (Control) lstnrs[i]; 1731 if (ctrl.isEnabled()) 1732 try { 1733 ctrl.itemPressed(item, e); 1734 } catch ( Exception ex ) { 1735 s_logger.warning( 1736 "Exception thrown by Control: " + ex + "\n" + 1737 StringLib.getStackTrace(ex)); 1738 } 1739 } 1740 } 1741 1742 private void fireItemReleased(VisualItem item, MouseEvent e) { 1743 Object [] lstnrs = m_controls.getArray(); 1744 for (int i = 0; i < lstnrs.length; ++i) { 1745 Control ctrl = (Control) lstnrs[i]; 1746 if (ctrl.isEnabled()) 1747 try { 1748 ctrl.itemReleased(item, e); 1749 } catch ( Exception ex ) { 1750 s_logger.warning( 1751 "Exception thrown by Control: " + ex + "\n" + 1752 StringLib.getStackTrace(ex)); 1753 } 1754 } 1755 } 1756 1757 private void fireItemEntered(VisualItem item, MouseEvent e) { 1758 item.setHover(true); 1759 Object [] lstnrs = m_controls.getArray(); 1760 for (int i = 0; i < lstnrs.length; ++i) { 1761 Control ctrl = (Control) lstnrs[i]; 1762 if (ctrl.isEnabled()) 1763 try { 1764 ctrl.itemEntered(item, e); 1765 } catch ( Exception ex ) { 1766 s_logger.warning( 1767 "Exception thrown by Control: " + ex + "\n" + 1768 StringLib.getStackTrace(ex)); 1769 } 1770 } 1771 } 1772 1773 private void fireItemExited(VisualItem item, MouseEvent e) { 1774 if ( item.isValid() ) item.setHover(false); 1775 Object [] lstnrs = m_controls.getArray(); 1776 for (int i = 0; i < lstnrs.length; ++i) { 1777 Control ctrl = (Control) lstnrs[i]; 1778 if (ctrl.isEnabled()) 1779 try { 1780 ctrl.itemExited(item, e); 1781 } catch ( Exception ex ) { 1782 s_logger.warning( 1783 "Exception thrown by Control: " + ex + "\n" + 1784 StringLib.getStackTrace(ex)); 1785 } 1786 } 1787 } 1788 1789 private void fireItemKeyPressed(VisualItem item, KeyEvent e) { 1790 Object [] lstnrs = m_controls.getArray(); 1791 if (lstnrs.length == 0) 1792 return; 1793 for (int i = 0; i < lstnrs.length; ++i) { 1794 Control ctrl = (Control) lstnrs[i]; 1795 if (ctrl.isEnabled()) 1796 try { 1797 ctrl.itemKeyPressed(item, e); 1798 } catch ( Exception ex ) { 1799 s_logger.warning( 1800 "Exception thrown by Control: " + ex + "\n" + 1801 StringLib.getStackTrace(ex)); 1802 } 1803 } 1804 } 1805 1806 private void fireItemKeyReleased(VisualItem item, KeyEvent e) { 1807 Object [] lstnrs = m_controls.getArray(); 1808 for (int i = 0; i < lstnrs.length; ++i) { 1809 Control ctrl = (Control) lstnrs[i]; 1810 if (ctrl.isEnabled()) 1811 try { 1812 ctrl.itemKeyReleased(item, e); 1813 } catch ( Exception ex ) { 1814 s_logger.warning( 1815 "Exception thrown by Control: " + ex + "\n" + 1816 StringLib.getStackTrace(ex)); 1817 } 1818 } 1819 } 1820 1821 private void fireItemKeyTyped(VisualItem item, KeyEvent e) { 1822 Object [] lstnrs = m_controls.getArray(); 1823 for (int i = 0; i < lstnrs.length; ++i) { 1824 Control ctrl = (Control) lstnrs[i]; 1825 if (ctrl.isEnabled()) 1826 try { 1827 ctrl.itemKeyTyped(item, e); 1828 } catch ( Exception ex ) { 1829 s_logger.warning( 1830 "Exception thrown by Control: " + ex + "\n" + 1831 StringLib.getStackTrace(ex)); 1832 } 1833 } 1834 } 1835 1836 private void fireMouseEntered(MouseEvent e) { 1837 Object [] lstnrs = m_controls.getArray(); 1838 for (int i = 0; i < lstnrs.length; ++i) { 1839 Control ctrl = (Control) lstnrs[i]; 1840 if (ctrl.isEnabled()) 1841 try { 1842 ctrl.mouseEntered(e); 1843 } catch ( Exception ex ) { 1844 s_logger.warning( 1845 "Exception thrown by Control: " + ex + "\n" + 1846 StringLib.getStackTrace(ex)); 1847 } 1848 } 1849 } 1850 1851 private void fireMouseExited(MouseEvent e) { 1852 Object [] lstnrs = m_controls.getArray(); 1853 for (int i = 0; i < lstnrs.length; ++i) { 1854 Control ctrl = (Control) lstnrs[i]; 1855 if (ctrl.isEnabled()) 1856 try { 1857 ctrl.mouseExited(e); 1858 } catch ( Exception ex ) { 1859 s_logger.warning( 1860 "Exception thrown by Control: " + ex + "\n" + 1861 StringLib.getStackTrace(ex)); 1862 } 1863 } 1864 } 1865 1866 private void fireMousePressed(MouseEvent e) { 1867 Object [] lstnrs = m_controls.getArray(); 1868 for (int i = 0; i < lstnrs.length; ++i) { 1869 Control ctrl = (Control) lstnrs[i]; 1870 if (ctrl.isEnabled()) 1871 try { 1872 ctrl.mousePressed(e); 1873 } catch ( Exception ex ) { 1874 s_logger.warning( 1875 "Exception thrown by Control: " + ex + "\n" + 1876 StringLib.getStackTrace(ex)); 1877 } 1878 } 1879 } 1880 1881 private void fireMouseReleased(MouseEvent e) { 1882 Object [] lstnrs = m_controls.getArray(); 1883 for (int i = 0; i < lstnrs.length; ++i) { 1884 Control ctrl = (Control) lstnrs[i]; 1885 if (ctrl.isEnabled()) 1886 try { 1887 ctrl.mouseReleased(e); 1888 } catch ( Exception ex ) { 1889 s_logger.warning( 1890 "Exception thrown by Control: " + ex + "\n" + 1891 StringLib.getStackTrace(ex)); 1892 } 1893 } 1894 } 1895 1896 private void fireMouseClicked(MouseEvent e) { 1897 Object [] lstnrs = m_controls.getArray(); 1898 for (int i = 0; i < lstnrs.length; ++i) { 1899 Control ctrl = (Control) lstnrs[i]; 1900 if (ctrl.isEnabled()) 1901 try { 1902 ctrl.mouseClicked(e); 1903 } catch ( Exception ex ) { 1904 s_logger.warning( 1905 "Exception thrown by Control: " + ex + "\n" + 1906 StringLib.getStackTrace(ex)); 1907 } 1908 } 1909 } 1910 1911 private void fireMouseDragged(MouseEvent e) { 1912 Object [] lstnrs = m_controls.getArray(); 1913 for (int i = 0; i < lstnrs.length; ++i) { 1914 Control ctrl = (Control) lstnrs[i]; 1915 if (ctrl.isEnabled()) 1916 try { 1917 ctrl.mouseDragged(e); 1918 } catch ( Exception ex ) { 1919 s_logger.warning( 1920 "Exception thrown by Control: " + ex + "\n" + 1921 StringLib.getStackTrace(ex)); 1922 } 1923 } 1924 } 1925 1926 private void fireMouseMoved(MouseEvent e) { 1927 Object [] lstnrs = m_controls.getArray(); 1928 for (int i = 0; i < lstnrs.length; ++i) { 1929 Control ctrl = (Control) lstnrs[i]; 1930 if (ctrl.isEnabled()) 1931 try { 1932 ctrl.mouseMoved(e); 1933 } catch ( Exception ex ) { 1934 s_logger.warning( 1935 "Exception thrown by Control: " + ex + "\n" + 1936 StringLib.getStackTrace(ex)); 1937 } 1938 } 1939 } 1940 1941 private void fireMouseWheelMoved(MouseWheelEvent e) { 1942 Object [] lstnrs = m_controls.getArray(); 1943 for (int i = 0; i < lstnrs.length; ++i) { 1944 Control ctrl = (Control) lstnrs[i]; 1945 if (ctrl.isEnabled()) 1946 try { 1947 ctrl.mouseWheelMoved(e); 1948 } catch ( Exception ex ) { 1949 s_logger.warning( 1950 "Exception thrown by Control: " + ex + "\n" + 1951 StringLib.getStackTrace(ex)); 1952 } 1953 } 1954 } 1955 1956 private void fireKeyPressed(KeyEvent e) { 1957 Object [] lstnrs = m_controls.getArray(); 1958 for (int i = 0; i < lstnrs.length; ++i) { 1959 Control ctrl = (Control) lstnrs[i]; 1960 if (ctrl.isEnabled()) 1961 try { 1962 ctrl.keyPressed(e); 1963 } catch ( Exception ex ) { 1964 s_logger.warning( 1965 "Exception thrown by Control: " + ex + "\n" + 1966 StringLib.getStackTrace(ex)); 1967 } 1968 } 1969 } 1970 1971 private void fireKeyReleased(KeyEvent e) { 1972 Object [] lstnrs = m_controls.getArray(); 1973 for (int i = 0; i < lstnrs.length; ++i) { 1974 Control ctrl = (Control) lstnrs[i]; 1975 if (ctrl.isEnabled()) 1976 try { 1977 ctrl.keyReleased(e); 1978 } catch ( Exception ex ) { 1979 s_logger.warning( 1980 "Exception thrown by Control: " + ex + "\n" + 1981 StringLib.getStackTrace(ex)); 1982 } 1983 } 1984 } 1985 1986 private void fireKeyTyped(KeyEvent e) { 1987 Object [] lstnrs = m_controls.getArray(); 1988 for (int i = 0; i < lstnrs.length; ++i) { 1989 Control ctrl = (Control) lstnrs[i]; 1990 if (ctrl.isEnabled()) 1991 try { 1992 ctrl.keyTyped(e); 1993 } catch ( Exception ex ) { 1994 s_logger.warning( 1995 "Exception thrown by Control: " + ex + "\n" + 1996 StringLib.getStackTrace(ex)); 1997 } 1998 } 1999 } 2000 2001 } 2003 2004 2007 2011 public JTextComponent getTextEditor() { 2012 return m_editor; 2013 } 2014 2015 2019 public void setTextEditor(JTextComponent tc) { 2020 this.remove(m_editor); 2021 m_editor = tc; 2022 this.add(m_editor, 1); 2023 } 2024 2025 2033 public void editText(VisualItem item, String attribute) { 2034 if ( m_editing ) { stopEditing(); } 2035 Rectangle2D b = item.getBounds(); 2036 Rectangle r = m_transform.createTransformedShape(b).getBounds(); 2037 2038 if ( m_editor instanceof JTextArea ) { 2041 r.y -= 2; r.width += 22; r.height += 2; 2042 } else { 2043 r.x += 3; r.y += 1; r.width -= 5; r.height -= 2; 2044 } 2045 2046 Font f = getFont(); 2047 int size = (int)Math.round(f.getSize()*m_transform.getScaleX()); 2048 Font nf = new Font (f.getFontName(), f.getStyle(), size); 2049 m_editor.setFont(nf); 2050 2051 editText(item, attribute, r); 2052 } 2053 2054 2064 public void editText(VisualItem item, String attribute, Rectangle r) { 2065 if ( m_editing ) { stopEditing(); } 2066 String txt = item.getString(attribute); 2067 m_editItem = item; 2068 m_editAttribute = attribute; 2069 Color tc = ColorLib.getColor(item.getTextColor()); 2070 Color fc = ColorLib.getColor(item.getFillColor()); 2071 m_editor.setForeground(tc); 2072 m_editor.setBackground(fc); 2073 editText(txt, r); 2074 } 2075 2076 2085 public void editText(String txt, Rectangle r) { 2086 if ( m_editing ) { stopEditing(); } 2087 m_editing = true; 2088 m_editor.setBounds(r.x,r.y,r.width,r.height); 2089 m_editor.setText(txt); 2090 m_editor.setVisible(true); 2091 m_editor.setCaretPosition(txt.length()); 2092 m_editor.requestFocus(); 2093 } 2094 2095 2101 public void stopEditing() { 2102 m_editor.setVisible(false); 2103 if ( m_editItem != null ) { 2104 String txt = m_editor.getText(); 2105 m_editItem.set(m_editAttribute, txt); 2106 m_editItem = null; 2107 m_editAttribute = null; 2108 m_editor.setBackground(null); 2109 m_editor.setForeground(null); 2110 } 2111 m_editing = false; 2112 } 2113 2114} | Popular Tags |