Пример #1
0
class WeekViewWidget(SubFrame):
    def __init__(self, parent):
        super(WeekViewWidget, self).__init__("Week", None, parent)

        self._table_model = QStandardItemModel(1, 2, None)
        self._table_model.setHorizontalHeaderLabels(
            [_("# Ord."), _("Customer")])

        self.table_view = LeftRightTableView(self)
        self.table_view.setModel(self._table_model)
        self.table_view.verticalHeader().hide()
        self.table_view.setAlternatingRowColors(True)
        self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.table_view.horizontalHeader().setResizeMode(
            0, QHeaderView.ResizeToContents)
        self.table_view.horizontalHeader().setStretchLastSection(True)

        self.preorder_brush = QBrush(QColor(255, 255, 128))
        self.completed_order_brush = QBrush(QColor(128, 255, 128))

        self.layout().addWidget(self.table_view)

    def _set_last_row(self, data, role):
        for i in range(self._table_model.columnCount()):
            self._table_model.setData(
                self._table_model.index(self._table_model.rowCount() - 1, i),
                data, role)

    def set_data(self, data, base_date):
        self.set_title(date_to_dm(base_date))
        self._table_model.removeRows(0, self._table_model.rowCount())

        current_ndx = 0
        for d in data:
            order, customer_name = d

            number = order.preorder_label
            if order.accounting_label:
                number = order.accounting_label

            self._table_model.appendRow(
                [QStandardItem(str(number)),
                 QStandardItem(customer_name)])
            self._table_model.setData(self._table_model.index(current_ndx, 0),
                                      order.order_id, Qt.UserRole)

            if order.state == OrderStatusType.order_completed:
                self._set_last_row(self.completed_order_brush,
                                   Qt.BackgroundRole)
            elif order.state == OrderStatusType.preorder_definition:
                self._set_last_row(self.preorder_brush, Qt.BackgroundRole)

            current_ndx += 1
        # Not the slightest idea why this works
        # and not resizeColumnsToContents
        for i in range(self._table_model.columnCount()):
            self.table_view.resizeColumnToContents(i)
Пример #2
0
class QuickOrderViewWidget(SubFrame):
    def __init__(self, parent):
        super(QuickOrderViewWidget, self).__init__("Quick view", None, parent)

        self._table_model = QStandardItemModel(1, 2, None)
        self.table_view = QTableView(self)
        self.table_view.setModel(self._table_model)
        self.layout().addWidget(self.table_view)

        self._table_model.setHorizontalHeaderLabels(
            ['Part Nr.', 'Description'])

        self.table_view.verticalHeader().hide()
        headers_view = self.table_view.horizontalHeader()
        headers_view.setResizeMode(0, QHeaderView.ResizeToContents)
        headers_view.setResizeMode(1, QHeaderView.Stretch)

    def selected_order(self, cur, prev):
        if cur.isValid():
            order_id = cur.model().index(cur.row(), 0).data(Qt.UserRole)

            self._table_model.removeRows(0, self._table_model.rowCount())
            row = 0
            for label, description in dao.order_dao.load_quick_view(order_id):
                self._table_model.appendRow(
                    [QStandardItem(label),
                     QStandardItem(description)])
class MainWindow(QMainWindow):

    COLUMNS = ['Name', 'Comment']

    ITEMS = [
        ('Ford', "Don't panic"),
        ('Marvin', "I'm feeling so depressed")
    ]

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setupUi()
        # setup a sample model
        self.model = QStandardItemModel(self)
        self.model.setHorizontalHeaderLabels(self.COLUMNS)
        for row, item in enumerate(self.ITEMS):
            for column, cell in enumerate(item):
                self.model.setItem(row, column, QStandardItem(cell))
        self.proxy = QSortFilterProxyModel(self)
        self.proxy.setSourceModel(self.model)
        # filter all columns (use 0, 1, etc. to only filter by a specific column)
        self.proxy.setFilterKeyColumn(-1)
        # filter text case insensitively
        self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.view.setModel(self.proxy)
        for column, _ in enumerate(self.COLUMNS):
            self.view.resizeColumnToContents(column)
        self.searchBox.textChanged.connect(self.updateFilter)

    def updateFilter(self, text):
        self.proxy.setFilterFixedString(text)

    def setupUi(self):
        centralWidget = QWidget(self)
        centralWidget.setLayout(QVBoxLayout(centralWidget))
        self.view = QTableView(centralWidget)
        self.view.setSortingEnabled(True)
        centralWidget.layout().addWidget(self.view)
        self.searchBox = QLineEdit(centralWidget)
        centralWidget.layout().addWidget(self.searchBox)
        self.setCentralWidget(centralWidget)
        self.searchBox.setFocus()
Пример #4
0
class MainWindow(QMainWindow):

    COLUMNS = ['Name', 'Comment']

    ITEMS = [('Ford', "Don't panic"), ('Marvin', "I'm feeling so depressed")]

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setupUi()
        # setup a sample model
        self.model = QStandardItemModel(self)
        self.model.setHorizontalHeaderLabels(self.COLUMNS)
        for row, item in enumerate(self.ITEMS):
            for column, cell in enumerate(item):
                self.model.setItem(row, column, QStandardItem(cell))
        self.proxy = QSortFilterProxyModel(self)
        self.proxy.setSourceModel(self.model)
        # filter all columns (use 0, 1, etc. to only filter by a specific column)
        self.proxy.setFilterKeyColumn(-1)
        # filter text case insensitively
        self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.view.setModel(self.proxy)
        for column, _ in enumerate(self.COLUMNS):
            self.view.resizeColumnToContents(column)
        self.searchBox.textChanged.connect(self.updateFilter)

    def updateFilter(self, text):
        self.proxy.setFilterFixedString(text)

    def setupUi(self):
        centralWidget = QWidget(self)
        centralWidget.setLayout(QVBoxLayout(centralWidget))
        self.view = QTableView(centralWidget)
        self.view.setSortingEnabled(True)
        centralWidget.layout().addWidget(self.view)
        self.searchBox = QLineEdit(centralWidget)
        centralWidget.layout().addWidget(self.searchBox)
        self.setCentralWidget(centralWidget)
        self.searchBox.setFocus()
Пример #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)

        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
