def _refresh(self, clear=False): if clear: self.__domainObjectsToView = None self.setPresentation(self.createSorter(self.createFilter(self.domainObjectsToView()))) self.secondRefresher.updatePresentation() self.registerPresentationObservers() # Invalidate the UICommands used for the column popup menu: self.__columnUICommands = None # Clear the selection to remove the cached selection self.clear_selection() # If the widget is auto-resizing columns, turn it off temporarily to # make removing/adding columns faster autoResizing = self.widget.IsAutoResizing() if autoResizing: self.widget.ToggleAutoResizing(False) # Refresh first so that the list control doesn't think there are more # efforts than there really are when switching from aggregate mode to # detail mode. self.refresh() self._showWeekdayColumns(show=self.aggregation == "week") self._showTotalColumns(show=self.aggregation != "details") if autoResizing: self.widget.ToggleAutoResizing(True) self.__initRoundingToolBarUICommands() pub.sendMessage("effortviewer.aggregation")
def save(self): try: pub.sendMessage('taskfile.aboutToSave', taskFile=self) except: pass # When encountering a problem while saving (disk full, # computer on fire), if we were writing directly to the file, # it's lost. So write to a temporary file and rename it if # everything went OK. self.__saving = True try: self.mergeDiskChanges() if self.__needSave or not os.path.exists(self.__filename): fd = self._openForWrite() try: xml.XMLWriter(fd).write(self.tasks(), self.categories(), self.notes(), self.syncMLConfig(), self.guid()) finally: fd.close() self.markClean() finally: self.__saving = False self.__notifier.saved() try: pub.sendMessage('taskfile.justSaved', taskFile=self) except: pass
def setLocation(self, location): if location != self.__location: self.__location = location self.markDirty() pub.sendMessage(self.locationChangedEventType(), newValue=location, sender=self)
def __show_effort_aggregation(self, aggregation): ''' Change the aggregation mode. Can be one of 'details', 'day', 'week' and 'month'. ''' assert aggregation in ('details', 'day', 'week', 'month') self.aggregation = aggregation self.setPresentation(self.createSorter(self.createFilter(\ self.domainObjectsToView()))) self.secondRefresher.updatePresentation() self.registerPresentationObservers() # Invalidate the UICommands used for the column popup menu: self.__columnUICommands = None # Clear the selection to remove the cached selection self.clear_selection() # If the widget is auto-resizing columns, turn it off temporarily to # make removing/adding columns faster autoResizing = self.widget.IsAutoResizing() if autoResizing: self.widget.ToggleAutoResizing(False) # Refresh first so that the list control doesn't think there are more # efforts than there really are when switching from aggregate mode to # detail mode. self.refresh() self._showWeekdayColumns(show=aggregation == 'week') self._showTotalColumns(show=aggregation != 'details') if autoResizing: self.widget.ToggleAutoResizing(True) self.__initRoundingToolBarUICommands() pub.sendMessage('effortviewer.aggregation')
def setFilename(self, filename): if filename == self.__filename: return self.__lastFilename = filename or self.__filename self.__filename = filename self.__notifier.setFilename(filename) pub.sendMessage('taskfile.filenameChanged', filename=filename)
def save(self): pub.sendMessage('taskfile.aboutToSave', taskFile=self) # When encountering a problem while saving (disk full, # computer on fire), if we were writing directly to the file, # it's lost. So write to a temporary file and rename it if # everything went OK. self.__saving = True try: self.mergeDiskChanges() if self.__needSave or not os.path.exists(self.__filename): name, fd = self._openForWrite() xml.XMLWriter(fd).write(self.tasks(), self.categories(), self.notes(), self.syncMLConfig(), self.guid()) fd.close() if os.path.exists( self.__filename ): # Not using self.exists() because DummyFile.exists returns True os.remove(self.__filename) if name is not None: # Unit tests (AutoSaver) os.rename(name, self.__filename) self.markClean() finally: self.__saving = False self.__notifier.saved()
def load(self, filename=None): pub.sendMessage('taskfile.aboutToRead', taskFile=self) self.__loading = True if filename: self.setFilename(filename) try: if self.exists(): fd = self._openForRead() tasks, categories, globalcategories, notes, syncMLConfig, changes, guid = self._read( fd) fd.close() else: tasks = [] categories = [] notes = [] changes = dict() guid = generate() syncMLConfig = createDefaultSyncConfig(guid) self.clear() self.__monitor.reset() self.__changes = changes self.__changes[self.__monitor.guid()] = self.__monitor self.categories().extend(categories) self.tasks().extend(tasks) self.notes().extend(notes) def registerOtherObjects(objects): for obj in objects: if isinstance(obj, base.CompositeObject): registerOtherObjects(obj.children()) if isinstance(obj, note.NoteOwner): registerOtherObjects(obj.notes()) if isinstance(obj, attachment.AttachmentOwner): registerOtherObjects(obj.attachments()) if isinstance(obj, task.Task): registerOtherObjects(obj.efforts()) if isinstance(obj, note.Note) or \ isinstance(obj, attachment.Attachment) or \ isinstance(obj, effort.Effort): self.__monitor.setChanges(obj.id(), set()) registerOtherObjects(self.categories().rootItems()) registerOtherObjects(self.tasks().rootItems()) registerOtherObjects(self.notes().rootItems()) self.__monitor.resetAllChanges() self.__syncMLConfig = syncMLConfig self.__guid = guid if os.path.exists(self.filename()): # We need to reset the changes on disk because we're up to date. xml.ChangesXMLWriter(file(self.filename() + '.delta', 'wb')).write(self.__changes) except: self.setFilename('') raise finally: self.__loading = False self.markClean() self.__changedOnDisk = False pub.sendMessage('taskfile.justRead', taskFile=self)
def __extend_self_with_composites(self, new_composites, event=None): ''' Add composites to the aggregator. ''' super(EffortAggregator, self).extendSelf(new_composites, event=event) for new_composite in new_composites: if new_composite.isBeingTracked(): self.__trackedComposites.add(new_composite) pub.sendMessage(effort.Effort.trackingChangedEventType(), newValue=True, sender=new_composite)
def reset(self, forceEvent=False): ''' reset does the actual sorting. If the order of the list changes, observers are notified by means of the list-sorted event. ''' oldSelf = self[:] self.sort(key=self.createSortKeyFunction(), reverse=not self._sortAscending) if forceEvent or self != oldSelf: pub.sendMessage(self.sortEventType(), sender=self)
def load(self, filename=None): pub.sendMessage('taskfile.aboutToRead', taskFile=self) self.__loading = True if filename: self.setFilename(filename) try: if self.exists(): fd = self._openForRead() try: tasks, categories, notes, syncMLConfig, changes, guid = self._read(fd) finally: fd.close() else: tasks = [] categories = [] notes = [] changes = dict() guid = generate() syncMLConfig = createDefaultSyncConfig(guid) self.clear() self.__monitor.reset() self.__changes = changes self.__changes[self.__monitor.guid()] = self.__monitor self.categories().extend(categories) self.tasks().extend(tasks) self.notes().extend(notes) def registerOtherObjects(objects): for obj in objects: if isinstance(obj, base.CompositeObject): registerOtherObjects(obj.children()) if isinstance(obj, note.NoteOwner): registerOtherObjects(obj.notes()) if isinstance(obj, attachment.AttachmentOwner): registerOtherObjects(obj.attachments()) if isinstance(obj, task.Task): registerOtherObjects(obj.efforts()) if isinstance(obj, note.Note) or \ isinstance(obj, attachment.Attachment) or \ isinstance(obj, effort.Effort): self.__monitor.setChanges(obj.id(), set()) registerOtherObjects(self.categories().rootItems()) registerOtherObjects(self.tasks().rootItems()) registerOtherObjects(self.notes().rootItems()) self.__monitor.resetAllChanges() self.__syncMLConfig = syncMLConfig self.__guid = guid if os.path.exists(self.filename()): # We need to reset the changes on disk because we're up to date. xml.ChangesXMLWriter(file(self.filename() + '.delta', 'wb')).write(self.__changes) except: self.setFilename('') raise finally: self.__loading = False self.markClean() self.__changedOnDisk = False pub.sendMessage('taskfile.justRead', taskFile=self)
def clear(self, regenerate=True, event=None): pub.sendMessage('taskfile.aboutToClear', taskFile=self) try: self.tasks().clear(event=event) self.categories().clear(event=event) self.notes().clear(event=event) if regenerate: self.__guid = generate() self.__syncMLConfig = createDefaultSyncConfig(self.__guid) finally: pub.sendMessage('taskfile.justCleared', taskFile=self)
def setStart(self, startDateTime): if startDateTime == self._start: return self._start = startDateTime self.__updateDurationCache() pub.sendMessage(self.startChangedEventType(), newValue=startDateTime, sender=self) self.task().sendTimeSpentChangedMessage() self.sendDurationChangedMessage() if self.task().hourlyFee(): self.sendRevenueChangedMessage()
def expand(self, expand=True, context='None', notify=True): ''' Expands (or collapses) the composite object in the specified context. ''' if expand == self.isExpanded(context): return if expand: self.__expandedContexts.add(context) else: self.__expandedContexts.discard(context) if notify: pub.sendMessage(self.expansionChangedEventType(), newValue=expand, sender=self)
def onAddEffortToOrRemoveEffortFromTask(self, newValue, sender): if sender not in self.observable(): return newValue, oldValue = newValue effortsToAdd = [effort for effort in newValue if not effort in oldValue] effortsToRemove = [effort for effort in oldValue if not effort in newValue] super(EffortList, self).extendSelf(effortsToAdd) super(EffortList, self).removeItemsFromSelf(effortsToRemove) for effort in effortsToAdd + effortsToRemove: if effort.getStop() is None: pub.sendMessage(effort.trackingChangedEventType(), newValue=effort in effortsToAdd, sender=effort)
def save(self): pickle.dump([name for task, name in self._templates], file(os.path.join(self._path, 'list.pickle'), 'wb')) for task, name in self._templates: templateFile = file(os.path.join(self._path, name), 'w') writer = TemplateXMLWriter(templateFile) writer.write(task) templateFile.close() for task, name in self._toDelete: os.remove(os.path.join(self._path, name)) self._toDelete = [] pub.sendMessage('templates.saved')
def reset(self, forceEvent=False): ''' reset does the actual sorting. If the order of the list changes, observers are notified by means of the list-sorted event. ''' if self.isFrozen(): return oldSelf = self[:] # XXXTODO: create only one function with all keys ? Reversing may # be problematic. for sortKey in reversed(self._sortKeys): self.sort(key=self.createSortKeyFunction(sortKey.lstrip('-')), reverse=sortKey.startswith('-')) if forceEvent or self != oldSelf: pub.sendMessage(self.sortEventType(), sender=self)
def extendSelf(self, tasks, event=None): ''' This method is called when a task is added to the observed list. It overrides ObservableListObserver.extendSelf whose default behaviour is to add the item that is added to the observed list to the observing list (this list) unchanged. But we want to add the efforts of the tasks, rather than the tasks themselves. ''' effortsToAdd = [] for task in tasks: effortsToAdd.extend(task.efforts()) super(EffortList, self).extendSelf(effortsToAdd, event) for effort in effortsToAdd: if effort.getStop() is None: pub.sendMessage(effort.trackingChangedEventType(), newValue=True, sender=effort)
def removeItemsFromSelf(self, tasks, event=None): ''' This method is called when a task is removed from the observed list. It overrides ObservableListObserver.removeItemsFromSelf whose default behaviour is to remove the item that was removed from the observed list from the observing list (this list) unchanged. But we want to remove the efforts of the tasks, rather than the tasks themselves. ''' effortsToRemove = [] for task in tasks: effortsToRemove.extend(task.efforts()) for effort in effortsToRemove: if effort.getStop() is None: pub.sendMessage(effort.trackingChangedEventType(), newValue=False, sender=effort) super(EffortList, self).removeItemsFromSelf(effortsToRemove, event)
def testMenuIsUpdatedWhenTemplatesAreSaved(self): uicommands = [None] # Just a separator for testing purposes class TaskTemplateMenu(gui.menu.TaskTemplateMenu): def getUICommands(self): return uicommands settings = config.Settings(load=False) taskList = task.TaskList() menu = TaskTemplateMenu(self.frame, taskList, settings) self.assertEqual(1, len(menu)) uicommands.append(None) # Add another separator pub.sendMessage('templates.saved') self.assertEqual(2, len(menu))
def setTask(self, task): if self._task is None: # We haven't been fully initialised yet, so allow setting of the # task, without notifying observers. Also, don't call addEffort() # on the new task, because we assume setTask was invoked by the # new task itself. self._task = None if task is None else weakref.ref(task) return if task in (self.task(), None): # command.PasteCommand may try to set the parent to None return event = patterns.Event() # Change monitor needs one event to detect task change self._task().removeEffort(self) self._task = weakref.ref(task) self._task().addEffort(self) event.send() pub.sendMessage(self.taskChangedEventType(), newValue=task, sender=self)
def setTask(self, task): if self._task is None: # We haven't been fully initialised yet, so allow setting of the # task, without notifying observers. Also, don't call addEffort() # on the new task, because we assume setTask was invoked by the # new task itself. self._task = None if task is None else weakref.ref(task) return if task in (self.task(), None): # command.PasteCommand may try to set the parent to None return event = patterns.Event( ) # Change monitor needs one event to detect task change self._task().removeEffort(self) self._task = weakref.ref(task) self._task().addEffort(self) event.send() pub.sendMessage(self.taskChangedEventType(), newValue=task, sender=self)
def setStop(self, newStop=None): if newStop is None: newStop = date.DateTime.now() elif newStop == date.DateTime.max: newStop = None if newStop == self._stop: return previousStop = self._stop self._stop = newStop self.__updateDurationCache() if newStop == None: pub.sendMessage(self.trackingChangedEventType(), newValue=True, sender=self) self.task().sendTrackingChangedMessage(tracking=True) elif previousStop == None: pub.sendMessage(self.trackingChangedEventType(), newValue=False, sender=self) self.task().sendTrackingChangedMessage(tracking=False) self.task().sendTimeSpentChangedMessage() pub.sendMessage(self.stopChangedEventType(), newValue=self._stop, sender=self) self.sendDurationChangedMessage() if self.task().hourlyFee(): self.sendRevenueChangedMessage()
def save(self): pub.sendMessage('taskfile.aboutToSave', taskFile=self) # When encountering a problem while saving (disk full, # computer on fire), if we were writing directly to the file, # it's lost. So write to a temporary file and rename it if # everything went OK. self.__saving = True try: self.mergeDiskChanges() if self.__needSave or not os.path.exists(self.__filename): name, fd = self._openForWrite() xml.XMLWriter(fd).write(self.tasks(), self.categories(), self.notes(), self.syncMLConfig(), self.guid()) fd.close() if os.path.exists(self.__filename): # Not using self.exists() because DummyFile.exists returns True os.remove(self.__filename) if name is not None: # Unit tests (AutoSaver) os.rename(name, self.__filename) self.markClean() finally: self.__saving = False self.__notifier.saved()
def markClean(self): if self.__needSave: self.__needSave = False pub.sendMessage('taskfile.clean', taskFile=self)
def notifyObserversOfDurationOrEmpty(self): if self._getEfforts(): self.sendDurationChangedMessage() else: pub.sendMessage(self.compositeEmptyEventType(), sender=self)
def settext(self, section, option, value): if self.set(section, option, value): pub.sendMessage('settings.%s.%s' % (section, option), value=value)
def setboolean(self, section, option, value): if self.set(section, option, str(value)): pub.sendMessage('settings.%s.%s' % (section, option), value=value)
def sendRevenueChangedMessage(self): pub.sendMessage(self.revenueChangedEventType(), newValue=self.revenue(), sender=self)
def sendDurationChangedMessage(self): pub.sendMessage(self.durationChangedEventType(), newValue=self.duration(), sender=self)
def markDirty(self, force=False): if force or not self.__needSave: self.__needSave = True pub.sendMessage('taskfile.dirty', taskFile=self)
def onStatusChanged(self, viewer): if self.activeViewer() == viewer: self.sendViewerStatusEvent() pub.sendMessage('all.viewer.status', viewer=viewer)
def sendViewerStatusEvent(self): pub.sendMessage('viewer.status')
def sendViewerStatusEvent(self): pub.sendMessage(self.viewerStatusEventType(), viewer=self)
def copyTemplate(self, filename): shutil.copyfile(filename, os.path.join(self._path, os.path.split(filename)[-1])) pub.sendMessage('templates.saved')
def OnPowerState(self, state): pub.sendMessage('powermgt.%s' % { self.POWERON: 'on', self.POWEROFF: 'off' }[state])