mainwindow.py :  » Project-Management » Task-Coach » TaskCoach-1.0.3 » taskcoachlib » gui » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Project Management » Task Coach 
Task Coach » TaskCoach 1.0.3 » taskcoachlib » gui » mainwindow.py
# -*- coding: utf-8 -*-

'''
Task Coach - Your friendly task manager
Copyright (C) 2004-2010 Frank Niessink <frank@niessink.com>
Copyright (C) 2008-2009 Jrme Laheurte <fraca7@free.fr>

Task Coach is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Task Coach is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''

import wx
from taskcoachlib import meta,patterns,widgets,help# pylint: disable-msg=W0622
fromtaskcoachlib.i18n_
from taskcoachlib.gui.threads import DeferredCallMixin,synchronized
from taskcoachlib.gui.dialog.iphone import IPhoneSyncTypeDialog,IPhoneSyncDialog
from taskcoachlib.powermgt import PowerStateMixin
import taskcoachlib.thirdparty.aui as aui
import viewer, toolbar, uicommand, remindercontroller, artprovider


class WindowDimensionsTracker(object):
    ''' Track the dimensions (position and size) of a window in the 
        settings. '''
    def __init__(self, window, settings, *args, **kwargs):
        super(WindowDimensionsTracker, self).__init__(*args, **kwargs)
        self._settings = settings
        self._section = 'window'
        self._window = window
        self.setDimensions()
        self._window.Bind(wx.EVT_SIZE, self.onChangeSize)
        self._window.Bind(wx.EVT_MOVE, self.onChangePosition)
        self._window.Bind(wx.EVT_MAXIMIZE, self.onMaximize)
        if self.startIconized():
            if wx.Platform in ('__WXMAC__', '__WXGTK__'):
                # Need to show the window on Mac OS X first, otherwise it   
                # won't be properly minimized. On wxGTK we need to show the
                # window first, otherwise clicking the task bar icon won't
                # show it.
                self._window.Show()
            self._window.Iconize(True)
            wx.CallAfter(self._window.Hide)

    def startIconized(self):
        startIconized = self._settings.get(self._section, 'starticonized')
        if startIconized == 'Always':
            return True
        if startIconized == 'Never':
            return False
        return self.getSetting('iconized') 
        
    def setSetting(self, setting, value):
        self._settings.set(self._section, setting, str(value))
        
    def getSetting(self, setting):
        return eval(self._settings.get(self._section, setting))
        
    def setDimensions(self):
        width, height = self.getSetting('size')
        if wx.Platform == '__WXMAC__':
            # Under MacOS 10.5 and 10.4, when setting the size, the actual window height
            # is increased by 29 pixels. Dunno why, but it's highly annoying.
            height -= 29
        x, y = self.getSetting('position')
        self._window.SetDimensions(x, y, width, height)
        if self.getSetting('maximized'):
            self._window.Maximize()
        # Check that the window is on a valid display and move if necessary:
        if wx.Display.GetFromWindow(self._window) == wx.NOT_FOUND:
            self._window.SetDimensions(0, 0, width, height)

    def onChangeSize(self, event):
        # Ignore the EVT_SIZE when the window is maximized or iconized. 
        # Note how this depends on the EVT_MAXIMIZE being sent before the 
        # EVT_SIZE.
        maximized = self._window.IsMaximized()
        if not maximized and not self._window.IsIconized():
            self.setSetting('size', event.GetSize())
        # Jerome, 2008/07/12: On my system (KDE 3.5.7), EVT_MAXIMIZE
        # is not triggered, so set 'maximized' to True here as well as in 
        # onMaximize:
        self.setSetting('maximized', maximized)
        event.Skip()
        
    def onChangePosition(self, event):
        # Ignore the EVT_MOVE when the window is maximized. Note how this
        # depends on the EVT_MAXIMIZE being sent before the EVT_MOVE.
        if not self._window.IsMaximized():
            self.setSetting('position', self._window.GetPosition())
            self.setSetting('maximized', False)
        event.Skip()
        
    def onMaximize(self, event):
        self.setSetting('maximized', True)
        event.Skip()
                
    def savePosition(self):
        iconized = self._window.IsIconized()
        self.setSetting('iconized', iconized)
        if not iconized:
            self.setSetting('position', self._window.GetPosition())
        

class MainWindow(DeferredCallMixin, PowerStateMixin, widgets.AuiManagedFrameWithNotebookAPI):
    pageClosedEvent = aui.EVT_AUI_PANE_CLOSE
    
    def __init__(self, iocontroller, taskFile, settings,
                 splash=None, *args, **kwargs):
        super(MainWindow, self).__init__(None, -1, '', *args, **kwargs)
        self.dimensionsTracker = WindowDimensionsTracker(self, settings)
        self.iocontroller = iocontroller
        self.taskFile = taskFile
        self.settings = settings
        self.Bind(wx.EVT_CLOSE, self.onClose)
        self.splash = splash
        self.createWindowComponents()
        self.initWindowComponents()
        self.initWindow()
        self.registerForWindowComponentChanges()
        wx.CallAfter(self.closeSplash)
        wx.CallAfter(self.showTips)

        if settings.getboolean('feature', 'syncml'):
            try:
                import taskcoachlib.syncml.core # pylint: disable-msg=W0612
            except ImportError:
                if settings.getboolean('syncml', 'showwarning'):
                    dlg = widgets.SyncMLWarningDialog(self)
                    try:
                        if dlg.ShowModal() == wx.ID_OK:
                            settings.setboolean('syncml', 'showwarning', False)
                    finally:
                        dlg.Destroy()

        self.bonjourRegister = None

        if settings.getboolean('feature', 'iphone'):
            try:
                from taskcoachlib.thirdparty import pybonjour# pylint: disable-msg=W0612
fromtaskcoachlib.iphoneIPhoneAcceptor,BonjourServiceRegister

                acceptor = IPhoneAcceptor(self, settings, iocontroller)
                self.bonjourRegister = BonjourServiceRegister(settings, acceptor.port)
            except:
                from taskcoachlib.gui.dialog.iphone import IPhoneBonjourDialog

                dlg = IPhoneBonjourDialog(self, wx.ID_ANY, _('Warning'))
                try:
                    dlg.ShowModal()
                finally:
                    dlg.Destroy()

    def createWindowComponents(self):
        self.createViewerContainer()
        viewer.addViewers(self.viewer, self.taskFile, self.settings)
        self.createStatusBar()
        self.createMenuBar()
        self.createTaskBarIcon()
        self.createReminderController()
        
    def createViewerContainer(self):
        tabbed = self.__usingTabbedMainWindow = self.settings.getboolean('view', 
            'tabbedmainwindow') 
        containerWidget = widgets.AUINotebook(self) if tabbed else self
        self.viewer = viewer.ViewerContainer(containerWidget,
            self.settings, 'mainviewer') 
        
    def createStatusBar(self):
        import status
        self.SetStatusBar(status.StatusBar(self, self.viewer))
        
    def createMenuBar(self):
        import menu
        self.SetMenuBar(menu.MainMenu(self, self.settings, self.iocontroller, 
                                      self.viewer, self.taskFile))
    
    def createReminderController(self):
        self.reminderController = \
            remindercontroller.ReminderController(self, self.taskFile.tasks(), 
                self.settings)
        
    def AddPage(self, page, caption, *args):
        name = page.settingsSection()
        super(MainWindow, self).AddPage(page, caption, name)

    def initWindow(self):
        wx.GetApp().SetTopWindow(self)
        self.setTitle(self.taskFile.filename())
        self.SetIcons(artprovider.iconBundle('taskcoach'))
        self.displayMessage(_('Welcome to %(name)s version %(version)s')% \
            {'name': meta.name, 'version': meta.version}, pane=1)

    def initWindowComponents(self):
        self.onShowToolBar()
        # We use CallAfter because otherwise the statusbar will appear at the 
        # top of the window when it is initially hidden and later shown.
        wx.CallAfter(self.onShowStatusBar)
        if not self.__usingTabbedMainWindow:
            self.restorePerspective()
            
    def restorePerspective(self):
        perspective = self.settings.get('view', 'perspective')
        viewerTypes = viewer.viewerTypes()
        for viewerType in viewerTypes:
            if self.perspectiveAndSettingsHaveDifferentViewerCount(viewerType):
                # Different viewer counts may happen when the name of a viewer 
                # is changed between versions
                perspective = ''
                break

        self.manager.LoadPerspective(perspective)
        for pane in self.manager.GetAllPanes():
            # Prevent zombie panes by making sure all panes are visible
            if not pane.IsShown():
                pane.Show()
            # Ignore the titles that are saved in the perspective, they may be
            # incorrect when the user changes translation:
            if hasattr(pane.window, 'title'):
                pane.Caption(pane.window.title())
        self.manager.Update()
        
    def perspectiveAndSettingsHaveDifferentViewerCount(self, viewerType):
        perspective = self.settings.get('view', 'perspective')
        perspectiveViewerCount = perspective.count('name=%s'%viewerType)
        settingsViewerCount = self.settings.getint('view', '%scount'%viewerType)
        return perspectiveViewerCount != settingsViewerCount
    
    def registerForWindowComponentChanges(self):
        patterns.Publisher().registerObserver(self.onFilenameChanged, 
            eventType='taskfile.filenameChanged', eventSource=self.taskFile)
        patterns.Publisher().registerObserver(self.onShowStatusBar, 
            eventType='view.statusbar')
        patterns.Publisher().registerObserver(self.onShowToolBar, 
            eventType='view.toolbar')
        self.Bind(self.pageClosedEvent, self.onCloseToolBar)

    def showTips(self):
        if self.settings.getboolean('window', 'tips'):
            help.showTips(self, self.settings)
            
    def closeSplash(self):
        if self.splash:
            self.splash.Destroy()
                         
    def onShowStatusBar(self, event=None): # pylint: disable-msg=W0613
        self.showStatusBar(self.settings.getboolean('view', 'statusbar'))

    def onShowToolBar(self, event=None): # pylint: disable-msg=W0613
        self.showToolBar(eval(self.settings.get('view', 'toolbar')))

    def createTaskBarIcon(self):
        if self.canCreateTaskBarIcon():
            import taskbaricon, menu
            self.taskBarIcon = taskbaricon.TaskBarIcon(self, 
                self.taskFile.tasks(), self.settings)
            self.taskBarIcon.setPopupMenu(menu.TaskBarMenu(self.taskBarIcon,
                self.settings, self.taskFile, self.viewer))
        self.Bind(wx.EVT_ICONIZE, self.onIconify)

    def canCreateTaskBarIcon(self):
        try:
            import taskbaricon # pylint: disable-msg=W0612
            return True
        except:
            return False # pylint: disable-msg=W0702
        
    def onFilenameChanged(self, event):
        self.setTitle(event.value())

    def setTitle(self, filename):
        title = meta.name
        if filename:
            title += ' - %s'%filename
        self.SetTitle(title)
        
    def displayMessage(self, message, pane=0):
        self.GetStatusBar().SetStatusText(message, pane)

    def quit(self, force=False):
        if not self.iocontroller.close(force=force):
            return
        # Remember what the user was working on: 
        self.settings.set('file', 'lastfile', self.taskFile.lastFilename())
        self.saveViewerCounts()
        self.savePerspective()
        self.dimensionsTracker.savePosition()
        self.settings.save()
        if hasattr(self, 'taskBarIcon'):
            self.taskBarIcon.RemoveIcon()
        if self.bonjourRegister is not None:
            self.bonjourRegister.stop()
        wx.GetApp().ProcessIdle()
        wx.GetApp().ExitMainLoop()

        # For PowerStateMixin

        self.OnQuit()

    def saveViewerCounts(self):
        ''' Save the number of viewers for each viewer type. '''
        counts = {}
        for viewer in self.viewer:
            setting = viewer.__class__.__name__.lower() + 'count'
            counts[setting] = counts.get(setting, 0) + 1
        for key, value in counts.items():
            self.settings.set('view', key, str(value))
            
    def savePerspective(self):
        perspective = '' if self.__usingTabbedMainWindow else self.manager.SavePerspective()
        self.settings.set('view', 'perspective', perspective)
        
    def onClose(self, event):
        if event.CanVeto() and self.settings.getboolean('window', 
                                                        'hidewhenclosed'):
            event.Veto()
            self.Iconize()
        else:
            self.quit()

    def restore(self, event): # pylint: disable-msg=W0613
        self.Show()
        self.Raise()
        self.Refresh() # This is not necessary on Windows/Linux Ubuntu/Mac but
                       # might help to fix bug 1429540 (Linux Mandrake)
        self.Iconize(False)

    def onIconify(self, event):
        if event.Iconized() and self.settings.getboolean('window', 
                                                         'hidewheniconized'):
            self.Hide()
        else:
            event.Skip()
            
    def showStatusBar(self, show=True):
        # FIXME: First hiding the statusbar, then hiding the toolbar, then
        # showing the statusbar puts it in the wrong place (only on Linux?)
        self.GetStatusBar().Show(show)
        self.SendSizeEvent()
        
    def getToolBarUICommands(self):
        ''' UI commands to put on the toolbar of this window. ''' 
        uiCommands = [
                uicommand.FileOpen(iocontroller=self.iocontroller), 
                uicommand.FileSave(iocontroller=self.iocontroller), 
                uicommand.Print(viewer=self.viewer, settings=self.settings), 
                None, 
                uicommand.EditUndo(), 
                uicommand.EditRedo()]
        if self.settings.getboolean('feature', 'effort'):
            uiCommands.extend([ 
                None, 
                uicommand.EffortStartButton(taskList=self.taskFile.tasks()), 
                uicommand.EffortStop(taskList=self.taskFile.tasks())])
        return uiCommands
        
    def showToolBar(self, size):
        # Current version of wxPython (2.7.8.1) has a bug 
        # (https://sourceforge.net/tracker/?func=detail&atid=109863&aid=1742682&group_id=9863)
        # that makes adding controls to a toolbar not working. Also, when the 
        # toolbar is visible it's nearly impossible to enter text into text
        # controls. Immediately after you click on a text control the focus
        # is removed. We work around it by not having AUI manage the toolbar
        # on Mac OS X:
        if '__WXMAC__' in wx.PlatformInfo or self.__usingTabbedMainWindow:
            if self.GetToolBar():
                self.GetToolBar().Destroy()
            if size is not None:
                self.SetToolBar(toolbar.ToolBar(self, size=size))
            self.SendSizeEvent()
        else:
            currentToolbar = self.manager.GetPane('toolbar')
            if currentToolbar.IsOk():
                self.manager.DetachPane(currentToolbar.window)
                currentToolbar.window.Destroy()
            if size:
                bar = toolbar.ToolBar(self, size=size)
                self.manager.AddPane(bar, aui.AuiPaneInfo().Name('toolbar').
                                     Caption('Toolbar').ToolbarPane().Top().DestroyOnClose().
                                     LeftDockable(False).RightDockable(False))
            self.manager.Update()

    def onCloseToolBar(self, event):
        if event.GetPane().IsToolbar():
            self.settings.set('view', 'toolbar', 'None')
        event.Skip()

    # Power management

    def OnPowerState(self, state):
        patterns.observer.Event('powermgt.%s' % {self.POWERON: 'on', self.POWEROFF: 'off'}[state],
                                self).send()

    # iPhone-related methods. These are called from the asyncore thread so they're deferred.

    @synchronized
    def createIPhoneProgressDialog(self):
        return IPhoneSyncDialog(self.settings, self, wx.ID_ANY, _('iPhone/iPod'), size=wx.Size(400, 300))

    @synchronized
    def getIPhoneSyncType(self, guid):
        if guid == self.taskFile.guid():
            return 0 # two-ways

        dlg = IPhoneSyncTypeDialog(self, wx.ID_ANY, _('Synchronization type'))
        try:
            dlg.ShowModal()
            return dlg.value
        finally:
            dlg.Destroy()

    @synchronized
    def notifyIPhoneProtocolFailed(self):
        # This should actually never happen.
        wx.MessageBox(_('''An iPhone or iPod Touch device tried to synchronize with this\n'''
                      '''task file, but the protocol negotiation failed. Please file a\n'''
                      '''bug report.'''),
                      _('Error'), wx.OK)

    # The notification system is not thread-save; adding or modifying tasks
    # or categories from the asyncore thread crashes the app.

    @synchronized
    def clearTasks(self):
        self.taskFile.clear(False)

    @synchronized
    def restoreTasks(self, categories, tasks):
        self.taskFile.clear(False)
        self.taskFile.categories().extend(categories)
        self.taskFile.tasks().extend(tasks)

    @synchronized
    def addIPhoneCategory(self, category):
        self.taskFile.categories().append(category)

    @synchronized
    def removeIPhoneCategory(self, category):
        self.taskFile.categories().remove(category)

    @synchronized
    def modifyIPhoneCategory(self, category, name):
        category.setSubject(name)

    @synchronized
    def addIPhoneTask(self, task, categories):
        self.taskFile.tasks().append(task)
        for category in categories:
            task.addCategory(category)
            category.addCategorizable(task)

    @synchronized
    def removeIPhoneTask(self, task):
        self.taskFile.tasks().remove(task)

    @synchronized
    def addIPhoneEffort(self, task, effort):
        if task is not None:
            task.addEffort(effort)

    @synchronized
    def modifyIPhoneEffort(self, effort, subject, started, ended):
        effort.setSubject(subject)
        effort.setStart(started)
        effort.setStop(ended)

    @synchronized
    def modifyIPhoneTask(self, task, subject, description, startDate, dueDate, completionDate, categories):
        task.setSubject(subject)
        task.setDescription(description)
        task.setStartDate(startDate)
        task.setDueDate(dueDate)
        task.setCompletionDate(completionDate)

        if categories is not None: # Protocol v2
            for category in task.categories():
                task.removeCategory(category)
                category.removeCategorizable(task)

            for category in categories:
                task.addCategory(category)
                category.addCategorizable(task)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.