Пример #6
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()
Пример #7
0
class PresenceOverviewWidget(HorsePanel):
    @Slot(QModelIndex)
    def cell_entered(self, ndx):
        employee_id = self._employee_id_on_row(ndx)

        if not employee_id:
            self._show_totals_day_off(None)

        elif employee_id:
            chrono.chrono_start()

            employee = None
            for i in self.employees:
                if i.employee_id == employee_id:
                    employee = i
                    break

            self.detail_subframe.set_title(employee.fullname)
            self._show_totals_day_off(employee_id)

            d = date(
                self.base_date.year, self.base_date.month,
                min(
                    calendar.monthrange(self.base_date.year,
                                        self.base_date.month)[1],
                    max(1, ndx.column())))

            chrono.chrono_click("Retrieveing data")
            tars = dao.task_action_report_dao.get_reports_for_employee_id_on_date(
                employee_id, d)
            work_timetracks = dao.timetrack_dao.all_work_for_employee_date_manual(
                employee_id, d)
            presence_timetracks = dao.timetrack_dao.all_presence_for_employee_date_managed_by_code_full(
                employee_id, d)
            # employee = dao.employee_dao.find_by_id(employee_id)
            special_activities = dao.special_activity_dao.find_on_day(
                employee_id, d)

            chrono.chrono_click("Redrawing")
            self.time_report_view.redraw(datetime(d.year, d.month, d.day, 6,
                                                  0),
                                         tars,
                                         employee_id,
                                         work_timetracks,
                                         presence_timetracks,
                                         special_activities,
                                         view_title=_("Work on {}").format(
                                             date_to_dmy(d, full_month=True)))
            session().close(
            )  # FIXME Put his one line above; but that's tough ! SQLA doesn't help us much here !
            chrono.chrono_click("Session closed")

            # for event_type, duration in self.day_event_totals[employee_id].items():
            #     mainlog.debug("{}{}".format(event_type, duration))

        self._toggle_days_off_actions()

    def _employee_id_on_row(self, row_or_ndx):
        r = row_or_ndx
        if type(r) != int:
            r = row_or_ndx.row()

        return self._table_model.data(self._table_model.index(r, 0),
                                      Qt.UserRole)

    DAY_EVENT_PALETTE = {
        DayEventType.holidays: (Qt.GlobalColor.white, Qt.GlobalColor.red),
        DayEventType.day_off: (Qt.GlobalColor.white, Qt.GlobalColor.darkRed),
        DayEventType.unpaid_day_off:
        (Qt.GlobalColor.black, Qt.GlobalColor.magenta),
        DayEventType.free_day:
        (Qt.GlobalColor.white, Qt.GlobalColor.darkMagenta),
        DayEventType.overtime: (Qt.GlobalColor.black, Qt.GlobalColor.green),
        DayEventType.recuperation:
        (Qt.GlobalColor.white, Qt.GlobalColor.darkGreen),
        DayEventType.unemployment: (Qt.GlobalColor.white, Qt.GlobalColor.blue),
        DayEventType.unemployment_short:
        (Qt.GlobalColor.white, Qt.GlobalColor.darkBlue),
        DayEventType.work_accident: (Qt.GlobalColor.black,
                                     Qt.GlobalColor.yellow),
        DayEventType.sick_leave: (Qt.GlobalColor.white,
                                  Qt.GlobalColor.darkYellow)
    }

    MONTH_EVENT_COLUMN = 2
    YEAR_EVENT_COLUMN = 3

    def _make_total_days_off_panel(self):

        widget = QFrame()
        widget.setObjectName('HorseRegularFrame')

        widget.setFrameShape(QFrame.Panel)
        widget.setFrameShadow(QFrame.Sunken)
        layout = QVBoxLayout()

        #layout.addWidget(QLabel(_("Days off to date")))
        self.day_off_total_duration_labels = dict()
        self.day_off_month_duration_labels = dict()
        self.day_off_labels = dict()

        self._day_off_table_model = QStandardItemModel(10, 3)
        self._day_off_table_model.setHorizontalHeaderLabels(
            [None, None, _("This\nmonth"),
             _("Before")])
        self.day_off_table_view = QTableView(None)
        self.day_off_table_view.setModel(self._day_off_table_model)

        # self.day_off_table_view.setHorizontalHeader(self.headers_view)
        self.day_off_table_view.verticalHeader().hide()
        self.day_off_table_view.setAlternatingRowColors(True)
        self.day_off_table_view.setEditTriggers(
            QAbstractItemView.NoEditTriggers)

        self.day_off_table_view.hide()

        row = 0
        for det in DayEventType.symbols():
            ndx = self._day_off_table_model.index(row, 0)
            self._day_off_table_model.setData(ndx, det.description,
                                              Qt.DisplayRole)

            ndx = self._day_off_table_model.index(row, 1)
            fg, bg = self.DAY_EVENT_PALETTE[det]
            self._day_off_table_model.setData(ndx, QBrush(bg),
                                              Qt.BackgroundRole)
            self._day_off_table_model.setData(ndx, QBrush(fg),
                                              Qt.TextColorRole)
            self._day_off_table_model.setData(ndx,
                                              DayEventType.short_code(det),
                                              Qt.DisplayRole)

            row += 1

        layout.addWidget(self.day_off_table_view)

        grid = QGridLayout()
        self.days_off_layout = grid
        grid.setColumnStretch(3, 1)
        row = 0

        grid.addWidget(QLabel(_('Year')), row, self.YEAR_EVENT_COLUMN)
        grid.addWidget(QLabel(_('Month')), row, self.MONTH_EVENT_COLUMN)
        row += 1

        for det in DayEventType.symbols():
            self.day_off_total_duration_labels[det] = QLabel("-")
            self.day_off_month_duration_labels[det] = QLabel("-")
            self.day_off_labels[det] = QLabel(det.description)

            hlayout = QHBoxLayout()

            sl = QLabel()
            fg, bg = self.DAY_EVENT_PALETTE[det]

            def to_html_rgb(color):
                i = color.red() * 256 * 256 + color.green() * 256 + color.blue(
                )
                return "#{:06X}".format(i)

            p = QPalette()
            p.setColor(QPalette.Window, QColor(bg))
            p.setColor(QPalette.WindowText, QColor(fg))
            sl.setPalette(p)
            sl.setAlignment(Qt.AlignCenter)
            sl.setStyleSheet("border: 2px solid black; background: {}".format(
                to_html_rgb(QColor(bg))))

            t = DayEventType.short_code(det)
            mainlog.debug(t)
            sl.setAutoFillBackground(True)
            sl.setText(t)

            grid.addWidget(sl, row, 0)
            grid.addWidget(self.day_off_labels[det], row, 1)
            grid.addWidget(self.day_off_total_duration_labels[det], row,
                           self.YEAR_EVENT_COLUMN)
            grid.addWidget(self.day_off_month_duration_labels[det], row,
                           self.MONTH_EVENT_COLUMN)

            hlayout.addStretch()

            row += 1

        layout.addLayout(grid)
        layout.addStretch()

        self.day_off_table_view.resizeColumnsToContents()
        # self.day_off_table_view.setMinimumWidth( self.day_off_table_view.width())
        # self.day_off_table_view.resize( self.day_off_table_view.minimumWidth(),
        #                                 self.day_off_table_view.minimumHeight(),)

        widget.setLayout(layout)
        return widget

    def _show_totals_day_off(self, employee_id):

        mainlog.debug("_show_totals_day_off : {}".format(employee_id))

        def form_layout_row_set_visible(layout, row_ndx, is_visible):
            for i in range(layout.columnCount()):
                l = layout.itemAtPosition(row_ndx, i)
                if l and l.widget():
                    l.widget().setVisible(is_visible)

        det_to_show = dict()

        row = 0
        for det in DayEventType.symbols():

            yearly = 0
            if employee_id in self.all_events_in_year:
                if det in self.all_events_in_year[employee_id]:
                    yearly = nice_round(
                        self.all_events_in_year[employee_id][det])

            monthly = 0
            if employee_id in self.day_event_totals:
                if det in self.day_event_totals[employee_id]:
                    monthly = nice_round(
                        self.day_event_totals[employee_id][det])

                # ndx = self._day_off_table_model.index(row,self.YEAR_EVENT_COLUMN)
                # self._day_off_table_model.setData(ndx, v, Qt.DisplayRole)

            if yearly or monthly:
                det_to_show[det] = {'monthly': monthly, 'yearly': yearly}

        if det_to_show:
            # If there are some days spent on some counters, then we display
            # those counters *only*
            mainlog.debug("_show_totals_day_off : showing some events ".format(
                str(det_to_show)))
            row = 0
            for det in DayEventType.symbols():
                if det in det_to_show:
                    monthly = det_to_show[det]['monthly']
                    yearly = det_to_show[det]['yearly']
                    form_layout_row_set_visible(self.days_off_layout, row + 1,
                                                True)
                    self.day_off_total_duration_labels[det].setText(yearly
                                                                    or '-')
                    self.day_off_month_duration_labels[det].setText(monthly
                                                                    or '-')
                else:
                    form_layout_row_set_visible(self.days_off_layout, row + 1,
                                                False)
        else:
            # If there are no days spent on any counter, then we display
            # all counters at the 0 mark.
            mainlog.debug("_show_totals_day_off : showing no event")
            row = 0
            for det in DayEventType.symbols():
                form_layout_row_set_visible(self.days_off_layout, row + 1,
                                            True)
                row += 1

        # self.day_off_table_view.resizeColumnsToContents()

        self.days_off_panel.parent().update()

    @Slot()
    def refresh_action(self):
        global dao

        # mainlog.debug("refresh action started")
        self.hours_per_pers_subframe.set_title(date_to_my(
            self.base_date, True))

        chrono.chrono_start()
        all_events_in_month = people_admin_service.events_for_month(
            self.base_date)

        employee_with_events = [
            event.employee_id for event in all_events_in_month
        ]

        # mainlog.debug(all_events_in_month)

        self.all_events_in_year = people_admin_service.events_for_year(
            self.base_date.year)
        self.all_presences = all_presences = dao.employee_dao.presence_overview_for_month(
            self.base_date)

        all_correction_times = dict()
        for s in dao.month_time_synthesis_dao.load_all_synthesis(
                self.base_date.year, self.base_date.month):
            all_correction_times[s.employee_id] = s.correction_time

        special_activities = dao.special_activity_dao.find_on_month(
            self.base_date)

        employees = list(
            filter(
                lambda e: e.is_active or e.employee_id in all_presences or e.
                employee_id in all_correction_times or e.employee_id in
                special_activities or e.employee_id in employee_with_events,
                dao.employee_dao.list_overview()))
        self.employees = employees

        chrono.chrono_click()

        day_max = calendar.monthrange(self.base_date.year,
                                      self.base_date.month)[1]
        t_start = datetime(self.base_date.year, self.base_date.month, 1)
        t_end = datetime(self.base_date.year, self.base_date.month, day_max,
                         23, 59, 59, 999999)

        self._table_model.setRowCount(len(employees))
        self._table_model.setColumnCount(1 + day_max + 3)

        headers = QStandardItemModel(1, 1 + day_max + 3)

        headers.setHeaderData(0, Qt.Orientation.Horizontal, _("Employee"))
        for i in range(day_max):
            headers.setHeaderData(i + 1, Qt.Orientation.Horizontal,
                                  "{}".format(i + 1))
        headers.setHeaderData(day_max + 1, Qt.Orientation.Horizontal,
                              _("Correction"))
        headers.setHeaderData(day_max + 2, Qt.Orientation.Horizontal,
                              _("Total"))
        headers.setHeaderData(day_max + 3, Qt.Orientation.Horizontal,
                              _("Days off"))

        self.headers_view.setModel(
            headers)  # qt's doc : The view does *not* take ownership
        self.header_model = headers
        self.headers_view.setModel(
            self.header_model)  # qt's doc : The view does *not* take ownership

        # Compute all mondays indices
        monday = 0
        if t_start.weekday() > 0:
            monday = 7 - t_start.weekday()
        all_mondays = []

        while monday < day_max:
            all_mondays.append(monday)
            monday += 7

        today = date.today()

        # mainlog.debug("Running on employees")
        for row in range(self._table_model.rowCount()):

            # Clear the line
            for col in range(0, 32):
                ndx = self._table_model.index(row, col)
                self._table_model.setData(ndx, None, Qt.BackgroundRole)
                self._table_model.setData(ndx, QBrush(Qt.GlobalColor.black),
                                          Qt.TextColorRole)
                self._table_model.setData(ndx, None, Qt.DisplayRole)
                self._table_model.setData(ndx, None, Qt.UserRole)
                self._table_model.setData(ndx, None, Qt.UserRole + 1)
                #     else:
                #         self._table_model.setData(ndx,None,Qt.BackgroundRole)

                # else:
                #     self._table_model.setData(ndx,None,Qt.DisplayRole)
                #     self._table_model.setData(ndx,None,Qt.BackgroundRole)

            # Mark mondays
            for col in all_mondays:
                # col + 1 to account for the employee column
                self._table_model.setData(
                    self._table_model.index(row, col + 1),
                    QBrush(QColor(230, 230, 255)), Qt.BackgroundRole)

            # Mark today
            if today.month == self.base_date.month and today.year == self.base_date.year:
                self._table_model.setData(
                    self._table_model.index(row, today.day),
                    QBrush(QColor(255, 255, 128)), Qt.BackgroundRole)

        row = 0
        for employee in employees:  # employees are sorted

            self._table_model.setData(self._table_model.index(row, 0),
                                      employee.fullname,
                                      Qt.DisplayRole)  # FIXME Use a delegate
            self._table_model.setData(self._table_model.index(row, 0),
                                      employee.employee_id,
                                      Qt.UserRole)  # FIXME Use a delegate

            correction = 0
            if employee.employee_id in all_correction_times:
                correction = all_correction_times[employee.employee_id]
                self._table_model.setData(
                    self._table_model.index(row, day_max + 1),
                    duration_to_hm(correction, short_unit=True),
                    Qt.DisplayRole)
            else:
                self._table_model.setData(
                    self._table_model.index(row, day_max + 1), None,
                    Qt.DisplayRole)

            presence = 0
            if employee.employee_id in all_presences and len(
                    all_presences[employee.employee_id]) > 0:
                import functools
                presence = functools.reduce(
                    lambda acc, s: acc + s,
                    all_presences[employee.employee_id], 0)
            presence += correction

            if presence != 0:
                self._table_model.setData(ndx, QBrush(Qt.GlobalColor.black),
                                          Qt.TextColorRole)
                self._table_model.setData(
                    self._table_model.index(row, day_max + 2),
                    duration_to_hm(presence, short_unit=True), Qt.DisplayRole)
            else:
                self._table_model.setData(
                    self._table_model.index(row, day_max + 2), None,
                    Qt.DisplayRole)

            if employee.employee_id in all_presences and len(
                    all_presences[employee.employee_id]) > 0:
                for b in range(len(all_presences[employee.employee_id])):
                    ndx = self._table_model.index(row, b + 1)

                    p = all_presences[employee.employee_id][b]

                    if p > 0:
                        self._table_model.setData(
                            ndx, duration_to_hm(p, short_unit=True),
                            Qt.DisplayRole)
                        self._table_model.setData(ndx, p, Qt.UserRole)

                        if p >= 4 and p <= 8:
                            # Regular work load
                            self._table_model.setData(
                                ndx, QBrush(QColor(192, 255, 192)),
                                Qt.BackgroundRole)
                        elif p > 8 or (p < 4 and p > 0):
                            # Problematic work load
                            self._table_model.setData(
                                ndx, QBrush(QColor(255, 192, 192)),
                                Qt.BackgroundRole)

            if employee.employee_id in special_activities:
                sa_of_employee = special_activities[employee.employee_id]

                for sa in sa_of_employee:
                    start = max(t_start, sa.start_time)
                    end = min(t_end, sa.end_time)

                    for i in range(start.day, end.day + 1):
                        ndx = self._table_model.index(row, i)
                        self._table_model.setData(ndx,
                                                  QBrush(QColor(255, 128, 0)),
                                                  Qt.BackgroundRole)

                # self._table_model.setData(self._table_model.index(row,b+1),Qt.AlignRight | Qt.AlignVCenter,Qt.TextAlignmentRole)
            row += 1

        # Display day events

        employee_id_to_row = dict()  # little accelerator
        for row in range(len(employees)):
            employee_id_to_row[employees[row].employee_id] = row

        # Compute days off totals and show them

        self.day_event_totals = dict([(e.employee_id, dict())
                                      for e in employees])
        for day_event in all_events_in_month:
            # mainlog.debug("employee_id = {}".format(day_event.employee_id))
            # if day_event.employee_id not in self.day_event_totals:
            #    mainlog.debug(self.day_event_totals)

            t = self.day_event_totals[day_event.employee_id]
            if day_event.event_type not in t:
                t[day_event.event_type] = day_event.duration
            else:
                t[day_event.event_type] += day_event.duration

        for employee in employees:  # employees are sorted
            t = self.day_event_totals[employee.employee_id]
            mainlog.debug(t)
            total_off = sum(t.values())
            mainlog.debug(total_off)
            row = employee_id_to_row[employee.employee_id]
            mainlog.debug(row)
            if total_off:
                self._table_model.setData(
                    self._table_model.index(row, day_max + 3),
                    nice_round(total_off), Qt.DisplayRole)
            else:
                self._table_model.setData(
                    self._table_model.index(row, day_max + 3), None,
                    Qt.DisplayRole)

        # Show days off

        for day_event in all_events_in_month:
            row = employee_id_to_row[day_event.employee_id]
            col = day_event.date.day

            fg = bg = None
            if day_event.event_type in self.DAY_EVENT_PALETTE:
                fg, bg = self.DAY_EVENT_PALETTE[day_event.event_type]
            else:
                fg, bg = Qt.GlobalColor.red, Qt.GlobalColor.gray

            ndx = self._table_model.index(row, col)

            self._table_model.setData(ndx, day_event.day_event_id,
                                      Qt.UserRole + 1)

            # The problem here is to nicely blend the fact
            # that you can have a day event mixed with actual work
            # the very same day. Here's a poor man solution.

            active_time = self._table_model.data(ndx, Qt.UserRole)
            if not active_time:
                self._table_model.setData(
                    ndx, DayEventType.short_code(day_event.event_type),
                    Qt.DisplayRole)

                self._table_model.setData(ndx, QBrush(fg), Qt.TextColorRole)
                self._table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole)
            else:
                self._table_model.setData(ndx, QBrush(fg), Qt.TextColorRole)
                self._table_model.setData(ndx, QBrush(bg), Qt.BackgroundRole)

                self._table_model.setData(
                    ndx,
                    duration_to_hm(active_time, short_unit=True) +
                    DayEventType.short_code(day_event.event_type),
                    Qt.DisplayRole)

        chrono.chrono_click()

        #for i in range(len(all_mondays)):
        self.table_view.resizeColumnsToContents()

        # mainlog.debug("Reset selection")
        ndx = self.table_view.currentIndex()

        self.table_view.selectionModel().clear()
        # self.table_view.selectionModel().clearSelection()
        # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select)
        # self.table_view.selectionModel().select( self.table_view.model().index(ndx.row(),ndx.column()), QItemSelectionModel.Select)
        self.table_view.selectionModel().setCurrentIndex(
            self.table_view.model().index(ndx.row(), ndx.column()),
            QItemSelectionModel.Select)

        self.cell_entered(self.table_view.currentIndex())

    @Slot()
    def edit_tars(self):

        employee_id = self._employee_id_on_row(self.table_view.currentIndex())

        d = date(
            self.base_date.year, self.base_date.month,
            min(
                calendar.monthrange(self.base_date.year,
                                    self.base_date.month)[1],
                max(1, ndx.column())))

        dialog = TimeReportingScannerDialog(self)
        dialog.set_data(datetime(d.year, d.month, d.day, 6, 0), employee_id)
        dialog.exec_()

        if dialog.result() == QDialog.Accepted:
            # pub.sendMessage('time_report.changed')
            self.timetrack_changed.emit()
            self.refresh_action()

    @Slot()
    def show_actions(self):
        button = self.action_menu.parent()
        p = button.mapToGlobal(QPoint(0, button.height()))
        self.action_menu.exec_(p)

    @Slot()
    def delete_holidays(self):
        ndx = self.table_view.currentIndex()
        employee_id = self._employee_id_on_row(ndx)
        d = date(self.base_date.year, self.base_date.month, ndx.column())

        if dao.special_activity_dao.delete_by_employee_and_date(
                employee_id, d):
            self.refresh_panel()

    @Slot()
    def create_holidays(self):
        employee_id = self._employee_id_on_row(self.table_view.currentIndex())

        left_col = 1000
        right_col = 0
        for ndx in self.table_view.selectionModel().selectedIndexes():
            c = ndx.column()
            left_col = min(c, left_col)
            right_col = max(c, right_col)

        d_start = date(
            self.base_date.year, self.base_date.month,
            min(
                calendar.monthrange(self.base_date.year,
                                    self.base_date.month)[1], max(1,
                                                                  left_col)))

        d_end = date(
            self.base_date.year, self.base_date.month,
            min(
                calendar.monthrange(self.base_date.year,
                                    self.base_date.month)[1],
                max(1, right_col)))

        dialog = HolidaysDialog(self)

        sa = SpecialActivity()
        sa.employee_id = employee_id
        sa.reporter_id = user_session.user_id
        sa.encoding_date = date.today()
        sa.start_time = datetime(d_start.year, d_start.month, d_start.day, 6,
                                 0)
        sa.end_time = datetime(d_end.year, d_end.month, d_end.day, 14, 0)

        dialog.setup(sa, dao.employee_dao.find_by_id(employee_id).fullname)

        # dialog.set_data(employee,self.base_date,c)
        dialog.exec_()
        if dialog.result() == QDialog.Accepted:
            dao.special_activity_dao.save(sa)
            self.refresh_action()

    @Slot()
    def edit_month_correction(self):
        employee_id = self._employee_id_on_row(self.table_view.currentIndex())

        if employee_id:
            employee = dao.employee_dao.find_by_id(employee_id)
            c = dao.month_time_synthesis_dao.load_correction_time(
                employee_id, self.base_date.year, self.base_date.month)

            dialog = MonthTimeCorrectionDialog(self)
            dialog.set_data(employee.fullname, self.base_date, c)
            dialog.exec_()
            if dialog.result() == QDialog.Accepted:
                c = dao.month_time_synthesis_dao.save(employee_id,
                                                      self.base_date.year,
                                                      self.base_date.month,
                                                      dialog.correction_time)
                self.refresh_action()

    @Slot()
    def month_today(self):
        self.base_date = date.today()
        self.refresh_action()

    @Slot()
    def month_before(self):
        m = self.base_date.month

        if m > 1:
            self.base_date = date(self.base_date.year, m - 1, 1)
        else:
            self.base_date = date(self.base_date.year - 1, 12, 1)
        self.refresh_action()

    @Slot()
    def month_after(self):
        m = self.base_date.month

        if self.base_date.year < date.today().year + 1 \
                or m < date.today().month:
            if m < 12:
                self.base_date = date(self.base_date.year, m + 1, 1)
            else:
                self.base_date = date(self.base_date.year + 1, 1, 1)
            self.refresh_action()

    @Slot()
    def edit_timetrack_no_ndx(self):
        ndx = self.table_view.currentIndex()
        if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0:
            self.edit_timetrack(ndx)
        else:
            showWarningBox(_("Can't edit"),
                           _("You must first select a day/person."))

    timetrack_changed = Signal()

    @Slot(QModelIndex)
    def edit_timetrack(self, ndx):
        global dao
        global user_session

        if ndx.column() >= 1:
            edit_date = date(
                self.base_date.year, self.base_date.month,
                ndx.column())  # +1 already in because of employee's names
            employee_id = self._employee_id_on_row(ndx)

            tars = dao.task_action_report_dao.get_reports_for_employee_id_on_date(
                employee_id, edit_date)

            if len(tars) == 0:

                d = EditTimeTracksDialog(self, dao, edit_date)
                d.set_employee_and_date(employee_id, edit_date)
                d.exec_()
                if d.result() == QDialog.Accepted:
                    self.refresh_action()
                    self.timetrack_changed.emit()
                d.deleteLater()

            else:
                edit_date = datetime(self.base_date.year,
                                     self.base_date.month,
                                     ndx.column(),
                                     hour=6)
                from koi.TimeReportingScanner import TimeReportingScannerDialog
                d = TimeReportingScannerDialog(self)
                d.set_data(edit_date, employee_id)  # or 16
                d.exec_()
                if d.result() == QDialog.Accepted:
                    self.refresh_action()
                    self.timetrack_changed.emit()
                d.deleteLater()

    @Slot()
    def editTaskActionReports(self):
        if not user_session.has_any_roles(['TimeTrackModify']):
            return

        m = self.base_date.month
        ndx = self.table_view.currentIndex()

        if ndx.isValid() and ndx.column() >= 0 and ndx.row() >= 0:
            edit_date = date(
                self.base_date.year, m,
                ndx.column())  # +1 already in because of employee's names
            employee = self._table_model.data(
                self._table_model.index(ndx.row(), 0),
                Qt.UserRole)  # FIXME Use a delegate

            d = EditTaskActionReportsDialog(dao, self, edit_date)
            d.set_employee_date(employee, edit_date)
            d.exec_()
            if d.result() == QDialog.Accepted:
                self.refresh_action()
            d.deleteLater()
        else:
            showWarningBox(_("Can't edit"),
                           _("You must first select a day/person."))

    # @Slot(QModelIndex)
    # def timetrack_changed(self,ndx):

    #     selected_timetrack = self.controller.model.object_at(ndx)

    #     # Update the colors in the timetrack views
    #     # to show what action reports correspond to the
    #     # selected timetrack

    #     self.controller_actions.model.current_timetrack = selected_timetrack
    #     self.controller_actions.model.beginResetModel()
    #     self.controller_actions.model.endResetModel()

    #     # Make sure the first of the action reports is shown in the
    #     # table

    #     action_reports = self.controller_actions.model.objects
    #     for i in range(len(action_reports)-1,-1,-1):
    #         if action_reports[i] and action_reports[i].timetrack == selected_timetrack:
    #             self.controller_actions.view.scrollTo(self.controller_actions.model.index(i,0))
    #             break

    def _make_table_header(self):
        pass

    def __init__(self, parent, find_order_action_slot):
        super(PresenceOverviewWidget, self).__init__(parent)

        self.set_panel_title(_("Presence overview"))
        self.base_date = date.today()

        headers = QStandardItemModel(1, 31 + 3)
        self._table_model = QStandardItemModel(1, 31 + 3, None)

        self.headers_view = QHeaderView(Qt.Orientation.Horizontal, self)
        self.header_model = headers
        self.headers_view.setResizeMode(QHeaderView.ResizeToContents)
        self.headers_view.setModel(
            self.header_model)  # qt's doc : The view does *not* take ownership

        self.table_view = TableViewSignaledEvents(None)
        self.table_view.setModel(self._table_model)

        self.table_view.setHorizontalHeader(self.headers_view)
        self.table_view.verticalHeader().hide()
        self.table_view.setAlternatingRowColors(True)
        self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.table_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.table_view.customContextMenuRequested.connect(
            self.popup_context_menu)

        self.copy_action = QAction(_("Copy order parts"), self.table_view)
        self.copy_action.triggered.connect(self.copy_slot)
        self.copy_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_C))
        self.copy_action.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        self.table_view.addAction(self.copy_action)

        self.select_all_action = QAction(_("Select all"), self.table_view)
        self.select_all_action.triggered.connect(self.select_all_slot)
        self.select_all_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_A))
        self.select_all_action.setShortcutContext(
            Qt.WidgetWithChildrenShortcut)
        self.table_view.addAction(self.select_all_action)

        # self.table_view.setSelectionBehavior(QAbstractItemView.SelectItems)
        # self.table_view.setSelectionMode(QAbstractItemView.SingleSelection)

        navbar = NavBar(self, [(_("Month before"), self.month_before),
                               (_("Today"), self.month_today),
                               (_("Action"), self.show_actions),
                               (_("Month after"), self.month_after),
                               (_("Find"), find_order_action_slot)])

        self.action_menu = QMenu(navbar.buttons[2])
        navbar.buttons[2].setObjectName("specialMenuButton")
        navbar.buttons[4].setObjectName("specialMenuButton")

        self._make_days_off_menu_and_action_group()

        list_actions = [  # (_("Edit"),self.edit_tars, None, None),
            (_("Edit"), self.edit_timetrack_no_ndx, None, None),
            (_("Month correction"), self.edit_month_correction, None,
             [RoleType.modify_monthly_time_track_correction]),
            (self.days_off_menu, None), (self.copy_action, None),
            (self.select_all_action, None)
        ]

        # (_("Insert holidays"),self.create_holidays, None, None),
        # (_("Delete holidays"),self.delete_holidays, None, None) ]

        populate_menu(self.action_menu, self, list_actions)

        # mainlog.debug("tile widget")
        self.title_box = TitleWidget(_("Presence Overview"), self, navbar)
        self.vlayout = QVBoxLayout(self)
        self.vlayout.setObjectName("Vlayout")
        self.vlayout.addWidget(self.title_box)

        self.hours_per_pers_subframe = SubFrame(_("Overview"), self.table_view,
                                                self)
        self.vlayout.addWidget(self.hours_per_pers_subframe)

        self.time_report_view = TimeReportView(self)

        self.days_off_panel = self._make_total_days_off_panel()
        vbox = QVBoxLayout()
        vbox.addWidget(self.days_off_panel)
        vbox.addStretch()
        vbox.setStretch(0, 0)
        vbox.setStretch(1, 1)

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.time_report_view)
        hlayout.addLayout(vbox)
        hlayout.setStretch(0, 1)
        self.detail_subframe = SubFrame(_("Day"), hlayout, self)

        self.vlayout.addWidget(self.detail_subframe)

        self.setLayout(self.vlayout)

        # dbox = QVBoxLayout()
        # dbox.addWidget(QLabel("kjkljkj"))

        # self.total_active_hours = LabeledValue(_("Total activity"))
        # dbox.addWidget(self.total_active_hours)

        # hbox = QHBoxLayout()
        # hbox.addWidget(self.table_view)
        # hbox.addLayout(dbox)

        # self.selection_model = self.table_view.selectionModel()
        # mainlog.debug(m)
        #sm = QItemSelectionModel(self.table_view.model())
        #sm.setModel(self.table_view.model())
        # self.table_view.setSelectionModel(self.selection_model)

        self.table_view.selectionModel().currentChanged.connect(
            self.cell_entered)

        self.table_view.doubleClickedCell.connect(self.edit_timetrack)

    def _selection_to_period(self):
        left_col = 1000
        right_col = 0
        for ndx in self.table_view.selectionModel().selectedIndexes():
            c = ndx.column()
            left_col = min(c, left_col)
            right_col = max(c, right_col)

        d_start = date(
            self.base_date.year, self.base_date.month,
            min(
                calendar.monthrange(self.base_date.year,
                                    self.base_date.month)[1], max(1,
                                                                  left_col)))

        d_end = date(
            self.base_date.year, self.base_date.month,
            min(
                calendar.monthrange(self.base_date.year,
                                    self.base_date.month)[1],
                max(1, right_col)))

        return d_start, d_end

    def _toggle_days_off_actions(self):
        day_max = calendar.monthrange(self.base_date.year,
                                      self.base_date.month)[1]

        ndx = self.table_view.currentIndex()

        can_add = can_remove = False

        if ndx.column() >= 1 and ndx.column() <= day_max:

            day_event_id = ndx.data(Qt.UserRole + 1)

            can_add = True

            # if not day_event_id:
            #     day = ndx.column() - 1
            #
            #     employee_id = self._employee_id_on_row(ndx)
            #     if employee_id in self.all_presences:
            #         if not self.all_presences[employee_id][day]:
            #             can_add = True
            #         else:
            #             can_add = True
            #     else:
            #         can_add = True

            can_remove = day_event_id is not None

        self.days_off_add_submenu.setEnabled(can_add)
        for actions in self.days_off_action_group.actions():
            actions.setEnabled(can_add)

        self.day_off_remove_action.setEnabled(can_remove)

    def _add_day_off(self, action):

        if action.data() != 'Remove':

            day_event_type, day_event_duration = action.data()
            day_event_type = DayEventType.from_str(day_event_type)

            mainlog.debug("selected action {} {}".format(
                day_event_type, day_event_duration))

            ndx = self.table_view.currentIndex()
            day_event_id = ndx.data(Qt.UserRole + 1)

            # if day_event_id:
            #     showWarningBox(_("There's already a day off here"))
            #     return

            employee_id = self._employee_id_on_row(ndx)
            if employee_id in self.all_presences:
                day = ndx.column() - 1
                mainlog.debug("_add_day_off : employee_id={}, day={}".format(
                    employee_id, day))
                mainlog.debug(self.all_presences[employee_id])
                mainlog.debug(type(self.all_presences[employee_id]))

                # if self.all_presences[employee_id][day]:
                #     showWarningBox(_("One can't add day off where there is activity"))
                #     return
            else:
                mainlog.debug(
                    "_add_day_off : employee_id={} not yet known".format(
                        employee_id))

            day_event = DayEvent()
            day_event.employee_id = employee_id
            day_event.event_type = day_event_type

            day, last_day = self._selection_to_period()

            if day_event_duration in (0.5, 1):
                days_durations = [(day, day_event_duration)]
            else:
                days_durations = []
                day, last_day = self._selection_to_period()
                while day <= last_day:
                    days_durations.append(
                        (day, 1))  # One full work day on the day
                    day += timedelta(1)

            # mainlog.debug(days_durations)
            mainlog.debug("Creating day event of type {}".format(
                day_event.event_type))

            try:
                people_admin_service.set_event_on_days(day_event,
                                                       days_durations)
            except ServerException as ex:
                showErrorBox(ex.translated_message)

            self.refresh_action()

    def _remove_day_off(self):
        # Grab all the selected events

        event_ids = []
        for ndx in self.table_view.selectionModel().selectedIndexes():
            day_event_id = ndx.data(Qt.UserRole + 1)
            if day_event_id:
                mainlog.debug("Removing event: {}".format(day_event_id))
                event_ids.append(day_event_id)

        # Remove them

        if event_ids:
            people_admin_service.remove_events(event_ids)
            self.refresh_action()

    def _make_days_off_menu_and_action_group(self):
        # We use a action group to be able to use the data() of actions
        # when action is tigerred

        # Call this ONLY ONCE because there are signal/slot connections.

        self.days_off_menu = QMenu(_("Day off"))
        self.days_off_add_submenu = QMenu(_("Set day off"))
        self.days_off_action_group = QActionGroup(self)

        for det in DayEventType.symbols():
            a_one = QAction(_("Set one day"), self.days_off_action_group)
            a_one.setData((det.value, 1))

            a_half = QAction(_("Set half day"), self.days_off_action_group)
            a_half.setData((det.value, 0.5))

            a_period = QAction(_("Set period"), self.days_off_action_group)
            a_period.setData((det.value, 2))

            self.days_off_action_group.addAction(a_one)
            self.days_off_action_group.addAction(a_half)
            self.days_off_action_group.addAction(a_period)

            m = QMenu(_("Set time off for {}").format(det.description))
            m.addAction(a_one)
            m.addAction(a_half)
            m.addAction(a_period)
            self.days_off_add_submenu.addMenu(m)

        self.days_off_action_group.triggered.connect(self._add_day_off)

        self.day_off_remove_action = QAction(_("Remove day off"), self)
        self.day_off_remove_action.triggered.connect(self._remove_day_off)

        # Now we have the actions, we build the menu

        self.days_off_menu.addMenu(self.days_off_add_submenu)
        self.days_off_menu.addAction(self.day_off_remove_action)

    @Slot(QPoint)
    def popup_context_menu(self, position):
        self.days_off_menu.exec_(QCursor.pos())

    @Slot()
    def select_all_slot(self):
        m = self.table_view.model()
        all = QItemSelection(m.index(0, 0),
                             m.index(m.rowCount() - 1,
                                     m.columnCount() - 1))
        self.table_view.selectionModel().select(all,
                                                QItemSelectionModel.Select)

    @Slot()
    def copy_slot(self):

        # Collect the rows indices

        indices = self.table_view.selectedIndexes()

        if not indices:
            return

        min_row = max_row = indices[0].row()
        min_col = max_col = indices[0].column()

        def min_max(minimum, v, maximum):
            if v < minimum:
                return v, maximum
            elif v > maximum:
                return minimum, v
            else:
                return minimum, maximum

        for ndx in self.table_view.selectedIndexes():
            min_row, max_row = min_max(min_row, ndx.row(), max_row)
            min_col, max_col = min_max(min_col, ndx.column(), max_col)

        mainlog.debug("Copy from {},{} to {},{}".format(
            min_row, min_col, max_row, max_col))

        day_max = calendar.monthrange(self.base_date.year,
                                      self.base_date.month)[1]

        s = ""
        for r in range(min_row, max_row + 1):
            d = []
            for c in range(min_col, max_col + 1):
                ndx = self._table_model.item(r, c)

                if c == 0 or c > day_max:
                    d.append(ndx.data(Qt.DisplayRole) or "")
                else:
                    t = ndx.data(Qt.UserRole + 1)  # Day off
                    if t:
                        t = ndx.data(Qt.DisplayRole)
                    else:
                        hours = ndx.data(Qt.UserRole)  # Activity hours
                        if hours is not None:
                            t = str(hours).replace('.', ',')
                        else:
                            t = ""

                    d.append(t)

            s += "\t".join(d) + u"\n"

        QApplication.clipboard().setText(s)
