1 # -*- coding: utf-8 -*-
 
   4 Dboxswitch dropbox profile switcher
 
   6 license: Modified BSD License
 
   8 Copyright (c) 2012,  <stack@inventati.org>
 
  11 Redistribution and use in source and binary forms, with or without
 
  12 modification, are permitted provided that the following conditions are met:
 
  13     * Redistributions of source code must retain the above copyright
 
  14       notice, this list of conditions and the following disclaimer.
 
  15     * Redistributions in binary form must reproduce the above copyright
 
  16       notice, this list of conditions and the following disclaimer in the
 
  17       documentation and/or other materials provided with the distribution.
 
  18     * Neither the name of the <organization> nor the
 
  19       names of its contributors may be used to endorse or promote products
 
  20       derived from this software without specific prior written permission.
 
  22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 
  23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
  24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
  25 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 
  26 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
  27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
  28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
  29 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
  30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
  31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
  36 from PyQt4 import QtGui, QtCore
 
  37 from PyQt4.QtCore import SIGNAL
 
  38 from qmenutooltip import QMenuToolTip
 
  40 from apperror import AppError
 
  41 from settings import appconf
 
  43 class Gui(QtGui.QDialog):
 
  44     def __init__(self, prManager):
 
  45         """ Gui init, an QApplication is created and stored here, also a main window and a trayIcon
 
  47         Default actions are builded and profile manager stored from argument (instance of ProfHandler """
 
  49         self.app = QtGui.QApplication(sys.argv)
 
  51         #check if system tray is avaiable on the system
 
  52         if not QtGui.QSystemTrayIcon.isSystemTrayAvailable():
 
  53             QtGui.QMessageBox.critical(None, "Systray",
 
  54                     "I couldn't detect any system tray on this system.")
 
  57         QtGui.QApplication.setQuitOnLastWindowClosed(False)
 
  59         super(Gui, self).__init__()
 
  65         self.createMainDialog()
 
  69         self.profileManager = prManager
 
  72         """ Like Gtk application this main executes the app, thi is somewhat writed for
 
  73         compatibility in porting the app """
 
  76         sys.exit(self.app.exec_())
 
  78     def closeEvent(self, event):
 
  79         """ Handle application closing """
 
  81         if self.trayIcon.isVisible():
 
  85     def createActions(self):
 
  86         """ Create actions for the various components """
 
  88         self.manageprofiles = QtGui.QAction("Manage &Profiles", self,
 
  90         self.quitAction = QtGui.QAction("&Quit", self,
 
  91                 triggered=QtGui.qApp.quit)
 
  93         #profile manager component
 
  94         self.addProfile = QtGui.QAction("  Add &Profile  ", self,
 
  95                 triggered=self.addProfile)
 
  97     def createMainDialog(self):    
 
  98         """ Build the main dialog layout """ 
 
 100         self.setWindowTitle("Profile manager / Dboxswitch - dropbox profile switcher")
 
 102         self.la            = QtGui.QFormLayout()
 
 103         self.la.sizePolicy = QtGui.QSizePolicy.Expanding
 
 104         self.la.setVerticalSpacing(15)
 
 105         self.la.setHorizontalSpacing(15)
 
 106         self.formGroupBox = QtGui.QGroupBox("Manage profiles:")
 
 107         self.formGroupBox.setLayout(self.la)
 
 108         mainLayout = QtGui.QVBoxLayout()
 
 109         mainLayout.addWidget(self.formGroupBox)
 
 110         self.setLayout(mainLayout)
 
 114         """ Show the main dialog for dealing with profiles """
 
 116         profiles = self.profileManager.getProfilesList()
 
 119             prname = self.profileManager.getBaseProfileName(pr)
 
 120             delButton = QtGui.QToolButton()
 
 121             label     = QtGui.QLabel(prname)
 
 122             self.la.addRow(label, delButton)
 
 123             delAction = QtGui.QAction(QtGui.QIcon(appconf.delpicon),"Delete", self,
 
 124                 triggered=self.deleteProfileAction(prname, (label, delButton)))
 
 125             delButton.setDefaultAction(delAction)
 
 126             self.profiles.append((label, delButton))
 
 129         super(Gui, self).show()
 
 132         """ Hides the main dialog """
 
 134         #Hides all the profiles in the Dialog
 
 135         #User can deal with profiles like removing the dir manually
 
 137         for ws in self.profiles:
 
 141         super(Gui, self).hide()
 
 143     def createTrayIcon(self):
 
 144          """ Builds a new tray icon with a context menu and an action for the profile manager menu """
 
 146          #context menu build, right click
 
 147          self.trayIconMenu = QtGui.QMenu(self)
 
 148          self.trayIconMenu.addAction(self.manageprofiles)
 
 149          self.trayIconMenu.addSeparator()
 
 150          self.trayIconMenu.addAction(self.quitAction)
 
 152          #create the tray with an incon
 
 153          self.trayIcon = QtGui.QSystemTrayIcon(QtGui.QIcon(appconf.icon), self.app)
 
 154          #attach a context menu for the right click to the tray
 
 155          self.trayIcon.setContextMenu(self.trayIconMenu)
 
 156          #baloon on hover for the tray
 
 157          self.trayIcon.setToolTip(appconf.appname+" "+appconf.appversion+"\nRight Click to manage profiles.")
 
 158          #left click profiles show for the tray
 
 159          self.trayIcon.activated.connect(self.showTrayProfiles)
 
 162     def showTrayProfiles(self,reason):
 
 163         """ Pops up a system tray profile Manager with a list of activable profiles and an 
 
 164         action to add a new One """
 
 166         if reason in (QtGui.QSystemTrayIcon.Trigger, QtGui.QSystemTrayIcon.DoubleClick):
 
 167             self.menuProfiles = QMenuToolTip()
 
 168             self.menuProfiles.setTitle("Profiles")
 
 169             QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_DontShowIconsInMenus, False)
 
 171             #Get profiles from the ProfHandler embedded in the gui
 
 172             profiles = self.profileManager.getProfilesList()
 
 174             for prpath in profiles:
 
 175                 pr = self.profileManager.getBaseProfileName(prpath)
 
 176                 receiver = self.activateProfileAction(prpath)
 
 177                 menuItem_Profile = self.menuProfiles.addAction(pr)
 
 178                 if self.profileManager.isCurrentProfile(prpath):
 
 179                     menuItem_Profile.setIcon(QtGui.QIcon(appconf.cpicon))
 
 181                 #Using lambda function to pass additional arguments to the function, in this case the path of the profile
 
 182                 self.connect(menuItem_Profile, SIGNAL('triggered()'), receiver)
 
 183                 #set menu item ToolTip
 
 184                 menuItem_Profile.setToolTip("Activate profile: "+pr)
 
 186                 self.menuProfiles.addAction(menuItem_Profile)
 
 188             self.menuProfiles.addSeparator()
 
 189             #action and menu item for adding a New Profile
 
 190             self.menuProfiles.addAction(self.addProfile)
 
 192             self.menuProfiles.activateWindow()
 
 193             self.menuProfiles.popup(QtGui.QCursor.pos())
 
 195     def activateProfileAction(self, pr):
 
 196         """ Returns a callable to be passed as an action for the switching profile mechanism 
 
 197         to self.connect(menuItem_Profile... 
 
 198         It compiles a function with a profile name as argument and also handle the displaying of errors."""
 
 202                 self.profileManager.activateProfile(pr)
 
 204                 self.showError(str(e))
 
 207     def deleteProfileAction(self, pr, prWidgets):
 
 208         """ Returns a callable to be passed as an action for the profile manager mechanism 
 
 209         to self.connect(menuItem_Profile... 
 
 210         It compiles a function with a profile name as argument and also handle the displaying of errors."""
 
 214                 #self.profileManager.delProfile(pr)
 
 219                 self.showError(str(e))
 
 223     def addProfile(self):
 
 224         """ Gui frontend to add a new Profile, it requests the user a profile name 
 
 225         through a QInputDialog and creates a new profile with the help of the ProfHandler embedded in the Gui """
 
 227         self.setWindowTitle("Add New Profile - Dboxswitch - dropbox profile switcher")
 
 228         self.resize(300, 100)
 
 229         text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 
 
 230                             'Enter profile name:')
 
 233                 self.profileManager.addProfile(unicode(text))
 
 235                 self.showError(str(e))
 
 237     def showError(self, err):
 
 238         """ Display an error message through a QErrorMessage """
 
 240         self.setWindowTitle("Error - Dboxswitch - dropbox profile switcher")
 
 241         self.resize(200, 100)
 
 242         err = QtGui.QErrorMessage.showMessage(QtGui.QErrorMessage.qtHandler(), "Error: "+err)