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

Revision 4, 56.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#       
37############################################################################
38
39
40import xmlrpclib, base64, sys, cStringIO, socket, pickle, copy, os.path, os
41from httplib import HTTPException
42import wx.lib.scrolledpanel
43from wx import ProgressDialog
44from globals import ConvertPath, Message, ChoiceDialog, appPanelColor, dialogColor
45import help  #my own
46import preferences as prefs  #my own
47from math import ceil
48from threading import *
49from misc.imsize import imagesize  # reads the image header and gets the size from it
50import wx.lib.throbber as throb
51import urllib
52
53XMLRPC_PORT = "8800"
54FILE_GRABBER_PORT = "8801"
55FILE_SERVER_BIN_PORT = 8802
56PREVIEW_SIZE = (150,150)
57DEFAULT_TIMEOUT = None  # no timeout because server side processing might take a while
58
59# used so that we don't create two connections to the same server... no reason
60# keyed by IP address of the server (host)
61fileServers = {}
62def GetFileServer(host, port):
63    global fileServers
64    if fileServers.has_key(host):
65        return fileServers[host]
66    else:
67        fs = FileServer(host, port)
68        fileServers[host] = fs
69        return fileServers[host]
70
71
72
73### stuff needed in order to customize the sending...
74class MyConnection:
75    def __init__(self, conn):
76        self.conn = conn
77
78        ### if data is bigger (>100KB), show the progress bar
79    def send(self, data):
80        dataLength = len(data)
81        if dataLength > 100000:
82            try:
83                dlg = wx.ProgressDialog("Uploading File", "Uploading... (0/%.2f)"%(dataLength/1048576),
84                                        style=wx.PD_SMOOTH | wx.PD_CAN_ABORT |
85                                        wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE)
86                chunkSize = 8192
87                for i in range (0, dataLength, chunkSize):  #send chunks of 8192 bytes
88                    self.conn.send(data[i:i+chunkSize])
89                    if not dlg.Update(ceil((float(i)/dataLength)*100), "Uploading... (%.2f/%.2f MB)"%
90                                      (float(i)/1048576, float(dataLength)/1048576)):
91                        self.conn.close()
92                        break
93                dlg.Destroy()
94            except socket.error:
95                dlg.Destroy()
96                Message("Error while uploading file. This is most likely a server-side error and it should be possible to continue.", "Upload Failed")
97        else:
98            self.conn.send(data)
99
100        ### overridden so that we can catch the exception when we press cancel during upload
101    def getreply(self):
102        ret = (None, None, None)
103        try:
104            ret = self.conn.getreply()
105        except HTTPException:
106            pass
107        return ret
108
109   
110    def __getattr__(self, key):
111        return getattr(self.conn, key)
112
113
114class MyTransport(xmlrpclib.Transport):
115    def make_connection(self, host):
116        conn = xmlrpclib.Transport.make_connection(self, host)
117        return MyConnection(conn)
118
119
120
121
122class FileServer:
123   
124    def __init__(self, host, port):
125        self.host = host  #where the FileLibrary is running
126        self.port = port
127        self.connected = False
128        self.Connect()
129
130
131        # try to connect to the server
132        # if we failed, set the flag
133        # if we succeed, get the path where the images are stored so that we can send it to the app
134    def Connect(self):
135        if self.connected: return True
136
137        print "\nConnecting to XMLRPC server at: http://"+str(self.host)+":"+self.port
138        socket.setdefaulttimeout(DEFAULT_TIMEOUT)
139        self.server = xmlrpclib.ServerProxy("http://"+str(self.host)+":"+self.port, transport=MyTransport())
140        #socket.setdefaulttimeout(3)
141        try:
142            self.connected = True
143            global FILE_GRABBER_PORT
144            FILE_GRABBER_PORT = self.server.TestConnection()[1]  #just see if the connection opened correctly
145        except:
146            print "Could not connect to the file server at: "+str(self.host)+":"+self.port
147            self.connected = False
148            return False
149        else:
150            print "Connected to the XMLRPC server at: http://"+str(self.host)+":"+self.port
151            return True
152
153
154    def IsConnected(self):
155        return self.Connect()
156
157
158        # uploads the file to the file library and returns information about the file
159    def UploadFile(self, fullPath):
160        try:
161           
162            # get the file info first
163            fileInfo = self.GetNewFileInfo(fullPath)
164            if not fileInfo: return False            # file type not supported
165            else: (fileType, appName, fullRemotePath, size, params, fileExists) = fileInfo
166
167            if fileExists:                           # if the file exists on the server, just show it (ie. dont send it)
168                return (fileType, appName, fullRemotePath, size, params, fileExists) #no need to upload
169            else:
170                if self.__SendFile(fullPath):    # did upload fail for some reason?
171                    return (fileType, appName, fullRemotePath, size, params, fileExists)
172                else:                           
173                    return False                     # upload failed so whatever you are doing after this (ShowFile maybe), don't do it
174
175        except socket.error:
176            Message("Unable to upload file. There is no connection with the File Server.", "No connection")
177            return False
178        except xmlrpclib.ProtocolError:
179            return False
180   
181
182        # gets the information about the file and the supporting app from the file library
183    def GetNewFileInfo(self, fullPath):
184        #c = wx.BusyCursor()
185        (path, filename) = os.path.split(fullPath)
186        filename = "_".join(filename.split())
187
188        # check if the file type is supported
189        # if it is, it will get some data about the file (type, viewerApp ...)
190        fileSize = os.stat(fullPath).st_size  #get the size in bytes
191        fileInfo = self.server.GetFileInfo( filename, fileSize )
192        if not fileInfo:
193            #del c
194            extension = os.path.splitext(filename)[1].lower()
195            Message("File type <"+extension+"> not supported.", "Invalid File Type")
196            return False
197
198        (fileType, size, fullRemotePath, appName, params, fileExists) = fileInfo
199
200        # prepare for showing the file (filename, path, size if necessary...)
201        if fileType == "image":
202            try:
203                imsize = imagesize(fullPath)
204                size = (imsize[1], imsize[0])
205            except:
206                size = (-1,-1)
207        #del c
208        return (fileType, appName, fullRemotePath, size, params, fileExists)
209
210
211    def __SendFile(self, fullPath):
212        try:
213            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
214            s.connect((self.host, FILE_SERVER_BIN_PORT))
215
216            #convert to a filename with _ instead of empty spaces
217            convertedFilename = "_".join(os.path.basename(fullPath).split())
218           
219            # send the header first
220            fileSize = os.path.getsize(fullPath)
221            header = convertedFilename + " " + str(PREVIEW_SIZE[0]) + " " + str(PREVIEW_SIZE[1]) + \
222                     " " + str(fileSize) + "\n"
223            s.sendall(header)     
224
225            # show the progress dialog if file is bigger
226            doDlg = False
227            if fileSize > 100000:
228                dlg = wx.ProgressDialog("Uploading File", "Uploading... (0/%.2f)"%(fileSize/1048576),
229                                        style=wx.PD_SMOOTH | wx.PD_CAN_ABORT |
230                                        wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE)
231                doDlg = True
232               
233            # send the file data
234            f=open(fullPath, "rb")
235            t = 0
236            for line in f:
237                s.sendall(line)
238                t += len(line)
239
240                # update the dialog if needed
241                if doDlg and not dlg.Update(ceil((float(t)/fileSize)*100), "Uploading... (%.2f/%.2f MB)"%
242                                            (float(t)/1048576, float(fileSize)/1048576)):
243                    s.close()
244                    f.close()
245                    dlg.Destroy()
246                    return False
247
248            if doDlg:  dlg.Destroy()
249               
250            s.sendall("\n")  # because the other side is reading lines
251            f.close()
252            s.recv(1)
253        except:
254            print "Error sending file to File Server: ", sys.exc_info()[0], sys.exc_info()[1]
255            if doDlg:  dlg.Destroy()
256            Message("Could not upload file "+str(os.path.basename(fullPath)), "Error")
257            return False
258
259        return True
260               
261        #return self.server.UploadFile( convertedFilename, data, PREVIEW_SIZE )
262
263
264   
265### the base class for accepting the dropped files
266class FileDropTarget(wx.PyDropTarget):
267    def __init__(self, host=None, port=None):
268
269        # set up data objects  (Composite works correctly on Windows)
270        if "__WXMSW__" in wx.PlatformInfo:
271            self.do = wx.DataObjectComposite()
272            self.filedo = wx.FileDataObject()
273            self.textdo = wx.TextDataObject()
274            self.do.Add(self.textdo)
275            self.do.Add(self.filedo, True)
276        else:
277            self.do = wx.FileDataObject()
278       
279        wx.PyDropTarget.__init__(self, self.do)
280       
281        # delayed server object creation for the CanvasDropTarget
282        # when a file is dropped, we dont know which library to upload the file to so
283        # that's when we decide which library to connect to
284        if host!=None and port!=None:
285            self.serverObj = GetFileServer(host, port)
286            self.server = self.serverObj.server
287
288
289        # this must be called if you passed in None to the constructor
290    def SetLibrary(self, host, port):
291        self.serverObj = GetFileServer(host, port)
292        self.server = self.serverObj.server
293
294
295    def GetServer(self):
296        return self.server
297
298   
299    def OnData(self, x, y, d):
300        if not self.serverObj.IsConnected():
301            Message("Unable to upload/show file. There is no connection with the File Server.", "No connection")
302            return False
303       
304        self.GetData() #get the dropped data
305
306        if "__WXMSW__" in wx.PlatformInfo:
307            if self.textdo.GetTextLength() > 1:    # empty text has length = 1
308                droppedText = self.textdo.GetText()
309                self.textdo.SetText("")   #reset the text
310                return self.__DownloadURL(droppedText)
311            else:
312                files = self.filedo.GetFilenames()[0]  #extract the filenames from the dropped data
313                #files = unicode(files, "utf_8")
314                if os.path.isdir(files):
315                    Message("You cannot upload directories. Only individual files.", "Upload Failed")
316                    return False
317                else:
318                    return files
319        else:
320            filenames = self.do.GetFilenames()
321            if not filenames: return False
322            files = filenames[0]  #extract the filenames from the dropped data
323            if os.path.isdir(files):
324                Message("You cannot upload directories. Only individual files.", "Upload Failed")
325                return False
326            else:
327                return files
328
329           
330        # checks if the file the url is pointing to is valid,
331        # if so, it downloads it and returns the local path to it
332    def __DownloadURL(self, url):
333        if os.path.isfile(url):  # if it's a local file so just return it's path
334            return url
335        elif url.startswith("http://") or url.startswith("ftp://"):   #if it's a url
336            filename = os.path.basename(url)
337            if self.server.GetFileType(filename):   # is the file type even supported?
338                urllib.urlcleanup()  #cleanup the cache
339                urllib.urlretrieve(url, filename)
340                return os.path.abspath(filename)
341            else:                                # file type not supported
342                extension = os.path.splitext(filename)[1].lower()
343                Message("File type <"+extension+"> not supported.", "Invalid File Type")
344                return False
345        else:
346            return False
347
348
349
350class CanvasDropTarget(FileDropTarget):
351    def __init__(self, canvas):
352        self.canvas = canvas
353        self.sageGate = self.canvas.sageGate
354        self.sageHost = self.sageGate.sageHost  #for the filegrabber
355        self.lastX = 0
356        self.lastY = 0
357       
358        FileDropTarget.__init__(self) # dont provide library location right away since we will let the user choose that each time
359
360
361    def OnDrop(self, x, y):
362        self.lastX = self.canvas.ToSAGECoordsX(x, 0)
363        self.lastY = self.canvas.ToSAGECoordsY(y, 0)
364        return True
365
366
367    def OnData(self, x, y, d):   # just upload the file to the libary on the machine we are connected to
368        self.SetLibrary(self.sageHost, XMLRPC_PORT) 
369        filePath = FileDropTarget.OnData(self,x,y,d)
370        if filePath:
371            uploadResult = self.serverObj.UploadFile(filePath)
372        else: return False
373           
374        if uploadResult:  #if upload succeeded
375            (fileType, appName, fullRemotePath, size, params, fileExists) = uploadResult
376            self.ShowFile(fileType, appName, fullRemotePath, size, params, True)
377        return d
378
379
380            # run the show file in a thread
381    def ShowFile(self, fileType, fullPath, appName, params, size, doPrep=True):
382        t = Thread(target=self.DoShowFile, args=(fileType, fullPath, appName, params, size, doPrep))
383        t.start()
384
385
386    def DoShowFile(self, fileType, appName, fullRemotePath, size, params, doPrep=True):
387        # first, make sure that the file exists
388        if doPrep:
389            prepResult = self.server.PrepareFile(fullRemotePath, self.sageHost)
390            if not prepResult:
391                Message("Unable to show file because the transfer of file from the FileServer to the SAGE machine failed.", "Error")
392                return
393            else:
394                fullPath = prepResult #the path was returned by the FileGrabber
395           
396        totalDisplayWidth = self.canvas.tiledDisplayWidth
397        totalDisplayHeight = self.canvas.tiledDisplayHeight
398
399        if fileType == "image":
400            imageWidth = size[0]
401            imageHeight = size[1]
402            imageAspectRatio = float( imageWidth/imageHeight )
403
404            # figure out if the image is going to fit, if not, reposition and resize it as necessary
405            # first resize if necessary
406            widthRatio = float(totalDisplayWidth / float(imageWidth))
407            heightRatio = float(totalDisplayHeight / float(imageHeight))
408            if widthRatio < 1 or heightRatio < 1:
409                if widthRatio > heightRatio:   #we'll resize based on height
410                    resizeRatio = heightRatio
411                else:
412                    resizeRatio = widthRatio   #we'll resize based on width
413                imageWidth = int(imageWidth * resizeRatio)
414                imageHeight = int(imageHeight * resizeRatio)         
415
416            # now reposition
417            cornerX = self.lastX - int(imageWidth/2)
418            cornerY = self.lastY - int(imageHeight/2)
419
420            # is the image off the screen up or down?
421            if (totalDisplayHeight - cornerY) < imageHeight:
422                cornerY = int(totalDisplayHeight - imageHeight)
423            elif cornerY < 0:
424                cornerY = 0
425
426            # is the image off the screen left or right?
427            if (totalDisplayWidth - cornerX) < imageWidth:
428                cornerX = int(totalDisplayWidth - imageWidth)
429            elif cornerX < 0:
430                cornerX = 0
431            res = self.sageGate.executeApp(appName, pos=(cornerX, cornerY), size=(imageWidth,imageHeight), optionalArgs = fullRemotePath+" "+str(imageWidth)+" "+str(imageHeight)+" "+params)
432           
433        elif fileType == "video" or fileType == "pdf":
434            res = self.sageGate.executeApp(appName, pos=(self.lastX, self.lastY), optionalArgs=params+" "+fullRemotePath)
435           
436        else:  #for other types
437            res = self.sageGate.executeApp(appName, optionalArgs=fullRemotePath+" "+params)
438
439        if res == -1:
440            Message("Application not started. Either application failed, the application launcher is not running or the application  <<"+appName+">>  is not configured in application launcher.", "Application Launch Failed")
441       
442
443
444
445class LibraryDropTarget(FileDropTarget):
446    def __init__(self, parent, host, port=XMLRPC_PORT):
447        self.parent = parent
448        FileDropTarget.__init__(self, host, port)
449       
450       
451    def OnData(self, x, y, d):
452        filePath = FileDropTarget.OnData(self,x,y,d)
453        if not filePath: return
454        uploadResult = self.serverObj.UploadFile(filePath)
455        if uploadResult:  #if upload succeeded
456            (fileType, appName, fullRemotePath, size, params, fileExists) = uploadResult
457            if not fileExists:  #only add a file to the tree if the file doesnt exist yet
458                self.parent.OnNewFile(fullRemotePath, fileType)
459            else:
460                Message("File already exists in the file library.", "Duplicate Found")
461
462
463
464dirTextColor = wx.Colour(204, 153, 102)#250,50,50)
465
466class FileLibrary(wx.Frame):
467    def __init__(self, parent, parentPos):
468        self.canvas = parent
469        self.sageGate = self.canvas.sageGate
470        self.sageHost = self.sageGate.sageHost  #for the filegrabber
471
472        res = LibraryChoiceDialog(parent).ShowDialog()
473        if not res:
474            return  #user pressed cancel when choosing the library
475        else:
476            (self.libName, self.libHost) = res
477
478       
479        self.dt = LibraryDropTarget(self, self.libHost, XMLRPC_PORT)
480        self.server = self.dt.GetServer()
481        self.serverObj = self.dt.serverObj
482
483        # we must be connected in order to see the library
484        if not self.serverObj.IsConnected():
485            Message("Unable to access file library. There is no connection with the File Server.", "No connection")
486        else:
487            x,y=parentPos
488            wx.Frame.__init__(self, parent, help.FILE_LIBRARY, "File Library - "+self.libName, pos=(x+200, y+200), size=(550,550))
489            self.previewSize = (150,150)
490            self.panel = wx.lib.scrolledpanel.ScrolledPanel(self, wx.ID_ANY)
491            self.__SetupLayout()
492           
493            # so that wherever we press F1 key it will bring up help for that window
494            # the help.ShowHelp function will check for the correct key press
495            self.Bind(wx.EVT_CHAR, help.ShowHelp)
496
497            # make the tree
498            self.tree = wx.TreeCtrl(self.panel, wx.ID_ANY, style=wx.TR_NO_LINES | wx.TR_SINGLE | wx.TR_HAS_BUTTONS)
499            self.tree.SetDropTarget(self.dt)
500            self.root = self.tree.AddRoot("Files")
501            self.imList = wx.ImageList(16,16)
502            self.tree.SetImageList(self.imList)  #image list must be created in order to have DnD capabiliy... weird
503            self.MakeTree()
504            self.tree.Bind(wx.EVT_RIGHT_DOWN, self.OnRightClick)
505            self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged)
506            self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnShowButton)
507            self.tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
508            self.tree.Bind(wx.EVT_TREE_END_DRAG, self.OnEndDrag)
509            self.tree.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
510            self.tree.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
511            self.tree.Bind(wx.EVT_MOTION, self.OnMouseMove)
512            self.currentItemId = None
513
514            # set the colors
515            self.tree.SetBackgroundColour(appPanelColor)
516            self.tree.SetForegroundColour(wx.WHITE)
517            self.panel.SetBackgroundStyle(wx.BG_STYLE_COLOUR)
518            self.panel.SetBackgroundColour(dialogColor)
519            self.SetForegroundColour(wx.WHITE)
520
521            # add main things to the sizer
522            self.mainSizer.Add(self.tree, 1, wx.EXPAND)
523            self.mainSizer.Add(self.vertLine, 0, wx.EXPAND )
524            self.mainSizer.Add(self.vertSizer, 0, wx.EXPAND)
525
526            # attach the sizer to the panel
527            self.panel.SetSizer(self.mainSizer)
528            self.panel.SetAutoLayout(1)
529            self.panel.SetupScrolling()
530
531            wx.Frame.Show(self)
532
533
534    def __SetupLayout(self):       
535        self.mainSizer = wx.BoxSizer(wx.HORIZONTAL)
536        self.vertSizer = wx.BoxSizer(wx.VERTICAL)
537        self.horSizer = wx.BoxSizer(wx.HORIZONTAL)
538
539        self.vertLine = wx.StaticLine(self.panel, style=wx.LI_VERTICAL)
540
541        self.showBtn = wx.Button(self.panel, wx.ID_ANY, "Show")
542        self.showBtn.Bind(wx.EVT_BUTTON, self.OnShowButton)
543        self.showBtn.Disable()
544        self.__SetupButton(self.showBtn)
545       
546        self.shareCheckBox = wx.CheckBox(self.panel, wx.ID_ANY, "Shareable app? (uses sageBridge)")
547        self.shareCheckBox.SetForegroundColour(wx.WHITE)
548        self.shareCheckBox.SetBackgroundStyle(wx.BG_STYLE_COLOUR)
549        self.shareCheckBox.SetBackgroundColour(dialogColor)
550
551        self.dxtCheckBox = wx.CheckBox(self.panel, wx.ID_ANY, "Use DXT (faster, lower quality)")
552        self.dxtCheckBox.SetValue(True)
553        self.dxtCheckBox.SetForegroundColour(wx.WHITE)
554        self.dxtCheckBox.SetBackgroundStyle(wx.BG_STYLE_COLOUR)
555        self.dxtCheckBox.SetBackgroundColour(dialogColor)
556
557        self.refreshBtn = wx.Button(self.panel, wx.ID_ANY, "Refresh")
558        self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshTree)
559        self.__SetupButton(self.refreshBtn, smaller=True)
560               
561        self.browseBtn = wx.Button(self.panel, wx.ID_ANY, "Browse")
562        self.browseBtn.Bind(wx.EVT_BUTTON, self.BrowseFiles)
563        self.__SetupButton(self.browseBtn, smaller=True)
564       
565        self.helpBtn = wx.Button(self.panel, help.FILE_LIBRARY, "Help")
566        self.helpBtn.Bind(wx.EVT_BUTTON, help.ShowHelp)
567        self.__SetupButton(self.helpBtn, smaller=True)
568
569        self.searchField = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
570        self.searchField.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
571        self.searchField.Bind(wx.EVT_TEXT, self.OnSearch)
572        tTip = wx.ToolTip("Search as you type...\npress ENTER to search again")
573        tTip.SetDelay(200)
574        self.searchMsg = wx.StaticText(self.panel, wx.ID_ANY, "", style=wx.ALIGN_CENTRE)
575        self.searchMsg.SetForegroundColour(wx.WHITE)
576        self.searchField.SetToolTip(tTip)
577        self.searchBtn = wx.Button(self.panel, wx.ID_ANY, "Find Next")
578        self.searchBtn.Bind(wx.EVT_BUTTON, self.OnSearch)
579        self.__SetupButton(self.searchBtn)
580        tTip = wx.ToolTip("Same as pressing ENTER in the search field")
581        tTip.SetDelay(200)
582        self.searchBtn.SetToolTip(tTip)
583                             
584        self.horSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, border=5)
585        self.horSizer.Add(self.browseBtn, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, border=10)
586        self.horSizer.Add(self.helpBtn, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, border=5)
587       
588        self.no_preview = wx.Image(ConvertPath("images/no_preview.png")).ConvertToBitmap()
589        self.retrieving_preview = wx.Image(ConvertPath("images/retrieving_preview.png")).ConvertToBitmap()
590        self.currentImage = wx.StaticBitmap(self.panel, wx.ID_ANY, self.no_preview, size=self.previewSize)
591
592        self.vertSizer.Add(self.horSizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_TOP | wx.TOP | wx.EXPAND, border=10)
593        self.vertSizer.Add(wx.StaticLine(self.panel, style=wx.LI_HORIZONTAL), 0, wx.EXPAND | wx.ALIGN_TOP | wx.TOP, border=5)
594        self.vertSizer.AddSpacer((1,1),1)
595        self.vertSizer.Add(self.currentImage, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.TOP, border=20)
596        self.vertSizer.Add(self.showBtn, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, border=5)
597        self.vertSizer.Add(self.shareCheckBox, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, border=5)
598        self.vertSizer.Add(self.dxtCheckBox, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, border=5)
599        self.vertSizer.AddSpacer((1,1),1)
600        self.vertSizer.Add(wx.StaticLine(self.panel, style=wx.LI_HORIZONTAL), 0, wx.EXPAND | wx.ALIGN_BOTTOM | wx.BOTTOM, border=10)
601        self.vertSizer.Add(self.searchField, 0, wx.ALIGN_BOTTOM | wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
602        self.vertSizer.Add(self.searchMsg, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
603        self.vertSizer.Add(self.searchBtn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, border=10)
604
605       
606    def __SetupButton(self, btn, smaller=False):
607        if "__WXMAC__" not in wx.PlatformInfo:
608            if smaller:
609                sz = btn.GetSize()
610                btn.SetMaxSize((sz[0], sz[1]-5))
611            btn.SetBackgroundColour(wx.Colour(153, 255, 255))
612            btn.SetForegroundColour(wx.BLACK)
613       
614
615    def MakeTree(self):
616        self._alreadyFound = []  # a list of already found itemIds
617        self._searchString = ""  # a current search string
618        self.branches = {}   #treeItemIds keyed by file types
619        self.favoritesBranches = {}   #treeItemIds keyed by file types
620       
621        # get the newest list of files from the File Server
622        try:
623            fileHash = self.server.GetFiles()
624        except socket.error:
625            Message("Unable to retrieve the file list. No connection with the File Server.", "No Connection")
626            return
627
628        # make the parents (main folders)
629        for fType, tpl in fileHash.iteritems():
630            tpl[2].sort(lambda x, y: cmp(x.lower(), y.lower()))
631            self.MakeBranch(fType, tpl[0])  #include the dir path
632            self.MakeNodes(self.branches[fType], tpl)
633
634        # make the favorites folder
635        self.MakeBranch("favorites", None)  #None dirPath (virtual directory)
636        for fType in self.branches.iterkeys():
637            if fType != "favorites":
638                self.MakeFavoritesBranch(fType)
639
640        self.LoadFavorites()
641
642        # bind events and finish the tree
643        self.tree.Expand(self.root)
644
645
646        ### recursively go through the data retrieved from server.GetFiles
647        ### and recreate the directory structure as a tree
648    def MakeNodes(self, parentId, tpl):
649        (dirPath, dirs, files, fileType) = tpl
650        sortedDirs = dirs.keys()
651        sortedDirs.sort(lambda x, y: cmp(x.lower(), y.lower()))
652       
653        for dirName in sortedDirs:
654            dirTpl = dirs[dirName]
655            dirId = self.tree.AppendItem(parentId, dirName)
656            dirObj = Node(dirName, fileType, dirPath, False, False)
657            self.tree.SetItemTextColour(dirId, dirTextColor)
658            self.tree.SetPyData(dirId, dirObj)
659            self.MakeNodes(dirId, dirTpl)
660
661        files.sort(lambda x, y: cmp(x.lower(), y.lower()))
662        for f in files:
663            fileObj = Node(f, fileType, dirPath, True, False)
664            fId = self.tree.AppendItem(parentId, f)
665            self.tree.SetPyData(fId, fileObj)
666
667
668        ### opens a file browser for your local machine so that you can add files to the library that way
669    def BrowseFiles(self, event=None):
670        dlg = wx.FileDialog(self, str("Add to the file library"), defaultDir=os.getcwd(), style=wx.OPEN | wx.FILE_MUST_EXIST)
671        if dlg.ShowModal() == wx.ID_OK:
672            uploadResult = self.serverObj.UploadFile(dlg.GetPath())  # do the checks, upload the file...
673            if uploadResult:  #if upload succeeded
674                (fileType, appName, fullRemotePath, size, params, fileExists) = uploadResult
675                if not fileExists:  #only add a file to the tree if the file doesnt exist yet
676                    self.OnNewFile(fullRemotePath, fileType)
677                else:
678                    Message("File already exists in the file library.", "Duplicate Found")
679        dlg.Destroy()
680
681
682        # called when a new file is dropped on the file library
683    def OnNewFile(self, fullRemotePath, fileType):
684        def AlreadyThere(filename, type):
685            (siblingId, cookie) = self.tree.GetFirstChild(self.branches[type])
686            while siblingId.IsOk():
687                if self.tree.GetPyData(siblingId).GetName() == filename:
688                    return True   # no need to add to favorites since it already exists in there
689                siblingId = self.tree.GetNextSibling(siblingId)
690            return False  #if we got here that means that there are no files with the same name already
691           
692
693        (path, filename) = os.path.split(fullRemotePath)
694        if AlreadyThere(filename, fileType): return   # check if the file already exists in the tree... if it does no need to update it
695        itemId = self.tree.AppendItem(self.branches[fileType], filename)
696        self.tree.SetPyData(itemId, Node(filename, fileType, path, True, False))
697     
698
699    def MakeBranch(self, fType, path):
700        if not self.branches.has_key(fType):
701            self.branches[fType] = self.tree.AppendItem(self.root, fType)
702            self.tree.SetItemTextColour(self.branches[fType], dirTextColor)
703            self.tree.SetPyData(self.branches[fType], Node("", fType, path, False, fType=="favorites", True))
704
705
706    def MakeFavoritesBranch(self, fType):
707        if not self.favoritesBranches.has_key(fType):
708            self.favoritesBranches[fType] = self.tree.AppendItem(self.branches["favorites"], fType)
709            self.tree.SetItemTextColour(self.favoritesBranches[fType], dirTextColor)
710            self.tree.SetPyData(self.favoritesBranches[fType], Node(fType, fType, "", False, True, True))
711
712
713    def RefreshTree(self, event=None):  #does not refresh favorites
714        self.tree.CollapseAndReset(self.root)
715        self.MakeTree()
716        self.currentImage.SetBitmap(self.no_preview)
717
718
719    def OnSearch(self, event):
720        self.searchMsg.SetLabel("")
721        if event.GetEventType() == wx.EVT_TEXT_ENTER.evtType[0] or event.GetEventType() == wx.EVT_BUTTON.evtType[0]:
722            selectedItemId = self.tree.GetSelection()
723            self._alreadyFound.append(selectedItemId)
724        elif event.GetEventType() == wx.EVT_TEXT.evtType[0]:
725            self._alreadyFound = []  #if the user changed the search string, restart the search
726
727        # start the search
728        resultItemId = self.SearchAndSelect(self.root, self.searchField.GetValue())
729        if resultItemId != None and resultItemId.IsOk():
730            self.tree.SelectItem(resultItemId, True)
731        else:
732            if resultItemId == None:
733                self.searchMsg.SetLabel("")  # display nothing for the empty string
734            else:
735                self.searchMsg.SetLabel("Nothing Found.")
736
737
738        # search through a tree recursively
739    def SearchAndSelect(self, root, searchString):
740        if len(searchString) == 0:
741            self._alreadyFound = []
742            return  #dont search for an empty string
743
744        (itemId, cookie) = self.tree.GetFirstChild( root )
745        while itemId.IsOk():
746            itemData = self.tree.GetPyData(itemId)
747            if itemData.GetType() == "favorites":  #skip the favorites
748                (itemId, cookie) = self.tree.GetNextChild( root, cookie )
749                continue
750            if searchString.lower() in itemData.GetName().lower():
751                if not itemId in self._alreadyFound:
752                    self._alreadyFound.append(itemId)
753                    return itemId
754            if self.tree.ItemHasChildren( itemId ):
755                itemId = self.SearchAndSelect( itemId, searchString )
756                if itemId.IsOk():
757                    return itemId
758            (itemId, cookie) = self.tree.GetNextChild( root, cookie )
759
760        return itemId
761
762           
763    def OnBeginDrag(self, evt):
764        self.draggedItemId = evt.GetItem()
765        itemData = self.tree.GetPyData(self.draggedItemId)
766        if itemData != None and itemData.IsFile() and not itemData.IsFavorite():
767            evt.Allow()
768           
769
770    def OnEndDrag(self, evt):
771        onItemId = evt.GetItem() #, flags = self.tree.HitTest(evt.GetPoint())
772        if onItemId.IsOk():  #make sure that we dropped it on a legal spot
773            onItemData = self.tree.GetPyData(onItemId)
774            draggedItemData = self.tree.GetPyData(self.draggedItemId)
775            draggedFilename = draggedItemData.GetName()
776            draggedType = draggedItemData.GetType()
777
778            if onItemData == None:  # files can't be dropped on the root nor can we drag favorites
779                return
780
781            elif onItemData.IsFavorite():   # we dropped it in the "favorites" part
782                if prefs.favorite_files.AlreadyFavorite(draggedItemData): return # check if the file already exists in favs
783                favItemId = self.tree.AppendItem(self.favoritesBranches[draggedType], draggedFilename)
784                favItemData = copy.copy(draggedItemData)
785                favItemData.SetFavorite(True)  #we use the same data from the original file but mark it as a favorite
786                self.tree.SetPyData(favItemId, favItemData)
787                prefs.favorite_files.AddFavorite(favItemData)
788           
789            elif onItemData.GetType() == draggedType:  # dropped it in a non-favorites area
790                parentId = onItemId
791                newFullPath = os.path.join(onItemData.GetFullPath(), draggedFilename)
792                oldFullPath = draggedItemData.GetFullPath()
793                if onItemData.IsFile():  #if we dropped it on a file pretend it was the directory above
794                    parentId = self.tree.GetItemParent(onItemId)
795                    parentData = self.tree.GetPyData(parentId)
796                    newFullPath = os.path.join( parentData.GetFullPath(), draggedFilename)
797                if newFullPath == oldFullPath:  #if the file was dropped within the same dir, do nothing
798                    return
799                try:
800                    res = self.server.MoveFile(oldFullPath, newFullPath)
801                except socket.error:
802                    Message("Error moving file. No connection with the File Server.", "No Connection")
803                else:
804                    if res != True:
805                        Message("Error occured while moving the file. Does the file exist?", "Move Unsuccessful")
806                        self.RefreshTree()
807                    else: # move successful, rearrange the tree
808                        self.MoveNode(parentId, self.draggedItemId, newFullPath, draggedType)
809
810
811    def MoveNode(self, parentId, oldItemId, newFullPath, fileType):
812        self.tree.Delete(oldItemId)  #first delete the old node
813        (path, filename) = os.path.split(newFullPath)
814        itemId = self.tree.AppendItem(parentId, filename)
815        self.tree.SetPyData(itemId, Node(filename, fileType, path, True, False))
816
817
818    def LoadFavorites(self):  #must be done after the favorites branches have been built already
819        favoriteFilesData = prefs.favorite_files.GetFavorites()
820        if not favoriteFilesData: return
821
822        # insert the nodes into the tree
823        for type, items in favoriteFilesData.iteritems():
824            if self.favoritesBranches.has_key(type):
825                for itemData in items:
826                    itemId = self.tree.AppendItem(self.favoritesBranches[type], itemData.GetName())
827                    self.tree.SetPyData(itemId, itemData)
828
829
830    def OnMouseEnter(self, evt):
831        if evt.Dragging():
832            self.__DestroyPopup()
833            evt.Skip()
834
835
836    def OnMouseLeave(self, evt):
837        self.__DestroyPopup()
838
839
840    def __DestroyPopup(self):
841        if hasattr(self, "pd"): self.pd.Destroy(); del self.pd
842        self.currentItemId = None
843
844
845        # when we move a mouse over a file, display its metadata
846    def OnMouseMove(self, evt):
847        if evt.Dragging():
848            evt.Skip()
849        pt = evt.GetPosition()
850        itemId, flags = self.tree.HitTest(pt)
851        if not itemId.IsOk():
852            self.__DestroyPopup()
853            return
854        self.ShowMetadata(itemId)
855
856        # shows the metadata for a given itemId inside a popup
857    def ShowMetadata(self, itemId):
858        itemData = self.tree.GetPyData(itemId)
859       
860        if itemData == None:
861            self.__DestroyPopup()
862            return    #don't do anything if the user moved over the root
863
864        # get the metadata now if it's a file we are over
865        if itemData.IsFile():
866            if self.currentItemId == itemId:
867                return   #we already have a tooltip for this one
868            self.__DestroyPopup()
869            self.currentItemId = itemId
870            metadata = self.server.GetMetadata(itemData.GetFullPath())
871            if metadata:
872                md = itemData.GetName()+"\n"+metadata
873                self.pd = PopupDialog(self.tree)#popup.PopupControl(self.tree)#, metadata)
874                text = wx.StaticText(self.pd, wx.ID_ANY, md, pos=(2,2))# etadata.lstrip())
875                self.pd.SetContent(text)
876                self.pd.Display()
877            else:
878                self.__DestroyPopup()
879
880
881    def OnRightClick(self, evt):
882        pt = evt.GetPosition()
883        itemId, flags = self.tree.HitTest(pt)
884        itemData = self.tree.GetPyData(itemId)
885        self.tree.SelectItem(itemId)
886       
887        if itemData == None:
888            return    #don't do anything if the user right-clicked on the root
889
890        # show the menu now
891        menu = wx.Menu()
892        if itemData.IsFile():                     # FILES
893            if itemData.IsFavorite():
894                delMenuItem = menu.Append(1, "Remove From Favorites")
895                menu.Bind(wx.EVT_MENU, self.OnRemoveFavoritesFile, delMenuItem)
896            else:
897                delMenuItem = menu.Append(1, "Delete File")
898                menu.Bind(wx.EVT_MENU, self.OnDeleteFile, delMenuItem)
899        elif not itemData.IsFile() and not itemData.IsFavorite():        # FOLDERS
900            newMenuItem = menu.Append(1, "New Folder")
901            menu.Bind(wx.EVT_MENU, self.OnNewFolder, newMenuItem)
902            if not itemData.IsBranch():           # you cant delete branches
903                delMenuItem = menu.Append(2, "Delete Folder")
904                menu.Bind(wx.EVT_MENU, self.OnDeleteFolder, delMenuItem)
905       
906        self.tree.PopupMenu(menu)
907
908
909    def OnNewFolder(self, evt):
910        newFolderName = wx.GetTextFromUser("New Folder Name: ", "New Folder")
911        newFolderName = "_".join(newFolderName.split())
912        if newFolderName == "":
913            return
914        itemId = self.tree.GetSelection()
915        itemData = self.tree.GetPyData(itemId)
916        itemType = itemData.GetType()
917        newFullPath = os.path.join( itemData.GetFullPath(), newFolderName)
918        try:
919            res = self.server.NewFolder(newFullPath)
920        except socket.error:
921            Message("Error creating the directory. No connection with the File Server.", "No Connection")
922        else:
923            if res != True:
924                Message("Error occured while creating the directory. Does the path leading to it still exist?", "Directory Creation Unsuccessful")
925                self.RefreshTree()
926            else: # make successful, rearrange the tree
927                self.MakeFolder(itemId, newFullPath, itemType)
928
929
930    def MakeFolder(self, parentId, newFullPath, fileType):
931        (path, name) = os.path.split(newFullPath)
932        itemId = self.tree.AppendItem(parentId, name)
933        self.tree.SetItemTextColour(itemId, dirTextColor)
934        self.tree.SetPyData(itemId, Node(name, fileType, path, False, False))
935        self.tree.SelectItem(itemId)
936       
937
938    def OnDeleteFolder(self, evt):
939        itemId = self.tree.GetSelection()
940        itemData = self.tree.GetPyData(itemId)
941        fullPath = itemData.GetFullPath()
942        if ChoiceDialog("This will delete the DIR and all the FILES in it permanently from the File Server which means that no " \
943                        "user will be able to access it/them anymore.\nAre you sure you want to continue?", \
944                        "Deletion Confirmation"):
945            try:
946                res = self.server.DeleteFolder(fullPath)
947            except socket.error:
948                Message("Error deleting the directory. No connection with the File Server.", "No Connection")
949            else:
950                if res != True:
951                    Message("Error occured while deleting the directory. Are the permissions correct?", "Directory Deletion Unsuccessful")
952                    self.RefreshTree()
953                else: # delete successful, rearrange the tree
954                    self.tree.Delete(itemId)
955
956
957    def OnDeleteFile(self, evt):
958        itemId = self.tree.GetSelection()
959        itemData = self.tree.GetPyData(itemId)
960        if itemData == None:
961            return  #the user clicked on one of the root folders which are not modifiable
962        if ChoiceDialog("This will delete the file permanently from the File Server which means that no " \
963                        "user will be able to access it anymore.\nAre you sure you want to continue?", \
964                        "Deletion Confirmation"):
965            try:
966                res = self.server.DeleteFile(itemData.GetFullPath())
967            except socket.error:
968                Message("Error deleting file. No connection with the File Server.", "No Connection")
969            else:
970                if res != True:
971                    Message("Error occured while deleting file. Does the file exist?", "Delete Unsuccessful")
972                    self.RefreshTree()
973                else:  # delete that node from the tree
974                    self.tree.Delete(itemId)
975                    self.currentImage.SetBitmap(self.no_preview)
976
977
978    def OnRemoveFavoritesFile(self, event):
979        itemId = self.tree.GetSelection()
980        prefs.favorite_files.RemoveFavorite(self.tree.GetPyData(itemId))  # you must do this before deleting the item
981        self.tree.Delete(itemId)
982        #self.SaveFavorites()
983
984
985
986    def OnShowButton(self, evt):
987        itemId = self.tree.GetSelection()
988        if not itemId.IsOk():
989            return
990        itemData = self.tree.GetPyData( itemId )
991 
992
993        # check if the user double clicked on a parent and not a leaf
994        # if so, expand/collapse the parent
995        if itemData == None or not itemData.IsFile():
996            if self.tree.IsExpanded(itemId):
997                self.tree.Collapse(itemId)
998            else:
999                self.tree.Expand(itemId)
1000            return
1001
1002        # show the file (check if it exists first)
1003        try:
1004            c = wx.BusyCursor()
1005            fileInfo = self.server.GetFileInfo(itemData.GetName(), 0, itemData.GetPath())
1006            del c
1007            if not fileInfo:   #file not supported
1008                Message("File type <"+os.path.splitext(itemData.GetName())[1]+"> not supported.", "Invalid File Type")
1009                return
1010            (fileType, size, fullPath, appName, params, fileExists) = fileInfo
1011            if not fileExists:  #file doesnt exist
1012                Message("File <"+itemData.GetName()+"> does not seem to exist anymore.\nTry closing the library and opening it again in order to refresh the file list.", "Invalid Filename")
1013                return
1014            self.ShowFile(fileType, fullPath, appName, params, size)
1015        except socket.error:
1016            del c
1017            Message("Unable to show file. There is no connection with the File Library.", "No connection")
1018       
1019
1020    def OnSelChanged(self, evt):
1021        class PreviewTimer(wx.Timer):
1022            def __init__(self, action, itemId):
1023                self.action = action
1024                self.itemId = itemId
1025                wx.Timer.__init__(self)
1026            def Notify(self):
1027                c = wx.BusyCursor()
1028                try:
1029                    self.action(self.itemId)
1030                except socket.error, xmlrpclib.ProtocolError:
1031                    pass   # no preview retrieved... oh well
1032                del c
1033
1034        def GetPreview(itemId):
1035            itemData = self.tree.GetPyData(itemId)
1036            if itemData == None or not itemData.IsFile():
1037                self.showBtn.Disable()
1038                return  # do nothing if a leaf wasn't selected
1039            self.showBtn.Enable()
1040
1041            # no preview if we didnt select an image
1042            #if itemData.GetType() != "image":
1043            #    self.currentImage.SetBitmap(self.no_preview)
1044            #    return
1045
1046            # get the preview from the server (and get other info like the size of the image)
1047            self.currentImage.SetBitmap(self.retrieving_preview)
1048            try:
1049                preview = self.server.GetPreview(itemData.GetFullPath(), self.previewSize)
1050            except:
1051                self.currentImage.SetBitmap(self.no_preview)
1052                return
1053            if not preview:  #preview retrieval failed for some reason
1054                self.currentImage.SetBitmap(self.no_preview)
1055            else:
1056                (previewData, isBinary) = preview
1057                if isBinary:
1058                    stream=wx.InputStream(cStringIO.StringIO(base64.decodestring(previewData))) #make a stream out of the image
1059                    im = wx.ImageFromStream(stream)
1060                    im.Rescale(PREVIEW_SIZE[0], PREVIEW_SIZE[1])
1061                else:
1062                    im = wx.EmptyImage(PREVIEW_SIZE[0], PREVIEW_SIZE[1])
1063                    im.SetData(base64.decodestring(previewData))
1064
1065                if im.Ok():
1066                    self.currentImage.SetBitmap(im.ConvertToBitmap())
1067                else:
1068                    self.currentImage.SetBitmap(self.no_preview)
1069               
1070        # show the preview on a timer (done so that search-as-you-type is faster)
1071        if hasattr(self, "_timer"):
1072            if self._timer.IsRunning():
1073                self._timer.Stop()
1074        self._timer = PreviewTimer(GetPreview, evt.GetItem())
1075        self._timer.Start(300, True)
1076
1077
1078        # run the show file in a thread
1079    def ShowFile(self, fileType, fullPath, appName, params, size):
1080        t = Thread(target=self.DoShowFile, args=(fileType, fullPath, appName, params, size))
1081        t.start()
1082
1083   
1084    def DoShowFile(self, fileType, fullPath, appName, params, size):
1085        prepResult = self.server.PrepareFile(fullPath, self.sageHost)
1086        bridge = self.shareCheckBox.GetValue()
1087
1088        if self.dxtCheckBox.GetValue():
1089            useDXT = ""
1090        else:
1091            useDXT = "-show_original"
1092       
1093        if not prepResult:
1094            Message("Unable to show file because the transfer of file from the FileServer to the SAGE machine failed.", "Error")
1095            return
1096        else:
1097            fullPath = prepResult #the path was returned by the FileGrabber
1098
1099        if fileType == "image":
1100            totalDisplayWidth = self.canvas.tiledDisplayWidth
1101            totalDisplayHeight = self.canvas.tiledDisplayHeight
1102            imageWidth = size[0]
1103            imageHeight = size[1]
1104            imageAspectRatio = float( imageWidth/imageHeight )
1105
1106            # figure out if the image is going to fit, if not, reposition and resize it as necessary
1107            # first resize if necessary
1108            widthRatio = float(totalDisplayWidth / float(imageWidth))
1109            heightRatio = float(totalDisplayHeight / float(imageHeight))
1110            if widthRatio < 1 or heightRatio < 1:
1111                if widthRatio > heightRatio:   #we'll resize based on height
1112                    resizeRatio = heightRatio
1113                else:
1114                    resizeRatio = widthRatio   #we'll resize based on width
1115                imageWidth = int(imageWidth * resizeRatio)
1116                imageHeight = int(imageHeight * resizeRatio)         
1117
1118            # now reposition
1119            cornerX = 0
1120            cornerY = 0
1121           
1122            res = self.sageGate.executeApp(appName ,pos=(cornerX, cornerY), size=(imageWidth,imageHeight), optionalArgs = fullPath+" "+str(imageWidth)+" "+str(imageHeight)+" "+useDXT+" "+params, useBridge=bridge)
1123        elif fileType == "video" or fileType=="pdf":
1124            res = self.sageGate.executeApp(appName, optionalArgs=params+" "+fullPath, useBridge=bridge)
1125        else:  #for other types
1126            res = self.sageGate.executeApp(appName, optionalArgs=fullPath+" "+params, useBridge=bridge)
1127
1128        if res == -1:
1129            Message("Application not started. Either application failed, the application launcher is not running or the application  <<"+appName+">>  is not configured in application launcher.", "Application Launch Failed")
1130           
1131
1132
1133### shows the dialog with a list of file libraries that you can
1134### connect to. ShowDialog returns the selected library (Actually it's
1135### IP address)
1136class LibraryChoiceDialog(wx.Dialog):
1137    def __init__(self, parent):
1138        wx.Dialog.__init__(self, parent, wx.ID_ANY, "Choose File Library", style=wx.CAPTION | wx.CLOSE_BOX | wx.RESIZE_BORDER)
1139        self.SetBackgroundColour(dialogColor)
1140        self.SetForegroundColour(wx.WHITE)
1141        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
1142        self.horSizer1 = wx.BoxSizer(wx.HORIZONTAL)
1143        self.SetMinSize((100, 200))
1144        self.__MakeControls()
1145       
1146
1147
1148    def __MakeControls(self):
1149        self.ID_ADD = 500
1150        self.ID_DEL = 501
1151        self.ID_UPLOAD = 502
1152        self.ID_SHOW = 503
1153       
1154        self.libList = wx.ListBox(self, wx.ID_ANY, choices=prefs.fileLib.GetLibraryList(), style=wx.LB_SINGLE)
1155        self.libList.SetStringSelection( prefs.fileLib.GetDefault()[0] )
1156        self.libList.Bind(wx.EVT_LISTBOX_DCLICK, self.__OnDoubleClick)
1157        #self.libList.SetTextColour(wx.WHITE)
1158        #self.libList.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
1159        #self.libList.SetBackgroundColour(appPanelColor)       
1160        #self.libList.SetBackgroundColour(appPanelColor)
1161        #self.libList.SetForegroundColour(wx.WHITE)
1162       
1163        addBtn = wx.Button(self, self.ID_ADD, "Add", size=(60,20))
1164        smallFont = addBtn.GetFont()
1165        smallFont.SetPointSize(smallFont.GetPointSize() - smallFont.GetPointSize()/5)
1166        addBtn.SetFont(smallFont)
1167        delBtn = wx.Button(self, self.ID_DEL, "Remove", size=(60,20))
1168        delBtn.SetFont(smallFont)
1169
1170        self.horSizer1.Add(addBtn, 0, wx.ALIGN_CENTER | wx.RIGHT, border=10)
1171        self.horSizer1.Add(delBtn, 0, wx.ALIGN_CENTER )
1172        self.mainSizer.Add(self.libList, 1, wx.ALIGN_CENTER | wx.ALIGN_TOP | wx.ALL | wx.EXPAND, border=20)
1173        self.mainSizer.Add(self.horSizer1, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, border=5)
1174        self.mainSizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_CENTER | wx.ALL, border=15)
1175        self.SetSizer(self.mainSizer)
1176        self.Fit()
1177
1178        self.Bind(wx.EVT_BUTTON, self.__OnAdd, id=self.ID_ADD)
1179        self.Bind(wx.EVT_BUTTON, self.__OnRemove, id=self.ID_DEL)
1180        self.Bind(wx.EVT_BUTTON, self.__OnUpload, id=self.ID_UPLOAD)
1181        self.Bind(wx.EVT_BUTTON, self.__OnShow, id=self.ID_SHOW)
1182       
1183
1184    def ShowDialog(self):
1185        res = self.ShowModal()
1186        if res==wx.ID_OK or res==self.ID_UPLOAD:
1187            libName = self.libList.GetStringSelection()
1188            libHost = prefs.fileLib.GetLibraryIP( libName )
1189            prefs.fileLib.SetDefault( libName, libHost )
1190            self.Destroy()
1191            return (libName, libHost)
1192        else:
1193            self.Destroy()
1194            return False
1195
1196
1197    def __OnDoubleClick(self, evt):
1198        self.EndModal(self.ID_UPLOAD)
1199
1200
1201    def __OnUpload(self, evt):
1202        self.EndModal(self.ID_UPLOAD)
1203
1204
1205    def __OnShow(self, evt):
1206        self.EndModal(self.ID_SHOW)
1207
1208           
1209    def __OnAdd(self, evt):
1210        dlg = wx.Dialog(self, wx.ID_ANY, "Add New Library")
1211        sizer = wx.BoxSizer(wx.VERTICAL)
1212        nameLabel = wx.StaticText(dlg, wx.ID_ANY, "Library name (label):")
1213        nameField = wx.TextCtrl(dlg, wx.ID_ANY)
1214        ipLabel = wx.StaticText(dlg, wx.ID_ANY, "Library IP address (or hostname):")
1215        ipField = wx.TextCtrl(dlg, wx.ID_ANY)
1216
1217        sizer.Add(nameLabel, 0, wx.ALIGN_LEFT | wx.ALL, border = 10)
1218        sizer.Add(nameField, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 10)
1219        sizer.Add(ipLabel, 0, wx.ALIGN_LEFT | wx.ALL, border = 10)
1220        sizer.Add(ipField, 0, wx.ALIGN_LEFT | wx.LEFT | wx.BOTTOM | wx.RIGHT | wx.EXPAND, border = 10)
1221        sizer.Add(dlg.CreateButtonSizer(wx.OK|wx.CANCEL), 0, wx.ALIGN_CENTER|wx.ALL, border=10)
1222        dlg.SetSizer(sizer)
1223        dlg.Fit()
1224        if dlg.ShowModal() == wx.ID_OK:
1225            prefs.fileLib.AddLibrary(ipField.GetValue(), nameField.GetValue())
1226            self.libList.Set(prefs.fileLib.GetLibraryList())
1227            self.libList.SetStringSelection(nameField.GetValue())
1228            dlg.Destroy()
1229        else:
1230            dlg.Destroy()
1231       
1232
1233    def __OnRemove(self, evt):
1234        selection = self.libList.GetStringSelection()
1235        if selection != "":
1236            prefs.fileLib.RemoveLibrary(selection)
1237            self.libList.Set(prefs.fileLib.GetLibraryList())
1238        return
1239
1240
1241
1242class PopupDialog(wx.Dialog):
1243    def __init__(self,parent,content = None):
1244        wx.Dialog.__init__(self,parent,wx.ID_ANY,'', style = wx.NO_BORDER | wx.STAY_ON_TOP)
1245
1246        self.ctrl = parent
1247        self.win = wx.Window(self,-1,pos = (0,0),style = wx.NO_BORDER)
1248
1249        if content:
1250            self.SetContent(content)
1251
1252
1253    def SetContent(self,content):
1254        self.content = content
1255        self.content.Reparent(self.win)
1256        self.win.SetBackgroundColour(dialogColor)#102, 153, 153))#appPanelColor)
1257        self.content.SetForegroundColour(wx.WHITE)
1258        self.content.Show(True)
1259        sz = self.content.GetSize()
1260        self.win.SetClientSize((sz[0]+6, sz[1]+6))
1261        self.SetSize(self.win.GetSize())
1262       
1263
1264    def Display(self):
1265        class InfoTimer(wx.Timer):
1266            def __init__(self, action):
1267                self.action = action
1268                wx.Timer.__init__(self)
1269            def Notify(self):
1270                try:
1271                    self.action()
1272                except socket.error, xmlrpclib.ProtocolError:
1273                    pass   # no info retrieved... oh well
1274           
1275        def __ActuallyShow():
1276            pos = wx.GetMousePosition()
1277            posX = pos[0]-(30+self.GetSize()[0])
1278            posY = pos[1]
1279            self.Move((posX, posY))
1280            self.Show()
1281       
1282        # show the info on a timer
1283        if hasattr(self, "_timer"):
1284            if self._timer.IsRunning():
1285                self._timer.Stop()
1286        self._timer = InfoTimer(__ActuallyShow)
1287        self._timer.Start(500, True)
1288
1289   
1290
1291       
1292   
1293class Node:
1294    def __init__(self, name, fileType, path, isFile, isFavorite, isBranch=False):
1295        self.name = name
1296        self.fileType = fileType
1297        self.path = path
1298        self.isFavorite = isFavorite
1299        self.isFile = isFile
1300        self.isBranch = isBranch
1301   
1302    def GetName(self):
1303        return self.name
1304
1305    def GetPath(self):
1306        return self.path
1307
1308    def GetFullPath(self):
1309        return os.path.join(self.path, self.name)
1310
1311    def GetType(self):
1312        return self.fileType
1313
1314    def IsFavorite(self):
1315        return self.isFavorite
1316
1317    def SetFavorite(self, fav):
1318        self.isFavorite = fav
1319
1320    def IsFile(self):
1321        return self.isFile
1322
1323    def IsBranch(self):
1324        return self.isBranch
1325
1326    def __eq__(self, other):
1327        if hasattr(other, "GetName"):
1328            if self.GetName() == other.GetName():
1329                return True
1330            else:
1331                return False
1332        return False
1333   
1334    def __ne__(self, other):
1335        if hasattr(other, "GetName"):
1336            if self.GetName() != other.GetName():
1337                return True
1338            else:
1339                return False
1340        return True
1341   
1342   
Note: See TracBrowser for help on using the repository browser.