source: trunk/src/testing/ui/SAGEShape.py @ 4

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

Added modified SAGE sources

Line 
1############################################################################
2#
3# SAGE UI - A Graphical User Interface for SAGE
4# Copyright (C) 2005 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#         Allan Spale
37#         Andrew Johnson
38#
39############################################################################
40
41
42
43import wx.lib.ogl as ogl
44from wx.lib.ogl._oglmisc import *     #needed for the MyShape class (MakeControlPoints)
45import wx
46from globals import *
47from Mywx import MyImage
48
49
50############################################################################
51#
52#  CLASS:       MyShape
53#
54#  DESCRIPTION: Extends the original ogl.Shape class so that the resizing
55#               control points behave as we want them. There are only 4 control
56#               points on the corners and they resize the shape while preserving
57#               the aspect ratio (optional). The control points have also been
58#               overridden to MyControlPoint.
59#
60#  DATE:        October 11, 2004
61#
62############################################################################
63
64class MyShape(ogl.Shape):
65    def __init__(self, canvas = None):
66      ogl.Shape.__init__(self, canvas)
67      self._shadowOffsetX = 6
68      self._shadowOffsetY = 6
69      self._shadowBrush = wx.Brush(shapeShadowColor)
70      self._timer = MouseCaptureTimer()
71      self._timer.SetCallback(self.ReleaseCapture)
72
73   
74    # Overriden method for creating the resizing points
75    # (corner ones behave the same as the ones on the side)
76
77    def MakeControlPoints(self):
78        """Make a list of control points (draggable handles) appropriate to
79        the shape.
80        """
81
82        maxX, maxY = self.GetBoundingBoxMax()
83        minX, minY = self.GetBoundingBoxMin()
84
85        widthMin = minX - CONTROL_POINT_SIZE   #+ CONTROL_POINT_SIZE + 2
86        heightMin = minY - CONTROL_POINT_SIZE  #+ CONTROL_POINT_SIZE + 2
87
88        # Offsets from main object
89        top = -heightMin / 2.0
90        bottom = heightMin / 2.0 #+ (maxY - minY)
91        left = -widthMin / 2.0
92        right = widthMin / 2.0 #+ (maxX - minX)
93
94
95        # TOP LEFT
96        control = MyControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, top, CONTROL_POINT_DIAGONAL, "NW")
97        self._canvas.AddShape(control)
98        self._controlPoints.append(control)
99
100        # TOP RIGHT
101        control = MyControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, top, CONTROL_POINT_DIAGONAL, "NE")
102        self._canvas.AddShape(control)
103        self._controlPoints.append(control)
104
105        # BOTTOM RIGHT
106        control = MyControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, bottom, CONTROL_POINT_DIAGONAL, "SE")
107        self._canvas.AddShape(control)
108        self._controlPoints.append(control)
109
110        # BOTTOM LEFT
111        control = MyControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, bottom, CONTROL_POINT_DIAGONAL, "SW")
112        self._canvas.AddShape(control)
113        self._controlPoints.append(control)
114
115
116
117    ### RJ 2005-02-02
118    ### overridden to have the shapes resizable from corners only
119
120    def ResetControlPoints(self):
121        """Reset the positions of the control points (for instance when the
122        shape's shape has changed).
123        """
124        self.ResetMandatoryControlPoints()
125
126        if len(self._controlPoints) == 0:
127            return
128
129        maxX, maxY = self.GetBoundingBoxMax()
130        minX, minY = self.GetBoundingBoxMin()
131
132        widthMin = minX - CONTROL_POINT_SIZE  #+ CONTROL_POINT_SIZE + 2
133        heightMin = minY - CONTROL_POINT_SIZE  #+ CONTROL_POINT_SIZE + 2
134
135        # Offsets from main object
136        top = -heightMin / 2.0
137        bottom = heightMin / 2.0 #+ (maxY - minY)
138        left = -widthMin / 2.0
139        right = widthMin / 2.0 #+ (maxX - minX)
140
141
142        self._controlPoints[0]._xoffset = left
143        self._controlPoints[0]._yoffset = top
144
145        self._controlPoints[1]._xoffset = right
146        self._controlPoints[1]._yoffset = top
147
148        self._controlPoints[2]._xoffset = right
149        self._controlPoints[2]._yoffset = bottom
150
151        self._controlPoints[3]._xoffset = left
152        self._controlPoints[3]._yoffset = bottom
153       
154
155#----------------------------------------------------------------------
156
157
158    ### RJ 2005-02-07
159    ### overridden to have the shapes resizable from corners only
160    # Control points ('handles') redirect control to the actual shape, to
161    # make it easier to override sizing behaviour.
162
163    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
164        bound_x, bound_y = self.GetBoundingBoxMin()
165
166        dc = wx.ClientDC(self.GetCanvas())
167        self.GetCanvas().PrepareDC(dc)
168
169        dc.SetLogicalFunction(OGLRBLF)
170
171        dottedPen =  wx.Pen(wx.Colour(1, 0, 1), 2, wx.SOLID)
172        dc.SetPen(dottedPen)
173        dc.SetBrush(wx.TRANSPARENT_BRUSH)
174
175        # Don't maintain the same centre point
176        newX1 = min(pt._controlPointDragStartX, x)
177        newY1 = min(pt._controlPointDragStartY, y)
178        newX2 = max(pt._controlPointDragStartX, x)
179        newY2 = max(pt._controlPointDragStartY, y)
180
181        if self.GetMaintainAspectRatio():
182            newH = (newX2 - newX1) * (float(pt._controlPointDragStartHeight) / pt._controlPointDragStartWidth)
183            if self.GetY() > pt._controlPointDragStartY:
184                newY2 = newY1 + newH
185            else:
186                newY1 = newY2 - newH
187
188        newWidth = float(newX2 - newX1)
189        newHeight = float(newY2 - newY1)
190
191        pt._controlPointDragPosX = newX1 + newWidth / 2.0
192        pt._controlPointDragPosY = newY1 + newHeight / 2.0
193        if self.GetFixedWidth():
194            newWidth = bound_x
195
196        if self.GetFixedHeight():
197            newHeight = bound_y
198
199        pt._controlPointDragEndWidth = newWidth
200        pt._controlPointDragEndHeight = newHeight
201        self.GetEventHandler().OnDrawOutline(dc, pt._controlPointDragPosX, pt._controlPointDragPosY, newWidth, newHeight)
202
203
204#----------------------------------------------------------------------
205
206
207    ### RJ 2005-02-07
208    ### overridden to have the shapes resizable from corners only
209
210    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
211        self._canvas.CaptureMouse()
212        self._timer.Start(5000, True)   # automatically release the mouse after 5 secs
213       
214
215        dc = wx.ClientDC(self.GetCanvas())
216        self.GetCanvas().PrepareDC(dc)
217
218        dc.SetLogicalFunction(OGLRBLF)
219
220        bound_x, bound_y = self.GetBoundingBoxMin()
221        self.GetEventHandler().OnBeginSize(bound_x, bound_y)
222
223        # Choose the 'opposite corner' of the object as the stationary
224        # point in case this is non-centring resizing.
225        if pt.GetX() < self.GetX():
226            pt._controlPointDragStartX = self.GetX() + bound_x / 2.0
227        else:
228            pt._controlPointDragStartX = self.GetX() - bound_x / 2.0
229
230        if pt.GetY() < self.GetY():
231            pt._controlPointDragStartY = self.GetY() + bound_y / 2.0
232        else:
233            pt._controlPointDragStartY = self.GetY() - bound_y / 2.0
234
235        # We may require the old width and height
236        pt._controlPointDragStartWidth = bound_x
237        pt._controlPointDragStartHeight = bound_y
238
239        dottedPen = wx.Pen(wx.Colour(1, 0, 1), 2, wx.SOLID)
240        dc.SetPen(dottedPen)
241        dc.SetBrush(wx.TRANSPARENT_BRUSH)
242
243        # Don't maintain the same centre point
244        newX1 = min(pt._controlPointDragStartX, x)
245        newY1 = min(pt._controlPointDragStartY, y)
246        newX2 = max(pt._controlPointDragStartX, x)
247        newY2 = max(pt._controlPointDragStartY, y)
248
249        if self.GetMaintainAspectRatio():
250            newH = (newX2 - newX1) * (float(pt._controlPointDragStartHeight) / pt._controlPointDragStartWidth)
251            if pt.GetY() > pt._controlPointDragStartY:
252                newY2 = newY1 + newH
253            else:
254                newY1 = newY2 - newH
255
256        newWidth = float(newX2 - newX1)
257        newHeight = float(newY2 - newY1)
258
259        pt._controlPointDragPosX = newX1 + newWidth / 2.0
260        pt._controlPointDragPosY = newY1 + newHeight / 2.0
261        if self.GetFixedWidth():
262            newWidth = bound_x
263
264        if self.GetFixedHeight():
265            newHeight = bound_y
266
267        pt._controlPointDragEndWidth = newWidth
268        pt._controlPointDragEndHeight = newHeight
269        self.GetEventHandler().OnDrawOutline(dc, pt._controlPointDragPosX, pt._controlPointDragPosY, newWidth, newHeight)
270
271
272#----------------------------------------------------------------------
273
274
275    ### RJ 2005-02-07
276    ### overridden to have the shapes resizable from corners only
277
278    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
279        dc = wx.ClientDC(self.GetCanvas())
280        self.GetCanvas().PrepareDC(dc)
281
282        if self._canvas.HasCapture():
283            self._canvas.ReleaseMouse()
284        dc.SetLogicalFunction(wx.COPY)
285        self.Recompute()
286        self.ResetControlPoints()
287
288        self.Erase(dc)
289
290        self.SetSize(pt._controlPointDragEndWidth, pt._controlPointDragEndHeight)
291        self.Move(dc, pt._controlPointDragPosX, pt._controlPointDragPosY)
292
293        # Recursively redraw links if we have a composite
294        if len(self.GetChildren()):
295            self.DrawLinks(dc, -1, True)
296
297        width, height = self.GetBoundingBoxMax()
298        self.GetEventHandler().OnEndSize(width, height)
299
300        if not self._canvas.GetQuickEditMode() and pt._eraseObject:
301            self._canvas.Redraw(dc)
302       
303
304    def ReleaseCapture(self):
305        """ called with a timer to release the capture after a certain time """
306        if self._canvas.HasCapture():
307            self._canvas.ReleaseMouse()
308
309
310class MouseCaptureTimer(wx.Timer):
311    def SetCallback(self, cb):
312        self.callback = cb
313    def Notify(self):
314        self.callback()
315
316
317
318
319
320class MyControlPoint(ogl.ControlPoint):
321    def __init__(self, theCanvas, object, size, the_xoffset, the_yoffset, the_type, location):
322        ogl.ControlPoint.__init__(self, theCanvas, object, size, the_xoffset, the_yoffset, the_type)
323        self.location = location
324        self.SetPen(wx.TRANSPARENT_PEN)
325        self.SetBrush(wx.TRANSPARENT_BRUSH)
326
327
328# these are the buttons for maximizing, minimizing and closing shapes
329# they are not buttons per se but just images drawn on top of shapes
330# the clicks are handled manually (by comparing mouse coordinates) through
331# the OnLeftClick event handler for the shape
332class ShapeButton(MyImage):
333    def __init__(self, shape, handler, bitmap, x=0, y=0, rotate=False):
334        MyImage.__init__(self, bitmap, x,y, False, 20, 20)
335        self.shape = shape
336        self.handler = handler
337        self._visible = True
338        self.rotate = rotate
339
340        if rotate:
341            self.originalImage = self.image  # preserve the orientation of the original image
342           
343
344    def SetOrientation(self, degrees):
345        if degrees > 0:
346            timesToRotate = int(degrees/90)
347            self.image = self.originalImage
348            for i in range(timesToRotate):
349                self.image = self.image.Rotate90(False)
350        else:
351            self.image = self.originalImage
352        self.bitmap = wx.BitmapFromImage(self.image)
353       
354       
355    def OnPaint(self, dc):
356        if self.GetVisible():
357            dc.DrawBitmap(self.bitmap, self.GetX(), self.GetY(), self.transparent)
358
359    def SetVisible(self, visible):
360        self._visible = visible
361
362    def GetVisible(self):
363        return self._visible
364
365    def Move(self, newX, newY):
366        self.SetX(newX)
367        self.SetY(newY)
368
369        # test if a click on the shape landed on this button (used instead of an event handler)
370    def HitTest(self, x, y):
371        btnWidth = self.bitmap.GetWidth()
372        btnHeight = self.bitmap.GetHeight()
373       
374        if ((x >= self.GetX()) and (x <= self.GetX()+btnWidth) and
375            (y >= self.GetY()) and (y <= self.GetY()+btnHeight)):
376            self.handler()  # call the correct handler because we hit the button
377            return True
378        else:
379            return False    # it was a miss
380   
381
382
383
384   
385############################################################################
386#
387#  CLASS:       MyRectangleShape
388#
389#  DESCRIPTION: Extends the original ogl.RectangleShape class. The original
390#               RectangleShape class extends ogl.Shape but we overrode ogl.Shape
391#               so we had to make MyRectangleShape extend MyShape. However,
392#               since this is still a rectangle, we also have to extend from
393#               ogl.RectangleShape. The only method we override is the
394#               constructor since this is where we specify which Shape class
395#               are we based on. Hope this makes sense.
396#
397#  DATE:        October 12, 2004
398#
399############################################################################
400
401class MyRectangleShape(ogl.RectangleShape, MyShape):
402   
403    #overridden constructor so that it uses MyShape class and not ogl.Shape
404    def __init__(self, w, h, app, sageGate, canvas=None, title="untitled"):
405        MyShape.__init__(self, canvas)
406        self.app = app
407        self.initialWidth = w
408        self.initialHeight = h
409        self._width = w
410        self._height = h
411        self._cornerRadius = 0.0
412        self.SetDefaultRegionSize()
413        self.Z = 0
414        self.name = ""
415        self.title = title
416        self.borderColor = wx.Colour(255,255,255)
417        self.titleColor = wx.Colour(255,255,255)
418        self.sageGate = sageGate
419        self.displayCanvas = canvas
420        self.oldWidth = 0
421        self.oldHeight = 0
422        self.oldX = 0
423        self.oldY = 0
424        self.maximized = False
425        self.minimized = False
426        self.minimizedPosition = -1
427
428        self.oldLeft = 0
429        self.oldRight = 0
430        self.oldTop = 0
431        self.oldBottom = 0
432
433        # control buttons in the top right corner
434        self.buttons = []
435        self.buttonsVisible = True
436        self.buttons.append( ShapeButton(self, self.Close, "images/close_shape.gif", self._xpos, self._ypos))
437        self.buttons.append( ShapeButton(self, self.Maximize, "images/maximize_shape.gif", self._xpos, self._ypos))
438        self.buttons.append( ShapeButton(self, self.Rotate, "images/rotate_shape.gif", self._xpos, self._ypos, rotate=True))
439
440        self.SetOrientation(self.app.getOrientation())
441
442
443        # go through all the buttons and check if we clicked on any of the shape's buttons
444        # if we did, return True, otherwise False
445    def ButtonsClicked(self, x,y):
446        if self.buttons[0].GetVisible():  #if buttons are visible at all, check for clicks
447            for button in self.buttons:
448                if button.HitTest(x,y):  # HitTest will also call the right event handler if the button was indeed hit
449                    return True
450        return False
451   
452
453    def Close(self):
454        self.sageGate.shutdownApp(self.GetId())
455
456
457    def Rotate(self):
458        self.sageGate.rotateWindow(self.GetId(), 90)
459
460
461    def Maximize(self):
462        if not self.GetMaximized():
463            dispUIBounds = self.displayCanvas.GetUIDisplayBounds(self.app.getDisplayId())
464            width, height = self.GetBoundingBoxMin()
465
466            # old values are already preserved if the app was minimized
467            if not self.GetMinimized(): 
468                self.PreserveOldParams()
469
470            # now set the new parameters (maximized)
471            # place the maximized shape in the center of the screen
472            self.SetX(round(dispUIBounds.left + dispUIBounds.getWidth() / 2))
473            self.SetY(round(dispUIBounds.top + dispUIBounds.getHeight() / 2))
474
475            # figure out by how much to multiply the size of the shape in order
476            # to fill the tiled display and not go over all while preserving the aspect ratio (if needed)
477            if self.displayCanvas.GetMaintainAspectRatio():
478                widthRatio = dispUIBounds.getWidth() / float(width)
479                heightRatio = dispUIBounds.getHeight() / float(height)
480                if widthRatio > heightRatio:
481                    maximizeRatio = heightRatio
482                else:
483                    maximizeRatio = widthRatio
484                self.SetSize( round(width*maximizeRatio), round(height*maximizeRatio))
485            else:  #do not maintain aspect ratio
486                self.SetSize( dispUIBounds.getWidth(), dispUIBounds.getHeight() )
487
488            self.SetMaximized(True)
489           
490        else:   #restore
491            #pull out the old parameters and resize/move the shape
492            self.RestoreOldParams()
493            self.SetMaximized(False)
494
495        self.SetMinimized(False) # an app can't be maximized and minimized at the same time
496        self.displayCanvas.FreeMinimizedPosition(self.minimizedPosition, self.app)
497        self.UpdateSAGEParams()
498
499       
500           
501    def Minimize(self):
502        if self.GetMinimized(): #restore
503            #pull out the old parameters and resize/move the shape
504            self.RestoreOldParams()
505            self.SetMinimized(False)
506            self.displayCanvas.FreeMinimizedPosition(self.minimizedPosition, self.app)
507        else:  #minimize
508            dispUIBounds = self.displayCanvas.GetUIDisplayBounds(self.app.getDisplayId())
509            minimizedWidth = dispUIBounds.getWidth()/10  #height of the minimized apps
510           
511            width, height = self.GetBoundingBoxMin()
512            self.SetMinimized(True)
513            if not self.GetMaximized():
514                self.PreserveOldParams()
515       
516            aspectRatio = float( height / float(width) )
517            newHeight = aspectRatio * minimizedWidth
518            self.SetSize( minimizedWidth, newHeight )
519            self.minimizedPosition = self.displayCanvas.GetNextMinimizedPosition(self.app)
520            self.SetX( dispUIBounds.left + minimizedWidth/2 + minimizedWidth*self.minimizedPosition)
521            self.SetY( dispUIBounds.bottom - newHeight/2 )
522
523        self.SetMaximized(False)  # an app can't be maximized and minimized at the same time
524        self.UpdateSAGEParams()
525
526                   
527    def HideButtons(self):
528        for button in self.buttons:
529            button.SetVisible(False)
530        self.buttonsVisible = False
531
532    def ShowButtons(self):
533        for button in self.buttons:
534            button.SetVisible(True)
535        self.buttonsVisible = True
536
537                       
538        # overridden so that we can display buttons on the shape (min, max, close...)
539    def OnDrawContents(self, dc):
540        self.SetFont(StandardFont())
541        btnWidth = self.buttons[0].GetWidth()
542        if btnWidth+20 < self.GetWidth():
543            self.ShowButtons()
544        else:
545            self.HideButtons()
546
547        # dont draw anything if buttons aren't visible (there's not enough space for them)
548        if not self.buttonsVisible:
549            return
550       
551        i = 0
552        for i in range(len(self.buttons)):
553            self.buttons[i].Move( self.GetRight() - btnWidth*(i+1)-1, self.GetTop()+1 )
554            self.buttons[i].OnPaint(dc)
555            i = i+1
556        ogl.Shape.OnDrawContents(self, dc)
557
558
559
560        # convert all the boundries and send a message to SAGE about the new size and/or position
561        # of the app
562    def UpdateSAGEParams(self):
563        left = self.displayCanvas.ToSAGECoordsX( self.GetLeft(), self.app.getDisplayId() )
564        right = self.displayCanvas.ToSAGECoordsX( self.GetRight(), self.app.getDisplayId() )
565        top = self.displayCanvas.ToSAGECoordsY( self.GetTop(), self.app.getDisplayId() )
566        bottom = self.displayCanvas.ToSAGECoordsY( self.GetBottom(), self.app.getDisplayId() )
567        self.sageGate.resizeWindow( self.GetId(), left, right, bottom, top )
568       
569
570#----------------------------------------------------------------------
571
572
573    # these methods have been overridden in order to make the center of the
574    # shape appear to be in the top left corner and not in the center
575    # NOTE!!!!
576    # if you are going to use these methods at the same time when you are changing
577    # the size of the shape, make sure you CHANGE THE SIZE OF THE SHAPE FIRST and
578    # then use these functions since they rely on the size
579    def GetMyX(self):
580        width, height = self.GetBoundingBoxMin()
581        return ogl.Shape.GetX(self) - width/2
582     
583    def GetMyY(self):
584        width, height = self.GetBoundingBoxMin()
585        return ogl.Shape.GetY(self) - height/2
586
587    def SetMyY(self, newY):
588        width, height = self.GetBoundingBoxMin()
589        self.SetY( newY + height/2 )
590
591    def SetMyX(self, newX):
592        width, height = self.GetBoundingBoxMin()
593        self.SetX( newX + width/2 )
594
595#----------------------------------------------------------------------
596
597
598    # for returning the boundries of the shape
599    def GetLeft(self):
600        return self.GetMyX()
601
602    def GetRight(self):
603        width, height = self.GetBoundingBoxMin()
604        return self.GetMyX() + width
605
606    def GetTop(self):
607        return self.GetMyY()
608
609    def GetBottom(self):
610        width, height = self.GetBoundingBoxMin()
611        return self.GetMyY() + height
612
613    def GetWidth(self):
614        return self.GetBoundingBoxMin()[0]
615
616    def GetHeight(self):
617        return self.GetBoundingBoxMin()[1]
618   
619
620    #----------------------------------------------------------------------
621
622
623    def SetOrientation(self, degrees):
624        for b in self.buttons:
625            if b.rotate: b.SetOrientation(degrees)
626   
627
628    #for maximizing/minimizing
629    def SetOldWidth(self, w):
630        self.oldWidth = w
631
632    def SetOldHeight(self, h):
633        self.oldHeight = h
634
635    def GetOldWidth(self):
636        return self.oldWidth
637
638    def GetOldHeight(self):
639        return self.oldHeight
640
641
642    def SetOldX(self, x):
643        self.oldX = x
644
645    def SetOldY(self, y):
646        self.oldY = y
647
648    def GetOldX(self):
649        return self.oldX
650
651    def GetOldY(self):
652        return self.oldY
653
654
655    def PreserveOldParams(self):
656        width, height = self.GetBoundingBoxMin()
657        self.SetOldX(self.GetLeft() + width/2)
658        self.SetOldY(self.GetTop() + height/2)
659        self.SetOldWidth(width)
660        self.SetOldHeight(height)
661
662    def RestoreOldParams(self):
663        self.SetSize(self.GetOldWidth(), self.GetOldHeight())
664        self.SetX(self.GetOldX())
665        self.SetY(self.GetOldY())
666
667
668    def GetMaximized(self):
669        return self.maximized
670
671    def SetMaximized(self, max):
672        self.maximized = max
673
674    def GetMinimized(self):
675        return self.minimized
676
677    def SetMinimized(self, min):
678        self.minimized = min
679     
680
681#----------------------------------------------------------------------
682
683    # z-ordering
684    def SetZ(self, newZ):
685        self.Z = newZ
686
687    def GetZ(self):
688        return self.Z
689
690
691    # sets the title of the app
692    def SetTitle(self, title):
693        self.title = title
694        self.ClearText()
695        self.AddText(self.name + " " + str(self.GetId()))
696        self.AddText(title)
697
698    def GetTitle(self):
699        return self.title
700   
701
702    # sets the name of the app
703    def SetName(self, name):
704        self.name = name
705        #self.ClearText()
706        #self.AddText(name)
707
708    def GetName(self):
709        return self.name
710
711
712    # highlights the app when a mouse goes over the app instance button
713    def Highlight(self, doHightlight):
714        if doHightlight:
715            self.SetBrush(wx.Brush(shapeHighlightColor))#wx.MEDIUM_GREY_BRUSH)
716        else:
717            if not self.Selected():
718                self.SetBrush(wx.Brush(shapeColor))
719            else:
720                self.SetBrush(wx.Brush(shapeSelectedColor))#wx.GREY_BRUSH)
721        self.GetCanvas().Redraw()
722
723
724    # applies the new size and position to the shape (after screen resize)
725    def Recalculate(self, left, right, top, bottom):
726        self.SetSize( (right - left), (bottom - top) )
727        self.SetMyX(left)
728        self.SetMyY(top)
729
730        self.ResetControlPoints()
731
732    # changes the border of the shape if it's selected
733    def Select(self, doSelect, dc=None):
734        if doSelect:
735            self.SetTextColour(shapeSelectedTextColor)
736            self.SetPen(wx.Pen(shapeSelectedBorderColor, 2))#.SetWidth(2)
737            self.SetBrush( wx.Brush(shapeSelectedColor) ) #wx.GREY_BRUSH )
738        else:
739            self.SetTextColour(shapeTextColor)
740            self.SetPen(wx.Pen(shapeBorderColor,1))#wx.Colour(0,0,0), 1))#self.SetPen().SetWidth(1)
741            self.SetBrush( wx.Brush(shapeColor) ) #wx.GREY_BRUSH )
742        ogl.RectangleShape.Select(self, doSelect, dc)
743
744           
745#----------------------------------------------------------------------
746
747
748    ### this function figures out the right Z order of the windows and sends it to SAGE
749    def BringToFront(self):
750        if self.GetCanvas().GetTopShape() == self.GetId():
751            return  #the shape is already on top
752       
753        # now send a message to SAGE
754        self.sageGate.bringToFront(self.GetId())
755               
756
757#----------------------------------------------------------------------
758
759
760    # if we try to resize the shape beyond the edges of the display, this will
761    # size the shape down so that it fits inside the display
762    def SizeDown(self, pt):
763     
764        displayCanvas = self.GetCanvas()
765        myWidth, myHeight = self.GetBoundingBoxMin()  #get the size without the shadow
766        newWidth = myWidth
767        newHeight = myHeight
768        ratio = myWidth/myHeight  #remember the aspect ratio
769        minSize = 30    # the min size of the window on the screen
770
771           
772        # figure out if we resized the shape too much so that it's out of bounds
773        # of the tiled display
774        if self.GetBottom() > displayCanvas.GetBottom():
775            newHeight = displayCanvas.GetBottom() - self.GetTop()
776            newWidth = ratio * newHeight
777            if pt.location == "SE":
778                self.SetX(self.GetLeft() + newWidth/2)
779            else:
780                self.SetX(self.GetRight() - newWidth/2)
781            self.SetY(self.GetTop() + newHeight/2)
782            self.SetSize(newWidth, newHeight)
783        if self.GetTop() < displayCanvas.GetTop():
784            width, height = self.GetBoundingBoxMin()
785            newHeight = self.GetBottom() - displayCanvas.GetTop()
786            newWidth = ratio * newHeight
787            if pt.location == "NE":
788                self.SetX(self.GetLeft() + newWidth/2)
789            else:
790                self.SetX(self.GetRight() - newWidth/2)
791            self.SetY(self.GetBottom() - newHeight/2)
792            self.SetSize(newWidth, newHeight)
793        if self.GetLeft() < displayCanvas.GetLeft():
794            width, height = self.GetBoundingBoxMin()
795            newWidth = self.GetRight() - displayCanvas.GetLeft()
796            newHeight = newWidth / ratio
797            self.SetX(self.GetRight() - newWidth/2)
798            if pt.location == "SW":
799                self.SetY(self.GetTop() + newHeight/2)
800            else:
801                self.SetY(self.GetBottom() - newHeight/2)
802            self.SetSize(newWidth, newHeight)
803        if self.GetRight() > displayCanvas.GetRight():
804            width, height = self.GetBoundingBoxMin()
805            newWidth = displayCanvas.GetRight() - self.GetLeft()
806            newHeight = newWidth / ratio
807            self.SetX(self.GetLeft() + newWidth/2)
808            if pt.location == "SE":
809                self.SetY(self.GetTop() + newHeight/2)
810            else:
811                self.SetY(self.GetBottom() - newHeight/2)
812            self.SetSize(newWidth, newHeight)
813
814
815        # dont allow the windows to be smaller than "minSize" pixels
816        if newWidth < minSize or newHeight < minSize:
817            widthRatio = minSize / newWidth
818            heightRatio = minSize / newHeight
819            if widthRatio > heightRatio:
820                resizeRatio = widthRatio
821            else:
822                resizeRatio = heightRatio
823            self.SetSize( round(newWidth * resizeRatio), round(newHeight * resizeRatio) )
824
825
826        # set the final size of the app window
827        #self.SetSize( int(newWidth), int(newHeight) )
828        self.SetX( round(self.GetX()) )
829        self.SetY( round(self.GetY()) )
830
831
832        # since we manually changed the parameters of the shape,
833        # we manually have to redraw the screen
834        self.ResetControlPoints()
835        displayCanvas.Redraw()
836
837
838#----------------------------------------------------------------------
839
840
841    # if we tried to move the app window beyond the edges of the tiled display
842    # this would snap it to the edge
843
844    def SnapToEdges(self, oldx, oldy):
845        width, height = self.GetBoundingBoxMin()
846        displayCanvas = self.GetCanvas()
847       
848        # RJ
849        # october 15, 2004
850        # modified Gideon's code to "snap" windows to the edges if they go over
851        if self.GetTop() < displayCanvas.GetTop():
852            self.SetMyY(displayCanvas.GetTop())       
853            self.SetMyX(self.GetMyX())
854        if self.GetLeft() < displayCanvas.GetLeft():
855            self.SetMyX(displayCanvas.GetLeft())       
856            self.SetMyY(self.GetMyY())
857        if self.GetRight() > displayCanvas.GetRight():
858            self.SetMyX(displayCanvas.GetRight() - width)   
859            self.SetMyY(self.GetMyY())
860        if self.GetBottom() > displayCanvas.GetBottom():
861            self.SetMyY(displayCanvas.GetBottom() - height)       
862            self.SetMyX(self.GetMyX())
863
864
865        # redraw the screen
866        self.ResetControlPoints()
867        displayCanvas.Redraw()
868     
869
870
871
872
873############################################################################
874#
875#  CLASS:       MyEvtHandler
876#
877#  DESCRIPTION: Extends the original ogl.ShapeEvtHandler class.
878#               This is the main event handler for all the actions performed
879#               on the shapes themselves. It handles resizing, moving, click
880#               and double click. It also sends corresponding messages to SAGE
881#               about the actions performed.
882#
883#  DATE:        October 2004
884#
885############################################################################
886
887class MyEvtHandler(ogl.ShapeEvtHandler):
888
889    def __init__(self, log, statBarFrame, sageGate, displayCanvas):
890        ogl.ShapeEvtHandler.__init__(self)
891        self.log = log
892        self.statbarFrame = statBarFrame
893        self.sageGate = sageGate #we need access to the SAGE messenger
894        self.displayCanvas = displayCanvas
895        self.usersData = getUsersData()
896       
897#----------------------------------------------------------------------
898
899    def UpdateStatusBar(self, shape):
900        x, y = shape.GetX(), shape.GetY()
901        width, height = shape.GetBoundingBoxMax()
902        myID = shape.GetId() # always 0
903        #self.statbarFrame.SetStatusText("ID: %d Pos: (%d, %d)  Size: (%d, %d)" %
904        #                                (myID, x, y, width, height))
905
906       
907#----------------------------------------------------------------------
908
909    def OnLeftClick(self, x, y, keys=0, attachment=0):
910        shape = self.GetShape()
911        canvas = shape.GetCanvas()
912        dc = wx.ClientDC(canvas)
913        canvas.PrepareDC(dc)
914
915
916        # here we check if the user actually clicked on any of the shape buttons (min, max, close...)
917        # and if so, don't continue with this event handler
918        if shape.ButtonsClicked(x,y):
919            return
920               
921        # we clicked on the shape so bring it to front (above all other windows) and send a message to SAGE
922        shape.BringToFront()
923       
924        if shape.Selected():
925            return
926        else:
927            redraw = False
928            shapeList = canvas.GetDiagram().GetShapeList()
929            toUnselect = []
930
931            for s in shapeList:
932                if s.Selected():
933                    # If we unselect it now then some of the objects in
934                    # shapeList will become invalid (the control points are
935                    # shapes too!) and bad things will happen...
936                    toUnselect.append(s)
937
938            shape.Select(True, dc)
939
940            if toUnselect:
941                for s in toUnselect:
942                    s.Select(False, dc)
943                    #s.GetPen().SetWidth(1)
944                #canvas.Redraw(dc)
945
946        del dc
947                   
948        self.UpdateStatusBar(shape)
949
950        # bring up the performance data
951        canvas.GetParent().GetAppInfoPanel().GetInfoPanel().ShowData(shape.GetName(), shape.GetId())
952       
953        # since we manually changed the parameters of the shape,
954        # we manually have to redraw the screen
955        #canvas.Redraw(dc)#Refresh()
956       
957       
958       
959
960#----------------------------------------------------------------------
961
962    def OnEndDragLeft(self, x, y, keys=0, attachment=0):
963        shape = self.GetShape()
964
965        # test if the user dropped the application window onto another tab
966        # if so, just send a message to SAGE to stream that app to that new display
967        if shape.GetCanvas().GetParent().GetNotebook().DropTest(shape.GetId(), (x,y)):
968            if shape.GetCanvas().HasCapture():
969                shape.GetCanvas().ReleaseMouse()
970            return
971
972        #if not shape.Selected():
973        self.OnLeftClick(x, y, keys, attachment)
974
975
976        oldx, oldy = shape.GetX(), shape.GetY()
977        ogl.ShapeEvtHandler.OnEndDragLeft(self, x, y, keys, attachment)
978
979
980        # check for move (note: NOT resize) out of bounds
981        # i.e. shape partially outside of "tiled" region
982        #shape.SnapToEdges(oldx, oldy)
983
984
985        # figure out the distance moved and send a message to SAGE
986        self.sageGate.moveWindow(shape.GetId(),
987                                 self.displayCanvas.ToSAGEDistX( oldx, shape.GetX(), shape.app.getDisplayId() ),
988                                 self.displayCanvas.ToSAGEDistY( oldy, shape.GetY(), shape.app.getDisplayId() ))
989
990        self.UpdateStatusBar(shape)
991       
992
993#----------------------------------------------------------------------
994
995
996    def OnSizingEndDragLeft(self, pt, x, y, keys, attch):
997      shape = self.GetShape()
998     
999      # call the overridden method for handling the sizing
1000      shape.OnSizingEndDragLeft(pt, x, y, keys, attch)
1001
1002      # figure out if we resized the shape beyond the edge of our UI tiled display
1003      # if so, resize it down to the maximum allowable size
1004      #shape.SizeDown(pt)
1005
1006      # convert the new coords and send a message to SAGE
1007      left = self.displayCanvas.ToSAGECoordsX( shape.GetLeft(), shape.app.getDisplayId() )
1008      right = self.displayCanvas.ToSAGECoordsX( shape.GetRight(), shape.app.getDisplayId() )
1009      top = self.displayCanvas.ToSAGECoordsY( shape.GetTop(), shape.app.getDisplayId() )
1010      bottom = self.displayCanvas.ToSAGECoordsY( shape.GetBottom(), shape.app.getDisplayId() )
1011      self.sageGate.resizeWindow( shape.GetId(), left, right, bottom, top )
1012     
1013      # if we were maximized and we tried to resize a shape, reset the maximized state
1014      shape.SetMaximized(False)
1015      self.UpdateStatusBar(shape)
1016
1017#----------------------------------------------------------------------
1018
1019    # (AKS 2005-01-28)
1020    # Use this for displaying performance graphs
1021    def OnRightClick(self, x, y, keys=0, attachment=0):
1022        shape = self.GetShape()
1023        menu = wx.Menu()
1024   
1025        machineMenu = wx.Menu()       
1026        machines = self.usersData.GetMachinesStatus()
1027        c = 30
1028        for mId, m in machines.iteritems():
1029            if m.IsAlive(): txt=m.GetName()+"  ( + )"
1030            else: txt=m.GetName()+"  ( - )"
1031            mi = machineMenu.Append(c, txt)
1032            mi.SetHelp(mId)   #set the help to the machineId... we will use this later to get the right SAGEMachine
1033            machineMenu.Bind(wx.EVT_MENU, self.OnStreamMenu, id=c)
1034            c+=1
1035           
1036        menu.Append(1, "Edit Properties")       
1037        menu.AppendMenu(3, "Stream To:", machineMenu)
1038        menu.Bind(wx.EVT_MENU, self.OnContextMenu)
1039        self.displayCanvas.PopupMenu(menu)
1040        #self.rightClickFunctionCallback( shape.GetName(), shape.GetId() )
1041
1042
1043    def OnStreamMenu(self, event):
1044        mi = event.GetEventObject().FindItemById(event.GetId())
1045        machine = self.usersData.GetMachine(mi.GetHelp())  # help string actually contains the machineId
1046        self.sageGate.streamApp(self.GetShape().GetId(), machine.GetIP(), machine.GetPort())
1047       
1048
1049    def OnContextMenu(self, event):
1050        shape = self.GetShape()
1051        eventId = event.GetId()
1052       
1053        if eventId == 1:  #this must be the "Edit Properties"
1054            self._ShowPropertiesDialog(shape)
1055        elif eventId >= 30 and eventId < 40:
1056            mi = event.GetEventObject().FindItemById(event.GetId())
1057            machine = self.usersData.GetMachine(mi.GetHelp())  # help string actually contains the machineId
1058            self.sageGate.streamApp(self.GetShape().GetId(), machine.GetIP(), machine.GetPort())
1059
1060           
1061    def _ShowPropertiesDialog(self, shape):
1062        dlg = wx.Dialog(None, -1, "Edit Properties", style=wx.CLOSE_BOX | wx.RESIZE_BORDER)
1063        okBtn = wx.Button(dlg, wx.ID_OK, "OK")
1064        cancelBtn = wx.Button(dlg, wx.ID_CANCEL, "Cancel")
1065       
1066        line1 = "Enter a new title for "+str(shape.GetName()+" "+str(shape.GetId()))
1067        text1 = wx.StaticText(dlg, -1, line1, style = wx.ALIGN_LEFT)
1068        titleEntry = wx.TextCtrl(dlg, -1, shape.GetTitle())
1069        titleColorBtn = wx.Button(dlg, -1, "Title Color")
1070        self.titleColorText = wx.StaticText(dlg, -1, "unchanged", style = wx.ALIGN_CENTER)
1071        titleColorBtn.Bind(wx.EVT_BUTTON, self._ChangeTitleColor) #(shape.titleColor, "title", titleColorText))
1072        borderColorBtn = wx.Button(dlg, -1, "Border Color")
1073        self.borderColorText = wx.StaticText(dlg, -1, "unchanged", style = wx.ALIGN_CENTER)
1074        borderColorBtn.Bind(wx.EVT_BUTTON, self._ChangeBorderColor) #(shape.borderColor, "border", borderColorText))
1075        self.sliderText = "Maximum frame rate: "
1076        self.frameRateText = wx.StaticText(dlg, -1, self.sliderText + "UNCHANGED", style = wx.ALIGN_CENTER)
1077        self.frameRateSlider = wx.Slider(dlg, -1, 0, 0, 60, style = wx.SL_LABELS | wx.SL_HORIZONTAL)
1078        self.frameRateSlider.Bind(wx.EVT_SCROLL, self._ProcessSliderEvent)
1079
1080        hSizer1 = wx.BoxSizer(wx.HORIZONTAL)
1081        hSizer1.Add(titleColorBtn, 0, wx.ALIGN_LEFT | wx.LEFT,  border=0)
1082        hSizer1.Add(self.titleColorText, 0, wx.ALIGN_LEFT | wx.LEFT, border=20)
1083        hSizer2 = wx.BoxSizer(wx.HORIZONTAL)
1084        hSizer2.Add(borderColorBtn, 0, wx.ALIGN_LEFT | wx.LEFT, border=0)
1085        hSizer2.Add(self.borderColorText, 0, wx.ALIGN_LEFT | wx.LEFT, border=10)
1086        hSizer3 = wx.BoxSizer(wx.HORIZONTAL)
1087        hSizer3.Add(cancelBtn, 0, wx.ALIGN_CENTER | wx.RIGHT | wx.LEFT, border=10)
1088        hSizer3.Add(okBtn, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, border=10)
1089       
1090        sizer = wx.BoxSizer(wx.VERTICAL)
1091        sizer.AddSpacer((20,20))
1092        sizer.Add(text1, 0, wx.ALIGN_LEFT | wx.LEFT, border=15)
1093        sizer.Add(titleEntry, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, border=15)
1094        sizer.AddSpacer((20,20))
1095        sizer.Add(hSizer1, 0, wx.ALIGN_LEFT | wx.LEFT, border=15)
1096        sizer.AddSpacer((10,10))
1097        sizer.Add(hSizer2, 0, wx.ALIGN_LEFT | wx.LEFT, border=15)
1098        sizer.AddSpacer((25,25))
1099        sizer.Add(self.frameRateText, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT, border=15)
1100        sizer.Add(self.frameRateSlider, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, border=15)
1101        sizer.AddSpacer((30,30))
1102        sizer.Add(hSizer3, 0, wx.ALIGN_CENTER, border=15)
1103        sizer.AddSpacer((10,10))
1104       
1105
1106        dlg.SetSizer(sizer)
1107        dlg.Fit()
1108        if dlg.ShowModal() == wx.ID_OK:
1109            shape.SetTitle(titleEntry.GetValue())
1110            retValTitleColor = (-1,-1,-1)
1111            retValBorderColor = (-1,-1,-1)
1112            if not self.titleColorText.GetLabel() == "unchanged":
1113                retValTitleColor = (shape.titleColor.Red(),shape.titleColor.Green(),shape.titleColor.Blue())
1114            if not self.borderColorText.GetLabel() == "unchanged":
1115                retValBorderColor = (shape.borderColor.Red(),shape.borderColor.Green(),shape.borderColor.Blue())
1116       
1117            self.sageGate.changeAppProperties(shape.GetId(), shape.GetTitle(), retValTitleColor, retValBorderColor)
1118
1119            if not self.frameRateSlider.GetValue() == 0:
1120                self.sageGate.changeAppFrameRate(shape.GetId(), self.frameRateSlider.GetValue())
1121            self.displayCanvas.Redraw()
1122
1123        dlg.Destroy()
1124       
1125
1126    def _ChangeTitleColor(self, event):
1127        shape = self.GetShape()
1128        newColor = ShowColorDialog(shape.titleColor)
1129        if not newColor == (-1,-1,-1):
1130            shape.titleColor = newColor
1131            self.titleColorText.SetLabel( str(newColor) )
1132
1133       
1134    def _ChangeBorderColor(self, event):
1135        shape = self.GetShape()
1136        newColor = ShowColorDialog(shape.borderColor)
1137        if not newColor == (-1,-1,-1):
1138            shape.borderColor = newColor
1139            self.borderColorText.SetLabel( str(newColor) )
1140
1141    def _ProcessSliderEvent(self, event):
1142        if self.frameRateSlider.GetValue() == 0:
1143            self.frameRateText.SetLabel(self.sliderText + str("UNCHANGED"))
1144        else:
1145            if not self.frameRateText.GetLabel() == self.sliderText:
1146                self.frameRateText.SetLabel(self.sliderText)
1147
1148       
1149#----------------------------------------------------------------------
1150
1151
1152    def OnMovePost(self, dc, x, y, oldX, oldY, display):
1153        ogl.ShapeEvtHandler.OnMovePost(self, dc, x, y, oldX, oldY, display)
1154        self.UpdateStatusBar(self.GetShape())
1155       
1156
1157#----------------------------------------------------------------------
1158
1159   # (AKS 2005-01-28) Added so that GraphManager gets called when someone
1160   # right clicks on the SAGE window
1161    #def RegisterRightClickFunction( self, function ):
1162    #    self.rightClickFunctionCallback = function
1163
1164
1165
Note: See TracBrowser for help on using the repository browser.