Пример #8
0
class ReprintDeliverySlipDialog(QDialog):
    def __init__(self, parent, dao):
        super(ReprintDeliverySlipDialog, self).__init__(parent)
        self.dao = dao

        title = _("Print a delivery slip")
        self.setWindowTitle(title)
        top_layout = QVBoxLayout()
        self.title_widget = TitleWidget(title, None)

        self.buttons = QDialogButtonBox()
        self.buttons.addButton(QDialogButtonBox.StandardButton.Cancel)
        self.buttons.addButton(QDialogButtonBox.Ok)

        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel(_("Slip number")))
        self.slip_number = QLineEdit()
        hlayout.addWidget(self.slip_number)
        hlayout.addStretch()

        self.search_results_view = QTableView()
        self.search_results_model = QStandardItemModel()
        self.search_results_model.setHorizontalHeaderLabels(
            [_("Slip Nr"), _("Date"),
             _("Customer"), _("Order")])

        self.search_results_view.setModel(self.search_results_model)
        # self.search_results_view.setHorizontalHeader(self.headers_view)
        self.search_results_view.setEditTriggers(
            QAbstractItemView.NoEditTriggers)

        self.search_results_view.horizontalHeader().setResizeMode(
            1, QHeaderView.ResizeToContents)
        self.search_results_view.horizontalHeader().setResizeMode(
            2, QHeaderView.Stretch)
        self.search_results_view.verticalHeader().hide()

        self.slip_part_view = DeliverySlipViewWidget(self)

        hlayout_results = QHBoxLayout()
        hlayout_results.addWidget(self.search_results_view)
        hlayout_results.addWidget(self.slip_part_view)

        self.search_results_model.removeRows(
            0, self.search_results_model.rowCount())
        delivery_slips = self.dao.delivery_slip_part_dao.find_recent()
        for slip in delivery_slips:
            self.search_results_model.appendRow([
                QStandardItem(str(slip[0])),
                QStandardItem(date_to_dmy(slip[1])),
                QStandardItem(slip[2]),
                QStandardItem(slip[3])
            ])

        top_layout.addWidget(self.title_widget)
        top_layout.addLayout(hlayout)
        top_layout.addLayout(hlayout_results)
        top_layout.addWidget(self.buttons)
        top_layout.setStretch(2, 100)
        self.setLayout(top_layout)

        self.search_results_view.setSelectionBehavior(
            QAbstractItemView.SelectRows)
        self.search_results_view.setSelectionMode(
            QAbstractItemView.SingleSelection)

        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)
        self.search_results_view.activated.connect(self.row_activated)
        self.search_results_view.selectionModel().currentRowChanged.connect(
            self.row_selected)

    @Slot(QModelIndex, QModelIndex)
    def row_selected(self, cur_ndx, prev_ndx):
        slip_id = cur_ndx.model().index(cur_ndx.row(), 0).data()
        self.slip_number.setText(str(slip_id))
        self.slip_part_view.set_delivery_slip_parts(
            dao.delivery_slip_part_dao.load_slip_parts_frozen(slip_id))

    @Slot(QModelIndex)
    def row_activated(self, ndx):
        slip_id = ndx.model().index(ndx.row(), 0).data()
        self.slip_number.setText(str(slip_id))
        self.accept()

    @Slot()
    def accept(self):
        try:
            try:
                slip_id = int(self.slip_number.text())
            except ValueError as e:
                makeErrorBox(
                    _("The delivery slip number {} is not valid").format(
                        self.slip_number.text())).exec_()
                return

            if self.dao.delivery_slip_part_dao.id_exists(slip_id):
                print_delivery_slip(self.dao, slip_id)
            else:
                makeErrorBox(
                    _("The delivery slip {} doesn't exist").format(
                        slip_id)).exec_()
                return
        except Exception as e:
            mainlog.exception(e)
            msgBox = makeErrorBox(_("Something wrong happened while printing"))
            msgBox.exec_()

        return super(ReprintDeliverySlipDialog, self).accept()

    @Slot()
    def reject(self):
        return super(ReprintDeliverySlipDialog, self).reject()
