Esempio n. 1
0
class List(QDialog):
    """All Notes dialog"""

    def __init__(self, app, *args, **kwargs):
        QDialog.__init__(self, *args, **kwargs)
        self.app = app
        self.closed = False
        self.sort_order = None
        self.ui = Ui_List()
        self.ui.setupUi(self)
        self.setWindowIcon(get_icon())

        self.notebooksModel = QStandardItemModel()
        self.ui.notebooksList.setModel(self.notebooksModel)
        self.ui.notebooksList.selection.connect(self.selection_changed)
        self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notebooksList.customContextMenuRequested.connect(self.notebook_context_menu)

        self.notesModel = QStandardItemModel()
        self.notesModel.setHorizontalHeaderLabels(
            [self.tr('Title'), self.tr('Last Updated')])

        self.ui.notesList.setModel(self.notesModel)
        self.ui.notesList.doubleClicked.connect(self.note_dblclicked)
        self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notesList.customContextMenuRequested.connect(self.note_context_menu)
        self.ui.notesList.header().sortIndicatorChanged.connect(self.sort_order_updated)

        self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new'))
        self.ui.newNotebookBtn.clicked.connect(self.new_notebook)

        self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new'))
        self.ui.newNoteBtn.clicked.connect(self.new_note)

        self.ui.newNoteBtn.setShortcut(QKeySequence(self.tr('Ctrl+n')))
        self.ui.newNotebookBtn.setShortcut(QKeySequence(self.tr('Ctrl+Shift+n')))
        QShortcut(QKeySequence(self.tr('Ctrl+q')), self, self.close)

    @Slot(QItemSelection, QItemSelection)
    def selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.notebook_selected(selected.indexes()[-1])

    def showEvent(self, *args, **kwargs):
        QDialog.showEvent(self, *args, **kwargs)
        self._reload_notebooks_list()
        self.readSettings()

    def writeSettings(self):
        self.app.settings.setValue('list-geometry', self.saveGeometry())
        for key, widget in self._getRestorableItems():
            self.app.settings.setValue(key, widget.saveState())

    def _getRestorableItems(self):
        return (
            ('list-splitter-state', self.ui.splitter),
            ('list-header-state', self.ui.notesList.header()),
        )

    def readSettings(self):
        geometry = self.app.settings.value('list-geometry')
        if geometry:
            self.restoreGeometry(geometry)

        for key, widget in self._getRestorableItems():
            state = self.app.settings.value(key)
            if state:
                widget.restoreState(state)

    def closeEvent(self, event):
        self.writeSettings()
        event.ignore()
        self.closed = True
        self.hide()

    @Slot(int, Qt.SortOrder)
    def sort_order_updated(self, logicalIndex, order):
        self.sort_order = (logicalIndex, order.name)
        self.app.settings.setValue('list-notes-sort-order', self.sort_order)

    def notebook_selected(self, index):
        self.notesModel.setRowCount(0)

        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            notebook_id = item.notebook.id
        else:
            notebook_id = 0
        notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array([], signature='i')
        notes = self.app.provider.find_notes(
            '', notebook_filter, dbus.Array([], signature='i'),
            0, 2 ** 31 - 1, Note.ORDER_TITLE, -1,
        )  # fails with sys.maxint in 64
        for note_struct in notes:
            note = Note.from_tuple(note_struct)
            self.notesModel.appendRow(QNoteItemFactory(note).make_items())

        sort_order = self.sort_order
        if sort_order is None:
            sort_order = self.app.settings.value('list-notes-sort-order')

        if sort_order:
            logicalIndex, order = sort_order
            order = Qt.SortOrder.values[order]
            self.ui.notesList.sortByColumn(int(logicalIndex), order)

    @Slot()
    def note_dblclicked(self, index):
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def new_notebook(self):
        name, status = self._notebook_new_name(self.tr('Create new notebook'))
        if status:
            notebook_struct = self.app.provider.create_notebook(name)
            notebook = Notebook.from_tuple(notebook_struct)

            self.app.send_notify(self.tr('Notebook "%s" created!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def rename_notebook(self):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        notebook = item.notebook
        name, status = self._notebook_new_name(
            self.tr('Rename notebook'), notebook.name,
        )
        if status:
            notebook.name = name
            self.app.provider.update_notebook(notebook.struct)
            self.app.send_notify(self.tr('Notebook "%s" renamed!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def remove_notebook(self):
        msg = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a notebook"),
            self.tr("Are you sure want to delete this notebook and its notes?"),
            QMessageBox.Yes | QMessageBox.No
        )
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.notebooksList.currentIndex()
            item = self.notebooksModel.itemFromIndex(index)
            self.app.provider.delete_notebook(item.notebook.id)
            self.app.send_notify(self.tr('Notebook "%s" deleted!') % item.notebook.name)
            self._reload_notebooks_list()

    @Slot()
    def new_note(self):
        index = self.ui.notebooksList.currentIndex()
        notebook_id = NONE_ID
        if index.row():
            item = self.notebooksModel.itemFromIndex(index)
            notebook_id = item.notebook.id

        self.app.indicator.create(notebook_id=notebook_id)

    @Slot()
    def edit_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def remove_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        msgBox = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a note"),
            self.tr('Are you sure want to delete note "%s"?') % item.note.title,
            QMessageBox.Yes | QMessageBox.No
        )
        if msgBox.exec_() == QMessageBox.Yes:
            self.app.provider.delete_note(item.note.id)
            self.app.send_notify(self.tr('Note "%s" deleted!') % item.note.title)
            self.notebook_selected(self.ui.notebooksList.currentIndex())

    @Slot(QPoint)
    def notebook_context_menu(self, pos):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            menu = QMenu(self.ui.notebooksList)
            menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_notebook)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_notebook)
            menu.exec_(self.ui.notebooksList.mapToGlobal(pos))

    @Slot(QPoint)
    def note_context_menu(self, pos):
        menu = QMenu(self.ui.notesList)
        menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'), self.edit_note)
        menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_note)
        menu.exec_(self.ui.notesList.mapToGlobal(pos))

    def _reload_notebooks_list(self, select_notebook_id=None):
        self.notebooksModel.clear()
        root = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Notes'))
        self.notebooksModel.appendRow(root)

        selected_item = root
        for notebook_struct in self.app.provider.list_notebooks():
            notebook = Notebook.from_tuple(notebook_struct)
            count = self.app.provider.get_notebook_notes_count(notebook.id)
            item = QNotebookItem(notebook, count)
            root.appendRow(item)

            if select_notebook_id and notebook.id == select_notebook_id:
                selected_item = item

        self.ui.notebooksList.expandAll()
        if selected_item:
            index = self.notebooksModel.indexFromItem(selected_item)
            self.ui.notebooksList.setCurrentIndex(index)
            self.notebook_selected(index)

    def _notebook_new_name(self, title, exclude=''):
        names = map(lambda nb: Notebook.from_tuple(nb).name, self.app.provider.list_notebooks())
        try:
            names.remove(exclude)
        except ValueError:
            pass
        name, status = QInputDialog.getText(self, title, self.tr('Enter notebook name:'), text=exclude)
        while name in names and status:
            message = self.tr('Notebook with this name already exist. Enter notebook name')
            name, status = QInputDialog.getText(self, title, message)
        return name, status
