source: trunk/src/testing/dim/overlays/app.py @ 4

Revision 4, 22.5 KB checked in by ajaworski, 13 years ago (diff)

Added modified SAGE sources

Line 
1############################################################################
2#
3# DIM - A Direct Interaction Manager for SAGE
4# Copyright (C) 2007 Electronic Visualization Laboratory,
5# University of Illinois at Chicago
6#
7# All rights reserved.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions are met:
11#
12#  * Redistributions of source code must retain the above copyright
13#    notice, this list of conditions and the following disclaimer.
14#  * Redistributions in binary form must reproduce the above
15#    copyright notice, this list of conditions and the following disclaimer
16#    in the documentation and/or other materials provided with the distribution.
17#  * Neither the name of the University of Illinois at Chicago nor
18#    the names of its contributors may be used to endorse or promote
19#    products derived from this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32#
33# Direct questions, comments etc about SAGE UI to www.evl.uic.edu/cavern/forum
34#
35# Author: Ratko Jagodic
36#       
37############################################################################
38
39
40
41from overlay import Overlay
42from eventHandler import EventHandler
43from globals import *
44from math import cos, sin, radians
45from wx._core import EVT_ENTER_WINDOW
46
47
48def makeNew(overlayId, app):
49    """ this is how we instantiate the device object from
50        the main program that loads this plugin """
51    return App(overlayId, app)
52
53
54
55# pointer states
56RESET  = 0
57DRAG   = 1
58RESIZE = 2
59DOWN   = 3
60UP     = 4
61
62Z_CHANGE = 3
63ON_CORNER = 4
64
65ON_APP = 5
66
67ROTATE = 6
68
69FULLSCREEN = 5
70
71
72# corner orientations
73APP          = 0
74APP_BOTTOM   = -2
75APP_TOP      = -1
76BOTTOM_LEFT  = 1
77TOP_LEFT     = 2
78TOP_RIGHT    = 3
79BOTTOM_RIGHT = 4
80
81
82
83class App(Overlay, EventHandler):
84   
85    def __init__(self, overlayId, app):
86        Overlay.__init__(self, OVERLAY_APP, overlayId)
87        EventHandler.__init__(self)
88        self.app = app   # which app is this overlay tied to
89        self.bounds.setAll(app.left, app.right, app.top, app.bottom)
90        self.displayId = app.getDisplayId()
91        self.aspectRatio = self.bounds.getWidth() / float(self.bounds.getHeight())
92        self.minAppSize = 300   # min app size in either direction
93        self.z = self.app.getZvalue()
94        self.__resetOverlay()
95        self.capturedCorner = APP
96        self.lastClickPos = (0,0)
97        self.lastClickDeviceId = None
98        self.overCorner = APP
99       
100        self.lastClickAction = 0
101       
102        self.modifyTicks = 0
103               
104        # register for the events that are fired when an app changes
105        self.registerForEvent(EVT_APP_INFO, self.__onAppChanged)
106        self.registerForEvent(EVT_Z_CHANGE, self.__onZChanged)
107
108        # register for the events that are fired by devices
109        self.registerForEvent(EVT_MOVE, self.__onOver)
110        self.registerForEvent(EVT_CLICK, self.__onClick)
111        self.registerForEvent(EVT_DRAG, self.__onDrag)
112        self.registerForEvent(EVT_ROTATE, self.__onRotate)
113        self.registerForEvent(EVT_ZOOM, self.__onZoom)
114        self.registerForEvent(EVT_ARROW, self.__onArrow)
115        self.registerForEvent(EVT_LEFT_WINDOW, self.__onLeftWindow)
116
117        # register for the events from the special devices
118        self.registerForEvent(EVT_MOVE_SPECIAL, self.__onSpecialMove)
119        self.registerForEvent(EVT_DRAG_SPECIAL, self.__onDragSpecial)
120        self.registerForEvent(EVT_ROTATE_SPECIAL, self.__onRotateSpecial)
121        self.registerForEvent(EVT_ENTERED_WINDOW_SPECIAL, self.__onEnteredWindowSpecial)
122        self.registerForEvent(EVT_LEFT_WINDOW_SPECIAL, self.__onLeftWindowSpecial)
123        self.registerForEvent(EVT_CLICK_SPECIAL, self.__onClickSpecial)
124        self.registerForEvent(EVT_KEY_SPECIAL, self.__onKeySpecial)
125
126
127    # -------    DENERIC  EVENTS   -----------------   
128   
129    def __onAppChanged(self, event):
130        if self.app == event.app:
131            app = event.app
132            self.bounds.setAll(app.left, app.right, app.top, app.bottom)
133            self.aspectRatio = self.bounds.getWidth() / float(self.bounds.getHeight())
134            self.displayId = app.getDisplayId()
135            self.__resetOverlay()
136                       
137
138    def __onZChanged(self, event):
139        self.z = self.app.getZvalue()
140        self.sendOverlayMessage(Z_CHANGE, self.z)
141
142
143    def __onLeftWindow(self, event):
144        # change the app overlay to default
145        if self.overCorner != APP:
146            self.sendOverlayMessage(ON_CORNER, self.overCorner, 0)
147            self.overCorner = APP
148
149        # change the pointer to default
150        pointer = event.device.pointer
151        event.device.dragMode = False
152        if pointer:
153            pointer.showOutApp()
154            pointer.resetPointer()
155           
156        self.sendOverlayMessage(ON_APP, 0)
157           
158
159    # -------    SPECIAL DEVICE EVENTS   -----------------
160
161    def __onSpecialMove(self, event):
162        x,y = self.__toAppCoords(event.x, event.y)
163        dx,dy = self.__toAppDist(event.dX, event.dY)
164        self.sageGate.sendAppEvent(EVT_MOVE, self.app.getSailID(), event.device.getSpecialId(), x, y, dx, dy)
165
166
167    def __onEnteredWindowSpecial(self, event):
168        self.sendOverlayMessage(ON_APP, 1)
169        pointer = event.device.pointer
170#        if pointer: pointer.hide()
171
172
173    def __onLeftWindowSpecial(self, event):
174        self.sendOverlayMessage(ON_APP, 0)
175        pointer = event.device.pointer
176#        if pointer: pointer.show()
177
178
179
180    # -------    DEVICE EVENTS   -----------------
181   
182    def __onOver(self, event):
183        corner = self.app.hitTest(event.x, event.y, self.cornerSize)
184        device = event.device
185        pointer = device.pointer
186        if pointer:
187            if corner > APP:    # corners
188                pointer.showResizePointer(corner)
189                pointer.showOutApp()
190                device.dragMode = True
191                if self.overCorner != corner:
192                    self.sendOverlayMessage(ON_CORNER, corner, 1)
193                    self.sendOverlayMessage(ON_CORNER, self.overCorner, 0)
194                    self.overCorner = corner
195                   
196            elif corner < APP:   # top and bottom area
197                pointer.showDragPointer()
198                pointer.showOutApp()
199                device.dragMode = True
200                if self.overCorner != corner:
201                    self.sendOverlayMessage(ON_CORNER, corner, 1)
202                    self.sendOverlayMessage(ON_CORNER, self.overCorner, 0)
203                    self.overCorner = corner
204
205            else:      # interior of the app
206                #pointer.resetPointer()
207                pointer.showInApp()
208                device.dragMode = False
209                if self.overCorner != corner:
210                    self.sendOverlayMessage(ON_CORNER, self.overCorner, 0)
211                    self.overCorner = corner
212
213
214               
215    def __onClick(self, event):
216        corner = self.app.hitTest(event.x, event.y, self.cornerSize)
217        pointer = event.device.pointer
218        x, y = event.x, event.y
219       
220        # BUTTON DOWN
221        if event.isDown:
222            self.capturedCorner = corner
223            self.lastClickPos = (x, y)
224            event.device.toEvtHandler = self   # tell the device to forward all events here (until the button goes up)
225            if pointer: pointer.pushState()    # preserve the state of the pointer
226           
227            if corner < APP:  # top or bottom
228                self.captured = True
229                self.lastClickDeviceId = event.device.deviceId
230                if pointer: pointer.showDragPointer()
231                self.sendOverlayMessage(DRAG, 0,0, corner)
232                self.sageGate.bringToFront(self.app.getId())
233
234            elif corner > APP:  # corner
235                self.captured = True
236                self.lastClickDeviceId = event.device.deviceId
237                if pointer: pointer.showResizePointer(corner)
238                self.sendOverlayMessage(RESIZE, 0,0,corner)
239
240            else:         # to app
241                x,y = self.__toAppCoords(x,y)
242                self.sageGate.sendAppEvent(EVT_CLICK, self.app.getSailID(), -1, x, y, event.btnId, 1, event.forEvt)
243
244
245        # BUTTON UP
246        elif not event.isDown:
247            self.captured = False
248            dX = x - self.lastClickPos[0]
249            dY = y - self.lastClickPos[1]
250            if pointer: pointer.popState()   # return to the previous pointer state
251               
252            if self.capturedCorner < APP:    # top/bottom
253                if event.device.deviceId == self.lastClickDeviceId:
254                    self.lastClickDeviceId = None
255                    self.sageGate.moveWindow(self.app.getId(), dX, dY)
256
257            elif self.capturedCorner > APP:  # corners
258                if event.device.deviceId == self.lastClickDeviceId:
259                    self.lastClickDeviceId = None
260                    self.__resizeBounds(dX, self.capturedCorner)
261                    self.sageGate.resizeWindow(self.app.getId(), self.bounds.left,
262                                               self.bounds.right, self.bounds.bottom,
263                                               self.bounds.top)
264            else:    # to app
265                x,y = self.__toAppCoords(x,y)
266                self.sageGate.sendAppEvent(EVT_CLICK, self.app.getSailID(), -1, x, y, event.btnId, 0, event.forEvt)
267               
268    def __whichCorner(self,x,y):
269        normX, normY = self.__toAppCoords(x, y)
270       
271        print normX, " ", normY
272       
273        if (normX < 0.5):
274            if (normY < 0.5):
275                return BOTTOM_LEFT
276            else:
277                return TOP_LEFT
278        else:
279            if (normY < 0.5):
280                return BOTTOM_RIGHT
281            else:
282                return TOP_RIGHT
283
284    def __onClickSpecial(self, event):
285        pointer = event.device.pointer
286        x, y = event.x, event.y
287       
288        # BUTTON DOWN
289        if event.isDown:
290            self.lastClickPos = (x, y)
291            event.device.toEvtHandler = self   # tell the device to forward all events here (until the button goes up)
292#            if pointer: pointer.pushState()    # preserve the state of the pointer
293           
294#            if corner < APP:  # top or bottom
295            self.captured = True
296            self.lastClickDeviceId = event.device.deviceId
297            if event.btnId == 1:
298                self.sendOverlayMessage(DRAG, 0,0, APP_BOTTOM)
299                if pointer: pointer.showDragPointer()
300            elif event.btnId == 3:
301                self.sendOverlayMessage(RESIZE, 0,0, self.capturedCorner)
302                self.capturedCorner = self.__whichCorner(x,y)
303                print self.capturedCorner
304                if pointer: pointer.showResizePointer(self.capturedCorner)
305            self.sageGate.bringToFront(self.app.getId())
306            self.lastClickAction = event.btnId
307           
308            if pointer: pointer.showDownPointer()
309
310#            elif corner > APP:  # corner
311#                self.captured = True
312#                self.lastClickDeviceId = event.device.deviceId
313#                if pointer: pointer.showResizePointer(corner)
314#                self.sendOverlayMessage(RESIZE, 0,0,corner)
315#
316#            else:         # to app
317#                x,y = self.__toAppCoords(x,y)
318#                self.sageGate.sendAppEvent(EVT_CLICK, self.app.getSailID(), -1, x, y, event.btnId, 1, event.forEvt)
319
320
321        # BUTTON UP
322        elif not event.isDown:
323            dX = x - self.lastClickPos[0]
324            dY = y - self.lastClickPos[1]
325            self.modifyTicks = 0
326#            if pointer: pointer.popState()   # return to the previous pointer state
327               
328#            if self.capturedCorner < APP:    # top/bottom
329            if event.device.deviceId == self.lastClickDeviceId:
330                self.lastClickDeviceId = None
331                if self.lastClickAction == 1:
332                    self.sageGate.moveWindow(self.app.getId(), dX, dY)
333                elif self.lastClickAction == 3:
334                    self.__resizeBounds(dX, self.capturedCorner)
335                    self.sageGate.resizeWindow(self.app.getId(), self.bounds.left,
336                                               self.bounds.right, self.bounds.bottom,
337                                               self.bounds.top)
338            self.captured = False
339            if pointer: pointer.showUpPointer()
340
341#            else:    # to app
342#                x,y = self.__toAppCoords(x,y)
343#                self.sageGate.sendAppEvent(EVT_CLICK, self.app.getSailID(), -1, x, y, event.btnId, 0, event.forEvt)
344
345    def __onDrag(self, event):
346        x,y = event.x, event.y
347        dx, dy, dz = event.dX, event.dY, event.dZ
348        pointer = event.device.pointer
349       
350        if self.capturedCorner < APP:  # top or bottom
351            self.sendOverlayMessage(DRAG, dx, dy, self.capturedCorner)
352
353        elif self.capturedCorner > APP:       # corner
354            self.sendOverlayMessage(RESIZE, dx, dy, self.capturedCorner)
355
356        else:               # to app
357            x,y = self.__toAppCoords(x,y)
358            dx,dy = self.__toAppDist(dx, dy)
359            self.sageGate.sendAppEvent(EVT_PAN, self.app.getSailID(), -1, x, y, dx, dy, dz)
360            if pointer: pointer.showDragPointer()
361           
362    def __onDragSpecial(self, event):
363        x,y = event.x, event.y
364        dx, dy, dz = event.dX, event.dY, event.dZ
365        pointer = event.device.pointer
366       
367        if self.lastClickAction == 1:
368            self.sendOverlayMessage(DRAG, dx, dy, APP_BOTTOM)
369            self.modifyTicks+=1
370            if self.modifyTicks == 10:
371                dX = x - self.lastClickPos[0]
372                dY = y - self.lastClickPos[1]
373                self.sageGate.moveWindow(self.app.getId(), dX, dY)
374                self.lastClickPos = (x,y)
375                self.modifyTicks = 0
376        elif self.lastClickAction == 3:
377            self.sendOverlayMessage(RESIZE, dx, dy, self.capturedCorner)
378            self.modifyTicks += 1
379            if self.modifyTicks == 10:
380                dX = x - self.lastClickPos[0]
381                dY = y - self.lastClickPos[1]
382                self.__resizeBounds(dX, self.capturedCorner)
383                self.sageGate.resizeWindow(self.app.getId(), self.bounds.left,
384                                           self.bounds.right, self.bounds.bottom,
385                                           self.bounds.top)
386                self.lastClickPos = (x,y)
387                self.modifyTicks = 0
388       
389#        self.sendOverlayMessage(RESIZE, dx, dy, self.capturedCorner)
390
391
392    def __onRotate(self, event):
393        dx, dy, dz = event.dX, event.dY, event.dZ
394        pointer = event.device.pointer
395       
396        if self.capturedCorner == APP:  # to app
397            x,y = self.__toAppCoords(event.x, event.y)
398            dx,dy = self.__toAppDist(dx, dy)
399            self.sageGate.sendAppEvent(EVT_ROTATE, self.app.getSailID(), -1, x, y, dx, dy, dz)
400            if pointer: pointer.showRotatePointer()
401           
402    def __onRotateSpecial(self, event):
403#        self.sageGate.moveWindow(self.app.getId(), event.dX, event.dY)
404        self.sageGate.rotateWindow(self.app.getId(), event.angle)
405        self.sageGate.bringToFront(self.app.getId())
406
407    def __onZoom(self, event):
408        x,y = event.x, event.y
409        dx, dy, dz = event.dX, event.dY, event.dZ
410        pointer = event.device.pointer
411       
412        if self.capturedCorner == APP:  # to app
413            x,y = self.__toAppCoords(x,y)
414            dx,dy = self.__toAppDist(dx, dy)
415            self.sageGate.sendAppEvent(EVT_ZOOM, self.app.getSailID(), -1, x, y, dx, dy, dz)
416            if pointer: pointer.showZoomPointer()
417
418           
419    def __onArrow(self, event):
420        self.sageGate.sendAppEvent(EVT_ARROW, self.app.getSailID(), -1, event.arrow)
421       
422    def __onKeySpecial(self, event):
423        if event.key == 1:
424            currentZ = self.app.getZvalue()
425            self.sageGate.bringToFront(self.app.getId())
426        elif event.key == -1:
427            bringToFrontOrder = getSageData().getAllAppIDs()[:]
428            bringToFrontOrder.remove(self.app.getId())
429            bringToFrontOrder.sort(key=getSageData().getZvalue, reverse=True)
430            for i in bringToFrontOrder:
431                self.sageGate.bringToFront(i)
432        elif event.key == FULLSCREEN:
433            width = (getSageData().getDisplayInfo().sageW)
434            height = (getSageData().getDisplayInfo().sageH)
435           
436            #!!!Dirty hack for installation in WestPoint!!!
437            width = 8*width/9
438           
439            screenRatio = (float)(width)/(float)(height)
440           
441            print self.aspectRatio, screenRatio
442           
443            if self.aspectRatio < screenRatio:
444                newHeight = (int)(height)
445                newWidth = (int)(round(self.aspectRatio * newHeight))
446                margin = ((width - newWidth) / 2)
447               
448                self.sageGate.resizeWindow(self.app.getId(), margin, width - margin, 0, newHeight)
449            else:
450                newWidth = (int)(width)
451                newHeight = (int)(round((float)(newWidth) / self.aspectRatio))
452                margin = ((height - newHeight) / 2)
453               
454                self.sageGate.resizeWindow(self.app.getId(), 0, newWidth, margin, height - margin)
455
456    # -------    HELPER METHODS   -----------------
457   
458
459    def __resetOverlay(self):
460        self.__setCornerSize()
461        self.sendOverlayMessage(RESET, self.bounds.left,
462                                self.bounds.right, self.bounds.top,
463                                self.bounds.bottom, self.z,
464                                self.cornerSize, self.displayId)
465        self.overCorner = APP
466
467   
468    def __toAppCoords(self, x, y):
469        """ converts to the normalized coords with respect to the app window
470            accounts for orientation of the sage window
471        """
472        orientation = self.app.getOrientation()
473       
474        if orientation == 0:
475            x -= self.bounds.left
476            y -= self.bounds.bottom
477            normX = float(x)/self.bounds.getWidth()
478            normY = float(y)/self.bounds.getHeight()
479        elif orientation == 180:
480            x = self.bounds.right - x
481            y = self.bounds.top - y
482            normX = float(x)/self.bounds.getWidth()
483            normY = float(y)/self.bounds.getHeight()
484        elif orientation == 90:
485            tmp = y
486            y = self.bounds.right - x
487            x = tmp - self.bounds.bottom
488            normX = float(x)/self.bounds.getHeight()
489            normY = float(y)/self.bounds.getWidth()
490           
491        elif orientation == 270:
492            tmp = y
493            y = x - self.bounds.left
494            x = self.bounds.top - tmp
495            normX = float(x)/self.bounds.getHeight()
496            normY = float(y)/self.bounds.getWidth()
497           
498        return normX, normY
499
500           
501    def __toAppDist(self, dx, dy):
502        """ converts to the normalized coords with respect to the app window
503            accounts for orientation of the sage window
504        """
505        orientation = self.app.getOrientation()
506
507        if orientation == 0:
508            normX = float(dx)/self.bounds.getWidth()
509            normY = float(dy)/self.bounds.getHeight()
510        elif orientation == 180:
511            dx = -dx
512            dy = -dy
513            normX = float(dx)/self.bounds.getWidth()
514            normY = float(dy)/self.bounds.getHeight()
515        elif orientation == 90:
516            tmp = dy
517            dy = -dx
518            dx = tmp
519            normX = float(dx)/self.bounds.getHeight()
520            normY = float(dy)/self.bounds.getWidth()
521        elif orientation == 270:
522            tmp = dy
523            dy = dx
524            dx = -tmp
525            normX = float(dx)/self.bounds.getHeight()
526            normY = float(dy)/self.bounds.getWidth()
527           
528        #normX = float(dx)/self.bounds.getWidth()
529        #normY = float(dy)/self.bounds.getHeight()
530
531        return normX, normY
532
533        #return float(dx)/self.bounds.getWidth(), float(dy)/self.bounds.getHeight()
534           
535
536    def __setCornerSize(self):
537        maxSize = 400
538        minSize = 150
539        self.cornerSize = min(maxSize, max(minSize, int(min(self.bounds.getHeight(),
540                                                            self.bounds.getWidth()) / 8)))
541
542       
543    def __resizeBounds(self, dx, corner):
544        # change the bounds based on the amount moved since the click
545        # also, don't resize smaller than the minAppSize
546        (l,r,t,b) = self.bounds.getAll()
547       
548        if corner == BOTTOM_LEFT:
549            l+=dx; b=b+dx/self.aspectRatio
550            if r-l < self.minAppSize and (r-l) <= (t-b):
551                l = r - self.minAppSize
552                b = t - self.minAppSize / self.aspectRatio
553            elif t-b < self.minAppSize and (t-b) < (r-l):
554                b = t - self.minAppSize
555                l = r - self.minAppSize * self.aspectRatio
556        elif corner == TOP_LEFT:
557            l+=dx; t=t-dx/self.aspectRatio
558            if r-l < self.minAppSize and (r-l) <= (t-b):
559                l = r - self.minAppSize
560                t = b + self.minAppSize / self.aspectRatio
561            elif t-b < self.minAppSize and (t-b) < (r-l):
562                t = b + self.minAppSize
563                l = r - self.minAppSize * self.aspectRatio
564        elif corner == TOP_RIGHT:
565            r+=dx; t=t+dx/self.aspectRatio
566            if r-l < self.minAppSize and (r-l) <= (t-b):
567                r = l + self.minAppSize
568                t = b + self.minAppSize / self.aspectRatio
569            elif t-b < self.minAppSize and (t-b) < (r-l):
570                t = b + self.minAppSize
571                r = l + self.minAppSize * self.aspectRatio
572        elif corner == BOTTOM_RIGHT:
573            r+=dx; b=b-dx/self.aspectRatio
574            if r-l < self.minAppSize and (r-l) <= (t-b):
575                r = l + self.minAppSize
576                b = t - self.minAppSize / self.aspectRatio
577            elif t-b < self.minAppSize and (t-b) < (r-l):
578                b = t - self.minAppSize
579                r = l + self.minAppSize * self.aspectRatio
580
581        self.bounds.setAll(l,r,t,b)
582
583   
Note: See TracBrowser for help on using the repository browser.