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

Revision 4, 68.6 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#
38############################################################################
39
40
41
42# my imports
43from SAGEShape import *
44from sageData import *
45from Mywx import *
46import Graph
47from globals import *
48#from pointers import Pointers
49import fileViewer
50import preferences as prefs
51
52# python imports
53import time
54from threading import Timer
55from time import sleep
56from math import floor,ceil
57import os.path
58import ConfigParser
59import wx.lib.buttons as btns
60
61
62DEBUG = False
63if DEBUG: import pdb
64
65
66
67
68class OneDisplay:
69    """ contains the shapes for on """
70    def __init__(self, l, r, t, b, sageDisplay, canvas):
71        self.sageDisplay = sageDisplay
72        self.canvas = canvas
73        self.diagram = canvas.GetDiagram()
74        self.cols = self.sageDisplay.cols
75        self.rows = self.sageDisplay.rows
76        self.sageW = self.sageDisplay.sageW
77        self.sageH = self.sageDisplay.sageH
78        self.bounds = Bounds(l,r,t,b)   # these are the bounds of the display... we have to make these panels fit here
79       
80        # create all the panels (Shapes)
81        self.__CreatePanels()
82
83
84    def __CreatePanels(self):
85        self.__panels = [ [None]*self.cols for i in range(self.rows) ]
86       
87        panelW = self.bounds.getWidth() / self.cols
88        panelH = self.bounds.getHeight() / self.rows
89       
90        for xOffset in range(self.cols):
91            for yOffset in range(self.rows):
92                self.__panels[yOffset][xOffset] = self.__AddShape(
93                    ogl.RectangleShape(panelW, panelH),
94                    xOffset*panelW + (panelW/2) + self.bounds.left,
95                    yOffset*panelH + (panelH/2) + self.bounds.top,
96                    wx.Pen(wx.Color(51, 102, 102), 1), wx.Brush(tileColor), "")
97
98
99    # draw boxes in the background as the frames
100    # which should not be choosable or clickable
101    def __AddShape(self, shape, x, y, pen, brush, text):
102        shape.SetDraggable(False, False)
103        shape.SetCanvas(self.canvas)
104        shape.SetX(x)
105        shape.SetY(y)
106        if pen:    shape.SetPen(pen)
107        if brush:  shape.SetBrush(brush)
108        if text:
109            for line in text.split('\n'):
110                shape.AddText(line)
111        self.diagram.AddShape(shape)
112        shape.Show(True)
113       
114        return shape
115
116
117    def Resize(self, l, r, t, b):
118        # resize and reposition the panels here
119        self.bounds.setAll(l,r,t,b)
120
121        # resize and reposition the display tiles
122        panelW = self.bounds.getWidth() / self.cols
123        panelH = self.bounds.getHeight() / self.rows
124       
125        for xOffset in range(self.cols):
126            for yOffset in range(self.rows):
127                self.__panels[yOffset][xOffset].SetX(xOffset*panelW + (panelW/2) + self.bounds.left)
128                self.__panels[yOffset][xOffset].SetY(yOffset*panelH + (panelH/2) + self.bounds.top)
129                self.__panels[yOffset][xOffset].SetSize(panelW, panelH)
130                   
131   
132
133
134class Backdrop:
135    """ consists of OneDisplays """
136    def __init__(self, sageDisplayInfo, canvas):
137        self.displays = {}  # key=displayId, value=OneDisplay
138        self.bounds = Bounds() # what's the area we have available for drawing
139        self.sageDisplayInfo = sageDisplayInfo
140        self.canvas = canvas
141        self.dispByPos = {}   # key=placement, value=OneDisplay
142        self.widths = {}      # key=placement, value=width in UI coords
143        self.heights = {}     # key=placement, value=height in UI coords
144        self.positions = {}   # key=placement, value=tuple of (l,r,t,b)
145
146
147
148    def GetDisplayBounds(self, displayId):
149        return self.displays[displayId].bounds
150   
151
152    def SetupDisplays(self,l,r,t,b):
153        self.bounds.setAll(l,r,t,b)
154               
155        # first create all the displays
156        for sageDisplay in self.sageDisplayInfo.getAllDisplays():
157            newDisp = OneDisplay(l,r,t,b,sageDisplay,self.canvas)
158            self.displays[ sageDisplay.getId() ] = newDisp
159            self.dispByPos[ sageDisplay.placement ] = newDisp
160           
161        self.SetSize(self.bounds.left, self.bounds.right, self.bounds.top, self.bounds.bottom)
162       
163           
164    def SetSize(self, l, r, t, b):
165        self.bounds.setAll(l,r,t,b)
166       
167        self.__CalculateDisplayWidths()
168        self.__CalculateDisplayHeights()
169
170        # loop through each display and set the correct bounds for each
171        for pos, disp in self.dispByPos.iteritems():
172            if pos == LEFT_DISP:
173                l = self.bounds.left
174                r = l + self.widths[LEFT_DISP]
175                t = self.bounds.top + self.heights[TOP_DISP]
176                b = t + self.heights[LEFT_DISP]
177               
178            elif pos == MIDDLE_DISP:
179                l = self.bounds.left + self.widths[LEFT_DISP]
180                r = l + self.widths[MIDDLE_DISP]
181                t = self.bounds.top + self.heights[TOP_DISP]
182                b = t + self.heights[MIDDLE_DISP]
183                           
184            elif pos == RIGHT_DISP:
185                l = self.bounds.left + self.widths[LEFT_DISP] + self.widths[MIDDLE_DISP]
186                r = l + self.widths[RIGHT_DISP]
187                t = self.bounds.top + self.heights[TOP_DISP]
188                b = t + self.heights[RIGHT_DISP]
189               
190            elif pos == TOP_DISP:
191                l = self.bounds.left + self.widths[LEFT_DISP]
192                r = l + self.widths[TOP_DISP]
193                t = self.bounds.top
194                b = t + self.heights[TOP_DISP]
195
196            elif pos == BOTTOM_DISP:
197                l = self.bounds.left + self.widths[LEFT_DISP]
198                r = l + self.widths[BOTTOM_DISP]
199                t = self.bounds.top + self.heights[TOP_DISP] + self.heights[MIDDLE_DISP]
200                b = t + self.heights[BOTTOM_DISP]
201           
202            # finally apply the size
203            disp.Resize(l,r,t,b)
204           
205               
206                           
207
208    def __CalculateDisplayWidths(self):
209        self.widths = {MIDDLE_DISP:0, LEFT_DISP:0, RIGHT_DISP:0, BOTTOM_DISP:0, TOP_DISP:0}
210        totalW = self.sageDisplayInfo.getTotalWidth()
211       
212        # in UI coords
213        for pos, disp in self.dispByPos.iteritems():
214            self.widths[pos] = (disp.sageW / float(totalW)) * self.bounds.getWidth()
215           
216    def __CalculateDisplayHeights(self):
217        self.heights = {MIDDLE_DISP:0, LEFT_DISP:0, RIGHT_DISP:0, BOTTOM_DISP:0, TOP_DISP:0}
218        totalH = self.sageDisplayInfo.getTotalHeight()
219       
220        # in UI coords
221        for pos, disp in self.dispByPos.iteritems():
222            self.heights[pos] = (disp.sageH / float(totalH)) * self.bounds.getHeight()
223
224
225
226
227
228############################################################################
229#
230#  CLASS: DisplayCanvas
231
232#  DESCRIPTION: This class describes the canvas where the tiled display
233#               representation is drawn. It draws all the shapes, updates
234#               the visual state of the UI as a result of messages received
235#               from SAGE, it draws all the decorative graphics pertaining
236#               to it. It also contains all the coordinate conversion methods.
237#               All the drawing is done in the OnPaint() and Redraw() methods
238#               so that way when you call canvas.Refresh() or canvas.Redraw()
239#               it will use these overloaded methods. Another reason for
240#               overloading these methods is to force them to use double buffering
241#
242#  DATE:        October, 2004
243#
244############################################################################
245
246class DisplayCanvas(ogl.ShapeCanvas):
247    def __init__(self, log, parent, sageGate, sageData, gmGraphManager, title):
248        # the size passed in here is being changed later so it doesnt really matter
249        ogl.ShapeCanvas.__init__(self, parent, -1, (0,0), (710,300), wx.NO_BORDER | wx.NO_FULL_REPAINT_ON_RESIZE)
250
251
252        # some global parameters
253        displaySize = wx.DisplaySize()  #the resolution of the current screen... we will base our frame size on that
254        if displaySize[1] > 900:
255            self._maxCanvasWidth = 800
256            self._borderWidth = 25
257        else:
258            self._maxCanvasWidth = 650
259            self._borderWidth = 20
260        self.frame = parent.GetFrame()#wx.GetApp().GetTopWindow() #parent
261        self.parent = parent
262        self._maxCanvasHeight = 0
263        self._maxDisplayHeight = 0
264        self._maxDisplayWidth = 0
265        self._aspectRatio = 1   #the aspect ratio for the whole canvas... this is not for the apps
266        self.title = title
267        self.displayInfoArrived = False
268        self._minimizedShapes = {}  # key=displayId, value=hash of minimized shapes
269        self._shapesTiled = False
270       
271        # main data and communication objects
272        self.sageGate = sageGate
273        self.sageData = sageData
274        self.__gmGraphManager = gmGraphManager
275
276        # support for file viewing and drag-and-dropping inside the UI
277        self.dt = fileViewer.CanvasDropTarget(self)
278        self.SetDropTarget(self.dt)
279       
280        # register sageData callbacks
281        self.sageData.registerCallbackFunction(40000, self.OnSAGEStatusMsg)
282        self.sageData.registerCallbackFunction(40001, self.OnSAGEAppInfoMsg)
283        self.sageData.registerCallbackFunction(40002, self.OnSAGEPerfInfoMsg)
284        self.sageData.registerCallbackFunction(40003, self.OnSAGEAppShutdownMsg)
285        self.sageData.registerCallbackFunction(40004, self.OnSAGEDisplayInfoMsg)
286        self.sageData.registerCallbackFunction(40005, self.OnSAGEZChangeMsg)
287        self.sageData.registerCallbackFunction(40006, self.OnSAGEAppExecConfig)
288        self.sageData.registerCallbackFunction(40007, self.OnSAGEDisplaysConnection)
289
290        # set up the canvas
291        self.log = log
292        self.SetBackgroundColour('#006666')
293        self.diagram = ogl.Diagram()
294        self.SetDiagram(self.diagram)
295        self.diagram.SetCanvas(self)
296        self.shapes = {}  #a hash of all the apps that are CURRENTLY RUNNING (keyed by windowId)
297        self.availableApps = {}     #a hash of all the apps available for running
298                                        # in this form  key:value = windowId:SAGEAppInitial
299        # now tell SAGE that we exist so we can get the current SAGE status
300        self.sageGate.registerSage()
301       
302        # at this point we know we succeeded in connecting to SAGE so load all the images
303        self.LoadImages()   #creates a hash with the "Images" keyed by the name       
304        self.Bind(wx.EVT_ERASE_BACKGROUND, self.Chillout)
305        self.Bind(wx.EVT_MOTION, self.OnMouseEnter)
306        self.Bind(wx.EVT_CLOSE, self.OnClose)
307        self.cursorSet = False
308 
309
310
311#----------------------------------------------------------------------
312# DRAWING METHODS
313#----------------------------------------------------------------------   
314   
315    def OnPaint( self, event ):
316        if DEBUG: print "OnPaint --> DisplayCanvas"; sys.stdout.flush()
317        if not self.displayInfoArrived:
318            if "__WXMSW__" in wx.PlatformInfo:  #windows needs a PaintDC created even if we dont use it
319                dc = wx.PaintDC(self)
320            return
321        dc = wx.BufferedPaintDC( self, self.swapBuffer )
322
323    def Chillout(self, event):
324        """ For intercepting the EVT_ERASE_BACKGROUND. Do nothing here on purpose
325            in order to reduce the flicker during drawing"""
326        pass
327
328
329    def OnClose(self, evt):
330        pass
331
332
333#----------------------------------------------------------------------
334
335
336    # overridden so that we can specify the order in which the drawing occurs (because of the decorative images)
337    def Redraw( self, dc=None):
338        if DEBUG: print "Redraw --> DisplayCanvas"; sys.stdout.flush()
339        if not self.displayInfoArrived:
340            return
341        # create the right kind of DC for double buffering
342        if dc == None:
343            cdc = wx.ClientDC(self)
344            self.PrepareDC(cdc)
345            dc = wx.BufferedDC(cdc, self.swapBuffer)
346            dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
347            dc.Clear()
348
349        if self.GetDiagram().GetShapeList():
350            self.SetCursor(wx.HOURGLASS_CURSOR)
351
352        # first draw the underlying shapes (LCD panels)
353        for object in self.GetDiagram().GetShapeList():
354            if ((object.__class__.__name__ != MyRectangleShape.__name__ ) and
355               (object.__class__.__name__ != MyControlPoint.__name__ )):
356                   object.Draw(dc)
357
358        # then draw the decorative images
359        for name, image in self.images.iteritems():
360            dc.DrawBitmap( image.bitmap, image.GetX(), image.GetY(), image.IsTransparent() )
361
362        # draw the title (user @ machine)
363        dc.SetTextForeground(wx.WHITE)
364        textSize = dc.GetTextExtent(self.title)
365        dc.DrawText(self.title, self.GetCanvasWidth()/2 - textSize[0]/2, (self.GetBorderWidth() - textSize[1])/2)
366
367        # finally draw the app shapes on top of everything
368        for object in self.GetDiagram().GetShapeList():
369            if ((object.__class__.__name__ == MyRectangleShape.__name__ ) or
370               (object.__class__.__name__ == MyControlPoint.__name__ )):
371                   object.Draw(dc)
372   
373        self.SetCursor(wx.STANDARD_CURSOR)
374
375
376    #----------------------------------------------------------------------
377
378
379    def OnSize(self, event=None):
380        if DEBUG: print "OnSize --> DisplayCanvas"; sys.stdout.flush()       
381        if not self.displayInfoArrived:
382            return
383        # manually maintain the aspect ratio of the display canvas
384        parentSize = self.parent.GetClientSize()
385        self.SetCanvasWidth( parentSize.width )
386        self.SetCanvasHeight( round(self.GetCanvasWidth() / self.GetAspectRatio()) )
387        self.SetSize((self.GetCanvasWidth(), self.GetCanvasHeight()))
388       
389        # change the display width now
390        self.SetDisplayWidth( self.GetCanvasWidth() - 2 * self.GetBorderWidth() )
391        heightFactor = self.tiledDisplayWidth / float (self.GetDisplayWidth() )
392        self.SetDisplayHeight( self.GetCanvasHeight() - 1.5*self.GetBorderWidth()) #round (self.tiledDisplayHeight / heightFactor ) )
393
394        # update the size of the swap buffer
395        self.swapBuffer = wx.EmptyBitmap(self.GetCanvasWidth(), self.GetCanvasHeight())
396       
397        # reposition and resize all of the following
398        self.RecalculateImages() #decorative images
399        self.RecalculateTiles() 
400        self.RecalculateShapes()
401       
402        self.Redraw()
403        #event.Skip()
404
405           
406    #----------------------------------------------------------------------
407
408
409    def LoadImages(self):
410        # load all the decorative images
411        wx.InitAllImageHandlers()
412        self.images = {}
413        self.imageNames = [ "top_left", "top", "top_right", "left",
414                            "right", "bottom_left", "bottom", "bottom_right" ]
415        for i in range(1, 9):
416            self.images[ self.imageNames[i-1] ] = MyImage( "images/SAGEapp_0"+str(i)+".jpg" )       
417
418
419    def RecalculateImages(self):
420        # figure out all the sizes and positions for the decorative images and apply them
421        positions = [ (0, 0),                 (self.GetLeft(), 0),                 (self.GetRight(), 0),
422                      (0, self.GetTop()),                                          (self.GetRight(), self.GetTop()),
423                      (0, self.GetBottom()),  (self.GetLeft(), self.GetBottom()),  (self.GetRight(), self.GetBottom()) ]
424
425        sizes = [ (self.GetBorderWidth(), self.GetBorderWidth()),    (self.GetDisplayWidth(), self.GetBorderWidth()),    (self.GetBorderWidth(), self.GetBorderWidth()),
426                  (self.GetBorderWidth(), self.GetDisplayHeight()),                                                      (self.GetBorderWidth(), self.GetDisplayHeight()),
427                  (self.GetBorderWidth(), self.GetBorderWidth()/2),  (self.GetDisplayWidth(), self.GetBorderWidth()/2),  (self.GetBorderWidth(), self.GetBorderWidth()/2) ]
428
429        for i in range(0, 8):
430            image = self.images[ self.imageNames[i] ]
431            image.Rescale( sizes[i][0], sizes[i][1] )
432            image.SetX( positions[i][0] )
433            image.SetY( positions[i][1] )
434           
435
436    def RecalculateTiles(self):
437        l = self.GetBorderWidth()
438        r = self.GetCanvasWidth() - self.GetBorderWidth()
439        t = self.GetBorderWidth()
440        b = t + self.GetDisplayHeight()
441        self.backdrop.SetSize(l,r,t,b)
442       
443
444    def RecalculateShapes(self):
445        # resize and reposition all the shapes
446        # get the SAGE coordinates for the shape and convert them to UI coords again
447        for s in self.GetShapeList():
448            sageApp = self.sageData.getApp(s.GetId())
449            s.Recalculate(
450                self.ToUICoordsX(sageApp.getLeft(), sageApp.getDisplayId()),
451                self.ToUICoordsX(sageApp.getRight(), sageApp.getDisplayId()),
452                self.ToUICoordsY(sageApp.getTop(), sageApp.getDisplayId()),
453                self.ToUICoordsY(sageApp.getBottom(), sageApp.getDisplayId())
454                )
455
456
457
458
459#----------------------------------------------------------------------
460# TILING SHAPES
461#----------------------------------------------------------------------
462
463    def TileShapes(self):
464        if self._shapesTiled:  # restore the shape to their previous state
465            for shape in self.GetShapeList():
466                if not shape.GetMinimized():
467                    shape.RestoreOldParams()
468                    shape.UpdateSAGEParams()
469            self._shapesTiled = False
470            return
471
472
473
474        # figure out by how much we need to size down each shape so that they have a chance
475        # of fitting them inside the tiled display (if perfectly tiled... which is very unlikely to happen))
476        displayArea = self.GetDisplayHeight() * self.GetDisplayWidth()
477        totalShapeArea = 0
478        for shape in self.GetShapeList():
479            if not shape.GetMinimized():
480                totalShapeArea = totalShapeArea + shape.GetWidth() * shape.GetHeight()
481
482        resizeRatio = 1
483        if displayArea <= totalShapeArea*0.8:
484            resizeRatio = totalShapeArea*0.8 / displayArea
485
486        # set aside the shapes that are eligible for tiling (meaning no minimized shapes)
487        # and preserve their size and position for later restore
488        hashByArea = {}
489        for shape in self.GetShapeList():
490            if shape.GetMinimized():
491                continue       #skip minimized shapes
492            if not shape.GetMaximized():
493                shape.PreserveOldParams()  #preserve the current size and position
494            shape.SetSize( shape.GetWidth()/resizeRatio, shape.GetHeight()/resizeRatio )
495            shape.UpdateSAGEParams()
496            hashByArea[ (shape.GetWidth() * shape.GetHeight()) ] = shape  #key the shapes eligible for tiling by their area
497
498
499        # now the hardest part, reposition the shapes so that they fit nicely (or as nice as it gets)
500        # actually, try the other way around... first reposition the shapes and then resize them
501        # so that they all fit inside the display
502       
503
504        self._shapesTiled = True
505
506       
507#----------------------------------------------------------------------
508# DEALING WITH THE SHAPES
509#----------------------------------------------------------------------
510
511    def GetShape(self, windowId):
512        if self.shapes.has_key(windowId):
513            return self.shapes[windowId]
514        else:
515            return None
516
517    def GetShapeList(self):  # returns a copy of the shape list
518        tempList = []
519        for s in self.diagram.GetShapeList():
520            if ( s.__class__.__name__ == MyRectangleShape.__name__ ):
521                tempList.append(s)
522        return tempList
523
524    def GetTopShape(self):  # returns the windowId of the shape that's on top
525        minZ = sys.maxint
526        windowId = sys.maxint
527        for s in self.shapes.values():
528            if s.GetZ() < minZ or (s.GetZ() == minZ and s.GetId() < windowId):
529                minZ = s.GetZ()
530                windowId = s.GetId()
531        return windowId
532   
533##     # draw boxes in the background as the frames
534##     # which should not be choosable or clickable
535##     def MyAddBackShape(self, shape, x, y, pen, brush, text):
536##         # Composites have to be moved for all children to get in place
537##         shape.SetDraggable(False, False)
538##         shape.SetCanvas(self)
539##         shape.SetX(x)
540##         shape.SetY(y)
541##         if pen:    shape.SetPen(pen)
542##         if brush:  shape.SetBrush(brush)
543##         if text:
544##             for line in text.split('\n'):
545##                 shape.AddText(line)
546##         self.diagram.AddShape(shape)
547##         shape.Show(True)
548
549##         return shape
550
551
552    #----------------------------------------------------------------------
553
554
555    # creates the shape that corresponds to the app and adds it to the diagram.
556    def MyAddShape(self, app):
557        name = app.getName()
558        left = self.ToUICoordsX(app.getLeft(), app.getDisplayId())
559        right = self.ToUICoordsX(app.getRight(), app.getDisplayId())
560        bottom = self.ToUICoordsY(app.getBottom(), app.getDisplayId())
561        top = self.ToUICoordsY(app.getTop(), app.getDisplayId())
562        width = right - left
563        height = bottom - top
564        id = app.getId()
565        z = app.getZvalue()
566        title = app.getTitle()
567       
568        shape = MyRectangleShape(width, height, app, self.sageGate, self, title)
569        text = name + " " + str(id)
570                           
571        shape.SetDraggable(True, True)
572        shape.SetCanvas(self)
573        shape.SetMyX(left)
574        shape.SetMyY(top)
575        shape.SetId(id) #this is windowId
576        shape.SetZ(z)
577        shape.SetPen(wx.Pen(shapeBorderColor))#wx.BLACK_PEN)
578        shape.SetBrush( wx.Brush(shapeColor) ) #wx.GREY_BRUSH )
579        shape.SetTextColour("grey")
580        shape.SetFont(wx.Font(14, wx.MODERN, wx.NORMAL, wx.BOLD))
581           
582        if text:
583            shape.SetName(name)
584            for line in text.split('\n'):
585                shape.AddText(line)
586            shape.AddText(shape.GetTitle())
587        shape.SetShadowMode(ogl.SHADOW_NONE)#RIGHT) # uncomment for shadow
588        shape.SetMaintainAspectRatio(prefs.visual.GetKeepAspectRatio())
589        shape.SetCentreResize(False)   #resize the shape from one corner only
590        self.diagram.AddShape(shape)  #add the shape to the end (draws on the top)
591        shape.Show(True)
592
593        evthandler = MyEvtHandler(self.log, self.frame, self.sageGate, self)
594        evthandler.SetShape(shape)
595        evthandler.SetPreviousHandler(shape.GetEventHandler())
596        shape.SetEventHandler(evthandler)
597
598        self.shapes[id] = shape #store the newly created shape in a hash
599        return shape
600
601    #----------------------------------------------------------------------
602
603    # removes the shape from the canvas and deletes it
604    def MyRemoveShape(self, id):
605        shape = self.shapes[id]
606        canvas = shape.GetCanvas()
607        self.FreeMinimizedPosition(shape.minimizedPosition, shape.app)
608        del self.shapes[id] #remove the shape from the hash
609        shape.RemoveFromCanvas(self)
610        del shape
611
612    #----------------------------------------------------------------------
613
614##      # here there's no need to refill the diagram since all the shapes are in order
615##     # we just have to update their z values. No messages are sent to SAGE
616##     def UpdateZsForAdd(self):
617##         for s in self.GetShapeList(): #[:]:
618##             s.SetZ(s.GetZ()+1)
619
620    def OrderByZ(self):
621        diagram = self.GetDiagram()
622        shapeList = self.GetShapeList()
623        tempHash = {}  #shapes keyed by Z value for easy ordering
624        selectedShape = None
625
626        # now go through all the shapes and update them visually according to the new z values
627        for s in shapeList[:]:
628            s.SetZ( self.sageData.getZvalue(s.GetId()) )
629            tempHash[ s.GetZ() ] = s
630            if s.Selected():
631                selectedShape = s
632            diagram.RemoveShape(s)
633       
634        # sort the shapes so that they are in the right order for drawing
635        tempKeys = tempHash.keys()
636        tempKeys.sort()
637        tempKeys.reverse()
638
639        # now add all the shapes back sorted by Z
640        for z in tempKeys:
641            tempHash[z].DeleteControlPoints()
642            diagram.AddShape(tempHash[z])
643
644        # select the previously selected shape
645        if selectedShape is not None:
646            selectedShape.MakeControlPoints()
647
648       
649    #----------------------------------------------------------------------
650
651    # detects when the mouse pointer is over a control point and changes the pointer itself
652    def OnMouseEnter(self, event):
653        x = event.GetX()
654        y = event.GetY()
655
656        # stop this event here so that we can make other events
657        # (clicks on the shape to resize it)
658        event.StopPropagation()
659        event.Skip()
660       
661        shape = self.FindShape(x,y)[0]
662        if shape == None:
663            return
664
665        # set the correct cursor based on the location of the control point
666        if shape.__class__.__name__ == MyControlPoint.__name__:
667            if not self.cursorSet:   # set the cursor only once (ie, if it's set, don't set it again)
668                if shape.location == "NE" or shape.location == "SW":
669                    self.SetCursor(wx.StockCursor(wx.CURSOR_SIZENESW))
670                else:
671                    self.SetCursor(wx.StockCursor(wx.CURSOR_SIZENWSE))
672                self.cursorSet = True
673
674        elif self.cursorSet:    # we only want to reset the cursor if it was previously set
675            self.cursorSet = False
676            self.SetCursor(wx.STANDARD_CURSOR)
677
678
679    #----------------------------------------------------------------------
680
681        # this keeps track of where the minimized app should be located
682        # the hash holds windowIds but the keys are actually more important
683        # if a key exists, that means that there is an app in that spot so
684        # return the first free spot
685    def GetNextMinimizedPosition(self, app):
686        for i in range(0,30):
687            if not i in self._minimizedShapes[app.getDisplayId()]:
688                self._minimizedShapes[app.getDisplayId()][i] = app
689                return i
690
691        # when the app is not minimized anymore, it has to free its spot
692    def FreeMinimizedPosition(self, pos, app):
693        if pos in self._minimizedShapes[app.getDisplayId()]:
694            if self._minimizedShapes[app.getDisplayId()][pos] == app:
695                del self._minimizedShapes[app.getDisplayId()][pos]
696           
697
698#----------------------------------------------------------------------
699# CANVAS PREFERENCES AND PROPERTIES
700#----------------------------------------------------------------------
701
702    def ShowLibrary(self):
703        #reload(fileViewer)  # for debugging only!
704        fileViewer.FileLibrary(self, self.frame.GetPositionTuple())
705
706    def ChangeBackgroundColor(self, (r,g,b)):
707        return
708   
709    ##     tileColor = wx.Colour(r,g,b)
710##         for xOffset in range(self.iTileColumns):
711##             for yOffset in range(self.iTileRows):
712##                 self._LCDPanels[yOffset][xOffset].SetBrush(wx.Brush(tileColor))
713##         self.Redraw()
714       
715
716    def SetMaintainAspectRatio(self, ar):
717        prefs.visual.SetKeepAspectRatio(ar)
718
719    def GetMaintainAspectRatio(self):
720        return prefs.visual.GetKeepAspectRatio()
721
722
723        # stop or start receiving performance data
724    def ReceivePerformanceData(self, doReceive):
725        if doReceive:
726            prefs.visual.SetReceivePerformanceData(True)
727            for shape in self.GetShapeList():
728                self.sageGate.startPerformance(shape.GetId())
729        else:
730            prefs.visual.SetReceivePerformanceData(False)
731            for shape in self.GetShapeList():
732                self.sageGate.stopPerformance(shape.GetId())
733
734
735        # returns the bounds of the display in UI coords
736    def GetUIDisplayBounds(self, displayId):
737        return self.backdrop.GetDisplayBounds(displayId)
738   
739   
740    # for getting the bounds of the area where the displays will be
741    def GetLeft(self):
742        return self.GetBorderWidth()
743
744    def GetRight(self):
745        return self.GetDisplayWidth() + self.GetBorderWidth()
746
747    def GetTop(self):
748        return self.GetBorderWidth()
749
750    def GetBottom(self):
751        return self.GetDisplayHeight() + self.GetBorderWidth()
752
753
754    # tiled display are size (in the UI) - this is not for every individual display...
755    def GetDisplayWidth(self):
756        return self._maxDisplayWidth
757
758    def GetDisplayHeight(self):
759        return self._maxDisplayHeight
760
761    def SetDisplayWidth(self, newWidth):
762        self._maxDisplayWidth = newWidth
763       
764    def SetDisplayHeight(self, newHeight):
765        self._maxDisplayHeight = newHeight
766
767
768    # the top part of the UI (the window that holds the tiled display)
769    def GetCanvasWidth(self):
770        return self._maxCanvasWidth
771
772    def GetCanvasHeight(self):
773        return self._maxCanvasHeight
774
775    def SetCanvasWidth(self, newWidth):
776        self._maxCanvasWidth = newWidth
777
778    def SetCanvasHeight(self, newHeight):
779        self._maxCanvasHeight = newHeight
780
781    def GetBorderWidth(self):
782        return self._borderWidth
783
784    def SetBorderWidth(self, newWidth):
785        self._borderWidth = newWidth
786
787    def SetAspectRatio(self, ratio):
788        self._aspectRatio = ratio
789
790    def GetAspectRatio(self):
791        return self._aspectRatio
792       
793
794    def GetGraphManager(self):
795        return self.__gmGraphManager
796
797
798
799#----------------------------------------------------------------------
800# SAGEGATE CALLBACKS
801#----------------------------------------------------------------------
802
803    ### 40000 ###
804    def OnSAGEStatusMsg(self):
805        # this is where we make all the buttons for all the apps that SAGE supports
806        hashAvailableApps = self.sageData.getAvailableApps()
807
808        # figure out the positions for all the app buttons so that they are evenly spaced and centered
809        # then, create the SAGEAppInitial for every one of them and make the buttons on the bottom panel
810        buttonOffset = 40
811        buttonNum = 1 
812
813        # add the remote app button first
814        self.parent.GetAppPanel().AddRemoteButton("REMOTE APPS", (buttonNum*buttonOffset, 10), self.sageGate)
815
816        # now add the rest of the buttons from the local appLauncher (local to our SAGE)
817        apps = hashAvailableApps.keys()
818        apps.sort(lambda x, y: cmp(x.lower(), y.lower()))
819       
820        for appName in apps:
821            buttonNum = buttonNum + 2
822            temp = hashAvailableApps[appName]
823            self.availableApps[appName] = hashAvailableApps[appName]
824            self.parent.GetAppPanel().AddButton(self.availableApps[appName], (buttonNum*buttonOffset, 10))
825            self.parent.GetAppPanel().AddConfigMenu(self.availableApps[appName])
826
827               
828    #----------------------------------------------------------------------
829   
830
831    ### 40001 ###
832    def OnSAGEAppInfoMsg(self, sageApp):
833        # Here we just check if a shape for this "sageApp" exists and
834        # if it does, well, then we dont wanna create a new one but modify the old one
835        windowId = sageApp.getId()
836        if not self.shapes.has_key(windowId):  # CREATING A NEW SHAPE
837            newShape = self.MyAddShape(sageApp)
838            self.parent.GetAppInfoPanel().AddInstanceButton(sageApp)
839            self.OrderByZ()  # the shapes may arrive out of order
840           
841            # (AKS 2005-04-05) Only start performance monitoring when a new application
842            # is generated.
843            if prefs.visual.GetReceivePerformanceData():
844                self.sageGate.startPerformance( windowId, 2 )
845           
846        else:                          # MODIFYING A SHAPE
847            shape = self.shapes[windowId]
848            newWidth = self.ToUIWidth( sageApp.getLeft(), sageApp.getRight(), sageApp.getDisplayId() )
849            newHeight = self.ToUIHeight( sageApp.getBottom(), sageApp.getTop(), sageApp.getDisplayId() )
850            shape.SetSize(newWidth, newHeight)         
851            shape.SetX( round (self.ToUICoordsX( sageApp.getLeft(), sageApp.getDisplayId() ) + int(newWidth/2)) )
852            shape.SetY( round (self.ToUICoordsY( sageApp.getTop(), sageApp.getDisplayId() ) + int(newHeight/2)) )
853            shape.SetTitle( sageApp.getTitle() )
854            shape.SetOrientation( sageApp.getOrientation() )
855            shape.ResetControlPoints()
856           
857
858        self.Redraw()
859           
860               
861    #----------------------------------------------------------------------
862   
863
864    # 40002
865    def OnSAGEPerfInfoMsg(self, windowId ):
866        # (AKS 2005-04-05) Performance monitoring information
867        ## print "**** sageui >> 40002 >> update graph manager"
868        self.__gmGraphManager.update( windowId )
869        #print
870        #print "TOTALS"
871        #print "------"
872        stats = self.sageData.getTotals()
873        #print "Render total bandwidth = ", stats[ self.sageData.I_RENDER_TOTAL_BANDWIDTH ]
874        #print "Render average framerate = ", stats[ self.sageData.I_RENDER_AVG_FRAME_RATE ]
875        #print "Render total nodes = ", stats[ self.sageData.I_RENDER_TOTAL_NODES ]
876        #print "Display total bandwidth = ", stats[ self.sageData.I_DISPLAY_TOTAL_BANDWIDTH ]
877        #print "Display average framerate = ", stats[ self.sageData.I_DISPLAY_AVG_FRAME_RATE ]
878        #print "Display total nodes = ", stats[ self.sageData.I_DISPLAY_TOTAL_NODES ]
879
880
881
882    #----------------------------------------------------------------------
883
884
885    # 40003
886    def OnSAGEAppShutdownMsg(self, sageApp):
887        # turn the button off and close the app
888        self.MyRemoveShape(sageApp.getId())
889        self.parent.GetAppInfoPanel().RemoveInstanceButton(sageApp)
890        self.Redraw()
891
892
893        ## # (AKS 2005-04-05) Stop performance monitoring that began "by default"
894        # when a new application had started.
895        #self.sageGate.stopPerformance( windowId )  #this needs to be called BEFORE we close the app and not after...
896       
897        self.__gmGraphManager.removeGraph( sageApp.getId() )
898
899       
900    #----------------------------------------------------------------------
901   
902
903    # 40004
904    def OnSAGEDisplayInfoMsg(self):
905        self.displayInfo = self.sageData.getDisplayInfo()
906
907        # create the hash for each of the displays that holds the minimized shapes
908        for sageDisplay in self.displayInfo.getAllDisplays():
909            self._minimizedShapes[sageDisplay.displayId] = {}
910       
911        # for one display and old SAGE there are no connection messages so just
912        # pretend that one came in for drawing everything correctly
913        if self.displayInfo.getNumDisplays() == 1:
914            self.OnSAGEDisplaysConnection()
915
916             
917    #----------------------------------------------------------------------
918
919
920    # 40007
921    def OnSAGEDisplaysConnection(self):
922        self.tiledDisplayWidth = self.displayInfo.getTotalWidth()
923        self.tiledDisplayHeight = self.displayInfo.getTotalHeight()
924       
925        # now we have all the data needed to set up the sizes of the canvas and display part
926        self.SetDisplayWidth( self.GetCanvasWidth() - 2 * self.GetBorderWidth() )
927        heightFactor = self.tiledDisplayWidth / float (self.GetDisplayWidth() )
928        self.SetDisplayHeight( round (self.tiledDisplayHeight / heightFactor ) )
929        self.SetCanvasHeight( self.GetDisplayHeight() + 1.5 * self.GetBorderWidth())
930       
931        self.EnableScrolling(False, False)
932        self.SetSize((self.GetCanvasWidth(), self.GetCanvasHeight()))
933        self.SetAspectRatio( float(self.GetCanvasWidth()) / self.GetCanvasHeight() )
934
935
936        # figure out the backdrop panels
937        self.displayInfoArrived = True  #set the flag so that we can begin resizing
938        l = self.GetBorderWidth()
939        r = self.GetCanvasWidth() - self.GetBorderWidth()
940        t = self.GetBorderWidth()
941        b = self.GetCanvasHeight() - self.GetBorderWidth()
942
943        self.backdrop = Backdrop(self.displayInfo, self)
944        self.backdrop.SetupDisplays(l,r,t,b)
945
946
947        # resize the parent to be of the right size for its contents
948        if self.parent.GetNotebook().GetPageCount() < 2:  #if this is the first display, resize the frame
949            displaySize = wx.DisplaySize()  #the resolution of the current screen
950            if prefs.visual.GetFrameSize() != None:  # read the last state from the preferences
951                self.frame.SetSizeHints(400, 350, displaySize[0], displaySize[1])
952                self.frame.SetClientSize( prefs.visual.GetFrameSize() )
953            else:                                    # there are no previous preferences saved so figure it out
954                appCanvasWidth, appCanvasHeight = self.parent.GetAppInfoPanel().GetSize()
955                self.frame.SetSizeHints(400, 350, displaySize[0], displaySize[1])  #minimum size = (400, 400)
956                self.frame.SetClientSize((self.GetCanvasWidth(), self.GetCanvasHeight() + appCanvasHeight)) # set the initial size of the frame
957                if self.frame.GetSize().GetHeight() > displaySize[1]:  #is the frame too big for the desktop??? if so, size it back down
958                    heightDifference = self.frame.GetSize().GetHeight() - displaySize[1]
959                    self.frame.SetClientSize((self.GetCanvasWidth(), self.GetCanvasHeight() + appCanvasHeight - heightDifference))
960           
961            # figure out the positions and the sizes of all the decorative images
962            self.RecalculateImages()
963            ##set up a swap buffer for double buffering
964            self.swapBuffer = wx.EmptyBitmap(self.GetCanvasWidth(), self.GetCanvasHeight())
965            dc = wx.BufferedDC(None, self.swapBuffer)
966            dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
967            dc.Clear()
968            self.Redraw(dc)
969        else:   # since we didn't resize the frame, we have to fit the contents inside of it
970            ##set up a swap buffer for double buffering
971            self.swapBuffer = wx.EmptyBitmap(self.GetCanvasWidth(), self.GetCanvasHeight())
972            dc = wx.BufferedDC(None, self.swapBuffer)
973            dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
974            dc.Clear()
975            self.parent.OnSize()
976            #self.Redraw(dc)
977        del dc
978
979        # call the parent if something needs to be done
980        self.parent.OnDisplayInfo()
981
982        # add the pointers
983        #self.ptrs = Pointers(self.sageGate, self.sageData)
984       
985        # show the frame when all the buttons have been created (just for visual purposes)
986        self.frame.Show()
987        if self.frame.usersClient.connected:
988            if prefs.visual.IsChatShown() == None or prefs.visual.IsChatShown():
989                self.frame.GetUsersPanel().Show()
990
991
992   
993       
994    #----------------------------------------------------------------------
995   
996
997    # 40005
998    def OnSAGEZChangeMsg(self):
999        self.OrderByZ() # redraw the shapes based on the new order
1000        self.Redraw()
1001
1002       
1003    #----------------------------------------------------------------------
1004   
1005
1006    # 40006
1007    def OnSAGEAppExecConfig(self, appInitial):
1008        self.parent.GetAppPanel().AddConfigMenu(appInitial)
1009
1010
1011
1012#----------------------------------------------------------------------
1013#  UI ---> SAGE coordinate conversion
1014#----------------------------------------------------------------------
1015
1016    def ToSAGECoordsX (self, uiX, displayId, doRound=True):
1017        bounds = self.backdrop.GetDisplayBounds(displayId)
1018        uiX = uiX - bounds.left
1019        sageX = (float(uiX) / bounds.getWidth()) * self.displayInfo.getDisplay(displayId).sageW
1020        if doRound:
1021            sageX = round(sageX)
1022        return sageX
1023
1024    def ToSAGECoordsY (self, uiY, displayId, doRound=True):
1025        bounds = self.backdrop.GetDisplayBounds(displayId)
1026        uiY = uiY - bounds.top
1027        sageY = (1-(float(uiY) / bounds.getHeight())) * self.displayInfo.getDisplay(displayId).sageH
1028        if doRound:
1029            sageY = round(sageY)
1030        return sageY
1031
1032    def ToSAGEDistX (self, oldX, newX, displayId):   
1033        return round( self.ToSAGECoordsX( newX, displayId, False ) - self.ToSAGECoordsX( oldX, displayId, False ) )
1034
1035    def ToSAGEDistY (self, oldY, newY, displayId):   
1036        return round( self.ToSAGECoordsY( newY, displayId, False ) - self.ToSAGECoordsY( oldY, displayId, False ) )
1037   
1038
1039#----------------------------------------------------------------------
1040#  SAGE ---> UI coordinate conversion   
1041#----------------------------------------------------------------------
1042
1043    def ToUICoordsX (self, sageX, displayId, doRound=True):
1044        bounds = self.backdrop.GetDisplayBounds(displayId)
1045        uiX = (float(sageX) / self.displayInfo.getDisplay(displayId).sageW) * bounds.getWidth()
1046        uiX += bounds.left
1047       
1048        if doRound:
1049            uiX = round(uiX)
1050        return uiX
1051
1052    def ToUICoordsY (self, sageY, displayId, doRound=True):
1053        bounds = self.backdrop.GetDisplayBounds(displayId)
1054        uiY = (1 - (float(sageY) / self.displayInfo.getDisplay(displayId).sageH)) * bounds.getHeight()
1055        uiY += bounds.top
1056       
1057        if doRound:
1058            uiY = round(uiY)
1059        return uiY
1060
1061    def ToUIWidth (self, leftSageX, rightSageX, displayId):
1062        return round( abs(self.ToUICoordsX( leftSageX, displayId, False ) - self.ToUICoordsX( rightSageX, displayId, False )) )
1063   
1064    def ToUIHeight (self, bottomSageY, topSageY, displayId):
1065        return round( abs(self.ToUICoordsY( bottomSageY, displayId, False ) - self.ToUICoordsY( topSageY, displayId, False )) )
1066
1067
1068
1069
1070
1071############################################################################
1072#
1073#  CLASS: AppInfoPanel
1074
1075#  DESCRIPTION: It describes the canvas that holds all the buttons for
1076#               starting and stopping SAGE apps from within the SAGE UI.
1077#               It also draws all the graphics that is used instead of the
1078#               regular wx controls. It handles button clicks and other app
1079#               specific actions. It does all the drawing with double buffering.
1080#
1081#  DATE:        December 26, 2004
1082#
1083############################################################################
1084
1085class AppInfoPanel(wx.Panel):
1086    def __init__(self, frame, sageGate, parent, id, position, size, style):
1087        wx.Panel.__init__(self, parent, id, position, size, style)
1088        self.SetBackgroundColour('#888888')
1089        self.sageGate = sageGate
1090        self.parent = parent
1091        self.visible = True
1092        self.shrinkInfoPanel = False
1093        self.infoPanelMinimized = False
1094
1095        # load the images for the arrows
1096        self.arrowLeft = MyBitmapButton(self, (self.GetDisplayCanvas().GetBorderWidth(), 10), (20,40), "images/scrollLeft_up.jpg", "images/scrollLeft_down.jpg", "images/scrollLeft_over.jpg")
1097        self.arrowRight = MyBitmapButton(self, (self.GetDisplayCanvas().GetBorderWidth(), 10), (20,40), "images/scrollRight_up.jpg", "images/scrollRight_down.jpg", "images/scrollRight_over.jpg")
1098        self.arrowLeft.Bind(wx.EVT_LEFT_DOWN, self.OnLeftArrowDown)
1099        self.arrowLeft.Bind(wx.EVT_LEFT_UP, self.OnLeftArrowUp)
1100        self.arrowRight.Bind(wx.EVT_LEFT_DOWN, self.OnRightArrowDown)
1101        self.arrowRight.Bind(wx.EVT_LEFT_UP, self.OnRightArrowUp)
1102
1103        # load the decorative images (edges)
1104        border = self.GetDisplayCanvas().GetBorderWidth()
1105        self.borderImages = []
1106        self.borderImages.append( MyImage( "images/SAGEapp_10.jpg", 0,0, False, border, size[1] - border/2))
1107        self.borderImages.append( MyImage( "images/SAGEapp_14.jpg", border,0, False, size[0]-2*border, size[1]-border/2))
1108        self.borderImages.append( MyImage( "images/SAGEapp_15.jpg", size[0]-border,0, False, border, size[1] - border/2))
1109        self.borderImages.append( MyImage( "images/SAGEapp_17.jpg", 0,size[1]-border/2, False, border, border/2))
1110        self.borderImages.append( MyImage( "images/SAGEapp_18.jpg", border,size[1]-border/2, False, size[0]-2*border,border/2))
1111        self.borderImages.append( MyImage( "images/SAGEapp_19.jpg", size[0]-border,size[1]-border/2, False, border,border/2))
1112       
1113        # bind events for the panel
1114        self.Bind(wx.EVT_PAINT, self.OnPaint)
1115        self.Bind(wx.EVT_ERASE_BACKGROUND, self.Chillout)
1116        if "__WXMSW__" in wx.PlatformInfo:  # this is needed on windows for proper painting after resize
1117            self.Bind(wx.EVT_SIZE, self.OnSize)
1118
1119        # set up the area for the buttons to be in
1120        self.appPanel = AppPanel(self, 20,20, border+self.arrowLeft.GetSize().width,10)
1121        self.infoPanel = InfoPanel(self, 50,20, 100,20)
1122
1123        # the show/hide buttons
1124        self.hideAppPanelButton = MyBitmapButton(self, (0,0), (40,12), "images/handle1.jpg")
1125        self.hideAppPanelButton.Bind(wx.EVT_LEFT_UP, self.OnHideAppPanelButton)
1126        self.hideInfoPanelButton = MyBitmapButton(self, (0,0), (12,40), "images/handle.jpg")
1127        self.hideInfoPanelButton.Bind(wx.EVT_LEFT_UP, self.OnHideInfoPanelButton)
1128
1129       
1130
1131    #----------------------------------------------------------------------
1132
1133
1134    def OnPaint(self, event=None):
1135        if DEBUG: print "    OnPaint --> AppInfoPanel"; sys.stdout.flush()
1136        dc = wx.PaintDC(self)
1137        if not self.GetDisplayCanvas().displayInfoArrived:
1138            return
1139        self.Redraw(dc)  # redraw first and then swap the buffer
1140           
1141
1142    #----------------------------------------------------------------------
1143   
1144
1145    def Redraw(self, dc=None):
1146        if DEBUG: print "    Redraw --> AppInfoPanel"; sys.stdout.flush()
1147        if not self.GetDisplayCanvas().displayInfoArrived:
1148            return
1149        if dc == None:
1150            if "__WXMAC__" in wx.PlatformInfo:
1151                self.Refresh()
1152                return
1153            dc = wx.ClientDC(self)
1154
1155        # decorative images
1156        for image in self.borderImages:
1157            dc.DrawBitmap( image.bitmap, image.GetX(), image.GetY(), image.IsTransparent() )       
1158
1159        del dc # to stop mac os from complaining about nested wxDCs
1160
1161        # redraw the children
1162        self.infoPanel.Redraw()
1163        self.appPanel.Redraw()
1164
1165        # redraw the controls on the panel
1166        self.hideAppPanelButton.OnPaint()
1167        self.hideInfoPanelButton.OnPaint()
1168        self.arrowLeft.OnPaint()
1169        self.arrowRight.OnPaint()
1170       
1171
1172       
1173
1174    #----------------------------------------------------------------------
1175
1176   
1177    def Chillout(self, event):
1178        """ For intercepting the EVT_ERASE_BACKGROUND. Do nothing here on purpose
1179            in order to reduce the flicker during drawing"""
1180        pass
1181
1182
1183    #----------------------------------------------------------------------
1184
1185
1186    def OnSize(self, event=None):
1187        if DEBUG: print "    OnSize --> AppInfoPanel"; sys.stdout.flush()
1188        if not self.GetDisplayCanvas().displayInfoArrived:
1189            return
1190        # move the panel first to the bottom of the canvas
1191        canvasHeight = self.parent.GetDisplayCanvas().GetCanvasHeight()
1192        self.MoveXY(0, canvasHeight - 1)
1193       
1194        # resize the main AppInfoPanel now
1195        parentSize = self.parent.GetClientSize()
1196        if "__WXMSW__" in wx.PlatformInfo:  # a visual hack for windows
1197            newHeight = parentSize.height - canvasHeight - 21#canvasSize.height - 21
1198        else:
1199            newHeight = parentSize.height - canvasHeight #canvasSize.height
1200        if newHeight < 1:
1201           newHeight = 1
1202           
1203        self.SetSize( (parentSize.width, newHeight) )
1204
1205        # rescale and reposition all the decorative images (edges)
1206        border = self.parent.GetDisplayCanvas().GetBorderWidth()
1207        size = self.GetSizeTuple()
1208        if size[0] > 0 and size[1] > 0:    # resize only if the parent has a reasonable size
1209            self.borderImages[0].Recalculate( border, size[1] - border/2, 0,0)
1210            self.borderImages[1].Recalculate( size[0]-2*border, size[1]-border/2, border,0)
1211            self.borderImages[2].Recalculate( border, size[1] - border/2, size[0]-border,0)
1212            self.borderImages[3].Recalculate( border, border/2, 0,size[1]-border/2)
1213            self.borderImages[4].Recalculate( size[0]-2*border,border/2, border,size[1]-border/2)
1214            self.borderImages[5].Recalculate( border,border/2, size[0]-border,size[1]-border/2)
1215
1216        # as we are sizing down, make the infoPanel disappear if the frame size got small enough
1217        self.shrinkInfoPanel = parentSize.width < (border*2 + 2*self.arrowLeft.GetSize().width + self.infoPanel.GetCurrentMinSize() + 50)
1218               
1219        if (self.shrinkInfoPanel and self.infoPanel.visible):  # hide the info panel
1220            self.infoPanel.Minimize()
1221            self.hideInfoPanelButton.Hide()
1222        elif (not self.shrinkInfoPanel) and (not self.infoPanel.visible) and (not self.infoPanelMinimized):  #show it (once)
1223            self.infoPanel.Maximize()
1224            self.hideInfoPanelButton.Show()
1225
1226        # now resize the children
1227        self.infoPanel.OnSize()
1228        self.appPanel.OnSize()
1229
1230        # reposition the scrolling arrows for the appPanel
1231        self.arrowLeft.MoveXY(self.GetDisplayCanvas().GetBorderWidth(), 20)
1232        self.arrowRight.MoveXY(self.GetDisplayCanvas().GetBorderWidth()+self.appPanel.GetSize().width+self.GetArrowWidth(), 20)
1233        self.hideAppPanelButton.MoveXY( size[0]/2 - self.hideAppPanelButton.GetSize()[0]/2,  0 )
1234        self.hideInfoPanelButton.MoveXY( self.infoPanel.GetPosition().x - 16,  self.infoPanel.GetPosition().y + self.infoPanel.GetSize().height/2 - self.hideInfoPanelButton.GetSize()[1]/2 )
1235
1236        self.Redraw()
1237
1238
1239    #----------------------------------------------------------------------
1240
1241
1242    def OnHideAppPanelButton(self, event=None):
1243        if self.visible:
1244            self.parent.HideAppInfoPanel()
1245            self.visible = False
1246            self.arrowRight.Hide()
1247            self.arrowLeft.Hide()
1248        else:
1249            self.parent.ShowAppInfoPanel()
1250            self.visible = True
1251            self.arrowRight.Show()
1252            self.arrowLeft.Show()
1253
1254    def OnHideInfoPanelButton(self, event=None):
1255        if self.infoPanel.visible:  #hide it
1256            self.infoPanel.Minimize()
1257            self.infoPanelMinimized = True
1258            self.OnSize()
1259        else:              # it's already hidden so show it
1260            self.infoPanel.Maximize()
1261            self.infoPanelMinimized = False
1262            self.OnSize()
1263
1264           
1265    def OnLeftArrowDown(self, event):
1266        self.arrowLeft.OnLeftDown(event)
1267        self.appPanel.ScrollLeft()
1268
1269    def OnRightArrowDown(self, event):
1270        self.arrowRight.OnLeftDown(event)
1271        self.appPanel.ScrollRight()
1272
1273    def OnLeftArrowUp(self, event):
1274        self.arrowLeft.OnLeftUp(event)
1275        self.appPanel.ScrollLeft()
1276
1277    def OnRightArrowUp(self, event):
1278        self.arrowRight.OnLeftUp(event)
1279        self.appPanel.ScrollRight()
1280       
1281
1282    #----------------------------------------------------------------------
1283   
1284   
1285    def AddInstanceButton(self, sageApp):
1286        # once the 40001 message arrives and the app doesnt exists yet, create an instance button for it
1287        self.appPanel.AddInstanceButton(sageApp)
1288        self.Refresh()
1289
1290    def RemoveInstanceButton(self, sageApp):
1291        # once the 40003 message arrives and we close the app, remove the instance button for it
1292        self.appPanel.RemoveInstanceButton(sageApp)
1293        self.Refresh()
1294
1295       
1296    #----------------------------------------------------------------------
1297
1298    def GetDisplayCanvas(self):
1299        return self.parent.GetDisplayCanvas()
1300
1301    def GetArrowWidth(self):
1302        return self.arrowLeft.GetSize().width
1303
1304    def GetInfoPanel(self):
1305        return self.infoPanel
1306   
1307    def GetAppPanel(self):
1308        return self.appPanel
1309
1310
1311
1312############################################################################
1313#
1314#  CLASS: AppPanel
1315
1316#  DESCRIPTION: It describes the canvas that holds all the buttons for
1317#               starting and stopping SAGE apps from within the SAGE UI.
1318#               It also draws all the graphics that is used instead of the
1319#               regular wx controls. It handles button clicks and other app
1320#               specific actions. It does all the drawing with double buffering.
1321#
1322#  DATE:        April, 2005
1323#
1324############################################################################
1325
1326class AppPanel(wx.Panel):
1327
1328    def __init__(self, parent, width, height, x, y):
1329        wx.Panel.__init__(self, parent, -1, (x,y), (width,height), wx.NO_BORDER | wx.NO_FULL_REPAINT_ON_RESIZE)
1330        self.SetBackgroundColour(appPanelColor)
1331        self.page = parent.parent
1332        self.parent = parent
1333        self.visible = True
1334        self._appButtonWidth = 40
1335        self.appButtons = {} # has to store all the buttons keyed by the app_inst_IDs
1336        self.appNames = [] # list of all app names that we got from sage
1337        self.buttonOffset = 0 #used for positioning the buttons
1338        self.scrollWidth = width  #the minimum width required to draw all the app buttons
1339        self.first = None  # used for scrolling... these are first and last app buttons
1340        self.last = None
1341
1342        # background for the app buttons
1343        self.bar = MyImage( "images/menu_bar.jpg", 0,10, False, 1500, self.GetButtonWidth())
1344        self.background = MyImage( "images/app_panel_background.png", 0,0, False, width, height)
1345
1346        self.Bind(wx.EVT_ERASE_BACKGROUND, self.Chillout)
1347        self.Bind(wx.EVT_PAINT, self.OnPaint)
1348        if wx.Platform == "__WXMSW__":  # this is needed on windows for proper painting after resize
1349            self.Bind(wx.EVT_SIZE, self.OnSize)
1350       
1351
1352    #----------------------------------------------------------------------
1353
1354    def OnPaint(self, event=None):
1355        if DEBUG: print "\tOnPaint --> AppPanel"; sys.stdout.flush()
1356        dc = wx.PaintDC(self)
1357        self.PrepareDC(dc)
1358        self.Redraw(dc)
1359
1360   
1361    def Redraw(self, dc=None):
1362        if DEBUG: print "\tRedraw --> AppPanel"; sys.stdout.flush()
1363        if dc == None:
1364            if "__WXMAC__" in wx.PlatformInfo:
1365                self.Refresh()
1366                return
1367            dc = wx.ClientDC(self)
1368            self.PrepareDC(dc)
1369
1370        # first draw the bar and the background
1371        dc.DrawBitmap( self.background.bitmap, self.background.GetX(), self.background.GetY(), self.background.IsTransparent())
1372        dc.DrawBitmap( self.bar.bitmap, self.bar.GetX(), self.bar.GetY(), self.bar.IsTransparent() )
1373        del dc  # for mac os to stop complaining about nested wxDCs
1374
1375        # draw all the app buttons in their respective locations
1376        for name, button in self.appButtons.iteritems():
1377            button.OnPaint()
1378       
1379
1380    def Chillout(self, event):
1381        """ For intercepting the EVT_ERASE_BACKGROUND. Do nothing here on purpose
1382            in order to reduce the flicker during drawing"""
1383        pass
1384
1385
1386    def OnSize(self, event=None):
1387        if DEBUG: print "\tOnSize --> AppPanel"; sys.stdout.flush()
1388        # get some measures that will help us determine the size of the panel
1389        parentSize = self.parent.GetClientSize()
1390        borderWidth = self.parent.GetDisplayCanvas().GetBorderWidth()
1391        arrowWidth = self.parent.GetArrowWidth()
1392        infoPanelWidth = self.parent.GetInfoPanel().GetSize().width#GetInfoPanelWidth()
1393
1394        # determine the new size
1395        newWidth = parentSize.width - infoPanelWidth - 2*arrowWidth - 2*borderWidth - 20
1396        newHeight = parentSize.height - 1.5*borderWidth
1397        if newHeight < 1:
1398           newHeight = 1
1399        if newWidth < 1:
1400           newWidth = 1
1401        self.SetSize( (newWidth, newHeight) )
1402
1403        #resize the background image
1404        self.background.Rescale(newWidth, newHeight)
1405        self.Redraw()
1406
1407
1408    #----------------------------------------------------------------------
1409
1410   
1411    def ScrollLeft(self):
1412        if not self.IsButtonVisible( self.first ):
1413            self.ScrollWindow( 30, 0 )
1414            self.Update()
1415               
1416
1417    def ScrollRight(self):
1418        if not self.IsButtonVisible( self.last ):
1419            self.ScrollWindow( -30, 0 )
1420            self.Update()
1421
1422
1423    def IsButtonVisible(self, button):
1424        # Used in scrolling... checks if a button is on the screen.
1425        # If not, return false so that we have to scroll
1426        x = button.GetPosition().x
1427        width = button.GetSize().width
1428
1429        if (x > 0) and ((x+width) < self.GetSize().width):
1430            return True
1431        else:
1432            return False
1433           
1434
1435    #----------------------------------------------------------------------
1436
1437
1438    def AddRemoteButton(self, name, (x,y), sageGate):
1439        self.appNames.append(name)
1440       
1441        # create the buttons and check if images are available for them
1442        if os.path.isfile( ConvertPath("images/remote_up.jpg")):
1443            bitmapName = "images/remote"
1444        else:
1445            bitmapName = "images/default"
1446        self.appButtons[ name ] = RemoteAppButton( self, bitmapName+"_up.jpg", bitmapName+"_down.jpg", bitmapName+"_over.jpg", (x,y), (self.GetButtonWidth(),self.GetButtonWidth()), sageGate)
1447        self.buttonOffset = self.buttonOffset + 1
1448
1449        # this is the total area taken by the buttons (so that they are all displayed at once)
1450        self.scrollWidth = self.GetButtonWidth() * 2 * len(self.appButtons)
1451   
1452        # remember which is the first and the last button... used for scrolling
1453        if self.first == None:
1454            self.first = self.appButtons[ name ]
1455            self.last = self.appButtons[ name ]  #the first is also the last
1456        else:
1457            self.last = self.appButtons[ name ]
1458
1459
1460    def AddButton(self, app, (x,y)):
1461        self.appNames.append(app.GetName())
1462       
1463        # create the buttons and check if images are available for them
1464        if os.path.isfile( ConvertPath("images/" + string.lower(app.GetName()) + "_up.jpg")):
1465            bitmapName = "images/" + string.lower(app.GetName())
1466        else:
1467            bitmapName = "images/default"
1468        self.appButtons[ app.GetName() ] = AppButton( self, bitmapName+"_up.jpg", bitmapName+"_down.jpg", bitmapName+"_over.jpg", (x,y), (self.GetButtonWidth(),self.GetButtonWidth()), app)
1469        self.buttonOffset = self.buttonOffset + 1
1470
1471        # this is the total area taken by the buttons (so that they are all displayed at once)
1472        self.scrollWidth = self.GetButtonWidth() * 2 * len(self.appButtons)
1473   
1474        # remember which is the first and the last button... used for scrolling
1475        if self.first == None:
1476            self.first = self.appButtons[ app.GetName() ]
1477        else:
1478            self.last = self.appButtons[ app.GetName() ]
1479
1480
1481    def RepositionAppButtons(self):
1482        # repositions all the app buttons and redraws them
1483        # the app buttons then reposition all the instance buttons and redraw them
1484        buttonOffset = round( self.GetSize().width / float( len(self.appNames)*2+1 ) )
1485        buttonNum = 1
1486       
1487        for k, btn in self.appButtons.iteritems():
1488            btn.MoveXY(buttonNum*buttonOffset, 10)
1489            buttonNum = buttonNum + 2
1490
1491
1492    def GetButtonWidth(self):
1493        return self._appButtonWidth
1494   
1495    def GetAppInfoPanel(self):
1496        return self.parent
1497
1498
1499    #----------------------------------------------------------------------
1500
1501    # fills the remote application button with the correct menu data for each app launcher
1502    def AddLauncherMenu(self, name, launcherHash):
1503        self.appButtons[ name ].AddLauncherMenu(launcherHash)   
1504
1505    # once the 40006 message arrives, we add a menu to the button
1506    def AddConfigMenu(self, appInitial):
1507        self.appButtons[ appInitial.GetName() ].AddConfigMenu(appInitial)
1508   
1509    # once the 40001 message arrives and the app doesnt exists yet, create an instance button for it
1510    def AddInstanceButton(self, sageApp):
1511        if sageApp.getName() in self.appButtons:
1512            self.appButtons[ sageApp.getName() ].AddInstanceButton(sageApp)
1513
1514    # once the 40003 message arrives and we close the app, remove the instance button for it
1515    def RemoveInstanceButton(self, sageApp):
1516        if sageApp.getName() in self.appButtons:
1517            self.appButtons[ sageApp.getName() ].RemoveInstanceButton(sageApp.getId())
1518            self.GetAppInfoPanel().GetInfoPanel().OnPerfPanelRemoved(sageApp.getId())
1519
1520
1521
1522
1523       
1524############################################################################
1525#
1526#  CLASS: InfoPanel
1527
1528#  DESCRIPTION: It describes the canvas that holds all the buttons for
1529#               starting and stopping SAGE apps from within the SAGE UI.
1530#               It also draws all the graphics that is used instead of the
1531#               regular wx controls. It handles button clicks and other app
1532#               specific actions. It does all the drawing with double buffering.
1533#
1534#  DATE:        April, 2005
1535#
1536############################################################################
1537
1538class InfoPanel(wx.Panel):
1539
1540    def __init__(self, parent, width, height, x, y):
1541        wx.Panel.__init__(self, parent, -1, (x,y), (width,height), wx.NO_BORDER | wx.NO_FULL_REPAINT_ON_RESIZE)
1542        self.SetBackgroundColour(appPanelColor)
1543        self.page = parent.parent
1544        self.parent = parent
1545        self.SetVirtualSize( (width, height) )
1546        self.visible = True
1547        self._minWidth = 250
1548        self.width = 0 #initial width 
1549        self.displayCanvas = self.page.GetDisplayCanvas()
1550        self.dataPanels = []
1551       
1552        self.background = MyImage( "images/app_panel_background.png", 0,0, False, width, height)
1553       
1554        if "__WXMSW__" in wx.PlatformInfo:  # this is needed on windows for proper painting after resize
1555            self.Bind(wx.EVT_SIZE, self.OnSize)
1556        elif "__WXGTK__" in wx.PlatformInfo:
1557            self.Bind(wx.EVT_PAINT, self.OnPaint)
1558        self.Bind(wx.EVT_ERASE_BACKGROUND, self.Chillout)
1559       
1560    #----------------------------------------------------------------------
1561
1562    def Chillout(self, event):
1563        """ For intercepting the EVT_ERASE_BACKGROUND. Do nothing here on purpose
1564            in order to reduce the flicker during drawing"""
1565        pass
1566
1567
1568    def OnPaint(self, event=None):
1569        if not self.visible: dc=wx.PaintDC(self); return
1570        if DEBUG: print "\tOnPaint --> InfoPanel"; sys.stdout.flush()
1571        dc = wx.PaintDC(self)
1572        self.PrepareDC(dc)
1573        self.Redraw(dc)
1574
1575   
1576    def Redraw(self, dc=None):
1577        if not self.visible: return
1578        if DEBUG: print "\tRedraw --> InfoPanel"; sys.stdout.flush()
1579        if dc == None:
1580            if "__WXMAC__" in wx.PlatformInfo:
1581                self.Refresh()
1582                return
1583            dc = wx.ClientDC(self)
1584            self.PrepareDC(dc)
1585       
1586        if "__WXMSW__" in wx.PlatformInfo:
1587            self.Refresh()
1588            self.Update()
1589
1590        dc.DrawBitmap( self.background.bitmap, self.background.GetX(), self.background.GetY(), self.background.IsTransparent())
1591        dc.SetBrush(wx.TRANSPARENT_BRUSH)
1592        dc.SetPen(wx.BLACK_PEN)
1593        dc.DrawRectangle(2, 2, self.GetClientSize()[0]-2, self.GetClientSize()[1]-2)
1594        dc.SetPen(wx.Pen(wx.Colour(153, 153, 153)))
1595        dc.DrawRectangle(0, 0, self.GetClientSize()[0]-2, self.GetClientSize()[1]-2)
1596        del dc
1597       
1598
1599       
1600    def OnSize(self, event=None):
1601        #if not self.visible: return
1602        if DEBUG: print "\tOnSize --> InfoPanel"; sys.stdout.flush()
1603        # get some measures that will help us determine the size of the panel
1604        parentSize = self.GetAppInfoPanel().GetClientSize()
1605        borderWidth = self.GetAppInfoPanel().GetDisplayCanvas().GetBorderWidth()
1606        arrowWidth = self.GetAppInfoPanel().GetArrowWidth()
1607
1608        # the new size depends on if there is a dataPanel in there or not
1609        # if not, the whole InfoPanel will be of default size
1610        if not self.visible or self.GetDataPanelWidth() == 0:   #not visible, setting width to 0
1611            newWidth = 0
1612            self.Hide()
1613        else:          #dataPanel, setting width to dataPanelWidth
1614            self.Show()
1615            newWidth = self.GetDataPanelWidth() + 7  #+5 added to allow for the border
1616        newHeight = parentSize.height - 1.5*borderWidth
1617
1618        # apply the changes
1619        self.SetSize( (newWidth, newHeight) )
1620        self.SetVirtualSize( (self.GetVirtualSize().width, self.GetVirtualSize().height) )
1621        self.MoveXY( parentSize.width - borderWidth - newWidth, self.GetPosition().y)
1622
1623        # hide the little scrolly thingy button from the AppInfoPanel
1624        if newHeight < 2:
1625            self.GetAppInfoPanel().hideInfoPanelButton.Hide()
1626        else:
1627            self.GetAppInfoPanel().hideInfoPanelButton.Show()
1628
1629        #resize the background image
1630        self.background.Rescale(newWidth, newHeight)
1631        self.Redraw()
1632       
1633
1634    #----------------------------------------------------------------------
1635   
1636
1637    def Minimize(self):
1638        self.visible = False
1639        self.Hide()
1640
1641    def Maximize(self):
1642        self.visible = True
1643        self.Show()
1644
1645    def GetCurrentMinSize(self):
1646        if self.GetDataPanelWidth() == 0:  #no dataPanel, setting width to default
1647            newWidth = self.GetMinimumWidth()
1648        else:          #dataPanel, setting width to dataPanelWidth
1649            newWidth = self.GetDataPanelWidth()
1650        return newWidth
1651
1652    def GetAppInfoPanel(self):
1653        return self.page.GetAppInfoPanel()
1654   
1655    def GetMinimumWidth(self):
1656        return self._minWidth
1657
1658    def SetMinimumWidth(self, newMinWidth):
1659        self._minWidth = newMinWidth
1660
1661       
1662    #----------------------------------------------------------------------
1663
1664
1665    # this shows all the data that's relevant to the app
1666    def ShowData(self, appName, windowId):
1667        if self.visible:
1668            self.ShowPerformanceData(appName, windowId)
1669       
1670
1671        # shows the performance data panel in this InfoPanel
1672    def ShowPerformanceData(self, appName, windowId):
1673        if self.displayCanvas.GetGraphManager().GetSparklineGraph(windowId) == -1: #does the panel exist yet?
1674            self.displayCanvas.GetGraphManager().addGraph(appName, windowId, self)
1675            dataPanel = self.displayCanvas.GetGraphManager().GetSparklineGraph(windowId)
1676            self.dataPanels.append(dataPanel)  # add it to the list of panels
1677            if len(self.dataPanels) < 2:
1678                self.Freeze()
1679                self.GetAppInfoPanel().OnSize()  #refresh/resize the panels
1680                self.Thaw()
1681        else:   #panel already exists so just show it on top of the other ones
1682            dataPanel = self.displayCanvas.GetGraphManager().GetSparklineGraph(windowId)
1683            self.Freeze()
1684            for p in self.dataPanels:  #hide all the other panels but show the one that's selected
1685                try:
1686                    p.Hide()
1687                except Exception:  # an exception will occur if there are panels in the list that dont exist anymore
1688                    del p  #this will delete it from the list as well
1689            dataPanel.Show()
1690            self.Thaw()
1691            #if not "__WXGTK__" in wx.PlatformInfo:  #extra refresh needed on Mac and Windows
1692            #    self.GetAppInfoPanel().OnSize()
1693
1694           
1695            # returns the width of the dataPanel, if none exist it returns a 0
1696    def GetDataPanelWidth(self):
1697        try:
1698            width = self.dataPanels[0].GetSize().width
1699        except Exception:
1700            del self.dataPanels
1701            self.dataPanels = []
1702            return 0
1703        else:
1704            return width
1705
1706
1707        # resizes the parent panel to a default size if there are no more perf panels in here
1708    def OnPerfPanelRemoved(self, windowId):
1709        dataPanel = self.displayCanvas.GetGraphManager().GetSparklineGraph(windowId)
1710
1711        try:
1712            self.dataPanels.remove(dataPanel)
1713        except Exception:
1714            pass
1715        else:
1716            if len(self.dataPanels) < 1:
1717                self.GetAppInfoPanel().OnSize()  #resize and refresh the parent panel
1718
1719
1720
Note: See TracBrowser for help on using the repository browser.