Esempio n. 2
0
class TreeViewWidget:
    def __init__(self):
        self.tree = QtGui.QTreeView()
        self.keys = {}
        self.data = None
        self.model = QStandardItemModel()
        self.tree.setHeaderHidden(True)
        self.brushConflictInChild = QtGui.QBrush(QtGui.QColor(255, 136, 139))
        self.brushConflictInChild.setStyle(QtCore.Qt.SolidPattern)
        self.brushConflictInItem = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        self.brushConflictInItem.setStyle(Qt.SolidPattern)
        self.brushChangedItem = QtGui.QBrush(QtGui.QColor(249, 233, 170))
        self.brushChangedItem.setStyle(Qt.SolidPattern)
        self.brushAddedItem = QtGui.QBrush(QtGui.QColor(221, 252, 199))
        self.brushAddedItem.setStyle(Qt.SolidPattern)
        self.whiteBrush = QtGui.QBrush(QtGui.QColor(177, 177, 177))
        self.whiteBrush.setStyle(Qt.SolidPattern)

    def _GetStyleSheet(self):
        return '\n\n                QTreeView {\n\n                }\n                QTreeView::item {\n                    background: none;\n                }\n                QTreeView::item:selected {\n                    background: none;\n                }\n                QTreeView::item:selected:active {\n                    background: none;\n                }\n                QTreeView::item:focus\n                {\n                    background: none;\n                }\n                QTreeView::branch {\n                    background: none;\n                }\n                QListWidget::item:selected {\n                    background: none;\n                }\n                QListWidget::item:selected:active {\n                    background: none;\n                }\n                QWidget::item:selected {\n                    background: none;\n                }\n\n               '

    def _WrapWord(self, columnWidth, value):
        valueAsList = list(value)
        currentIndex = 0
        doInsert = False
        for i in range(0, len(valueAsList)):
            if currentIndex * 7 > columnWidth and i > 0:
                doInsert = True
                currentIndex = 0
            currentIndex += 1
            if doInsert and valueAsList[i] == ' ':
                valueAsList[i] = '\n'
                doInsert = False

        value = ''.join(valueAsList)
        return value

    def BuildTree(self, data, columnWidth=0):
        self.model = QStandardItemModel()
        self.data = data
        self.AddItems(self.model, data, columnWidth=columnWidth)
        self.tree.setModel(self.model)

    def ClearPathToItem(self, location):
        self.ColorItemInLocation(location, self.whiteBrush)

    def UpdateSingleItem(self, data):
        itemIndex = self.tree.selectedIndexes()[0]
        item = self.model.itemFromIndex(itemIndex)
        if item is not None:
            if data is not None:
                item.removeRows(0, item.rowCount())
                itemData = item.data(0).split(': ')
                if type(data) in (dict, list):
                    item.setText(itemData[0])
                    self.AddItems(item, data)
                else:
                    if type(data) is unicode:
                        dataValue = data
                    else:
                        dataValue = str(data)
                    item.setText(itemData[0] + ': ' + dataValue)
            elif item.parent() is None:
                self.model.removeRow(itemIndex.row())
            else:
                item.parent().removeRow(itemIndex.row())

    def ConvertDataToString(self, data):
        value = ''
        if type(data) not in (list, dict):
            if type(data) is unicode:
                value = data
            elif data is None:
                value = '(DELETED)'
            else:
                value = str(data)
        return value

    def _MakeItemUneditable(self, item):
        item.setFlags(item.flags() & ~Qt.ItemIsEditable)

    def _AddDictToTree(self, data, parent, columnWidth=0):
        sortedKeys = sorted(data.keys())
        for key in sortedKeys:
            value = self.ConvertDataToString(data[key])
            if value == '':
                item = QStandardItem(str(key))
            else:
                item = QStandardItem(str(key) + ': ' + value)
                if len(value) > 10:
                    item.setToolTip(value)
            self._MakeItemUneditable(item)
            parent.appendRow(item)
            if type(data[key]) in (dict, list):
                self.AddItems(item, data[key], columnWidth=columnWidth)

    def _AddListToTree(self, data, parent):
        for attribute in data:
            item = QStandardItem(str(attribute))
            self._MakeItemUneditable(item)
            parent.appendRow(item)

    def _AddPrimitiveToTree(self, data, parent, columnWidth=0):
        if type(data) in (str, unicode):
            value = self._WrapWord(columnWidth, data)
        else:
            value = self.ConvertDataToString(data)
        item = QStandardItem(value)
        self._MakeItemUneditable(item)
        parent.appendRow(item)

    def AddItems(self, parent, data, columnWidth=0):
        if type(data) is dict:
            self._AddDictToTree(data, parent, columnWidth=columnWidth)
        elif type(data) is list:
            self._AddListToTree(data, parent)
        else:
            self._AddPrimitiveToTree(data, parent, columnWidth=columnWidth)

    def _BinarySearchKeys(self, keys, key, currentMin, currentMax):
        while currentMax >= currentMin:
            mid = currentMin + (currentMax - currentMin) / 2
            if keys[mid] < key:
                currentMin = mid + 1
            elif keys[mid] > key:
                currentMax = mid - 1
            else:
                return mid

        return -1

    def _FindIndexOfKey(self, currentData, key):
        if type(currentData) is dict:
            sortedKeys = sorted(currentData.keys())
            return self._BinarySearchKeys(sortedKeys, key, 0,
                                          len(sortedKeys) - 1)
        else:
            for index, data in enumerate(currentData):
                if index == key:
                    return key

            return -1

    def _GetNextRoot(self, index, root):
        if root is None:
            root = self.model.index(index, 0)
        else:
            root = root.child(index, 0)
        return root

    def _GetChildDataByKey(self, currentData, key):
        if type(currentData) is dict:
            return currentData.get(key, None)
        else:
            return key + 1

    def ColorItem(self, item, brush):
        item.setData(QtGui.QBrush(QtGui.QColor(brush.color())),
                     Qt.ForegroundRole)

    def ColorItemInLocation(self, location, brush):
        root = None
        currentData = self.data
        for key in location:
            index = self._FindIndexOfKey(currentData, key)
            root = self._GetNextRoot(index, root)
            if root is not None:
                item = self.model.itemFromIndex(root)
                if item is not None:
                    self.ColorItem(item, brush)
            else:
                break
            currentData = self._GetChildDataByKey(currentData, key)

    def ColorAddedItems(self, locations):
        for location in locations:
            self.ColorItemInLocation(location, self.brushAddedItem)

    def ColorChangedItems(self, locations):
        for location in locations:
            self.ColorItemInLocation(location, self.brushChangedItem)

    def _ColorText(self, data, color):
        return "<b><font style='font-size: 14px; color: " + color + ";'>" + data + '</font></b>'

    def _DiffTreeDataIsValid(self, data):
        if data is None:
            return False
        if data == '(DELETED)':
            return False
        if data == '(DOES NOT EXIST)':
            return False
        return True

    def _AddDiffInfoAsTooltipForItem(self, item, itemData, base):
        result = ''
        if self._DiffTreeDataIsValid(itemData) and self._DiffTreeDataIsValid(
                base):
            if not item.hasChildren():
                if ':' in itemData:
                    diffTreeData = itemData.split(':')[1]
                else:
                    diffTreeData = itemData
            else:
                diffTreeData = itemData
            diffBaseData = base
            if type(itemData) is not unicode:
                diffTreeData = str(diffTreeData)
            if type(base) is not unicode:
                diffBaseData = str(diffBaseData)
            for diff in difflib.ndiff(diffTreeData, diffBaseData):
                if diff.startswith('- '):
                    result += self._ColorText(diff.split('- ')[1], '#00ff00')
                elif diff.startswith('+ '):
                    result += self._ColorText(diff.split('+ ')[1], '#ff0000')
                else:
                    result += diff.split('  ')[1]

            item.setToolTip(result)

    def _FindItemByLocation(self, location):
        root = None
        currentData = self.data
        item = None
        for key in location:
            index = self._FindIndexOfKey(currentData, key)
            root = self._GetNextRoot(index, root)
            if root is not None:
                item = self.model.itemFromIndex(root)
            currentData = self._GetChildDataByKey(currentData, key)

        return item

    def AddDiffTooltipToLocations(self, locations):
        for data in locations:
            location = data[0]
            baseData = data[1]
            item = self._FindItemByLocation(location)
            if item is None:
                item = self.model.itemFromIndex(self.model.index(0, 0))
            if type(baseData) is list:
                lastItemIndex = 0
                if len(location) > 0:
                    lastItemIndex = len(location) - 1
                self._AddDiffInfoAsTooltipForItem(item,
                                                  item.data(lastItemIndex),
                                                  baseData[lastItemIndex])
            else:
                self._AddDiffInfoAsTooltipForItem(item, item.data(0), baseData)

    def _ColorLocationWithKeys(self, finalKey, keys):
        root = None
        currentData = self.data
        for key in keys:
            index = self._FindIndexOfKey(currentData, key)
            root = self._GetNextRoot(index, root)
            if root is not None:
                try:
                    item = self.model.itemFromIndex(root)
                    if key == finalKey:
                        brush = self.brushConflictInItem
                    else:
                        brush = self.brushConflictInChild
                    self.ColorItem(item, brush)
                except AttributeError:
                    pass

            else:
                break
            currentData = self._GetChildDataByKey(currentData, key)

    def ColorLocationsInTree(self, conflicts, base=None):
        if self.data is not None:
            for keys in conflicts:
                try:
                    finalKey = keys[-1]
                except IndexError:
                    continue

                self._ColorLocationWithKeys(finalKey, keys)

    def ExpandAndMoveTo(self, keys):
        if self.data is not None:
            root = None
            currentData = self.data
            for key in keys:
                if currentData is not None:
                    index = self._FindIndexOfKey(currentData, key)
                    root = self._GetNextRoot(index, root)
                    self.tree.setExpanded(root, True)
                    currentData = currentData.get(key, None)

            if root is not None:
                self.tree.setCurrentIndex(root)

    def GetWidget(self):
        return self.tree
