Exemplo n.º 1
0
    def __init__(self, view, dialogs, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self._view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()
        # self._show_toolbar = int(self._settings.value("show_toolbar", 1))
        # fix migration issue from old settings
        show_toolbar = self._settings.value("show_toolbar", 1)
        if show_toolbar in ("true", "false"):
            show_toolbar = 1
        self._show_toolbar = int(show_toolbar)
        self._add_created_date = int(
            self._settings.value("add_created_date", 1))
        self._auto_save = int(self._settings.value("auto_save", 1))
        self._auto_archive = int(self._settings.value("auto_archive", 1))
        self._hide_future_tasks = int(
            self._settings.value("hide_future_tasks", 1))

        self._dialogs = dialogs
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._setIsModified(False)
        self._view.closeEventSignal.connect(self._view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller._view.setSelectedFiltersByNames(filters)
Exemplo n.º 2
0
 def __init__(self, view, dialogs_service, task_editor_service, args):
     super(MainController, self).__init__()
     self._args = args
     self._view = view
     self._dialogs_service = dialogs_service
     self._task_editor_service = task_editor_service
     self._initControllers()
     self._file = File()
     self._fileObserver = FileObserver(self, self._file)
     self._is_modified = False
     self._settings = settings.Settings()
     self._setIsModified(False)
     self._view.closeEventSignal.connect(self._view_onCloseEvent)
Exemplo n.º 3
0
    def __init__(self, view, dialogs, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self.view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()
        # self._show_toolbar = int(self._settings.value("show_toolbar", 1))
        # fix migration issue from old settings
        show_toolbar = self._settings.value("show_toolbar", 1)
        if show_toolbar in ("true", "false"):
            show_toolbar = 1
        self._show_toolbar = int(show_toolbar)
        self._show_completed = True
        self._dialogs = dialogs
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._setIsModified(False)
        self.view.closeEventSignal.connect(self.view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller.view.setSelectedFiltersByNames(filters)
 def _createMockFile(self):
     today = today = date.today().strftime('%Y-%m-%d')
     file = File()
     file.tasks.append(tasklib.Task('my task1 @context1'))
     file.tasks.append(tasklib.Task('my task2 @context1 @context2'))
     file.tasks.append(tasklib.Task('due:' + today + ' my task3 +project1 @context2'))
     file.tasks.append(tasklib.Task('due:' + today + ' my task4'))
     return file
Exemplo n.º 5
0
 def openFileByName(self, filename):
     logger.debug('MainController.openFileByName called with filename="{}"'.format(filename))
     self._fileObserver.clear()
     self._file = File(self._todoFeatures)
     self._file.load(filename)
     self._loadFileToUI()
     self._settings.setLastOpenFile(filename)
     logger.debug('Adding {} to watchlist'.format(filename))
     self._fileObserver.addPath(self._file.filename)
Exemplo n.º 6
0
 def __init__(self, view, dialogs, task_editor_service, args):
     super(MainController, self).__init__()
     self._args = args
     self._view = view
     self.settings = QtCore.QSettings()
     # handle the bad bool handling of qsettings
     self._show_toolbar = True if self.settings.value(
         "show_toolbar", "true") == "true" else False
     self._dialogs = dialogs
     self._task_editor_service = task_editor_service
     self._initControllers()
     self._file = File()
     self._fileObserver = FileObserver(self, self._file)
     self._is_modified = False
     # FIXME use of custom settings should be removed
     self._settings = settings.Settings()
     self._setIsModified(False)
     self._view.closeEventSignal.connect(self._view_onCloseEvent)
Exemplo n.º 7
0
    def __init__(self, view, dialogs, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self.view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()
        self._show_completed = True
        self._dialogs = dialogs
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._setIsModified(False)
        self._fileObserver.fileChangetSig.connect(self.openFileByName)
        self.view.closeEventSignal.connect(self.view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller.view.setSelectedFiltersByNames(filters)
        self.hasTrayIcon = False
        self._menu_controller.updateRecentFileActions()
Exemplo n.º 8
0
 def __init__(self, view, dialogs_service, task_editor_service, args):
     super(MainController, self).__init__()
     self._args = args
     self._view = view
     self._dialogs_service = dialogs_service
     self._task_editor_service = task_editor_service
     self._initControllers()
     self._file = File()
     self._fileObserver = FileObserver(self, self._file)
     self._is_modified = False
     self._settings = settings.Settings()
     self._setIsModified(False)
     self._view.closeEventSignal.connect(self._view_onCloseEvent)
Exemplo n.º 9
0
 def __init__(self, view, dialogs, task_editor_service, args):
     super(MainController, self).__init__()
     self._args = args
     self._view = view
     self.settings = QtCore.QSettings()
     # handle the bad bool handling of qsettings
     self._show_toolbar = True if self.settings.value("show_toolbar", "true") == "true" else False
     self._dialogs = dialogs
     self._task_editor_service = task_editor_service
     self._initControllers()
     self._file = File()
     self._fileObserver = FileObserver(self, self._file)
     self._is_modified = False
     # FIXME use of custom settings should be removed
     self._settings = settings.Settings()
     self._setIsModified(False)
     self._view.closeEventSignal.connect(self._view_onCloseEvent)
Exemplo n.º 10
0
    def __init__(self, view, dialogs, args):
        super(MainController, self).__init__()
        self._args = args
        self.view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()

        self._show_completed = True
        self._dialogs = dialogs
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._initControllers()
        self._is_modified = False
        self._setIsModified(False)
        self._fileObserver.fileChangetSig.connect(self.openFileByName)
        self.view.closeEventSignal.connect(self.view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller.view.setSelectedFiltersByNames(filters)
        self.hasTrayIcon = False
        self._menu_controller.updateRecentFileActions()
Exemplo n.º 11
0
 def new(self):
     if self._canExit():
         self._file = File()
         self._loadFileToUI()
Exemplo n.º 12
0
 def new(self):
     if self._canExit():
         self._file = File()
         self._loadFileToUI()
Exemplo n.º 13
0
class MainController(QtCore.QObject):

    _show_toolbar = QtCore.pyqtSignal(int)

    def __init__(self, view, dialogs, args):
        super(MainController, self).__init__()
        self._args = args
        self.view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()

        self._show_completed = True
        self._dialogs = dialogs
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._initControllers()
        self._is_modified = False
        self._setIsModified(False)
        self._fileObserver.fileChangetSig.connect(self.openFileByName)
        self.view.closeEventSignal.connect(self.view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller.view.setSelectedFiltersByNames(filters)
        self.hasTrayIcon = False
        self._menu_controller.updateRecentFileActions()

    def auto_save(self):
        if int(self._settings.value("auto_save", 1)):
            self.save()

    def _initControllers(self):
        self._initFiltersTree()
        self._initTasksList()
        self._initContextualMenu()
        self._initActions()
        self._initMenuBar()
        self._initToolBar()
        self._initSearchText()

    def _initMenuBar(self):
        menu = self.view.menuBar()
        self._menu_controller = MenuController(self, menu)

    def _initActions(self):
        self.filterViewAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/sidepane.png'),
                                                  self.tr('Show &Filters'), self)
        self.filterViewAction.setCheckable(True)
        self.filterViewAction.setShortcuts(['Ctrl+Shift+F'])
        self.filterViewAction.triggered.connect(self._toggleFilterView)

        self.showFutureAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/future.png'),
                                                  self.tr('Show future &Tasks'), self)
        self.showFutureAction.setCheckable(True)
        self.showFutureAction.setShortcuts(['Ctrl+Shift+T'])
        self.showFutureAction.triggered.connect(self._toggleShowFuture)

        self.showCompletedAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/show_completed.png'),
                                                     self.tr('Show &Completed tasks'), self)
        self.showCompletedAction.setCheckable(True)
        self.showCompletedAction.setShortcuts(['Ctrl+Shift+C'])
        self.showCompletedAction.triggered.connect(self._toggleShowCompleted)

        self.archiveAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/archive.png'),
                                               self.tr('&Archive completed tasks'), self)
        self.archiveAction.setShortcuts(['Ctrl+Shift+A'])
        self.archiveAction.triggered.connect(self._archive_all_done_tasks)

        self.showToolBarAction = QtWidgets.QAction(self.tr('Show tool&Bar'), self)
        self.showToolBarAction.setCheckable(True)
        self.showToolBarAction.setShortcuts(['Ctrl+Shift+B'])
        self.showToolBarAction.triggered.connect(self._toggleShowToolBar)

        self.showSearchAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/ActionSearch.png'),
                                                  self.tr('Show search bar'), self)
        self.showSearchAction.setCheckable(True)
        self.showSearchAction.setShortcuts(['Ctrl+F'])
        self.showSearchAction.triggered.connect(self._toggleShowSearch)

    def _initToolBar(self):
        toolbar = self.view.addToolBar("Main Toolbar")
        toolbar.setObjectName("mainToolbar")

        toolbar.addAction(self.filterViewAction)
        toolbar.addAction(self.showFutureAction)
        toolbar.addAction(self.showCompletedAction)
        toolbar.addAction(self.showSearchAction)

        toolbar.addSeparator()

        toolbar.addAction(self._menu_controller.openAction)
        toolbar.addAction(self._menu_controller.saveAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.createTaskAction)
        toolbar.addAction(self._tasks_list_controller.createTaskActionOnTemplate)
        toolbar.addAction(self._tasks_list_controller.editTaskAction)
        toolbar.addAction(self._tasks_list_controller.copySelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.completeSelectedTasksAction)
        if int(self._settings.value("show_delete", 0)):
            toolbar.addAction(self._tasks_list_controller.deleteSelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.increasePrioritySelectedTasksAction)
        toolbar.addAction(self._tasks_list_controller.decreasePrioritySelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self.archiveAction)

        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.addLinkAction)

        self._show_toolbar.connect(toolbar.setVisible)

    def _toggleShowToolBar(self):
        if self.showToolBarAction.isChecked():
            self._settings.setValue("show_toolbar", 1)
            self._toolbar_visibility_changed(1)
        else:
            self._settings.setValue("show_toolbar", 0)
            self._toolbar_visibility_changed(0)

    def _toggleShowSearch(self):
        if self.showSearchAction.isChecked():
            self._settings.setValue("show_search", 1)
            self.view.tasks_view.tasks_search_view.setVisible(True)
        else:
            self._settings.setValue("show_search", 0)
            self.view.tasks_view.tasks_search_view.setVisible(False)
            self.view.tasks_view.tasks_search_view.setText("")

    def _toggleShowCompleted(self):
        if self.showCompletedAction.isChecked():
            self._settings.setValue("show_completed_tasks", 1)
            self._show_completed = True
            self.updateFilters()
        else:
            self._settings.setValue("show_completed_tasks", 0)
            self._show_completed = False
            self.updateFilters()
        self._filters_tree_controller.showFilters(self._file, self._show_completed)

    def _toggleShowFuture(self):
        if self.showFutureAction.isChecked():
            self._settings.setValue("show_future_tasks", 1)
            self.updateFilters()
        else:
            self._settings.setValue("show_future_tasks", 0)
            self.updateFilters()

    def _restoreShowFuture(self):
        val = int(self._settings.value("show_future_tasks", 1))
        if val:
            self.showFutureAction.setChecked(True)
            self._toggleShowFuture()
        else:
            self.showFutureAction.setChecked(False)
            self._toggleShowFuture()

    def _toggleFilterView(self):
        if self.filterViewAction.isChecked():
            self._settings.setValue("show_filter_tree", 1)
            self._filters_tree_controller.view.show()
        else:
            self._settings.setValue("splitter_pos", self.view.centralWidget().sizes())
            self._settings.setValue("show_filter_tree", 0)
            self._filters_tree_controller.view.hide()

    def _restoreFilterView(self):
        val = int(self._settings.value("show_filter_tree", 1))
        if val:
            self.filterViewAction.setChecked(True)
            self._toggleFilterView()
        else:
            self.filterViewAction.setChecked(False)
            self._toggleFilterView()

    def _toolbar_visibility_changed(self, val):
        self._show_toolbar.emit(val)

    def exit(self):
        self.view.close()
        sys.exit()

    def getView(self):
        return self.view

    def show(self):
        self._updateView()
        self.view.show()
        self._updateTitle()

        if self._args.file:
            filename = self._args.file
        else:
            filename = self._settings.value("last_open_file")

        if filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

        if self._args.quickadd:
            self._tasks_list_controller.createTask()
            self.save()
            self.exit()

    def _initFiltersTree(self):
        controller = self._filters_tree_controller = \
            FiltersTreeController(self.view.filters_tree_view)
        controller.filterSelectionChanged.connect(
            self._onFilterSelectionChanged)

    def _onFilterSelectionChanged(self, filters):
        self._applyFilters(filters=filters)

    def _applyFilters(self, filters=None, searchText=None):
        # First we filter with filters tree
        if filters is None:
            filters = self._filters_tree_controller.view.getSelectedFilters()
        tasks = tasklib.filterTasks(filters, self._file.tasks)
        # Then with our search text
        if searchText is None:
            searchText = self.view.tasks_view.tasks_search_view.getSearchText()
        tasks = tasklib.filterTasks([SimpleTextFilter(searchText)], tasks)
        # with future filter if needed
        if not self.showFutureAction.isChecked():
            tasks = tasklib.filterTasks([FutureFilter()], tasks)
        # with complete filter if needed
        if not CompleteTasksFilter() in filters and not self.showCompletedAction.isChecked():
            tasks = tasklib.filterTasks([IncompleteTasksFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initSearchText(self):
        self.view.tasks_view.tasks_search_view.searchTextChanged.connect(
            self._onSearchTextChanged)

    def _onSearchTextChanged(self, searchText):
        self._applyFilters(searchText=searchText)

    def _initTasksList(self):
        controller = self._tasks_list_controller = \
            TasksListController(self.view.tasks_view.tasks_list_view, self._file)

        controller.taskCreated.connect(self._tasks_list_taskCreated)
        controller.taskModified.connect(self._tasks_list_taskModified)
        controller.taskDeleted.connect(self._tasks_list_taskDeleted)
        controller.taskArchived.connect(self._tasks_list_taskArchived)

    def _initContextualMenu(self):

        # Context menu
        # controller.view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
        self._tasks_list_controller.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self._tasks_list_controller.view.customContextMenuRequested.connect(self.showContextMenu)
        self._contextMenu = QtWidgets.QMenu(self.view)
        self._contextMenu.addAction(self._tasks_list_controller.createTaskAction)
        self._contextMenu.addAction(self._tasks_list_controller.createTaskActionOnTemplate)
        self._contextMenu.addAction(self._tasks_list_controller.editTaskAction)
        self._contextMenu.addAction(self._tasks_list_controller.copySelectedTasksAction)
        self._contextMenu.addAction(self._tasks_list_controller.addLinkAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(self._tasks_list_controller.completeSelectedTasksAction)
        if int(self._settings.value("show_delete", 1)):
            self._contextMenu.addAction(self._tasks_list_controller.deleteSelectedTasksAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(self._tasks_list_controller.increasePrioritySelectedTasksAction)
        self._contextMenu.addAction(self._tasks_list_controller.decreasePrioritySelectedTasksAction)

    def showContextMenu(self, position):
        tasks = self._tasks_list_controller.view.getSelectedTasks()
        if tasks:
            self._contextMenu.exec_(self._tasks_list_controller.view.mapToGlobal(position))

    def _tasks_list_taskDeleted(self, task):
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _tasks_list_taskCreated(self, task):
        self._file.tasks.append(task)
        self._onFileUpdated()

    def _tasks_list_taskModified(self):
        self._onFileUpdated()

    def _tasks_list_taskArchived(self, task):
        self._file.saveDoneTask(task)
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _archive_all_done_tasks(self):
        done = [task for task in self._file.tasks if task.is_complete]
        for task in done:
            self._file.saveDoneTask(task)
            self._file.tasks.remove(task)
        self._onFileUpdated()

    def _onFileUpdated(self):
        self._filters_tree_controller.showFilters(self._file, self._show_completed)
        self._setIsModified(True)
        self.auto_save()

    def _canExit(self):
        if not self._is_modified:
            return True
        button = self._dialogs.showSaveDiscardCancel(self.tr('Unsaved changes...'))
        if button == QtWidgets.QMessageBox.Save:
            self.save()
            return True
        else:
            return button == QtWidgets.QMessageBox.Discard

    def view_onCloseEvent(self, closeEvent):

        if (self.hasTrayIcon and int(self._settings.value("close_to_tray", 0))):
            self.view.hide()
            closeEvent.ignore()
            return

        if self._canExit():
            if self.filterViewAction.isChecked():  # we only save size if it is visible
                self._settings.setValue("splitter_pos", self.view.centralWidget().sizes())
            self._settings.setValue("current_filters", self._filters_tree_controller.view.getSelectedFilterNames())
            self._settings.setValue("main_window_geometry", self.view.saveGeometry())
            self._settings.setValue("main_window_state", self.view.saveState())

            closeEvent.accept()
        else:
            closeEvent.ignore()

    def _setIsModified(self, is_modified):
        self._is_modified = is_modified
        self._updateTitle()
        self._menu_controller.saveAction.setEnabled(is_modified)
        self._menu_controller.revertAction.setEnabled(is_modified)

    def save(self):
        logger.debug('MainController.save called.')
        self._fileObserver.clear()
        filename = self._file.filename
        ok = True
        if not filename:
            (filename, ok) = \
                QtWidgets.QFileDialog.getSaveFileName(self.view, filter=FILENAME_FILTERS)
        if ok and filename:
            self._file.save(filename)
            self._settings.setValue("last_open_file", filename)
            self._settings.sync()
            self._setIsModified(False)
            logger.debug('Adding {} to watchlist'.format(filename))
            self._fileObserver.addPath(self._file.filename)

    def _updateTitle(self):
        title = 'QTodoTxt - '
        if self._file.filename:
            filename = os.path.basename(self._file.filename)
            title += filename
        else:
            title += 'Untitled'
        if self._is_modified:
            title += ' (*)'
        self.view.setWindowTitle(title)

    def open(self):
        (filename, ok) = \
            QtWidgets.QFileDialog.getOpenFileName(self.view, filter=FILENAME_FILTERS)

        if ok and filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def new(self):
        if self._canExit():
            self._file = File()
            self._loadFileToUI()

    def revert(self):
        if self._dialogs.showConfirm(self.tr('Revert to saved file (and lose unsaved changes)?')):
            try:
                self.openFileByName(self._file.filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def openFileByName(self, filename):
        logger.debug('MainController.openFileByName called with filename="{}"'.format(filename))
        self._fileObserver.clear()
        try:
            self._file.load(filename)
        except Exception as ex:
            currentfile = self._settings.value("last_open_file", "")
            if currentfile == filename:
                self._dialogs.showError(self.tr("Current file '{}' is not available.\nException: {}").
                                        format(filename, ex))
            else:
                self._dialogs.showError(self.tr("Error opening file: {}.\n Exception:{}").format(filename, ex))
            return
        self._loadFileToUI()
        self._settings.setValue("last_open_file", filename)
        self._settings.sync()
        logger.debug('Adding {} to watchlist'.format(filename))
        self._fileObserver.addPath(self._file.filename)
        self.updateRecentFile()

    def updateRecentFile(self):
        lastOpenedArray = self._menu_controller.getRecentFileNames()
        if self._file.filename in lastOpenedArray:
            lastOpenedArray.remove(self._file.filename)
        lastOpenedArray = lastOpenedArray[:self._menu_controller.maxRecentFiles]
        lastOpenedArray.insert(0, self._file.filename)
        self._settings.setValue("lastOpened", lastOpenedArray[: self._menu_controller.maxRecentFiles])
        self._menu_controller.updateRecentFileActions()

    def _loadFileToUI(self):
        self._setIsModified(False)
        self._filters_tree_controller.showFilters(self._file, self._show_completed)

    def _updateView(self):
        wgeo = self._settings.value("main_window_geometry", None)
        if wgeo is not None:
            self.view.restoreGeometry(wgeo)
        wstate = self._settings.value("main_window_state", None)
        if wstate is not None:
            self.view.restoreState(wstate)
        splitterPosition = self._settings.value("splitter_pos", (200, 400))
        splitterPosition = [int(x) for x in splitterPosition]
        self.view.centralWidget().setSizes(splitterPosition)
        self._restoreShowCompleted()
        self._restoreFilterView()
        self._restoreShowFuture()
        self._restoreShowToolBar()
        self._restoreShowSearch()

    def _restoreShowCompleted(self):
        val = int(self._settings.value("show_completed_tasks", 1))
        if val:
            self._show_completed = True
            self.showCompletedAction.setChecked(True)
        else:
            self._show_completed = False
            self.showCompletedAction.setChecked(False)

    def _restoreShowToolBar(self):
        val = int(self._settings.value("show_toolbar", 1))
        if val:
            self._toolbar_visibility_changed(1)
            self.showToolBarAction.setChecked(True)
        else:
            self._toolbar_visibility_changed(0)
            self.showToolBarAction.setChecked(False)

    def _restoreShowSearch(self):
        val = int(self._settings.value("show_search", 1))
        if val:
            self.view.tasks_view.tasks_search_view.setVisible(True)
            self.showSearchAction.setChecked(True)
        else:
            self.view.tasks_view.tasks_search_view.setVisible(False)
            self.showSearchAction.setChecked(False)

    def updateFilters(self):
        self._onFilterSelectionChanged(self._filters_tree_controller.view.getSelectedFilters())

    def toggleVisible(self):
        if self.view.isMinimized() or self.view.isHidden():
            self.view.show()
            self.view.activateWindow()
        else:
            self.view.hide()

    def anotherInstanceEvent(self, dir):
        tFile = dir + "/qtodo.tmp"
        if not os.path.isfile(tFile):
            return
        time.sleep(0.01)
        f = open(tFile, 'r+b')
        line = f.readline()
        line = line.strip()
        if line == b"1":
            self.view.show()
            self.view.activateWindow()
        if line == b"2":
            self.view.show()
            self.view.activateWindow()
            self._tasks_list_controller.createTask()

        f.close()
        os.remove(tFile)
Exemplo n.º 14
0
class MainController(QtCore.QObject):
    def __init__(self, view, dialogs, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self._view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()
        # self._show_toolbar = int(self._settings.value("show_toolbar", 1))
        # fix migration issue from old settings
        show_toolbar = self._settings.value("show_toolbar", 1)
        if show_toolbar in ("true", "false"):
            show_toolbar = 1
        self._show_toolbar = int(show_toolbar)
        self._add_created_date = int(
            self._settings.value("add_created_date", 1))
        self._auto_save = int(self._settings.value("auto_save", 1))
        self._auto_archive = int(self._settings.value("auto_archive", 1))
        self._hide_future_tasks = int(
            self._settings.value("hide_future_tasks", 1))

        self._dialogs = dialogs
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._setIsModified(False)
        self._view.closeEventSignal.connect(self._view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller._view.setSelectedFiltersByNames(filters)

    def auto_save(self):
        if self._auto_save:
            self.save()

    def _initControllers(self):
        self._initFiltersTree()
        self._initTasksList()
        self._initMenuBar()
        self._initToolBar()
        self._initFilterText()

    def _initMenuBar(self):
        menu = self._view.menuBar()
        self._menu_controller = MenuController(self, menu)

    def _initToolBar(self):
        toolbar = self._view.addToolBar("Main Toolbar")
        toolbar.setObjectName("mainToolbar")
        toolbar.addAction(self._menu_controller.openAction)
        toolbar.addAction(self._menu_controller.saveAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.createTaskAction)
        toolbar.addAction(self._tasks_list_controller.editTaskAction)
        toolbar.addSeparator()
        toolbar.addAction(
            self._tasks_list_controller.completeSelectedTasksAction)
        toolbar.addAction(
            self._tasks_list_controller.deleteSelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(
            self._tasks_list_controller.increasePrioritySelectedTasksAction)
        toolbar.addAction(
            self._tasks_list_controller.decreasePrioritySelectedTasksAction)
        toolbar.visibilityChanged.connect(self._toolbar_visibility_changed)
        if not self._show_toolbar:
            toolbar.hide()

    def _toolbar_visibility_changed(self, val):
        self._show_toolbar = int(val)

    def exit(self):
        self._view.close()
        sys.exit()

    def getView(self):
        return self._view

    def show(self):
        self._updateView()
        self._view.show()
        self._updateTitle()
        self._updateCreatePref()
        self._updateAutoSavePref()
        self._updateAutoArchivePref()
        self._updateHideFutureTasksPref()

        if self._args.file:
            filename = self._args.file
        else:
            filename = self._settings.value("last_open_file")

        if filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

        if self._args.quickadd:
            self._tasks_list_controller.createTask()
            self.save()
            self.exit()

    def _initFiltersTree(self):
        controller = self._filters_tree_controller = \
            FiltersTreeController(self._view.filters_tree_view)
        controller.filterSelectionChanged.connect(
            self._onFilterSelectionChanged)

    def _onFilterSelectionChanged(self, filters):
        # First we filter with filters tree
        treeTasks = tasklib.filterTasks(filters, self._file.tasks)
        # Then with our filter text
        filterText = self._view.tasks_view.tasks_filter.getText()
        tasks = tasklib.filterTasks([SimpleTextFilter(filterText)], treeTasks)
        # And finally with future filter if needed
        # TODO: refactor all that filters
        if self._hide_future_tasks:
            tasks = tasklib.filterTasks([FutureFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initFilterText(self):
        self._view.tasks_view.tasks_filter.filterTextChanged.connect(
            self._onFilterTextChanged)

    def _onFilterTextChanged(self, text):
        # First we filter with filters tree
        filters = self._filters_tree_controller._view.getSelectedFilters()
        treeTasks = tasklib.filterTasks(filters, self._file.tasks)
        # Then with our filter text
        tasks = tasklib.filterTasks([SimpleTextFilter(text)], treeTasks)
        # And finally with future filter if needed
        # TODO: refactor all that filters
        if self._hide_future_tasks:
            tasks = tasklib.filterTasks([FutureFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initTasksList(self):
        controller = self._tasks_list_controller = \
            TasksListController(self._view.tasks_view.tasks_list_view, self._task_editor_service)

        controller.taskCreated.connect(self._tasks_list_taskCreated)
        controller.taskModified.connect(self._tasks_list_taskModified)
        controller.taskDeleted.connect(self._tasks_list_taskDeleted)
        controller.taskArchived.connect(self._tasks_list_taskArchived)

        # Context menu
        # controller._view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
        controller._view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        controller._view.customContextMenuRequested.connect(
            self.showContextMenu)
        self._contextMenu = QtGui.QMenu()
        self._contextMenu.addAction(self._tasks_list_controller.editTaskAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(
            self._tasks_list_controller.completeSelectedTasksAction)
        self._contextMenu.addAction(
            self._tasks_list_controller.deleteSelectedTasksAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(
            self._tasks_list_controller.increasePrioritySelectedTasksAction)
        self._contextMenu.addAction(
            self._tasks_list_controller.decreasePrioritySelectedTasksAction)

    def showContextMenu(self, position):
        tasks = self._tasks_list_controller._view.getSelectedTasks()
        if tasks:
            self._contextMenu.exec_(
                self._tasks_list_controller._view.mapToGlobal(position))

    def _tasks_list_taskDeleted(self, task):
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _tasks_list_taskCreated(self, task):
        self._file.tasks.append(task)
        self._onFileUpdated()

    def _tasks_list_taskModified(self, task):
        self._onFileUpdated()

    def _tasks_list_taskArchived(self, task):
        self._file.saveDoneTask(task)
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _onFileUpdated(self):
        self._filters_tree_controller.showFilters(self._file)
        self._task_editor_service.updateValues(self._file)
        self._setIsModified(True)
        self.auto_save()

    def _canExit(self):
        if not self._is_modified:
            return True
        button = self._dialogs.showSaveDiscardCancel('Unsaved changes...')
        if button == QtGui.QMessageBox.Save:
            self.save()
            return True
        else:
            return button == QtGui.QMessageBox.Discard

    def _view_onCloseEvent(self, closeEvent):
        if self._canExit():
            self._settings.setValue("show_toolbar", self._show_toolbar)
            self._settings.setValue("splitter_pos",
                                    self._view.centralWidget().sizes())
            self._settings.setValue(
                "current_filters",
                self._filters_tree_controller._view.getSelectedFilterNames())
            self._settings.setValue("main_window_geometry",
                                    self._view.saveGeometry())
            self._settings.setValue("main_window_state",
                                    self._view.saveState())

            self._settings.setValue("add_created_date", self._add_created_date)
            self._settings.setValue("auto_save", self._auto_save)
            self._settings.setValue("auto_archive", self._auto_archive)
            self._settings.setValue("hide_future_tasks",
                                    self._hide_future_tasks)

            closeEvent.accept()
        else:
            closeEvent.ignore()

    def _setIsModified(self, is_modified):
        self._is_modified = is_modified
        self._updateTitle()
        self._menu_controller.saveAction.setEnabled(is_modified)
        self._menu_controller.revertAction.setEnabled(is_modified)

    def save(self):
        logger.debug('MainController.save called.')
        self._fileObserver.clear()
        filename = self._file.filename
        ok = True
        if not filename:
            (filename, ok) = \
                QtGui.QFileDialog.getSaveFileName(self._view, filter=FILENAME_FILTERS)
        if ok and filename:
            self._file.save(filename)
            self._settings.setValue("last_open_file", filename)
            self._settings.sync()
            self._setIsModified(False)
            logger.debug('Adding {} to watchlist'.format(filename))
            self._fileObserver.addPath(self._file.filename)

    def _updateTitle(self):
        title = 'QTodoTxt - '
        if self._file.filename:
            filename = os.path.basename(self._file.filename)
            title += filename
        else:
            title += 'Untitled'
        if self._is_modified:
            title += ' (*)'
        self._view.setWindowTitle(title)

    def open(self):
        (filename, ok) = \
            QtGui.QFileDialog.getOpenFileName(self._view, filter=FILENAME_FILTERS)

        if ok and filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def new(self):
        if self._canExit():
            self._file = File()
            self._loadFileToUI()

    def revert(self):
        if self._dialogs.showConfirm(
                'Revert to saved file (and lose unsaved changes)?'):
            try:
                self.openFileByName(self._file.filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def openFileByName(self, filename):
        logger.debug(
            'MainController.openFileByName called with filename="{}"'.format(
                filename))
        self._fileObserver.clear()
        self._file.load(filename)
        self._loadFileToUI()
        self._settings.setValue("last_open_file", filename)
        self._settings.sync()
        logger.debug('Adding {} to watchlist'.format(filename))
        self._fileObserver.addPath(self._file.filename)

    def _loadFileToUI(self):
        self._setIsModified(False)
        self._filters_tree_controller.showFilters(self._file)
        self._task_editor_service.updateValues(self._file)

    def _updateCreatePref(self):
        self._menu_controller.changeCreatedDateState(self._add_created_date)

    def _updateAutoSavePref(self):
        self._menu_controller.changeAutoSaveState(self._auto_save)

    def _updateAutoArchivePref(self):
        self._menu_controller.changeAutoArchiveState(self._auto_archive)

    def _updateHideFutureTasksPref(self):
        self._menu_controller.changeHideFutureTasksState(
            self._hide_future_tasks)

    def _updateView(self):
        self._view.restoreGeometry(
            self._settings.value("main_window_geometry"))
        self._view.restoreState(self._settings.value("main_window_state"))
        splitterPosition = self._settings.value("splitter_pos", None)
        if splitterPosition:
            splitterPosition = [int(x) for x in splitterPosition]
            self._view.centralWidget().setSizes(splitterPosition)

    def toggleCreatedDate(self):
        self._add_created_date = int(not self._add_created_date)
        self._settings.setValue("add_created_date", self._add_created_date)
        self._settings.sync()

    def toggleAutoSave(self):
        self._auto_save = int(not self._auto_save)
        self._settings.setValue("auto_save", self._auto_save)
        self._settings.sync()

    def toggleAutoArchive(self):
        self._auto_archive = int(not self._auto_archive)
        self._settings.setValue("auto_archive", self._auto_archive)
        self._settings.sync()

    def toggleHideFutureTasks(self):
        self._hide_future_tasks = int(not self._hide_future_tasks)
        self._settings.setValue("hide_future_tasks", self._hide_future_tasks)
        self._settings.sync()
        self._onFilterSelectionChanged(
            self._filters_tree_controller._view.getSelectedFilters())

    def toggleVisible(self):
        if self._view.isMinimized():
            self._view.showNormal()
            self._view.activateWindow()
        else:
            self._view.showMinimized()
Exemplo n.º 15
0
class MainController(QtCore.QObject):
    def __init__(self, view, dialogs_service, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self._view = view
        self._dialogs_service = dialogs_service
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._settings = settings.Settings()
        self._setIsModified(False)
        self._view.closeEventSignal.connect(self._view_onCloseEvent)

    def autoSave(self):
        if self._settings.getAutoSave():
            self.save()

    def _initControllers(self):
        self._initFiltersTree()
        self._initTasksList()
        self._initMenuBar()
        self._initFilterText()

    def _initMenuBar(self):
        menu = self._view.menuBar()
        self._menu_controller = MenuController(self, menu)

    def exit(self):
        self._view.close()
        sys.exit()

    def getView(self):
        return self._view

    def show(self):
        self._view.show()
        self._updateTitle()
        self._settings.load()
        self._updateCreatePref()
        self._updateAutoSavePref()
        self._updateAutoArchivePref()
        self._updateHideFutureTasksPref()
        self._updateView()

        if self._args.file:
            filename = self._args.file[0]
        else:
            filename = self._settings.getLastOpenFile()

        if filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs_service.showError(str(ex))

        if self._args.quickadd:
            self._tasks_list_controller.createTask()
            self.save()
            self.exit()

    def _initFiltersTree(self):
        controller = self._filters_tree_controller = \
            FiltersTreeController(self._view.filters_tree_view)
        controller.filterSelectionChanged.connect(
            self._onFilterSelectionChanged)

    def _onFilterSelectionChanged(self, filters):
        # First we filter with filters tree
        treeTasks = todolib.filterTasks(filters, self._file.tasks)
        # Then with our filter text
        filterText = self._view.tasks_view.filter_tasks.getText()
        tasks = todolib.filterTasks([SimpleTextFilter(filterText)], treeTasks)
        # And finally with future filter if needed
        # TODO: refactor all that filters
        if self._settings.getHideFutureTasks():
            tasks = todolib.filterTasks([FutureFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initFilterText(self):
        self._view.tasks_view.filter_tasks.filterTextChanged.connect(
            self._onFilterTextChanged)

    def _onFilterTextChanged(self, text):
        # First we filter with filters tree
        filters = self._filters_tree_controller._view.getSelectedFilters()
        treeTasks = todolib.filterTasks(filters, self._file.tasks)
        # Then with our filter text
        tasks = todolib.filterTasks([SimpleTextFilter(text)], treeTasks)
        # And finally with future filter if needed
        # TODO: refactor all that filters
        if self._settings.getHideFutureTasks():
            tasks = todolib.filterTasks([FutureFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initTasksList(self):
        controller = self._tasks_list_controller = \
            TasksListController(self._view.tasks_view.tasks_list_view, self._task_editor_service)

        controller.taskCreated.connect(self._tasks_list_taskCreated)
        controller.taskModified.connect(self._tasks_list_taskModified)
        controller.taskDeleted.connect(self._tasks_list_taskDeleted)
        controller.taskArchived.connect(self._tasks_list_taskArchived)

    def _tasks_list_taskDeleted(self, task):
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _tasks_list_taskCreated(self, task):
        self._file.tasks.append(task)
        self._onFileUpdated()

    def _tasks_list_taskModified(self, task):
        self._onFileUpdated()

    def _tasks_list_taskArchived(self, task):
        self._file.saveDoneTask(task)
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _onFileUpdated(self):
        self._filters_tree_controller.showFilters(self._file)
        self._task_editor_service.updateValues(self._file)
        self._setIsModified(True)
        self.autoSave()

    def _canExit(self):
        if not self._is_modified:
            return True
        button = self._dialogs_service.showSaveDiscardOrCancel(
            'Unsaved changes...')
        if button == QtGui.QMessageBox.Save:
            self.save()
            return True
        else:
            return button == QtGui.QMessageBox.Discard

    def _view_onCloseEvent(self, closeEvent):
        if self._canExit():
            self._saveView()
            closeEvent.accept()
        else:
            closeEvent.ignore()

    def _saveView(self):
        viewSize = self._view.size()
        viewPosition = self._view.pos()
        splitterPosition = self._view.centralWidget().sizes()
        self._settings.setViewHeight(viewSize.height())
        self._settings.setViewWidth(viewSize.width())
        self._settings.setViewPositionX(viewPosition.x())
        self._settings.setViewPositionY(viewPosition.y())
        self._settings.setViewSlidderPosition(splitterPosition)

    def _setIsModified(self, is_modified):
        self._is_modified = is_modified
        self._updateTitle()
        self._menu_controller.saveAction.setEnabled(is_modified)
        self._menu_controller.revertAction.setEnabled(is_modified)

    def save(self):
        logger.debug('MainController.save called.')
        self._fileObserver.clear()
        filename = self._file.filename
        ok = True
        if not filename:
            (filename, ok) = \
                QtGui.QFileDialog.getSaveFileName(self._view, filter=FILENAME_FILTERS)
        if ok and filename:
            self._file.save(filename)
            self._settings.setLastOpenFile(filename)
            self._setIsModified(False)
            logger.debug('Adding {} to watchlist'.format(filename))
            self._fileObserver.addPath(self._file.filename)

    def _updateTitle(self):
        title = 'QTodoTxt - '
        if self._file.filename:
            filename = os.path.basename(self._file.filename)
            title += filename
        else:
            title += 'Untitled'
        if self._is_modified:
            title += ' (*)'
        self._view.setWindowTitle(title)

    def open(self):
        (filename, ok) = \
            QtGui.QFileDialog.getOpenFileName(self._view, filter=FILENAME_FILTERS)

        if ok and filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs_service.showError(str(ex))

    def new(self):
        if self._canExit():
            self._file = File()
            self._loadFileToUI()

    def revert(self):
        if self._dialogs_service.showConfirm(
                'Revert to saved file (and lose unsaved changes)?'):
            try:
                self.openFileByName(self._file.filename)
            except ErrorLoadingFile as ex:
                self._dialogs_service.showError(str(ex))

    def openFileByName(self, filename):
        logger.debug(
            'MainController.openFileByName called with filename="{}"'.format(
                filename))
        self._fileObserver.clear()
        self._file.load(filename)
        self._loadFileToUI()
        self._settings.setLastOpenFile(filename)
        logger.debug('Adding {} to watchlist'.format(filename))
        self._fileObserver.addPath(self._file.filename)

    def _loadFileToUI(self):
        self._setIsModified(False)
        self._filters_tree_controller.showFilters(self._file)
        self._task_editor_service.updateValues(self._file)

    def _updateCreatePref(self):
        self._menu_controller.changeCreatedDateState(
            bool(self._settings.getCreateDate()))

    def _updateAutoSavePref(self):
        self._menu_controller.changeAutoSaveState(
            bool(self._settings.getAutoSave()))

    def _updateAutoArchivePref(self):
        self._menu_controller.changeAutoArchiveState(
            bool(self._settings.getAutoArchive()))

    def _updateHideFutureTasksPref(self):
        self._menu_controller.changeHideFutureTasksState(
            bool(self._settings.getHideFutureTasks()))

    def _updateView(self):
        height = self._settings.getViewHeight()
        width = self._settings.getViewWidth()
        if height and width:
            self._view.resize(width, height)

        positionX = self._settings.getViewPositionX()
        positionY = self._settings.getViewPositionY()
        if positionX and positionY:
            self._view.move(positionX, positionY)

        slidderPosition = self._settings.getViewSlidderPosition()
        if slidderPosition:
            self._view.centralWidget().setSizes(slidderPosition)

    def createdDate(self):
        if self._settings.getCreateDate():
            self._settings.setCreateDate(False)
        else:
            self._settings.setCreateDate(True)

    def toggleAutoSave(self):
        if self._settings.getAutoSave():
            self._settings.setAutoSave(False)
        else:
            self._settings.setAutoSave(True)

    def toggleAutoArchive(self):
        if self._settings.getAutoArchive():
            self._settings.setAutoArchive(False)
        else:
            self._settings.setAutoArchive(True)

    def toggleHideFutureTasks(self):
        if self._settings.getHideFutureTasks():
            self._settings.setHideFutureTasks(False)
        else:
            self._settings.setHideFutureTasks(True)
        self._onFilterSelectionChanged(
            self._filters_tree_controller._view.getSelectedFilters())

    def toggleVisible(self):
        if self._view.isMinimized():
            self._view.showNormal()
            self._view.activateWindow()
        else:
            self._view.showMinimized()
Exemplo n.º 16
0
class TestFile(unittest.TestCase):
    def setUp(self):
        self.file = File()
        self.tmpfile = mkstemp(text=True)[1]

    def tearDown(self):
        try:
            remove(self.tmpfile)
        except FileNotFoundError:
            pass
        except OSError as ex:  # maintain compatibility with Python 3.2
            if ex.errno != 2:
                raise
        except:
            raise

    def saveAndReload(self):
        self.file.save(self.tmpfile)
        self.file = File()
        self.file.load(self.tmpfile)

    def test_single_task(self):
        text = 'due:1999-10-10 do something +project1 @context1'
        self.file.tasks.append(Task(text))
        self.saveAndReload()
        self.assertEqual(self.file.tasks[0].text, text)
        self.assertEqual(self.file.tasks[0].contexts, ['context1'])
        self.assertEqual(self.file.tasks[0].projects, ['project1'])
        self.assertFalse(self.file.tasks[0].is_complete)
        self.assertFalse(self.file.tasks[0].priority)
        self.assertEqual(self.file.tasks[0].due, date(1999, 10, 10))

    def test_two_tasks(self):
        task1 = 'do something +project1 @context1'
        task2 = '(A) do something else +project1 @context2'
        self.file.tasks.extend([Task(task1), Task(task2)])
        self.saveAndReload()
        self.assertEqual(self.file.tasks[0].text, task2)
        self.assertEqual(self.file.tasks[1].text, task1)

    def test_five_tasks(self):
        task1 = Task('do something +project1 @context1')
        task2 = Task('(A) do something else +project1 @context2')
        task3 = Task('do something else +project1 @context2')
        task4 = Task('something else +project1 @context2')
        task5 = Task('abc +project1 @context2')
        self.file.tasks.extend([task1, task2, task3, task4, task5])
        self.saveAndReload()
        self.assertEqual(self.file.tasks[0].text, task2.text)
        self.assertEqual(self.file.tasks[1].text, task5.text)
        self.assertEqual(self.file.tasks[2].text, task1.text)
        self.assertEqual(self.file.tasks[3].text, task3.text)
        self.assertEqual(self.file.tasks[4].text, task4.text)

    def test_get_all_contexts(self):
        self.file.tasks.extend([
            Task('x task with @context1'),
            Task('task with @context2'),
            Task('task with @context1 and @context2'),
            Task('task with @context1 and @context2 and @context3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllContexts(), {
            'context1': 2,
            'context2': 3,
            'context3': 1
        })

    def test_get_all_incl_completed_contexts(self):
        self.file.tasks.extend([
            Task('x task with @context1'),
            Task('task with @context2'),
            Task('task with @context1 and @context2'),
            Task('x task with @context1 and @context2 and @context3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllContexts(True), {
            'context1': 1,
            'context2': 2,
            'context3': 0
        })

    def test_get_all_projects(self):
        self.file.tasks.extend([
            Task('x task with +project1'),
            Task('task with +project2'),
            Task('task with +project1 and +project2'),
            Task('task with +project1 and +project2 and +project3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllProjects(), {
            'project1': 2,
            'project2': 3,
            'project3': 1
        })

    def test_get_all_due_ranges(self):
        today = date.today().strftime('%Y-%m-%d')
        yesterday = (date.today() - timedelta(days=1)).strftime('%Y-%m-%d')

        self.file.tasks.extend([
            Task('x due:' + today + ' completed task of today'),
            Task('due:' + today + ' first task of today'),
            Task('due:' + today + ' second task of today'),
            Task('due:' + yesterday + ' task of yesterday'),
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllDueRanges()[0], {
            'Today': 2,
            'This week': 2,
            'This month': 2,
            'Overdue': 1
        })
        self.assertIsInstance(self.file.getAllDueRanges()[1], dict)

    def test_get_all_projects_incl_completed(self):
        self.file.tasks.extend([
            Task('x task with +project1'),
            Task('task with +project2'),
            Task('task with +project1 and +project2'),
            Task('x task with +project1 and +project2 and +project3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllProjects(True), {
            'project1': 1,
            'project2': 2,
            'project3': 0
        })

    def test_load_empty_filename(self):
        self.assertRaises(ErrorLoadingFile, self.file.load, '')

    def test_load_nonexisting_file(self):
        remove(self.tmpfile)
        self.assertRaises(ErrorLoadingFile, self.file.load, self.tmpfile)
Exemplo n.º 17
0
 def setUp(self):
     self.file = File()
     self.tmpfile = mkstemp(text=True)[1]
Exemplo n.º 18
0
 def new(self):
     if self._canExit():
         self._file = File(self._todoFeatures)
         self._loadFileToUI()
Exemplo n.º 19
0
class MainController(QtCore.QObject):

    _show_toolbar = QtCore.pyqtSignal(int)

    def __init__(self, view, dialogs, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self.view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()
        self._show_completed = True
        self._dialogs = dialogs
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._setIsModified(False)
        self._fileObserver.fileChangetSig.connect(self.openFileByName)
        self.view.closeEventSignal.connect(self.view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller.view.setSelectedFiltersByNames(filters)
        self.hasTrayIcon = False
        self._menu_controller.updateRecentFileActions()

    def auto_save(self):
        if int(self._settings.value("auto_save", 1)):
            self.save()

    def _initControllers(self):
        self._initFiltersTree()
        self._initTasksList()
        self._initContextualMenu()
        self._initActions()
        self._initMenuBar()
        self._initToolBar()
        self._initSearchText()

    def _initMenuBar(self):
        menu = self.view.menuBar()
        self._menu_controller = MenuController(self, menu)

    def _initActions(self):
        self.filterViewAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/sidepane.png'),
                                                  self.tr('Show &Filters'), self)
        self.filterViewAction.setCheckable(True)
        self.filterViewAction.setShortcuts(['Ctrl+Shift+F'])
        self.filterViewAction.triggered.connect(self._toggleFilterView)

        self.showFutureAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/future.png'),
                                                  self.tr('Show future &Tasks'), self)
        self.showFutureAction.setCheckable(True)
        self.showFutureAction.setShortcuts(['Ctrl+Shift+T'])
        self.showFutureAction.triggered.connect(self._toggleShowFuture)

        self.showCompletedAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/show_completed.png'),
                                                     self.tr('Show &Completed tasks'), self)
        self.showCompletedAction.setCheckable(True)
        self.showCompletedAction.setShortcuts(['Ctrl+Shift+C'])
        self.showCompletedAction.triggered.connect(self._toggleShowCompleted)

        self.archiveAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/archive.png'),
                                               self.tr('&Archive completed tasks'), self)
        self.archiveAction.setShortcuts(['Ctrl+Shift+A'])
        self.archiveAction.triggered.connect(self._archive_all_done_tasks)

        self.showToolBarAction = QtWidgets.QAction(self.tr('Show tool&Bar'), self)
        self.showToolBarAction.setCheckable(True)
        self.showToolBarAction.setShortcuts(['Ctrl+Shift+B'])
        self.showToolBarAction.triggered.connect(self._toggleShowToolBar)

        self.showSearchAction = QtWidgets.QAction(QtGui.QIcon(self.view.style + '/resources/ActionSearch.png'),
                                                  self.tr('Show search bar'), self)
        self.showSearchAction.setCheckable(True)
        self.showSearchAction.setShortcuts(['Ctrl+F'])
        self.showSearchAction.triggered.connect(self._toggleShowSearch)

    def _initToolBar(self):
        toolbar = self.view.addToolBar("Main Toolbar")
        toolbar.setObjectName("mainToolbar")

        toolbar.addAction(self.filterViewAction)
        toolbar.addAction(self.showFutureAction)
        toolbar.addAction(self.showCompletedAction)
        toolbar.addAction(self.showSearchAction)

        toolbar.addSeparator()

        toolbar.addAction(self._menu_controller.openAction)
        toolbar.addAction(self._menu_controller.saveAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.createTaskAction)
        toolbar.addAction(self._tasks_list_controller.createTaskActionOnTemplate)
        toolbar.addAction(self._tasks_list_controller.editTaskAction)
        toolbar.addAction(self._tasks_list_controller.copySelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.completeSelectedTasksAction)
        if int(self._settings.value("show_delete", 0)):
            toolbar.addAction(self._tasks_list_controller.deleteSelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.increasePrioritySelectedTasksAction)
        toolbar.addAction(self._tasks_list_controller.decreasePrioritySelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self.archiveAction)
        self._show_toolbar.connect(toolbar.setVisible)

    def _toggleShowToolBar(self):
        if self.showToolBarAction.isChecked():
            self._settings.setValue("show_toolbar", 1)
            self._toolbar_visibility_changed(1)
        else:
            self._settings.setValue("show_toolbar", 0)
            self._toolbar_visibility_changed(0)

    def _toggleShowSearch(self):
        if self.showSearchAction.isChecked():
            self._settings.setValue("show_search", 1)
            self.view.tasks_view.tasks_search_view.setVisible(True)
        else:
            self._settings.setValue("show_search", 0)
            self.view.tasks_view.tasks_search_view.setVisible(False)
            self.view.tasks_view.tasks_search_view.setText("")

    def _toggleShowCompleted(self):
        if self.showCompletedAction.isChecked():
            self._settings.setValue("show_completed_tasks", 1)
            self._show_completed = True
            self.updateFilters()
        else:
            self._settings.setValue("show_completed_tasks", 0)
            self._show_completed = False
            self.updateFilters()
        self._filters_tree_controller.showFilters(self._file, self._show_completed)

    def _toggleShowFuture(self):
        if self.showFutureAction.isChecked():
            self._settings.setValue("show_future_tasks", 1)
            self.updateFilters()
        else:
            self._settings.setValue("show_future_tasks", 0)
            self.updateFilters()

    def _restoreShowFuture(self):
        val = int(self._settings.value("show_future_tasks", 1))
        if val:
            self.showFutureAction.setChecked(True)
            self._toggleShowFuture()
        else:
            self.showFutureAction.setChecked(False)
            self._toggleShowFuture()

    def _toggleFilterView(self):
        if self.filterViewAction.isChecked():
            self._settings.setValue("show_filter_tree", 1)
            self._filters_tree_controller.view.show()
        else:
            self._settings.setValue("splitter_pos", self.view.centralWidget().sizes())
            self._settings.setValue("show_filter_tree", 0)
            self._filters_tree_controller.view.hide()

    def _restoreFilterView(self):
        val = int(self._settings.value("show_filter_tree", 1))
        if val:
            self.filterViewAction.setChecked(True)
            self._toggleFilterView()
        else:
            self.filterViewAction.setChecked(False)
            self._toggleFilterView()

    def _toolbar_visibility_changed(self, val):
        self._show_toolbar.emit(val)

    def exit(self):
        self.view.close()
        sys.exit()

    def getView(self):
        return self.view

    def show(self):
        self._updateView()
        self.view.show()
        self._updateTitle()

        if self._args.file:
            filename = self._args.file
        else:
            filename = self._settings.value("last_open_file")

        if filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

        if self._args.quickadd:
            self._tasks_list_controller.createTask()
            self.save()
            self.exit()

    def _initFiltersTree(self):
        controller = self._filters_tree_controller = \
            FiltersTreeController(self.view.filters_tree_view)
        controller.filterSelectionChanged.connect(
            self._onFilterSelectionChanged)

    def _onFilterSelectionChanged(self, filters):
        self._applyFilters(filters=filters)

    def _applyFilters(self, filters=None, searchText=None):
        # First we filter with filters tree
        if filters is None:
            filters = self._filters_tree_controller.view.getSelectedFilters()
        tasks = tasklib.filterTasks(filters, self._file.tasks)
        # Then with our search text
        if searchText is None:
            searchText = self.view.tasks_view.tasks_search_view.getSearchText()
        tasks = tasklib.filterTasks([SimpleTextFilter(searchText)], tasks)
        # with future filter if needed
        if not self.showFutureAction.isChecked():
            tasks = tasklib.filterTasks([FutureFilter()], tasks)
        # with complete filter if needed
        if not CompleteTasksFilter() in filters and not self.showCompletedAction.isChecked():
            tasks = tasklib.filterTasks([IncompleteTasksFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initSearchText(self):
        self.view.tasks_view.tasks_search_view.searchTextChanged.connect(
            self._onSearchTextChanged)

    def _onSearchTextChanged(self, searchText):
        self._applyFilters(searchText=searchText)

    def _initTasksList(self):
        controller = self._tasks_list_controller = \
            TasksListController(self.view.tasks_view.tasks_list_view, self._task_editor_service)

        controller.taskCreated.connect(self._tasks_list_taskCreated)
        controller.taskModified.connect(self._tasks_list_taskModified)
        controller.taskDeleted.connect(self._tasks_list_taskDeleted)
        controller.taskArchived.connect(self._tasks_list_taskArchived)

    def _initContextualMenu(self):

        # Context menu
        # controller.view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
        self._tasks_list_controller.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self._tasks_list_controller.view.customContextMenuRequested.connect(self.showContextMenu)
        self._contextMenu = QtWidgets.QMenu(self.view)
        self._contextMenu.addAction(self._tasks_list_controller.createTaskAction)
        self._contextMenu.addAction(self._tasks_list_controller.createTaskActionOnTemplate)
        self._contextMenu.addAction(self._tasks_list_controller.editTaskAction)
        self._contextMenu.addAction(self._tasks_list_controller.copySelectedTasksAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(self._tasks_list_controller.completeSelectedTasksAction)
        if int(self._settings.value("show_delete", 1)):
            self._contextMenu.addAction(self._tasks_list_controller.deleteSelectedTasksAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(self._tasks_list_controller.increasePrioritySelectedTasksAction)
        self._contextMenu.addAction(self._tasks_list_controller.decreasePrioritySelectedTasksAction)

    def showContextMenu(self, position):
        tasks = self._tasks_list_controller.view.getSelectedTasks()
        if tasks:
            self._contextMenu.exec_(self._tasks_list_controller.view.mapToGlobal(position))

    def _tasks_list_taskDeleted(self, task):
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _tasks_list_taskCreated(self, task):
        self._file.tasks.append(task)
        self._onFileUpdated()

    def _tasks_list_taskModified(self, task):
        self._onFileUpdated()

    def _tasks_list_taskArchived(self, task):
        self._file.saveDoneTask(task)
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _archive_all_done_tasks(self):
        done = [task for task in self._file.tasks if task.is_complete]
        for task in done:
            self._file.saveDoneTask(task)
            self._file.tasks.remove(task)
        self._onFileUpdated()

    def _onFileUpdated(self):
        self._filters_tree_controller.showFilters(self._file, self._show_completed)
        self._task_editor_service.updateValues(self._file)
        self._setIsModified(True)
        self.auto_save()

    def _canExit(self):
        if not self._is_modified:
            return True
        button = self._dialogs.showSaveDiscardCancel(self.tr('Unsaved changes...'))
        if button == QtWidgets.QMessageBox.Save:
            self.save()
            return True
        else:
            return button == QtWidgets.QMessageBox.Discard

    def view_onCloseEvent(self, closeEvent):

        if (self.hasTrayIcon and int(self._settings.value("close_to_tray", 0))):
            self.view.hide()
            closeEvent.ignore()
            return

        if self._canExit():
            if self.filterViewAction.isChecked():  # we only save size if it is visible
                self._settings.setValue("splitter_pos", self.view.centralWidget().sizes())
            self._settings.setValue("current_filters", self._filters_tree_controller.view.getSelectedFilterNames())
            self._settings.setValue("main_window_geometry", self.view.saveGeometry())
            self._settings.setValue("main_window_state", self.view.saveState())

            closeEvent.accept()
        else:
            closeEvent.ignore()

    def _setIsModified(self, is_modified):
        self._is_modified = is_modified
        self._updateTitle()
        self._menu_controller.saveAction.setEnabled(is_modified)
        self._menu_controller.revertAction.setEnabled(is_modified)

    def save(self):
        logger.debug('MainController.save called.')
        self._fileObserver.clear()
        filename = self._file.filename
        ok = True
        if not filename:
            (filename, ok) = \
                QtWidgets.QFileDialog.getSaveFileName(self.view, filter=FILENAME_FILTERS)
        if ok and filename:
            self._file.save(filename)
            self._settings.setValue("last_open_file", filename)
            self._settings.sync()
            self._setIsModified(False)
            logger.debug('Adding {} to watchlist'.format(filename))
            self._fileObserver.addPath(self._file.filename)

    def _updateTitle(self):
        title = 'QTodoTxt - '
        if self._file.filename:
            filename = os.path.basename(self._file.filename)
            title += filename
        else:
            title += 'Untitled'
        if self._is_modified:
            title += ' (*)'
        self.view.setWindowTitle(title)

    def open(self):
        (filename, ok) = \
            QtWidgets.QFileDialog.getOpenFileName(self.view, filter=FILENAME_FILTERS)

        if ok and filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def new(self):
        if self._canExit():
            self._file = File()
            self._loadFileToUI()

    def revert(self):
        if self._dialogs.showConfirm(self.tr('Revert to saved file (and lose unsaved changes)?')):
            try:
                self.openFileByName(self._file.filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def openFileByName(self, filename):
        logger.debug('MainController.openFileByName called with filename="{}"'.format(filename))
        self._fileObserver.clear()
        try:
            self._file.load(filename)
        except Exception as ex:
            currentfile = self._settings.value("last_open_file", "")
            if currentfile == filename:
                self._dialogs.showError(self.tr("Current file '{}' is not available.\nException: {}").
                                        format(filename, ex))
            else:
                self._dialogs.showError(self.tr("Error opening file: {}.\n Exception:{}").format(filename, ex))
            return
        self._loadFileToUI()
        self._settings.setValue("last_open_file", filename)
        self._settings.sync()
        logger.debug('Adding {} to watchlist'.format(filename))
        self._fileObserver.addPath(self._file.filename)
        self.updateRecentFile()

    def updateRecentFile(self):
        lastOpenedArray = self._menu_controller.getRecentFileNames()
        if self._file.filename in lastOpenedArray:
            lastOpenedArray.remove(self._file.filename)
        lastOpenedArray = lastOpenedArray[:self._menu_controller.maxRecentFiles]
        lastOpenedArray.insert(0, self._file.filename)
        self._settings.setValue("lastOpened", lastOpenedArray[: self._menu_controller.maxRecentFiles])
        self._menu_controller.updateRecentFileActions()

    def _loadFileToUI(self):
        self._setIsModified(False)
        self._filters_tree_controller.showFilters(self._file, self._show_completed)
        self._task_editor_service.updateValues(self._file)

    def _updateView(self):
        wgeo = self._settings.value("main_window_geometry", None)
        if wgeo is not None:
            self.view.restoreGeometry(wgeo)
        wstate = self._settings.value("main_window_state", None)
        if wstate is not None:
            self.view.restoreState(wstate)
        splitterPosition = self._settings.value("splitter_pos", (200, 400))
        splitterPosition = [int(x) for x in splitterPosition]
        self.view.centralWidget().setSizes(splitterPosition)
        self._restoreShowCompleted()
        self._restoreFilterView()
        self._restoreShowFuture()
        self._restoreShowToolBar()
        self._restoreShowSearch()

    def _restoreShowCompleted(self):
        val = int(self._settings.value("show_completed_tasks", 1))
        if val:
            self._show_completed = True
            self.showCompletedAction.setChecked(True)
        else:
            self._show_completed = False
            self.showCompletedAction.setChecked(False)

    def _restoreShowToolBar(self):
        val = int(self._settings.value("show_toolbar", 1))
        if val:
            self._toolbar_visibility_changed(1)
            self.showToolBarAction.setChecked(True)
        else:
            self._toolbar_visibility_changed(0)
            self.showToolBarAction.setChecked(False)

    def _restoreShowSearch(self):
        val = int(self._settings.value("show_search", 1))
        if val:
            self.view.tasks_view.tasks_search_view.setVisible(True)
            self.showSearchAction.setChecked(True)
        else:
            self.view.tasks_view.tasks_search_view.setVisible(False)
            self.showSearchAction.setChecked(False)

    def updateFilters(self):
        self._onFilterSelectionChanged(self._filters_tree_controller.view.getSelectedFilters())

    def toggleVisible(self):
        if self.view.isMinimized() or self.view.isHidden():
            self.view.show()
            self.view.activateWindow()
        else:
            self.view.hide()

    def anotherInstanceEvent(self, dir):
        tFile = dir + "/qtodo.tmp"
        if not os.path.isfile(tFile):
            return
        time.sleep(0.01)
        f = open(tFile, 'r+b')
        line = f.readline()
        line = line.strip()
        if line == b"1":
            self.view.show()
            self.view.activateWindow()
        if line == b"2":
            self.view.show()
            self.view.activateWindow()
            self._tasks_list_controller.createTask()

        f.close()
        os.remove(tFile)
Exemplo n.º 20
0
 def setUp(self):
     self.file = File()
     self.tmpfile = mkstemp(text=True)[1]
Exemplo n.º 21
0
class TestFile(unittest.TestCase):
    def setUp(self):
        self.file = File()
        self.tmpfile = mkstemp(text=True)[1]

    def tearDown(self):
        try:
            remove(self.tmpfile)
        except FileNotFoundError:
            pass
        except OSError as ex:    # maintain compatibility with Python 3.2
            if ex.errno != 2:
                raise
        except:
            raise

    def saveAndReaload(self):
        self.file.save(self.tmpfile)
        self.file = File()
        self.file.load(self.tmpfile)

    def test_single_task(self):
        text = 'do something +project1 @context1'
        self.file.tasks.append(Task(text))
        self.saveAndReaload()
        self.assertEqual(self.file.tasks[0].text, text)
        self.assertEqual(self.file.tasks[0].contexts, ['context1'])
        self.assertEqual(self.file.tasks[0].projects, ['project1'])
        self.assertEqual(self.file.tasks[0].is_complete, False)
        self.assertEqual(self.file.tasks[0].priority, None)

    def test_two_tasks(self):
        task1 = 'do something +project1 @context1'
        task2 = '(A) do something else +project1 @context2'
        self.file.tasks.extend([
            Task(task1),
            Task(task2)
        ])
        self.saveAndReaload()
        self.assertEqual(self.file.tasks[0].text, task2)
        self.assertEqual(self.file.tasks[1].text, task1)

    def test_five_tasks(self):
        task1 = Task('do something +project1 @context1')
        task2 = Task('(A) do something else +project1 @context2')
        task3 = Task('do something else +project1 @context2')
        task4 = Task('something else +project1 @context2')
        task5 = Task('abc +project1 @context2')
        self.file.tasks.extend([task1, task2, task3, task4, task5])
        self.saveAndReaload()
        self.assertEqual(self.file.tasks[0].text, task2.text)
        self.assertEqual(self.file.tasks[1].text, task5.text)
        self.assertEqual(self.file.tasks[2].text, task1.text)
        self.assertEqual(self.file.tasks[3].text, task3.text)
        self.assertEqual(self.file.tasks[4].text, task4.text)

    def test_get_all_contexts(self):
        self.file.tasks.extend([
            Task('x task with @context1'),
            Task('task with @context2'),
            Task('task with @context1 and @context2'),
            Task('task with @context1 and @context2 and @context3')
        ])
        self.saveAndReaload()
        self.assertEqual(self.file.getAllContexts(), {'context1': 2, 'context2': 3, 'context3': 1})

    def test_get_all_completed_contexts(self):
        self.file.tasks.extend([
            Task('x task with @context1'),
            Task('task with @context2'),
            Task('task with @context1 and @context2'),
            Task('x task with @context1 and @context2 and @context3')
        ])
        self.saveAndReaload()
        self.assertEqual(self.file.getAllCompletedContexts(), {'context1': 2, 'context2': 1, 'context3': 1})

    def test_get_all_projects(self):
        self.file.tasks.extend([
            Task('x task with +project1'),
            Task('task with +project2'),
            Task('task with +project1 and +project2'),
            Task('task with +project1 and +project2 and +project3')
        ])
        self.saveAndReaload()
        self.assertEqual(self.file.getAllProjects(), {'project1': 2, 'project2': 3, 'project3': 1})

    def test_get_all_completed_projects(self):
        self.file.tasks.extend([
            Task('x task with +project1'),
            Task('task with +project2'),
            Task('task with +project1 and +project2'),
            Task('x task with +project1 and +project2 and +project3')
        ])
        self.saveAndReaload()
        self.assertEqual(self.file.getAllCompletedProjects(), {'project1': 2, 'project2': 1, 'project3': 1})

    def test_load_empty_filename(self):
        self.assertRaises(ErrorLoadingFile, self.file.load, '')

    def test_load_nonexisting_file(self):
        remove(self.tmpfile)
        self.assertRaises(ErrorLoadingFile, self.file.load, self.tmpfile)
Exemplo n.º 22
0
class TestFile(unittest.TestCase):
    def setUp(self):
        self.file = File()
        self.tmpfile = mkstemp(text=True)[1]

    def tearDown(self):
        try:
            remove(self.tmpfile)
        except FileNotFoundError:
            pass
        except OSError as ex:    # maintain compatibility with Python 3.2
            if ex.errno != 2:
                raise
        except:
            raise

    def saveAndReload(self):
        self.file.save(self.tmpfile)
        self.file = File()
        self.file.load(self.tmpfile)

    def test_single_task(self):
        text = 'due:1999-10-10 do something +project1 @context1'
        self.file.tasks.append(Task(text))
        self.saveAndReload()
        self.assertEqual(self.file.tasks[0].text, text)
        self.assertEqual(self.file.tasks[0].contexts, ['context1'])
        self.assertEqual(self.file.tasks[0].projects, ['project1'])
        self.assertEqual(self.file.tasks[0].is_complete, False)
        self.assertEqual(self.file.tasks[0].priority, None)
        self.assertEqual(self.file.tasks[0].due, '1999-10-10')

    def test_two_tasks(self):
        task1 = 'do something +project1 @context1'
        task2 = '(A) do something else +project1 @context2'
        self.file.tasks.extend([
            Task(task1),
            Task(task2)
        ])
        self.saveAndReload()
        self.assertEqual(self.file.tasks[0].text, task2)
        self.assertEqual(self.file.tasks[1].text, task1)

    def test_five_tasks(self):
        task1 = Task('do something +project1 @context1')
        task2 = Task('(A) do something else +project1 @context2')
        task3 = Task('do something else +project1 @context2')
        task4 = Task('something else +project1 @context2')
        task5 = Task('abc +project1 @context2')
        self.file.tasks.extend([task1, task2, task3, task4, task5])
        self.saveAndReload()
        self.assertEqual(self.file.tasks[0].text, task2.text)
        self.assertEqual(self.file.tasks[1].text, task5.text)
        self.assertEqual(self.file.tasks[2].text, task1.text)
        self.assertEqual(self.file.tasks[3].text, task3.text)
        self.assertEqual(self.file.tasks[4].text, task4.text)

    def test_get_all_contexts(self):
        self.file.tasks.extend([
            Task('x task with @context1'),
            Task('task with @context2'),
            Task('task with @context1 and @context2'),
            Task('task with @context1 and @context2 and @context3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllContexts(), {'context1': 2, 'context2': 3, 'context3': 1})

    def test_get_all_completed_contexts(self):
        self.file.tasks.extend([
            Task('x task with @context1'),
            Task('task with @context2'),
            Task('task with @context1 and @context2'),
            Task('x task with @context1 and @context2 and @context3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllCompletedContexts(), {'context1': 2, 'context2': 1, 'context3': 1})

    def test_get_all_projects(self):
        self.file.tasks.extend([
            Task('x task with +project1'),
            Task('task with +project2'),
            Task('task with +project1 and +project2'),
            Task('task with +project1 and +project2 and +project3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllProjects(), {'project1': 2, 'project2': 3, 'project3': 1})

    def test_get_all_due_ranges(self):
        today = date.today().strftime('%Y-%m-%d')
        yesterday = (date.today() - timedelta(days=1)).strftime('%Y-%m-%d')

        self.file.tasks.extend([
            Task('x due:' + today + ' completed task of today'),
            Task('due:' + today + ' first task of today'),
            Task('due:' + today + ' second task of today'),
            Task('due:' + yesterday + ' task of yesterday'),
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllDueRanges()[0], {'Today': 2, 'This week': 2, 'This month': 2, 'Overdue': 1})
        self.assertIsInstance(self.file.getAllDueRanges()[1], dict)

    def test_get_all_completed_projects(self):
        self.file.tasks.extend([
            Task('x task with +project1'),
            Task('task with +project2'),
            Task('task with +project1 and +project2'),
            Task('x task with +project1 and +project2 and +project3')
        ])
        self.saveAndReload()
        self.assertEqual(self.file.getAllCompletedProjects(), {'project1': 2, 'project2': 1, 'project3': 1})

    def test_load_empty_filename(self):
        self.assertRaises(ErrorLoadingFile, self.file.load, '')

    def test_load_nonexisting_file(self):
        remove(self.tmpfile)
        self.assertRaises(ErrorLoadingFile, self.file.load, self.tmpfile)
Exemplo n.º 23
0
class MainController(QtCore.QObject):
    def __init__(self, view, dialogs_service, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self._view = view
        self._dialogs_service = dialogs_service
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._settings = settings.Settings()
        self._setIsModified(False)
        self._view.closeEventSignal.connect(self._view_onCloseEvent)

    def autoSave(self):
        if self._settings.getAutoSave():
            self.save()

    def _initControllers(self):
        self._initFiltersTree()
        self._initTasksList()
        self._initMenuBar()
        self._initFilterText()

    def _initMenuBar(self):
        menu = self._view.menuBar()
        self._menu_controller = MenuController(self, menu)

    def exit(self):
        self._view.close()
        sys.exit()

    def getView(self):
        return self._view

    def show(self):
        self._view.show()
        self._updateTitle()
        self._settings.load()
        self._updateCreatePref()
        self._updateAutoSavePref()
        self._updateAutoArchivePref()
        self._updateHideFutureTasksPref()
        self._updateView()

        if self._args.file:
            filename = self._args.file[0]
        else:
            filename = self._settings.getLastOpenFile()

        if filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs_service.showError(str(ex))

        if self._args.quickadd:
            self._tasks_list_controller.createTask()
            self.save()
            self.exit()

    def _initFiltersTree(self):
        controller = self._filters_tree_controller = \
            FiltersTreeController(self._view.filters_tree_view)
        controller.filterSelectionChanged.connect(
            self._onFilterSelectionChanged)

    def _onFilterSelectionChanged(self, filters):
        # First we filter with filters tree
        treeTasks = todolib.filterTasks(filters, self._file.tasks)
        # Then with our filter text
        filterText = self._view.tasks_view.filter_tasks.getText()
        tasks = todolib.filterTasks([SimpleTextFilter(filterText)], treeTasks)
        # And finally with future filter if needed
        # TODO: refactor all that filters
        if self._settings.getHideFutureTasks():
            tasks = todolib.filterTasks([FutureFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initFilterText(self):
        self._view.tasks_view.filter_tasks.filterTextChanged.connect(
            self._onFilterTextChanged)

    def _onFilterTextChanged(self, text):
        # First we filter with filters tree
        filters = self._filters_tree_controller._view.getSelectedFilters()
        treeTasks = todolib.filterTasks(filters, self._file.tasks)
        # Then with our filter text
        tasks = todolib.filterTasks([SimpleTextFilter(text)], treeTasks)
        # And finally with future filter if needed
        # TODO: refactor all that filters
        if self._settings.getHideFutureTasks():
            tasks = todolib.filterTasks([FutureFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initTasksList(self):
        controller = self._tasks_list_controller = \
            TasksListController(self._view.tasks_view.tasks_list_view, self._task_editor_service)

        controller.taskCreated.connect(self._tasks_list_taskCreated)
        controller.taskModified.connect(self._tasks_list_taskModified)
        controller.taskDeleted.connect(self._tasks_list_taskDeleted)
        controller.taskArchived.connect(self._tasks_list_taskArchived)

    def _tasks_list_taskDeleted(self, task):
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _tasks_list_taskCreated(self, task):
        self._file.tasks.append(task)
        self._onFileUpdated()

    def _tasks_list_taskModified(self, task):
        self._onFileUpdated()

    def _tasks_list_taskArchived(self, task):
        self._file.saveDoneTask(task)
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _onFileUpdated(self):
        self._filters_tree_controller.showFilters(self._file)
        self._task_editor_service.updateValues(self._file)
        self._setIsModified(True)
        self.autoSave()

    def _canExit(self):
        if not self._is_modified:
            return True
        button = self._dialogs_service.showSaveDiscardOrCancel('Unsaved changes...')
        if button == QtGui.QMessageBox.Save:
            self.save()
            return True
        else:
            return button == QtGui.QMessageBox.Discard

    def _view_onCloseEvent(self, closeEvent):
        if self._canExit():
            self._saveView()
            closeEvent.accept()
        else:
            closeEvent.ignore()

    def _saveView(self):
        viewSize = self._view.size()
        viewPosition = self._view.pos()
        splitterPosition = self._view.centralWidget().sizes()
        self._settings.setViewHeight(viewSize.height())
        self._settings.setViewWidth(viewSize.width())
        self._settings.setViewPositionX(viewPosition.x())
        self._settings.setViewPositionY(viewPosition.y())
        self._settings.setViewSlidderPosition(splitterPosition)

    def _setIsModified(self, is_modified):
        self._is_modified = is_modified
        self._updateTitle()
        self._menu_controller.saveAction.setEnabled(is_modified)
        self._menu_controller.revertAction.setEnabled(is_modified)

    def save(self):
        logger.debug('MainController.save called.')
        self._fileObserver.clear()
        filename = self._file.filename
        ok = True
        if not filename:
            (filename, ok) = \
                QtGui.QFileDialog.getSaveFileName(self._view, filter=FILENAME_FILTERS)
        if ok and filename:
            self._file.save(filename)
            self._settings.setLastOpenFile(filename)
            self._setIsModified(False)
            logger.debug('Adding {} to watchlist'.format(filename))
            self._fileObserver.addPath(self._file.filename)

    def _updateTitle(self):
        title = 'QTodoTxt - '
        if self._file.filename:
            filename = os.path.basename(self._file.filename)
            title += filename
        else:
            title += 'Untitled'
        if self._is_modified:
            title += ' (*)'
        self._view.setWindowTitle(title)

    def open(self):
        (filename, ok) = \
            QtGui.QFileDialog.getOpenFileName(self._view, filter=FILENAME_FILTERS)

        if ok and filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs_service.showError(str(ex))

    def new(self):
        if self._canExit():
            self._file = File()
            self._loadFileToUI()

    def revert(self):
        if self._dialogs_service.showConfirm('Revert to saved file (and lose unsaved changes)?'):
            try:
                self.openFileByName(self._file.filename)
            except ErrorLoadingFile as ex:
                self._dialogs_service.showError(str(ex))

    def openFileByName(self, filename):
        logger.debug('MainController.openFileByName called with filename="{}"'.format(filename))
        self._fileObserver.clear()
        self._file.load(filename)
        self._loadFileToUI()
        self._settings.setLastOpenFile(filename)
        logger.debug('Adding {} to watchlist'.format(filename))
        self._fileObserver.addPath(self._file.filename)

    def _loadFileToUI(self):
        self._setIsModified(False)
        self._filters_tree_controller.showFilters(self._file)
        self._task_editor_service.updateValues(self._file)

    def _updateCreatePref(self):
        self._menu_controller.changeCreatedDateState(bool(self._settings.getCreateDate()))

    def _updateAutoSavePref(self):
        self._menu_controller.changeAutoSaveState(bool(self._settings.getAutoSave()))

    def _updateAutoArchivePref(self):
        self._menu_controller.changeAutoArchiveState(bool(self._settings.getAutoArchive()))

    def _updateHideFutureTasksPref(self):
        self._menu_controller.changeHideFutureTasksState(bool(self._settings.getHideFutureTasks()))

    def _updateView(self):
        height = self._settings.getViewHeight()
        width = self._settings.getViewWidth()
        if height and width:
            self._view.resize(width, height)

        positionX = self._settings.getViewPositionX()
        positionY = self._settings.getViewPositionY()
        if positionX and positionY:
            self._view.move(positionX, positionY)

        slidderPosition = self._settings.getViewSlidderPosition()
        if slidderPosition:
            self._view.centralWidget().setSizes(slidderPosition)

    def createdDate(self):
        if self._settings.getCreateDate():
            self._settings.setCreateDate(False)
        else:
            self._settings.setCreateDate(True)

    def toggleAutoSave(self):
        if self._settings.getAutoSave():
            self._settings.setAutoSave(False)
        else:
            self._settings.setAutoSave(True)

    def toggleAutoArchive(self):
        if self._settings.getAutoArchive():
            self._settings.setAutoArchive(False)
        else:
            self._settings.setAutoArchive(True)

    def toggleHideFutureTasks(self):
        if self._settings.getHideFutureTasks():
            self._settings.setHideFutureTasks(False)
        else:
            self._settings.setHideFutureTasks(True)
        self._onFilterSelectionChanged(self._filters_tree_controller._view.getSelectedFilters())

    def toggleVisible(self):
        if self._view.isMinimized():
            self._view.showNormal()
            self._view.activateWindow()
        else:
            self._view.showMinimized()
Exemplo n.º 24
0
 def saveAndReload(self):
     self.file.save(self.tmpfile)
     self.file = File()
     self.file.load(self.tmpfile)
Exemplo n.º 25
0
 def saveAndReaload(self):
     self.file.save(self.tmpfile)
     self.file = File()
     self.file.load(self.tmpfile)
Exemplo n.º 26
0
class MainController(QtCore.QObject):
    def __init__(self, view, dialogs, task_editor_service, args):
        super(MainController, self).__init__()
        self._args = args
        self.view = view

        # use object variable for setting only used in this class
        # others are accessed through QSettings
        self._settings = QtCore.QSettings()
        # self._show_toolbar = int(self._settings.value("show_toolbar", 1))
        # fix migration issue from old settings
        show_toolbar = self._settings.value("show_toolbar", 1)
        if show_toolbar in ("true", "false"):
            show_toolbar = 1
        self._show_toolbar = int(show_toolbar)
        self._show_completed = True
        self._dialogs = dialogs
        self._task_editor_service = task_editor_service
        self._initControllers()
        self._file = File()
        self._fileObserver = FileObserver(self, self._file)
        self._is_modified = False
        self._setIsModified(False)
        self.view.closeEventSignal.connect(self.view_onCloseEvent)
        filters = self._settings.value("current_filters", ["All"])
        self._filters_tree_controller.view.setSelectedFiltersByNames(filters)

    def auto_save(self):
        if int(self._settings.value("auto_save", 1)):
            self.save()

    def _initControllers(self):
        self._initFiltersTree()
        self._initTasksList()
        self._initMenuBar()
        self._initActions()
        self._initToolBar()
        self._initSearchText()

    def _initMenuBar(self):
        menu = self.view.menuBar()
        self._menu_controller = MenuController(self, menu)

    def _initActions(self):
        self.filterViewAction = QtWidgets.QAction(getIcon('sidepane.png'), '&Show Filters', self)
        self.filterViewAction.setCheckable(True)
        # action.setShortcuts(['Ctrl+E']) # what should it be?
        self.filterViewAction.triggered.connect(self._toggleFilterView)

        self.showFutureAction = QtWidgets.QAction(getIcon('future.png'), '&Show Future Tasks', self)
        self.showFutureAction.setCheckable(True)
        # action.setShortcuts(['Ctrl+E']) # what should it be?
        self.showFutureAction.triggered.connect(self._toggleShowFuture)

        self.showCompletedAction = QtWidgets.QAction(getIcon('show_completed.png'), '&Show Completed Tasks', self)
        self.showCompletedAction.setCheckable(True)
        # action.setShortcuts(['Ctrl+E']) # what should it be?
        self.showCompletedAction.triggered.connect(self._toggleShowCompleted)

        self.archiveAction = QtWidgets.QAction(getIcon('archive.png'), '&Archive Completed Tasks', self)
        # action.setShortcuts(['Ctrl+E']) # what should it be?
        self.archiveAction.triggered.connect(self._archive_all_done_tasks)

    def _initToolBar(self):
        toolbar = self.view.addToolBar("Main Toolbar")
        toolbar.setObjectName("mainToolbar")

        toolbar.addAction(self.filterViewAction)
        toolbar.addAction(self.showFutureAction)
        toolbar.addAction(self.showCompletedAction)

        toolbar.addSeparator()

        toolbar.addAction(self._menu_controller.openAction)
        toolbar.addAction(self._menu_controller.saveAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.createTaskAction)
        toolbar.addAction(self._tasks_list_controller.editTaskAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.completeSelectedTasksAction)
        toolbar.addAction(self._tasks_list_controller.deleteSelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self._tasks_list_controller.increasePrioritySelectedTasksAction)
        toolbar.addAction(self._tasks_list_controller.decreasePrioritySelectedTasksAction)
        toolbar.addSeparator()
        toolbar.addAction(self.archiveAction)
        toolbar.visibilityChanged.connect(self._toolbar_visibility_changed)
        if not self._show_toolbar:
            toolbar.hide()

    def _toggleShowCompleted(self):
        if self.showCompletedAction.isChecked():
            self._settings.setValue("show_completed_tasks", 1)
            self._show_completed = True
            self.updateFilters()
        else:
            self._settings.setValue("show_completed_tasks", 0)
            self._show_completed = False
            self.updateFilters()
        self._filters_tree_controller.showFilters(self._file, self._show_completed)

    def _toggleShowFuture(self):
        if self.showFutureAction.isChecked():
            self._settings.setValue("show_future_tasks", 1)
            self.updateFilters()
        else:
            self._settings.setValue("show_future_tasks", 0)
            self.updateFilters()

    def _restoreShowFuture(self):
        val = int(self._settings.value("show_future_tasks", 1))
        if val:
            self.showFutureAction.setChecked(True)
            self._toggleShowFuture()
        else:
            self.showFutureAction.setChecked(False)
            self._toggleShowFuture()

    def _toggleFilterView(self):
        if self.filterViewAction.isChecked():
            self._settings.setValue("show_filter_tree", 1)
            self._filters_tree_controller.view.show()
        else:
            self._settings.setValue("splitter_pos", self.view.centralWidget().sizes())
            self._settings.setValue("show_filter_tree", 0)
            self._filters_tree_controller.view.hide()

    def _restoreFilterView(self):
        val = int(self._settings.value("show_filter_tree", 1))
        if val:
            self.filterViewAction.setChecked(True)
            self._toggleFilterView()
        else:
            self.filterViewAction.setChecked(False)
            self._toggleFilterView()

    def _toolbar_visibility_changed(self, val):
        self._show_toolbar = int(val)

    def exit(self):
        self.view.close()
        sys.exit()

    def getView(self):
        return self.view

    def show(self):
        self._updateView()
        self.view.show()
        self._updateTitle()

        if self._args.file:
            filename = self._args.file
        else:
            filename = self._settings.value("last_open_file")

        if filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

        if self._args.quickadd:
            self._tasks_list_controller.createTask()
            self.save()
            self.exit()

    def _initFiltersTree(self):
        controller = self._filters_tree_controller = \
            FiltersTreeController(self.view.filters_tree_view)
        controller.filterSelectionChanged.connect(
            self._onFilterSelectionChanged)

    def _onFilterSelectionChanged(self, filters):
        self._applyFilters(filters=filters)

    def _applyFilters(self, filters=None, searchText=None):
        # First we filter with filters tree
        if filters is None:
            filters = self._filters_tree_controller.view.getSelectedFilters()
        tasks = tasklib.filterTasks(filters, self._file.tasks)
        # Then with our search text
        if searchText is None:
            searchText = self.view.tasks_view.tasks_search_view.getSearchText()
        tasks = tasklib.filterTasks([SimpleTextFilter(searchText)], tasks)
        # with future filter if needed
        if not self.showFutureAction.isChecked():
            tasks = tasklib.filterTasks([FutureFilter()], tasks)
        # with complete filter if needed
        if not CompleteTasksFilter() in filters and not self.showCompletedAction.isChecked():
            tasks = tasklib.filterTasks([IncompleteTasksFilter()], tasks)
        self._tasks_list_controller.showTasks(tasks)

    def _initSearchText(self):
        self.view.tasks_view.tasks_search_view.searchTextChanged.connect(
            self._onSearchTextChanged)

    def _onSearchTextChanged(self, searchText):
        self._applyFilters(searchText=searchText)

    def _initTasksList(self):
        controller = self._tasks_list_controller = \
            TasksListController(self.view.tasks_view.tasks_list_view, self._task_editor_service)

        controller.taskCreated.connect(self._tasks_list_taskCreated)
        controller.taskModified.connect(self._tasks_list_taskModified)
        controller.taskDeleted.connect(self._tasks_list_taskDeleted)
        controller.taskArchived.connect(self._tasks_list_taskArchived)

        # Context menu
        # controller.view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
        controller.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        controller.view.customContextMenuRequested.connect(self.showContextMenu)
        self._contextMenu = QtWidgets.QMenu()
        self._contextMenu.addAction(self._tasks_list_controller.editTaskAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(self._tasks_list_controller.completeSelectedTasksAction)
        self._contextMenu.addAction(self._tasks_list_controller.deleteSelectedTasksAction)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(self._tasks_list_controller.increasePrioritySelectedTasksAction)
        self._contextMenu.addAction(self._tasks_list_controller.decreasePrioritySelectedTasksAction)

    def showContextMenu(self, position):
        tasks = self._tasks_list_controller.view.getSelectedTasks()
        if tasks:
            self._contextMenu.exec_(self._tasks_list_controller.view.mapToGlobal(position))

    def _tasks_list_taskDeleted(self, task):
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _tasks_list_taskCreated(self, task):
        self._file.tasks.append(task)
        self._onFileUpdated()

    def _tasks_list_taskModified(self, task):
        self._onFileUpdated()

    def _tasks_list_taskArchived(self, task):
        self._file.saveDoneTask(task)
        self._file.tasks.remove(task)
        self._onFileUpdated()

    def _archive_all_done_tasks(self):
        done = [task for task in self._file.tasks if task.is_complete]
        for task in done:
            self._file.saveDoneTask(task)
            self._file.tasks.remove(task)
        self._onFileUpdated()

    def _onFileUpdated(self):
        self._filters_tree_controller.showFilters(self._file, self._show_completed)
        self._task_editor_service.updateValues(self._file)
        self._setIsModified(True)
        self.auto_save()

    def _canExit(self):
        if not self._is_modified:
            return True
        button = self._dialogs.showSaveDiscardCancel('Unsaved changes...')
        if button == QtWidgets.QMessageBox.Save:
            self.save()
            return True
        else:
            return button == QtWidgets.QMessageBox.Discard

    def view_onCloseEvent(self, closeEvent):
        if self._canExit():
            self._settings.setValue("show_toolbar", self._show_toolbar)
            if self.filterViewAction.isChecked():  # we only save size if it is visible
                self._settings.setValue("splitter_pos", self.view.centralWidget().sizes())
            self._settings.setValue("current_filters", self._filters_tree_controller.view.getSelectedFilterNames())
            self._settings.setValue("main_window_geometry", self.view.saveGeometry())
            self._settings.setValue("main_window_state", self.view.saveState())

            closeEvent.accept()
        else:
            closeEvent.ignore()

    def _setIsModified(self, is_modified):
        self._is_modified = is_modified
        self._updateTitle()
        self._menu_controller.saveAction.setEnabled(is_modified)
        self._menu_controller.revertAction.setEnabled(is_modified)

    def save(self):
        logger.debug('MainController.save called.')
        self._fileObserver.clear()
        filename = self._file.filename
        ok = True
        if not filename:
            (filename, ok) = \
                QtWidgets.QFileDialog.getSaveFileName(self.view, filter=FILENAME_FILTERS)
        if ok and filename:
            self._file.save(filename)
            self._settings.setValue("last_open_file", filename)
            self._settings.sync()
            self._setIsModified(False)
            logger.debug('Adding {} to watchlist'.format(filename))
            self._fileObserver.addPath(self._file.filename)

    def _updateTitle(self):
        title = 'QTodoTxt - '
        if self._file.filename:
            filename = os.path.basename(self._file.filename)
            title += filename
        else:
            title += 'Untitled'
        if self._is_modified:
            title += ' (*)'
        self.view.setWindowTitle(title)

    def open(self):
        (filename, ok) = \
            QtWidgets.QFileDialog.getOpenFileName(self.view, filter=FILENAME_FILTERS)

        if ok and filename:
            try:
                self.openFileByName(filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def new(self):
        if self._canExit():
            self._file = File()
            self._loadFileToUI()

    def revert(self):
        if self._dialogs.showConfirm('Revert to saved file (and lose unsaved changes)?'):
            try:
                self.openFileByName(self._file.filename)
            except ErrorLoadingFile as ex:
                self._dialogs.showError(str(ex))

    def openFileByName(self, filename):
        logger.debug('MainController.openFileByName called with filename="{}"'.format(filename))
        self._fileObserver.clear()
        self._file.load(filename)
        self._loadFileToUI()
        self._settings.setValue("last_open_file", filename)
        self._settings.sync()
        logger.debug('Adding {} to watchlist'.format(filename))
        self._fileObserver.addPath(self._file.filename)

    def _loadFileToUI(self):
        self._setIsModified(False)
        self._filters_tree_controller.showFilters(self._file, self._show_completed)
        self._task_editor_service.updateValues(self._file)

    def _updateView(self):
        self.view.restoreGeometry(self._settings.value("main_window_geometry", b""))
        self.view.restoreState(self._settings.value("main_window_state", b""))
        splitterPosition = self._settings.value("splitter_pos", (200, 400))
        splitterPosition = [int(x) for x in splitterPosition]
        self.view.centralWidget().setSizes(splitterPosition)
        self._restoreShowCompleted()
        self._restoreFilterView()
        self._restoreShowFuture()

    def _restoreShowCompleted(self):
        val = int(self._settings.value("show_completed_tasks", 1))
        if val:
            self._show_completed = True
            self.showCompletedAction.setChecked(True)
        else:
            self._show_completed = False
            self.showCompletedAction.setChecked(False)

    def updateFilters(self):
        self._onFilterSelectionChanged(self._filters_tree_controller.view.getSelectedFilters())

    def toggleVisible(self):
        if self.view.isMinimized():
            self.view.showNormal()
            self.view.activateWindow()
        else:
            self.view.showMinimized()