[4] | 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 | |
---|
| 40 | import xmlrpclib, base64, sys, cStringIO, socket, pickle, copy, os.path, os |
---|
| 41 | from httplib import HTTPException |
---|
| 42 | import wx.lib.scrolledpanel |
---|
| 43 | from wx import ProgressDialog |
---|
| 44 | from globals import ConvertPath, Message, ChoiceDialog, appPanelColor, dialogColor |
---|
| 45 | import help #my own |
---|
| 46 | import preferences as prefs #my own |
---|
| 47 | from math import ceil |
---|
| 48 | from threading import * |
---|
| 49 | from misc.imsize import imagesize # reads the image header and gets the size from it |
---|
| 50 | import wx.lib.throbber as throb |
---|
| 51 | import urllib |
---|
| 52 | |
---|
| 53 | XMLRPC_PORT = "8800" |
---|
| 54 | FILE_GRABBER_PORT = "8801" |
---|
| 55 | FILE_SERVER_BIN_PORT = 8802 |
---|
| 56 | PREVIEW_SIZE = (150,150) |
---|
| 57 | DEFAULT_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) |
---|
| 61 | fileServers = {} |
---|
| 62 | def 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... |
---|
| 74 | class 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 | |
---|
| 114 | class 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 | |
---|
| 122 | class 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 |
---|
| 266 | class 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 | |
---|
| 350 | class 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 | |
---|
| 445 | class 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 | |
---|
| 464 | dirTextColor = wx.Colour(204, 153, 102)#250,50,50) |
---|
| 465 | |
---|
| 466 | class 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) |
---|
| 1136 | class 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 | |
---|
| 1242 | class 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 | |
---|
| 1293 | class 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 | |
---|