Esempio n. 3
0
class BrowserWindow(QMainWindow):

    MO_ROLE = Qt.UserRole+1

    def __init__(self, conn):
        super(BrowserWindow, self).__init__()
        self._conn = conn
        self._resolver = AsyncResolver()
        self._resolver.object_resolved.connect(self._data_resolved)
        self._resolver.start()
        self._init_models()
        self._init_gui()
        self._init_data()
        self._init_connections()

    def __del__(self):
        self._resolver.stop_work()
        self._resolver.terminate()

    def _init_models(self):
        self._hierarchy_model = QStandardItemModel()
        self._hierarchy_model.setColumnCount(2)
        self._hierarchy_model.setHorizontalHeaderLabels(['class', 'dn'])
        self._details_model = QStandardItemModel()
        self._details_model.setColumnCount(2)
        self._details_model.setHorizontalHeaderLabels(['Property', 'Value'])

    def _init_gui(self):
        self._widget = QSplitter(self, Qt.Horizontal)
        self._hierarchy_view = QTreeView(self._widget)
        self._details_view = QTableView(self._widget)

        self._widget.addWidget(self._hierarchy_view)
        self._widget.addWidget(self._details_view)
        self._widget.setStretchFactor(0, 2)
        self._widget.setStretchFactor(1, 1)
        self.setCentralWidget(self._widget)

        self._hierarchy_view.setModel(self._hierarchy_model)
        self._details_view.setModel(self._details_model)

        self._hierarchy_view.expanded.connect(self._mo_item_expand)

    def _init_data(self):
        item = self._row_for_mo(self._conn.resolve_dn(''))
        self._hierarchy_model.insertRow(0, item)

    def _init_connections(self):
        self.connect(self._resolver,
                        SIGNAL('object_resolved(QVariant)'),
                     self,
                        SLOT('_data_resolved(QVariant)'))
        self._hierarchy_view.activated.connect(self._item_activated)
        #self.connect(self._hierarchy_view.selectionModel(),
        #                SIGNAL('currentChanged(QModelIndex,QModelIndex)'),
        #             self,
        #                SLOT('_current_changed(QModelIndex, QModelIndex)'))
        self.connect(self._hierarchy_view.selectionModel(),
                        SIGNAL('activated(QModelIndex)'),
                     self,
                        SLOT('_item_activated(QModelIndex)'))


    def _row_for_mo(self, mo):
        row = [QStandardItem(mo.ucs_class), QStandardItem(mo.dn)]
        for item in row:
            item.setEditable(False)
        row[0].appendColumn([QStandardItem('Loading...')])
        row[0].setData(mo, self.MO_ROLE)
        return row

    def _add_mo_in_tree(self, mo, index=QtCore.QModelIndex()):
        item = None
        if index.isValid():
            item = self._hierarchy_model.itemFromIndex(index)
        else:
            item = self._get_item_for_dn(self._parent_dn(mo.dn))
        if item:
            item.appendColumn([self._row_for_mo(mo)[0]])
        self.auto_width()

    def _add_mos_in_tree(self, mos, index=QtCore.QModelIndex()):
        item = None
        if index.isValid():
            item = self._hierarchy_model.itemFromIndex(index)
        else:
            if not mos:
                return
            item = self._get_item_for_dn(self._parent_dn(mos[0].dn))
        while item.columnCount():
            item.removeColumn(0)
        items = map(self._row_for_mo, mos)
        if items:
            for x in xrange(len(items[0])):
                item.appendColumn([row[x] for row in items])
        self.auto_width()

    @staticmethod
    def _parent_dn(dn):
        parent_dn, _, rn = dn.rpartition('/')
        return parent_dn

    def _get_item_for_dn(self, dn):
        parent_dn = dn
        items = self._hierarchy_model.findItems(parent_dn, column=1)
        if items:
            return self._hierarchy_model.item(items[0].row())
        return None

    @QtCore.Slot('_data_resolved(QVariant)')
    def _data_resolved(self, datav):
        print 'Data resolved: ', datav
        index, data = datav
        if isinstance(data, UcsmObject):
            self._add_mo_in_tree(data, index=index)
        else:
            self._add_mos_in_tree(data, index=index)

    @QtCore.Slot('_current_changed(QModelIndex,QModelIndex)')
    def _current_changed(self, curr, prev):
        self._item_activated(curr)

    @QtCore.Slot('_item_activated(QModelIndex)')
    def _item_activated(self, index):
        print 'Activated: %s data %s' % (index, index.data(self.MO_ROLE))
        if index.sibling(0, 0).isValid():
            index = index.sibling(0, 0)
            data = index.data(self.MO_ROLE)
            self.set_detail_object(data)

    def _mo_item_expand(self, index):
        obj = index.data(self.MO_ROLE)
        print 'Expanded object: %s' % obj
        try:
            self._resolver.add_task(lambda: (index,
                                        self._conn.resolve_children(obj.dn)))
        except (KeyError, AttributeError):
            QtGui.QMessageBox.critical(0, 'Error', 'Object does not have dn')

    def auto_width(self):
        for view in [self._hierarchy_view, self._details_view]:
            for col in xrange(view.model().columnCount()):
                view.resizeColumnToContents(col)

    def set_detail_object(self, object):
        self._details_model.removeRows(0, self._details_model.rowCount())
        for k, v in object.attributes.iteritems():
            row = [QStandardItem(k), QStandardItem(v)]
            for item in row:
                item.setEditable(False)
            self._details_model.appendRow(row)
        self.auto_width()