Пример #9
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)
Пример #10
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
Пример #11
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)
Пример #12
0
class MainWindow(QMainWindow):
	def __init__(self, parent = None):
		super().__init__(parent)
		
		self.Logger = private_logger.create('MainWindow.py')
		
		self.Logger.debug('Create MainWindow.')
		self.setAcceptDrops(True)
		self.doLayout()
		self.Logger.debug('Created MainWindow.')

	def dropEvent(self, event):
		self.Logger.debug('Drop down files')
		self.FileListModel.clear()
		source = event.mimeData()
		filepaths = source.urls()
		self.Logger.debug('Drop on %s', filepaths)
		self.FileListModel.appendRow( [QStandardItem(path.toLocalFile()) for path in filepaths] )

	def dragEnterEvent(self, event):
		self.Logger.debug('Came to drop down.')
		mime = event.mimeData()
		if mime.hasUrls():
			self.Logger.debug('Accepted drop down items.')
			event.accept()
		else:
			self.Logger.debug('Rejected drop down items.')
			event.ignore()
		pass

	@Slot()
	def onConvert(self):
		error_files = list()
		index = 0
		while(True):
			item = self.FileListModel.item(index)
			if type(item) is QStandardItem:
				filepath = item.text()
				print(filepath)
				if(filepath == ''):
					continue
				try:
					self.convert(filepath)
				except Exception as e:
					error_files.append(filepath)
					break

			elif item is None or item == 0:
				break

			index += 1
		if(len(error_files) == 0):
			QMessageBox.information(self, self.tr('完了'),	 self.tr('変換が完了しました'))
		else:
			QMessageBox.critical(self, self.tr('エラー'), self.tr('下記のファイルでエラーが発生しました。Safeモードをお試しください。\n' + '\n'.join(error_files)))

	def convert(self, filepath):
		self.Logger.info('Processing {0}.'.format(filepath))

		ppt = None
		if self.SafeModeCheckbox.checkState():
			ppt = gpt.SafePresentation.SafePresentation(filepath)
		else:
			ppt = gpt.Presentation.Presentation(filepath)

		notes = ppt.getNoteTexts()
		filename, ext = os.path.splitext(filepath)
		with open(filename + '.txt', 'w+') as fp:
			for note in notes:
				fp.write('P.{0}\n'.format(note.Page))
				fp.write(note.Text)
				fp.write('\n')

	def doLayout(self):
		central_window = QWidget(self)
		vlayout = QVBoxLayout(central_window)

		self.FileListView = QListView(self)
		self.SafeModeCheckbox = QCheckBox(self.tr('Safeモードで読み込み(エラーが出る場合に使用ください。改行やページ番号等崩れる場合があります。)'), self)
		convert_button = QPushButton(self.tr('変換'), central_window)

		vlayout.addWidget(QLabel(self.tr('ドラッグ&ドロップでファイルを読み込みます'), self))
		vlayout.addWidget(self.FileListView)
		vlayout.addWidget(self.SafeModeCheckbox)
		vlayout.addWidget(convert_button)
		convert_button.clicked.connect(self.onConvert)

		self.FileListModel = QStandardItemModel(self)
		self.FileListModel.setHorizontalHeaderLabels([self.tr('ファイル名')])
		self.FileListView.setModel(self.FileListModel)
		self.FileListView.setAcceptDrops(True)

		self.setCentralWidget(central_window)