Source code: com/xerox/VTM/engine/AppletViewPanel.java
1 /* FILE: AppletViewPanel.java
2 * DATE OF CREATION: Jul 04 2002
3 * AUTHOR : Emmanuel Pietriga (emmanuel.pietriga@xrce.xerox.com)
4 * MODIF: Tue Aug 05 14:56:46 2003 by Emmanuel Pietriga (emmanuel@w3.org, emmanuel@claribole.net)
5 * Copyright (c) Xerox Corporation, XRCE/Contextual Computing, 2002. All Rights Reserved
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * For full terms see the file COPYING.
18 */
19
20 package com.xerox.VTM.engine;
21
22 import com.xerox.VTM.glyphs.*;
23 import javax.swing.*;
24 import java.awt.*;
25 import java.awt.geom.*;
26 import java.awt.image.*;
27 import java.awt.event.*;
28 import java.util.Vector;
29 import java.util.Date;
30 import java.util.Enumeration;
31
32 /**
33 * Each view runs in its own thread - uses double buffering - for use in JApplet only
34 * @author Emmanuel Pietriga
35 */
36 public class AppletViewPanel extends ViewPanel implements Runnable {
37
38 /**for Double Buffering*/
39 BufferedImage buffImg;
40
41 //get the BufferedImage or VolatileImage for this view
42 protected BufferedImage getImage(){
43 return this.buffImg;
44 }
45
46 public AppletViewPanel(Vector cameras,View v) {
47 addHierarchyListener(
48 new HierarchyListener() {
49 public void hierarchyChanged(HierarchyEvent e) {
50 if (isShowing()) {
51 start();
52 } else {
53 stop();
54 }
55 }
56 }
57 );
58 parent=v;
59 //init of camera array
60 cams=new Camera[cameras.size()]; //array of Camera
61 for (int nbcam=0;nbcam<cameras.size();nbcam++){
62 cams[nbcam]=(Camera)(cameras.get(nbcam));
63 }
64 //init other stuff
65 setBackground(Color.lightGray);
66 this.addMouseListener(this);
67 this.addMouseMotionListener(this);
68 start();
69 // for (int i=0;i<cams.length;i++){
70 // System.out.println(cams[i].toString());
71 // }
72 // BufferedImage cImage=new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB); //create a BufferedImage with transparent background
73 // try {//this buffer represents the mouse cursor shape (it does not appear since it is transparent)
74 // awtCursor=Toolkit.getDefaultToolkit().createCustomCursor(cImage,new Point(0,0),"myCursor");
75 // this.setCursor(awtCursor);
76 // }
77 // catch(IndexOutOfBoundsException e){if (parent.parent.debug){System.err.println("Error while creating custom cursor "+e);setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));}}
78 setAWTCursor(Cursor.CUSTOM_CURSOR); //custom cursor means VTM cursor
79 if (parent.parent.debug){System.out.println("View refresh time set to "+frameTime+"ms");}
80 }
81
82 public void start(){
83 Dimension size = getSize();
84 runView = new Thread(this);
85 runView.setPriority(Thread.NORM_PRIORITY);
86 runView.start();
87 }
88
89 public synchronized void stop() {
90 runView = null;
91 notify();
92 }
93
94 public void run() {
95 Thread me = Thread.currentThread();
96 while (getSize().width <= 0) { //Wait until the window actually exists
97 try {
98 runView.sleep(500);
99 }
100 catch (InterruptedException e) {
101 if (parent.parent.debug){System.err.println("viewpanel.run.runview.sleep "+e);}
102 return;
103 }
104 }
105 Graphics2D g2d = null;
106 Graphics2D BufferG2D = null;
107 Dimension oldSize=getSize();
108 //clipRect=new Rectangle(0,0,oldSize.width,oldSize.height);
109 while (runView==me) {
110 if (notBlank){
111 if (active){
112 if (repaintNow){
113 try {
114 repaintNow=false;//do this first as the thread can be interrupted inside this branch and we want to catch new requests for repaint
115 updateMouseOnly=false;
116 d1=System.currentTimeMillis();
117 Dimension size=getSize();
118 if (size.width != oldSize.width || size.height != oldSize.height) {
119 //each time the parent window is resized, adapt the buffer image size
120 buffImg=null;
121 if (BufferG2D!=null) {
122 BufferG2D.dispose();
123 BufferG2D=null;
124 }
125 //clipRect=new Rectangle(0,0,size.width,size.height);
126 if (parent.parent.debug){System.out.println("Resizing JPanel: ("+oldSize.width+"x"+oldSize.height+") -> ("+size.width+"x"+size.height+")");}
127 oldSize=size;
128 updateAntialias=true;
129 updateFont=true;
130 }
131 if (buffImg==null) {
132 buffImg=(BufferedImage) createImage(size.width,size.height);
133 updateAntialias=true;
134 updateFont=true;
135 }
136 if (BufferG2D == null) {
137 BufferG2D = buffImg.createGraphics();
138 updateAntialias=true;
139 updateFont=true;
140 }
141 if (updateFont){BufferG2D.setFont(VirtualSpaceManager.mainFont);updateFont=false;}
142 if (updateAntialias){if (antialias){BufferG2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);} else {BufferG2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);}updateAntialias=false;}
143 g2d = BufferG2D;
144 standardStroke=g2d.getStroke();
145 standardTransform=g2d.getTransform();
146 //parent.parent.constMgr.updateAttribsFromMvars();
147 synchronized(this){
148 g2d.setPaintMode();
149 g2d.setBackground(backColor);
150 g2d.clearRect(0,0,getWidth(),getHeight());
151 //begin actual drawing here
152 for (int nbcam=0;nbcam<cams.length;nbcam++){
153 if ((cams[nbcam]!=null) && (cams[nbcam].enabled) && ((cams[nbcam].eager) || (cams[nbcam].shouldRepaint()))){
154 camIndex=cams[nbcam].getIndex();
155 drawnGlyphs=cams[nbcam].parentSpace.getDrawnGlyphs(camIndex);
156 synchronized(drawnGlyphs){
157 drawnGlyphs.removeAllElements();
158 uncoef=(float)((cams[nbcam].focal+cams[nbcam].altitude)/cams[nbcam].focal);
159 viewW=this.getSize().width;//compute region's width and height
160 viewH=this.getSize().height;
161 viewWHu=(long)(cams[nbcam].posx+this.getSize().width/2*uncoef);//compute region seen from this view
162 viewHHu=(long)(cams[nbcam].posy+this.getSize().height/2*uncoef);//through camera
163 viewWLu=(long)(cams[nbcam].posx-this.getSize().width/2*uncoef);
164 viewHLu=(long)(cams[nbcam].posy-this.getSize().height/2*uncoef);
165 if (parent.detectMultipleFullFills){//if detect multiple fills option is ON
166 for (Enumeration en=cams[nbcam].parentSpace.getVisibleGlyphs().elements();en.hasMoreElements();){
167 gl=(Glyph)(en.nextElement());
168 if (gl.drawMe(viewWHu,viewHHu,viewWLu,viewHLu,camIndex)){
169 cams[nbcam].parentSpace.drewGlyph(gl,camIndex);
170 gl.project(cams[nbcam],this);
171 }
172 }
173 //drawnGlyphs=cams[nbcam].parentSpace.getDrawnGlyphs(camIndex);
174 beginAt=0;
175 for (int j=drawnGlyphs.size()-1;j>=0;j--){//glyphs must have been projected because fillsView uses
176 if (((Glyph)drawnGlyphs.elementAt(j)).fillsView(viewW,viewH,cams[nbcam].getIndex())){//projected coords
177 beginAt=j;
178 break;
179 }
180 }
181 for (int j=beginAt;j<drawnGlyphs.size();j++){
182 gl=(Glyph)drawnGlyphs.elementAt(j);
183 if (gl.isVisible()){
184 gl.draw(g2d,size.width,size.height,cams[nbcam].getIndex(),standardStroke,standardTransform);
185 }
186 }
187 }
188 else {//if detect multiple fills option is OFF
189 for (Enumeration en=cams[nbcam].parentSpace.getVisibleGlyphs().elements();en.hasMoreElements();){
190 gl=(Glyph)(en.nextElement());
191 if (gl.drawMe(viewWHu,viewHHu,viewWLu,viewHLu,camIndex)){
192 //if glyph is at least partially visible in the reg. seen from this view, display
193 synchronized(gl){
194 gl.project(cams[nbcam],this);
195 if (gl.isVisible()){
196 gl.draw(g2d,size.width,size.height,cams[nbcam].getIndex(),standardStroke,standardTransform);
197 }
198 }
199 cams[nbcam].parentSpace.drewGlyph(gl,camIndex);
200 }
201 }
202 }
203 }
204 }
205 }
206 if (inside){//deal with mouse glyph only if mouse cursor is inside this window
207 try {
208 parent.mouse.unProject(cams[activeLayer],this); //we project the mouse cursor wrt the appropriate coord sys
209 if (computeListAtEachRepaint && parent.mouse.isSensitive()){
210 parent.mouse.computeMouseOverList(evH,cams[activeLayer]);
211 }
212 }
213 catch (NullPointerException ex) {if (parent.parent.debug){System.err.println("viewpanel.run.drawdrag "+ex);}}
214 g2d.setColor(parent.mouse.color);
215 if (drawDrag){g2d.drawLine(origDragx,origDragy,parent.mouse.mx,parent.mouse.my);}
216 if (drawRect){g2d.drawRect(Math.min(origDragx,parent.mouse.mx),Math.min(origDragy,parent.mouse.my),Math.max(origDragx,parent.mouse.mx)-Math.min(origDragx,parent.mouse.mx),Math.max(origDragy,parent.mouse.my)-Math.min(origDragy,parent.mouse.my));}
217 if (drawOval){
218 if (circleOnly){
219 g2d.drawOval(origDragx-Math.abs(origDragx-parent.mouse.mx),origDragy-Math.abs(origDragx-parent.mouse.mx),2*Math.abs(origDragx-parent.mouse.mx),2*Math.abs(origDragx-parent.mouse.mx));
220 }
221 else {
222 g2d.drawOval(origDragx-Math.abs(origDragx-parent.mouse.mx),origDragy-Math.abs(origDragy-parent.mouse.my),2*Math.abs(origDragx-parent.mouse.mx),2*Math.abs(origDragy-parent.mouse.my));
223 }
224 }
225 if (drawVTMcursor){
226 synchronized(this){
227 g2d.setXORMode(backColor);
228 parent.mouse.draw(g2d);
229 oldX=parent.mouse.mx;
230 oldY=parent.mouse.my;
231 }
232 }
233 }
234 //end drawing here
235 if (g2d == BufferG2D) {
236 repaint();
237 }
238 d2=System.currentTimeMillis();
239 timeToSleep=frameTime-d2+d1; //40-(d2-d1) = wanted refresh rate - time needed to do the actual repaint operations
240 }
241 }
242 catch (NullPointerException ex0){if (parent.parent.debug){System.err.println("viewpanel.run (probably due to buffImg.createGraphics()) "+ex0);}}
243 //either we do - BETTER UNDER Win32
244 try {
245 runView.sleep((timeToSleep>10) ? timeToSleep : 10); //sleep ... ms
246 }
247 catch (InterruptedException e) {
248 if (parent.parent.debug){System.err.println("viewpanel.run.runview.sleep2 "+e);}
249 return;
250 }
251 //or this both seem to work well (have to test on several config) - BETTER UNDER SOLARIS
252 //Thread.yield();
253 }
254 else if (updateMouseOnly){
255 updateMouseOnly=false;//do this first as the thread can be interrupted inside this branch and we want to catch new requests for repaint
256 d1=System.currentTimeMillis();
257 try {
258 parent.mouse.unProject(cams[activeLayer],this); //we project the mouse cursor wrt the appropriate coord sys
259 if (computeListAtEachRepaint && parent.mouse.isSensitive()){parent.mouse.computeMouseOverList(evH,cams[activeLayer]);}
260 }
261 catch (NullPointerException ex) {if (parent.parent.debug){System.err.println("viewpanel.run.drawdrag "+ex);}}
262 if (drawVTMcursor){
263 synchronized(this){
264 try {
265 g2d.setXORMode(backColor);
266 g2d.setColor(parent.mouse.color);
267 g2d.drawLine(oldX-10,oldY,oldX+10,oldY);
268 g2d.drawLine(oldX,oldY-10,oldX,oldY+10);
269 g2d.drawLine(parent.mouse.mx-10,parent.mouse.my,parent.mouse.mx+10,parent.mouse.my);
270 g2d.drawLine(parent.mouse.mx,parent.mouse.my-10,parent.mouse.mx,parent.mouse.my+10);
271 oldX=parent.mouse.mx;
272 oldY=parent.mouse.my;
273 }//a nullpointerex on g2d seems to occur from time to time when going in or exiting from blank mode - just catch it and wait for next loop until we find out what's causing this
274 catch (NullPointerException ex47){if (parent.parent.debug){System.err.println("viewpanel.run.runview.drawVTMcursor "+ex47);}}
275 }
276 }
277 repaint();
278 d2=System.currentTimeMillis();
279 timeToSleep=frameTime-d2+d1; //40-(d2-d1) = wanted refresh rate - time needed to do the actual repaint operations
280 try {
281 runView.sleep((timeToSleep>10) ? timeToSleep : 10); //sleep ... ms
282 }
283 catch (InterruptedException e) {
284 if (parent.parent.debug){System.err.println("viewpanel.run.runview.sleep3 "+e);}
285 return;
286 }
287 }
288 else {
289 try {
290 runView.sleep(frameTime+20); //sleep ... ms
291 }
292 catch (InterruptedException e) {
293 if (parent.parent.debug){System.err.println("viewpanel.run.runview.sleep4 "+e);}
294 return;
295 }
296 }
297 }
298 else {
299 try {
300 runView.sleep(deactiveTime); //sleep ... ms
301 }
302 catch (InterruptedException e) {
303 if (parent.parent.debug){System.err.println("viewpanel.run.runview.sleep5 "+e);}
304 return;
305 }
306 }
307 }
308 else {
309 Dimension size=getSize();
310 if (size.width != oldSize.width || size.height != oldSize.height) {
311 //each time the parent window is resized, adapt the buffer image size
312 buffImg=null;
313 if (BufferG2D!=null) {
314 BufferG2D.dispose();
315 BufferG2D=null;
316 }
317 if (parent.parent.debug){System.out.println("Resizing JPanel in blank mode: ("+oldSize.width+"x"+oldSize.height+") -> ("+size.width+"x"+size.height+")");}
318 oldSize=size;
319 updateAntialias=true;
320 updateFont=true;
321 }
322 if (buffImg==null) {
323 buffImg=(BufferedImage) createImage(size.width,size.height);
324 updateAntialias=true;
325 updateFont=true;
326 }
327 if (BufferG2D == null) {
328 BufferG2D = buffImg.createGraphics();
329 updateAntialias=true;
330 updateFont=true;
331 }
332 if (updateFont){BufferG2D.setFont(VirtualSpaceManager.mainFont);updateFont=false;}
333 if (updateAntialias){if (antialias){BufferG2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);} else {BufferG2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);}updateAntialias=false;}
334 g2d = BufferG2D;
335 standardStroke=g2d.getStroke();
336 standardTransform=g2d.getTransform();
337 g2d.setPaintMode();
338 g2d.setColor(blankColor);
339 g2d.fillRect(0,0,getWidth(),getHeight());
340 repaint();
341 try {
342 runView.sleep(deactiveTime); //sleep ... ms
343 }
344 catch (InterruptedException e) {
345 if (parent.parent.debug){System.err.println("viewpanel.run.runview.sleep5 "+e);}
346 return;
347 }
348 }
349 }
350 if (g2d != null) {
351 g2d.dispose();
352 }
353 }
354
355 public void paint(Graphics g) {
356 synchronized (this) {
357 g2 = (Graphics2D) g;
358 if (buffImg != null) {
359 g2.drawImage(buffImg, null, 0, 0);
360 }
361 }
362 }
363
364 }