Esempio n. 4
0
class List(QMainWindow):
    """All Notes dialog"""
    def __init__(self, *args, **kwargs):
        QMainWindow.__init__(self, *args, **kwargs)
        self.app = QApplication.instance()
        self.closed = False
        self.sort_order = None
        self._init_interface()
        self.app.data_changed.connect(self._reload_data)
        self._init_notebooks()
        self._init_tags()
        self._init_notes()

    def _init_interface(self):
        self.ui = Ui_List()
        self.ui.setupUi(self)
        self.setWindowIcon(get_icon())
        self.setWindowTitle(self.tr("Everpad / All Notes"))
        self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new'))
        self.ui.newNotebookBtn.clicked.connect(self.new_notebook)

        self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new'))
        self.ui.newNoteBtn.clicked.connect(self.new_note)

        self.ui.newNoteBtn.setShortcut(QKeySequence(self.tr('Ctrl+n')))
        self.ui.newNotebookBtn.setShortcut(
            QKeySequence(self.tr('Ctrl+Shift+n')))
        QShortcut(QKeySequence(self.tr('Ctrl+q')), self, self.close)

    def _init_notebooks(self):
        self._current_notebook = None
        self.notebooksModel = QStandardItemModel()
        self.ui.notebooksList.setModel(self.notebooksModel)
        self.ui.notebooksList.selection.connect(self.selection_changed)
        self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notebooksList.customContextMenuRequested.connect(
            self.notebook_context_menu)

    def _init_tags(self):
        self._current_tag = None
        self.tagsModel = QStandardItemModel()
        self.ui.tagsList.setModel(self.tagsModel)
        self.ui.tagsList.selection.connect(self.tag_selection_changed)
        self.ui.tagsList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.tagsList.customContextMenuRequested.connect(
            self.tag_context_menu)

    def _init_notes(self):
        self._current_note = None
        self.notesModel = QStandardItemModel()
        self.notesModel.setHorizontalHeaderLabels(
            [self.tr('Title'), self.tr('Last Updated')])

        self.ui.notesList.setModel(self.notesModel)
        self.ui.notesList.selection.connect(self.note_selection_changed)
        self.ui.notesList.doubleClicked.connect(self.note_dblclicked)
        self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notesList.customContextMenuRequested.connect(
            self.note_context_menu)
        self.ui.notesList.header().sortIndicatorChanged.connect(
            self.sort_order_updated)

    @Slot(QItemSelection, QItemSelection)
    def selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.ui.tagsList.clearSelection()
            self.notebook_selected(selected.indexes()[-1])

    @Slot(QItemSelection, QItemSelection)
    def tag_selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.ui.notebooksList.clearSelection()
            self.tag_selected(selected.indexes()[-1])

    @Slot(QItemSelection, QItemSelection)
    def note_selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.note_selected(selected.indexes()[-1])

    def showEvent(self, *args, **kwargs):
        super(List, self).showEvent(*args, **kwargs)
        self._reload_data()
        self.readSettings()

    def writeSettings(self):
        self.app.settings.setValue('list-geometry', self.saveGeometry())
        for key, widget in self._getRestorableItems():
            self.app.settings.setValue(key, widget.saveState())

    def _getRestorableItems(self):
        return (
            ('list-splitter-state', self.ui.splitter),
            ('list-header-state', self.ui.notesList.header()),
        )

    def readSettings(self):
        geometry = self.app.settings.value('list-geometry')
        if geometry:
            self.restoreGeometry(geometry)

        for key, widget in self._getRestorableItems():
            state = self.app.settings.value(key)
            if state:
                widget.restoreState(state)

    def closeEvent(self, event):
        self.writeSettings()
        event.ignore()
        self.closed = True
        self.hide()

    @Slot(int, Qt.SortOrder)
    def sort_order_updated(self, logicalIndex, order):
        self.sort_order = (logicalIndex, order.name)
        self.app.settings.setValue('list-notes-sort-order', self.sort_order)

    def note_selected(self, index):
        self._current_note = index

    def notebook_selected(self, index):
        self.notesModel.setRowCount(0)

        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            notebook_id = item.notebook.id
        else:
            notebook_id = 0

        self._current_notebook = notebook_id
        self._current_tag = SELECT_NONE

        notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array(
            [], signature='i')

        if hasattr(
                item,
                'stack'):  # stack selected, retrieve all underlying notebooks
            notebook_filter = []
            for notebook_struct in self.app.provider.list_notebooks():
                notebook = Notebook.from_tuple(notebook_struct)
                if (notebook.stack == item.stack):
                    notebook_filter.append(notebook.id)

        notes = self.app.provider.find_notes(
            '',
            notebook_filter,
            dbus.Array([], signature='i'),
            0,
            2**31 - 1,
            Note.ORDER_TITLE,
            -1,
        )  # fails with sys.maxint in 64

        for note_struct in notes:
            note = Note.from_tuple(note_struct)
            self.notesModel.appendRow(QNoteItemFactory(note).make_items())

        sort_order = self.sort_order
        if sort_order is None:
            sort_order = self.app.settings.value('list-notes-sort-order')

        if sort_order:
            logicalIndex, order = sort_order
            order = Qt.SortOrder.values[order]
            self.ui.notesList.sortByColumn(int(logicalIndex), order)

    def tag_selected(self, index):
        self.notesModel.setRowCount(0)

        item = self.tagsModel.itemFromIndex(index)
        if hasattr(item, 'tag'):
            tag_id = item.tag.id
        else:
            tag_id = 0

        self._current_notebook = SELECT_NONE
        self._current_tag = tag_id

        tag_filter = [tag_id] if tag_id > 0 else dbus.Array([], signature='i')
        notes = self.app.provider.find_notes(
            '',
            dbus.Array([], signature='i'),
            tag_filter,
            0,
            2**31 - 1,
            Note.ORDER_TITLE,
            -1,
        )  # fails with sys.maxint in 64
        for note_struct in notes:
            note = Note.from_tuple(note_struct)
            self.notesModel.appendRow(QNoteItemFactory(note).make_items())

        sort_order = self.sort_order
        if sort_order is None:
            sort_order = self.app.settings.value('list-notes-sort-order')

        if sort_order:
            logicalIndex, order = sort_order
            order = Qt.SortOrder.values[order]
            self.ui.notesList.sortByColumn(int(logicalIndex), order)

    @Slot()
    def note_dblclicked(self, index):
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def new_notebook(self, oldStack=''):
        name, status, stack = self._notebook_new_name(
            self.tr('Create new notebook'), '', oldStack)
        if status:
            notebook_struct = self.app.provider.create_notebook(name, stack)
            notebook = Notebook.from_tuple(notebook_struct)

            self.app.send_notify(
                self.tr('Notebook "%s" created!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def rename_notebook(self):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        notebook = item.notebook
        name, status, stack = self._notebook_new_name(
            self.tr('Rename notebook'), notebook.name, notebook.stack)
        if status:
            notebook.name = name
            notebook.stack = stack
            self.app.provider.update_notebook(notebook.struct)
            self.app.send_notify(
                self.tr('Notebook "%s" renamed!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def remove_notebook(self):
        msg = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a notebook"),
            self.tr(
                "Are you sure want to delete this notebook and its notes?"),
            QMessageBox.Yes | QMessageBox.No)
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.notebooksList.currentIndex()
            item = self.notebooksModel.itemFromIndex(index)
            self.app.provider.delete_notebook(item.notebook.id)
            self.app.send_notify(
                self.tr('Notebook "%s" deleted!') % item.notebook.name)
            self._reload_notebooks_list()

    @Slot()
    def rename_stack(self):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        stack = item.stack
        name, status = self._stack_new_name(
            self.tr('Rename stack'),
            stack,
        )
        if status:
            # loop notebooks and update stack str
            for notebook_struct in self.app.provider.list_notebooks():
                notebook = Notebook.from_tuple(notebook_struct)
                if (notebook.stack == item.stack):
                    notebook.stack = name
                    self.app.provider.update_notebook(notebook.struct)

            self.app.send_notify(self.tr('Stack "%s" renamed!') % name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def remove_stack(self):
        msg = QMessageBox(
            QMessageBox.Critical, self.tr("You are trying to delete a stack"),
            self.
            tr("Are you sure want to delete this stack? Notebooks and notes are preserved."
               ), QMessageBox.Yes | QMessageBox.No)
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.notebooksList.currentIndex()
            item = self.notebooksModel.itemFromIndex(index)
            # loop notebooks and remove stack str
            for notebook_struct in self.app.provider.list_notebooks():
                notebook = Notebook.from_tuple(notebook_struct)
                if (notebook.stack == item.stack):
                    print "Clearing one notebook from its stack."
                    notebook.stack = ''
                    self.app.provider.update_notebook(notebook.struct)

            self._reload_notebooks_list()

    @Slot()
    def remove_tag(self):
        msg = QMessageBox(
            QMessageBox.Critical, self.tr("You are trying to delete a tag"),
            self.
            tr("Are you sure want to delete this tag and untag all notes tagged with it?"
               ), QMessageBox.Yes | QMessageBox.No)
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.tagsList.currentIndex()
            item = self.tagsModel.itemFromIndex(index)
            self.app.provider.delete_tag(item.tag.id)
            self.app.send_notify(self.tr('Tag "%s" deleted!') % item.tag.name)
            self._reload_tags_list()

    @Slot()
    def new_note(self):
        index = self.ui.notebooksList.currentIndex()
        notebook_id = NONE_ID
        if index.row():
            item = self.notebooksModel.itemFromIndex(index)
            notebook_id = item.notebook.id

        self.app.indicator.create(notebook_id=notebook_id)

    @Slot()
    def edit_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def remove_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        msgBox = QMessageBox(
            QMessageBox.Critical, self.tr("You are trying to delete a note"),
            self.tr('Are you sure want to delete note "%s"?') %
            item.note.title, QMessageBox.Yes | QMessageBox.No)
        if msgBox.exec_() == QMessageBox.Yes:
            self.app.provider.delete_note(item.note.id)
            self.app.send_notify(
                self.tr('Note "%s" deleted!') % item.note.title)
            self.notebook_selected(self.ui.notebooksList.currentIndex())

    @Slot(QPoint)
    def notebook_context_menu(self, pos):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            menu = QMenu(self.ui.notebooksList)
            menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'),
                           self.rename_notebook)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'),
                           self.remove_notebook)
            menu.exec_(self.ui.notebooksList.mapToGlobal(pos))
        if hasattr(item, 'stack'):
            menu = QMenu(self.ui.notebooksList)
            menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'),
                           self.rename_stack)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'),
                           self.remove_stack)
            menu.exec_(self.ui.notebooksList.mapToGlobal(pos))

    @Slot(QPoint)
    def tag_context_menu(self, pos):
        index = self.ui.tagsList.currentIndex()
        item = self.tagsModel.itemFromIndex(index)
        if hasattr(item, 'tag'):
            menu = QMenu(self.ui.tagsList)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'),
                           self.remove_tag)
            menu.exec_(self.ui.tagsList.mapToGlobal(pos))

    @Slot(QPoint)
    def note_context_menu(self, pos):
        menu = QMenu(self.ui.notesList)
        menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'),
                       self.edit_note)
        menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'),
                       self.remove_note)
        menu.exec_(self.ui.notesList.mapToGlobal(pos))

    def _reload_data(self):
        self._reload_notebooks_list(self._current_notebook)
        self._reload_tags_list(self._current_tag)
        self._mark_note_selected(self._current_note)

    def _reload_notebooks_list(self, select_notebook_id=None):
        # TODO could enable selecting an already selected stack
        self.notebooksModel.clear()
        root = QStandardItem(QIcon.fromTheme('user-home'),
                             self.tr('All Notes'))
        self.notebooksModel.appendRow(root)
        selected_item = root

        stacks = {}
        for notebook_struct in self.app.provider.list_notebooks():
            notebook = Notebook.from_tuple(notebook_struct)
            count = self.app.provider.get_notebook_notes_count(notebook.id)
            item = QNotebookItem(notebook, count)

            if (notebook.stack == ''):
                root.appendRow(item)
            else:
                if (notebook.stack not in stacks.keys()):
                    stack = QStandardItem(QIcon.fromTheme('user-home'),
                                          notebook.stack)
                    stack.stack = notebook.stack
                    root.appendRow(stack)
                    stacks[notebook.stack] = stack

                stacks[notebook.stack].appendRow(item)

            if select_notebook_id and notebook.id == select_notebook_id:
                selected_item = item

        self.ui.notebooksList.expandAll()

        if selected_item and not select_notebook_id == SELECT_NONE:
            index = self.notebooksModel.indexFromItem(selected_item)
            self.ui.notebooksList.setCurrentIndex(index)
            self.notebook_selected(index)

    def _notebook_new_name(self, title, exclude='', oldStack=''):
        names = map(lambda nb: Notebook.from_tuple(nb).name,
                    self.app.provider.list_notebooks())
        try:
            names.remove(exclude)
        except ValueError:
            pass
        name, status = QInputDialog.getText(self,
                                            title,
                                            self.tr('Enter notebook name:'),
                                            text=exclude)
        while name in names and status:
            message = self.tr(
                'Notebook with this name already exist. Enter notebook name')
            name, status = QInputDialog.getText(self, title, message)
        if status:
            stack, status = QInputDialog.getText(
                self,
                title,
                self.tr('Enter stack name (empty for no stack):'),
                text=oldStack)
        else:
            stack = oldStack
        return name, status, stack

    def _stack_new_name(self, title, value=''):
        name, status = QInputDialog.getText(self,
                                            title,
                                            self.tr('Enter stack name:'),
                                            text=value)
        return name, status

    def _reload_tags_list(self, select_tag_id=None):
        # TODO nested tags
        self.tagsModel.clear()
        tagRoot = QStandardItem(QIcon.fromTheme('user-home'),
                                self.tr('All Tags'))
        self.tagsModel.appendRow(tagRoot)
        selected_item = tagRoot

        for tag_struct in self.app.provider.list_tags():
            tag = Tag.from_tuple(tag_struct)
            count = self.app.provider.get_tag_notes_count(tag.id)
            item = QTagItem(tag, count)
            tagRoot.appendRow(item)

            if select_tag_id and tag.id == select_tag_id:
                selected_item = item

        self.ui.tagsList.expandAll()
        if selected_item and not select_tag_id == SELECT_NONE:
            index = self.tagsModel.indexFromItem(selected_item)
            self.ui.tagsList.setCurrentIndex(index)
            self.tag_selected(index)

    def _mark_note_selected(self, index):
        if index:
            self.ui.notesList.setCurrentIndex(index)
Esempio n. 5
0
class List(QDialog):
    """All Notes dialog"""
    def __init__(self, app, *args, **kwargs):
        QDialog.__init__(self, *args, **kwargs)
        self.app = app
        self.closed = False
        self.sort_order = None
        self.ui = Ui_List()
        self.ui.setupUi(self)
        self.setWindowIcon(get_icon())

        self.notebooksModel = QStandardItemModel()
        self.ui.notebooksList.setModel(self.notebooksModel)
        self.ui.notebooksList.selection.connect(self.selection_changed)
        self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notebooksList.customContextMenuRequested.connect(
            self.notebook_context_menu)

        self.notesModel = QStandardItemModel()
        self.notesModel.setHorizontalHeaderLabels(
            [self.tr('Title'), self.tr('Last Updated')])

        self.ui.notesList.setModel(self.notesModel)
        self.ui.notesList.doubleClicked.connect(self.note_dblclicked)
        self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notesList.customContextMenuRequested.connect(
            self.note_context_menu)
        self.ui.notesList.header().sortIndicatorChanged.connect(
            self.sort_order_updated)

        self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new'))
        self.ui.newNotebookBtn.clicked.connect(self.new_notebook)

        self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new'))
        self.ui.newNoteBtn.clicked.connect(self.new_note)

    @Slot(QItemSelection, QItemSelection)
    def selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.notebook_selected(selected.indexes()[-1])

    def showEvent(self, *args, **kwargs):
        QDialog.showEvent(self, *args, **kwargs)
        self._reload_notebooks_list()
        self.readSettings()

    def writeSettings(self):
        self.app.settings.setValue('list-geometry', self.saveGeometry())
        for key, widget in self._getRestorableItems():
            self.app.settings.setValue(key, widget.saveState())

    def _getRestorableItems(self):
        return (
            ('list-splitter-state', self.ui.splitter),
            ('list-header-state', self.ui.notesList.header()),
        )

    def readSettings(self):
        geometry = self.app.settings.value('list-geometry')
        if geometry:
            self.restoreGeometry(geometry)

        for key, widget in self._getRestorableItems():
            state = self.app.settings.value(key)
            if state:
                widget.restoreState(state)

    def closeEvent(self, event):
        self.writeSettings()
        event.ignore()
        self.closed = True
        self.hide()

    @Slot(int, Qt.SortOrder)
    def sort_order_updated(self, logicalIndex, order):
        self.sort_order = (logicalIndex, order.name)
        self.app.settings.setValue('list-notes-sort-order', self.sort_order)

    def notebook_selected(self, index):
        self.notesModel.setRowCount(0)

        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            notebook_id = item.notebook.id
        else:
            notebook_id = 0
        notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array(
            [], signature='i')
        notes = self.app.provider.find_notes(
            '',
            notebook_filter,
            dbus.Array([], signature='i'),
            0,
            2**31 - 1,
            Note.ORDER_TITLE,
            -1,
        )  # fails with sys.maxint in 64
        for note_struct in notes:
            note = Note.from_tuple(note_struct)
            self.notesModel.appendRow(QNoteItemFactory(note).make_items())

        sort_order = self.sort_order
        if sort_order is None:
            sort_order = self.app.settings.value('list-notes-sort-order')

        if sort_order:
            logicalIndex, order = sort_order
            order = Qt.SortOrder.values[order]
            self.ui.notesList.sortByColumn(int(logicalIndex), order)

    @Slot()
    def note_dblclicked(self, index):
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def new_notebook(self):
        name, status = self._notebook_new_name(self.tr('Create new notebook'))
        if status:
            notebook_struct = self.app.provider.create_notebook(name)
            notebook = Notebook.from_tuple(notebook_struct)

            self.app.send_notify(
                self.tr('Notebook "%s" created!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def rename_notebook(self):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        notebook = item.notebook
        name, status = self._notebook_new_name(
            self.tr('Rename notebook'),
            notebook.name,
        )
        if status:
            notebook.name = name
            self.app.provider.update_notebook(notebook.struct)
            self.app.send_notify(
                self.tr('Notebook "%s" renamed!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def remove_notebook(self):
        msg = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a notebook"),
            self.tr(
                "Are you sure want to delete this notebook and its notes?"),
            QMessageBox.Yes | QMessageBox.No)
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.notebooksList.currentIndex()
            item = self.notebooksModel.itemFromIndex(index)
            self.app.provider.delete_notebook(item.notebook.id)
            self.app.send_notify(
                self.tr('Notebook "%s" deleted!') % item.notebook.name)
            self._reload_notebooks_list()

    @Slot()
    def new_note(self):
        index = self.ui.notebooksList.currentIndex()
        notebook_id = NONE_ID
        if index.row():
            item = self.notebooksModel.itemFromIndex(index)
            notebook_id = item.notebook.id

        self.app.indicator.create(notebook_id=notebook_id)

    @Slot()
    def edit_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def remove_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        msgBox = QMessageBox(
            QMessageBox.Critical, self.tr("You are trying to delete a note"),
            self.tr('Are you sure want to delete note "%s"?') %
            item.note.title, QMessageBox.Yes | QMessageBox.No)
        if msgBox.exec_() == QMessageBox.Yes:
            self.app.provider.delete_note(item.note.id)
            self.app.send_notify(
                self.tr('Note "%s" deleted!') % item.note.title)
            self.notebook_selected(self.ui.notebooksList.currentIndex())

    @Slot(QPoint)
    def notebook_context_menu(self, pos):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            menu = QMenu(self.ui.notebooksList)
            menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'),
                           self.rename_notebook)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'),
                           self.remove_notebook)
            menu.exec_(self.ui.notebooksList.mapToGlobal(pos))

    @Slot(QPoint)
    def note_context_menu(self, pos):
        menu = QMenu(self.ui.notesList)
        menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'),
                       self.edit_note)
        menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'),
                       self.remove_note)
        menu.exec_(self.ui.notesList.mapToGlobal(pos))

    def _reload_notebooks_list(self, select_notebook_id=None):
        self.notebooksModel.clear()
        root = QStandardItem(QIcon.fromTheme('user-home'),
                             self.tr('All Notes'))
        self.notebooksModel.appendRow(root)

        selected_item = root
        for notebook_struct in self.app.provider.list_notebooks():
            notebook = Notebook.from_tuple(notebook_struct)
            count = self.app.provider.get_notebook_notes_count(notebook.id)
            item = QNotebookItem(notebook, count)
            root.appendRow(item)

            if select_notebook_id and notebook.id == select_notebook_id:
                selected_item = item

        self.ui.notebooksList.expandAll()
        if selected_item:
            index = self.notebooksModel.indexFromItem(selected_item)
            self.ui.notebooksList.setCurrentIndex(index)
            self.notebook_selected(index)

    def _notebook_new_name(self, title, exclude=''):
        names = map(lambda nb: Notebook.from_tuple(nb).name,
                    self.app.provider.list_notebooks())
        try:
            names.remove(exclude)
        except ValueError:
            pass
        name, status = QInputDialog.getText(self,
                                            title,
                                            self.tr('Enter notebook name:'),
                                            text=exclude)
        while name in names and status:
            message = self.tr(
                'Notebook with this name already exist. Enter notebook name')
            name, status = QInputDialog.getText(self, title, message)
        return name, status
Esempio n. 6
0
class List(QMainWindow):
    """All Notes dialog"""

    def __init__(self, *args, **kwargs):
        QMainWindow.__init__(self, *args, **kwargs)
        self.app = QApplication.instance()
        self.closed = False
        self.sort_order = None
        self._init_interface()
        self.app.data_changed.connect(self._reload_data)
        self._init_notebooks()
        self._init_tags()
        self._init_notes()

    def _init_interface(self):
        self.ui = Ui_List()
        self.ui.setupUi(self)
        self.setWindowIcon(get_icon())
        self.setWindowTitle(self.tr("Everpad / All Notes"))
        self.ui.newNotebookBtn.setIcon(QIcon.fromTheme('folder-new'))
        self.ui.newNotebookBtn.clicked.connect(self.new_notebook)

        self.ui.newNoteBtn.setIcon(QIcon.fromTheme('document-new'))
        self.ui.newNoteBtn.clicked.connect(self.new_note)

        self.ui.newNoteBtn.setShortcut(QKeySequence(self.tr('Ctrl+n')))
        self.ui.newNotebookBtn.setShortcut(QKeySequence(self.tr('Ctrl+Shift+n')))
        QShortcut(QKeySequence(self.tr('Ctrl+q')), self, self.close)

    def _init_notebooks(self):
        self._current_notebook = None
        self.notebooksModel = QStandardItemModel()
        self.ui.notebooksList.setModel(self.notebooksModel)
        self.ui.notebooksList.selection.connect(self.selection_changed)
        self.ui.notebooksList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notebooksList.customContextMenuRequested.connect(self.notebook_context_menu)

    def _init_tags(self):
        self._current_tag = None
        self.tagsModel = QStandardItemModel()
        self.ui.tagsList.setModel(self.tagsModel)
        self.ui.tagsList.selection.connect(self.tag_selection_changed)
        self.ui.tagsList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.tagsList.customContextMenuRequested.connect(self.tag_context_menu)

    def _init_notes(self):
        self._current_note = None
        self.notesModel = QStandardItemModel()
        self.notesModel.setHorizontalHeaderLabels(
            [self.tr('Title'), self.tr('Last Updated')])

        self.ui.notesList.setModel(self.notesModel)
        self.ui.notesList.selection.connect(self.note_selection_changed)
        self.ui.notesList.doubleClicked.connect(self.note_dblclicked)
        self.ui.notesList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.notesList.customContextMenuRequested.connect(self.note_context_menu)
        self.ui.notesList.header().sortIndicatorChanged.connect(self.sort_order_updated)

    @Slot(QItemSelection, QItemSelection)
    def selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.ui.tagsList.clearSelection()
            self.notebook_selected(selected.indexes()[-1])

    @Slot(QItemSelection, QItemSelection)
    def tag_selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.ui.notebooksList.clearSelection()
            self.tag_selected(selected.indexes()[-1])

    @Slot(QItemSelection, QItemSelection)
    def note_selection_changed(self, selected, deselected):
        if len(selected.indexes()):
            self.note_selected(selected.indexes()[-1])

    def showEvent(self, *args, **kwargs):
        super(List, self).showEvent(*args, **kwargs)
        self._reload_data()
        self.readSettings()

    def writeSettings(self):
        self.app.settings.setValue('list-geometry', self.saveGeometry())
        for key, widget in self._getRestorableItems():
            self.app.settings.setValue(key, widget.saveState())

    def _getRestorableItems(self):
        return (
            ('list-splitter-state', self.ui.splitter),
            ('list-header-state', self.ui.notesList.header()),
        )

    def readSettings(self):
        geometry = self.app.settings.value('list-geometry')
        if geometry:
            self.restoreGeometry(geometry)

        for key, widget in self._getRestorableItems():
            state = self.app.settings.value(key)
            if state:
                widget.restoreState(state)

    def closeEvent(self, event):
        self.writeSettings()
        event.ignore()
        self.closed = True
        self.hide()

    @Slot(int, Qt.SortOrder)
    def sort_order_updated(self, logicalIndex, order):
        self.sort_order = (logicalIndex, order.name)
        self.app.settings.setValue('list-notes-sort-order', self.sort_order)

    def note_selected(self, index):
        self._current_note = index

    def notebook_selected(self, index):
        self.notesModel.setRowCount(0)

        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            notebook_id = item.notebook.id
        else:
            notebook_id = 0

        self._current_notebook = notebook_id
        self._current_tag = SELECT_NONE

        notebook_filter = [notebook_id] if notebook_id > 0 else dbus.Array([], signature='i')

        if hasattr(item, 'stack'):  # stack selected, retrieve all underlying notebooks
            notebook_filter = []
            for notebook_struct in self.app.provider.list_notebooks():
                notebook = Notebook.from_tuple(notebook_struct)
                if(notebook.stack == item.stack):
                    notebook_filter.append(notebook.id)

        notes = self.app.provider.find_notes(
            '', notebook_filter, dbus.Array([], signature='i'),
            0, 2 ** 31 - 1, Note.ORDER_TITLE, -1,
        )  # fails with sys.maxint in 64

        for note_struct in notes:
            note = Note.from_tuple(note_struct)
            self.notesModel.appendRow(QNoteItemFactory(note).make_items())

        sort_order = self.sort_order
        if sort_order is None:
            sort_order = self.app.settings.value('list-notes-sort-order')

        if sort_order:
            logicalIndex, order = sort_order
            order = Qt.SortOrder.values[order]
            self.ui.notesList.sortByColumn(int(logicalIndex), order)

    def tag_selected(self, index):
        self.notesModel.setRowCount(0)

        item = self.tagsModel.itemFromIndex(index)
        if hasattr(item, 'tag'):
            tag_id = item.tag.id
        else:
            tag_id = 0

        self._current_notebook = SELECT_NONE
        self._current_tag = tag_id

        tag_filter = [tag_id] if tag_id > 0 else dbus.Array([], signature='i')
        notes = self.app.provider.find_notes(
            '', dbus.Array([], signature='i'), tag_filter,
            0, 2 ** 31 - 1, Note.ORDER_TITLE, -1,
        )  # fails with sys.maxint in 64
        for note_struct in notes:
            note = Note.from_tuple(note_struct)
            self.notesModel.appendRow(QNoteItemFactory(note).make_items())

        sort_order = self.sort_order
        if sort_order is None:
            sort_order = self.app.settings.value('list-notes-sort-order')

        if sort_order:
            logicalIndex, order = sort_order
            order = Qt.SortOrder.values[order]
            self.ui.notesList.sortByColumn(int(logicalIndex), order)

    @Slot()
    def note_dblclicked(self, index):
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def new_notebook(self, oldStack=''):
        name, status, stack = self._notebook_new_name(self.tr('Create new notebook'), '', oldStack)
        if status:
            notebook_struct = self.app.provider.create_notebook(name, stack)
            notebook = Notebook.from_tuple(notebook_struct)

            self.app.send_notify(self.tr('Notebook "%s" created!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def rename_notebook(self):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        notebook = item.notebook
        name, status, stack = self._notebook_new_name(
            self.tr('Rename notebook'), notebook.name, notebook.stack
        )
        if status:
            notebook.name = name
            notebook.stack = stack
            self.app.provider.update_notebook(notebook.struct)
            self.app.send_notify(self.tr('Notebook "%s" renamed!') % notebook.name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def remove_notebook(self):
        msg = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a notebook"),
            self.tr("Are you sure want to delete this notebook and its notes?"),
            QMessageBox.Yes | QMessageBox.No
        )
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.notebooksList.currentIndex()
            item = self.notebooksModel.itemFromIndex(index)
            self.app.provider.delete_notebook(item.notebook.id)
            self.app.send_notify(self.tr('Notebook "%s" deleted!') % item.notebook.name)
            self._reload_notebooks_list()

    @Slot()
    def rename_stack(self):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        stack = item.stack
        name, status = self._stack_new_name(
            self.tr('Rename stack'), stack,
        )
        if status:
            # loop notebooks and update stack str
            for notebook_struct in self.app.provider.list_notebooks():
                notebook = Notebook.from_tuple(notebook_struct)
                if(notebook.stack == item.stack):
                    notebook.stack = name
                    self.app.provider.update_notebook(notebook.struct)

            self.app.send_notify(self.tr('Stack "%s" renamed!') % name)
            self._reload_notebooks_list(notebook.id)

    @Slot()
    def remove_stack(self):
        msg = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a stack"),
            self.tr("Are you sure want to delete this stack? Notebooks and notes are preserved."),
            QMessageBox.Yes | QMessageBox.No
        )
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.notebooksList.currentIndex()
            item = self.notebooksModel.itemFromIndex(index)
            # loop notebooks and remove stack str
            for notebook_struct in self.app.provider.list_notebooks():
                notebook = Notebook.from_tuple(notebook_struct)
                if(notebook.stack == item.stack):
                    print "Clearing one notebook from its stack."
                    notebook.stack = ''
                    self.app.provider.update_notebook(notebook.struct)

            self._reload_notebooks_list()

    @Slot()
    def remove_tag(self):
        msg = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a tag"),
            self.tr("Are you sure want to delete this tag and untag all notes tagged with it?"),
            QMessageBox.Yes | QMessageBox.No
        )
        if msg.exec_() == QMessageBox.Yes:
            index = self.ui.tagsList.currentIndex()
            item = self.tagsModel.itemFromIndex(index)
            self.app.provider.delete_tag(item.tag.id)
            self.app.send_notify(self.tr('Tag "%s" deleted!') % item.tag.name)
            self._reload_tags_list()

    @Slot()
    def new_note(self):
        index = self.ui.notebooksList.currentIndex()
        notebook_id = NONE_ID
        if index.row():
            item = self.notebooksModel.itemFromIndex(index)
            notebook_id = item.notebook.id

        self.app.indicator.create(notebook_id=notebook_id)

    @Slot()
    def edit_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        self.app.indicator.open(item.note)

    @Slot()
    def remove_note(self):
        index = self.ui.notesList.currentIndex()
        item = self.notesModel.itemFromIndex(index)
        msgBox = QMessageBox(
            QMessageBox.Critical,
            self.tr("You are trying to delete a note"),
            self.tr('Are you sure want to delete note "%s"?') % item.note.title,
            QMessageBox.Yes | QMessageBox.No
        )
        if msgBox.exec_() == QMessageBox.Yes:
            self.app.provider.delete_note(item.note.id)
            self.app.send_notify(self.tr('Note "%s" deleted!') % item.note.title)
            self.notebook_selected(self.ui.notebooksList.currentIndex())

    @Slot(QPoint)
    def notebook_context_menu(self, pos):
        index = self.ui.notebooksList.currentIndex()
        item = self.notebooksModel.itemFromIndex(index)
        if hasattr(item, 'notebook'):
            menu = QMenu(self.ui.notebooksList)
            menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_notebook)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_notebook)
            menu.exec_(self.ui.notebooksList.mapToGlobal(pos))
        if hasattr(item, 'stack'):
            menu = QMenu(self.ui.notebooksList)
            menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Rename'), self.rename_stack)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_stack)
            menu.exec_(self.ui.notebooksList.mapToGlobal(pos))

    @Slot(QPoint)
    def tag_context_menu(self, pos):
        index = self.ui.tagsList.currentIndex()
        item = self.tagsModel.itemFromIndex(index)
        if hasattr(item, 'tag'):
            menu = QMenu(self.ui.tagsList)
            menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_tag)
            menu.exec_(self.ui.tagsList.mapToGlobal(pos))

    @Slot(QPoint)
    def note_context_menu(self, pos):
        menu = QMenu(self.ui.notesList)
        menu.addAction(QIcon.fromTheme('gtk-edit'), self.tr('Edit'), self.edit_note)
        menu.addAction(QIcon.fromTheme('gtk-delete'), self.tr('Remove'), self.remove_note)
        menu.exec_(self.ui.notesList.mapToGlobal(pos))

    def _reload_data(self):
        self._reload_notebooks_list(self._current_notebook)
        self._reload_tags_list(self._current_tag)
        self._mark_note_selected(self._current_note)

    def _reload_notebooks_list(self, select_notebook_id=None):
        # TODO could enable selecting an already selected stack
        self.notebooksModel.clear()
        root = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Notes'))
        self.notebooksModel.appendRow(root)
        selected_item = root

        stacks = {}
        for notebook_struct in self.app.provider.list_notebooks():
            notebook = Notebook.from_tuple(notebook_struct)
            count = self.app.provider.get_notebook_notes_count(notebook.id)
            item = QNotebookItem(notebook, count)

            if(notebook.stack == ''):
                root.appendRow(item)
            else:
                if(notebook.stack not in stacks.keys()):
                    stack = QStandardItem(QIcon.fromTheme('user-home'), notebook.stack)
                    stack.stack = notebook.stack
                    root.appendRow(stack)
                    stacks[notebook.stack] = stack

                stacks[notebook.stack].appendRow(item)

            if select_notebook_id and notebook.id == select_notebook_id:
                selected_item = item

        self.ui.notebooksList.expandAll()

        if selected_item and not select_notebook_id == SELECT_NONE:
            index = self.notebooksModel.indexFromItem(selected_item)
            self.ui.notebooksList.setCurrentIndex(index)
            self.notebook_selected(index)

    def _notebook_new_name(self, title, exclude='', oldStack=''):
        names = map(lambda nb: Notebook.from_tuple(nb).name, self.app.provider.list_notebooks())
        try:
            names.remove(exclude)
        except ValueError:
            pass
        name, status = QInputDialog.getText(self, title, self.tr('Enter notebook name:'), text=exclude)
        while name in names and status:
            message = self.tr('Notebook with this name already exist. Enter notebook name')
            name, status = QInputDialog.getText(self, title, message)
        if status:
            stack, status = QInputDialog.getText(self, title, self.tr('Enter stack name (empty for no stack):'), text=oldStack)
        else:
            stack = oldStack
        return name, status, stack

    def _stack_new_name(self, title, value=''):
        name, status = QInputDialog.getText(self, title, self.tr('Enter stack name:'), text=value)
        return name, status

    def _reload_tags_list(self, select_tag_id=None):
        # TODO nested tags
        self.tagsModel.clear()
        tagRoot = QStandardItem(QIcon.fromTheme('user-home'), self.tr('All Tags'))
        self.tagsModel.appendRow(tagRoot)
        selected_item = tagRoot

        for tag_struct in self.app.provider.list_tags():
            tag = Tag.from_tuple(tag_struct)
            count = self.app.provider.get_tag_notes_count(tag.id)
            item = QTagItem(tag, count)
            tagRoot.appendRow(item)

            if select_tag_id and tag.id == select_tag_id:
                selected_item = item

        self.ui.tagsList.expandAll()
        if selected_item and not select_tag_id == SELECT_NONE:
            index = self.tagsModel.indexFromItem(selected_item)
            self.ui.tagsList.setCurrentIndex(index)
            self.tag_selected(index)

    def _mark_note_selected(self, index):
        if index:
            self.ui.notesList.setCurrentIndex(index)