diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 77ac26d6..098e88f0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -731,16 +731,6 @@ class MainWindow(Window): tableWidget.horizontalHeaderItem(3).setText(_translate("MainWindow", "Sent", None)) tableWidget.setUpdatesEnabled(True) - def switchMessagelist( - self, view, account, - folder='inbox', search_option=None, search_line=None - ): - model = view.model() - update = {'folder': folder} - if account: - update['toaddress'] = account - model.updateFilter(update) - # Load messages from database file def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what="", unreadOnly = False): if folder == 'sent': @@ -2867,7 +2857,7 @@ class MainWindow(Window): self.treeWidgetChans ] if currentIndex >= 2 and currentIndex - 2 < len(treeWidgetList): - return treeWidgetList[currentIndex] + return treeWidgetList[currentIndex - 2] def getAccountTreeWidget(self, account): if account.type == AccountMixin.CHAN: @@ -2913,7 +2903,7 @@ class MainWindow(Window): self.textEditInboxMessageChans, ] if currentIndex >= 2 and currentIndex - 2 < len(messagelistList): - return messagelistList[currentIndex] + return messagelistList[currentIndex - 2] def getAccountTextedit(self, account): try: @@ -2935,9 +2925,9 @@ class MainWindow(Window): ] if currentIndex >= 2 and currentIndex - 2 < len(messagelistList): if retObj: - return messagelistList[currentIndex] + return messagelistList[currentIndex - 2] else: - return messagelistList[currentIndex].text().toUtf8().data() + return messagelistList[currentIndex - 2].text().toUtf8().data() def getCurrentSearchOption(self, currentIndex=None): if currentIndex is None: @@ -2947,7 +2937,7 @@ class MainWindow(Window): self.inboxSearchOptionChans, ] if currentIndex >= 2 and currentIndex - 2 < len(messagelistList): - return messagelistList[currentIndex].currentText().toUtf8().data() + return messagelistList[currentIndex - 2].currentText().toUtf8().data() # Group of functions for the Your Identities dialog box def getCurrentItem(self, treeWidget=None): @@ -3348,15 +3338,13 @@ class MainWindow(Window): messageTextedit = self.getCurrentMessageTextedit() if messageTextedit: messageTextedit.setPlainText(QtCore.QString("")) - messagelist = self.getCurrentMessagelist() or self.messagelistInbox + messagelist = self.getCurrentMessagelist() + if not messagelist: + return # ?? account = self.getCurrentAccount() folder = self.getCurrentFolder() treeWidget = self.getCurrentTreeWidget() - if isinstance(messagelist, QtGui.QTableView): - self.switchMessagelist( - messagelist, account, folder, searchOption, searchLine) - return # refresh count indicator self.propagateUnreadCount(account.address if hasattr(account, 'address') else None, folder, treeWidget, 0) self.loadMessagelist(messagelist, account, folder, searchOption, searchLine) @@ -3442,12 +3430,6 @@ class MainWindow(Window): messageTextedit.setTextColor(QtGui.QColor()) messageTextedit.setContent(message) - # def messagelistSelect(self, msg): - # messageTextedit = self.getCurrentMessageTextedit() - # if not messageTextedit: - # return - # messageTextedit.setContent(msg) - def tableWidgetAddressBookItemChanged(self, item): if item.type == AccountMixin.CHAN: self.rerenderComboBoxSendFrom() diff --git a/src/bitmessageqt/main.py b/src/bitmessageqt/main.py index 6a242bfb..2518c348 100644 --- a/src/bitmessageqt/main.py +++ b/src/bitmessageqt/main.py @@ -41,7 +41,6 @@ class Window(settingsmixin.SMainWindow): # Hide all menu action containers for toolbar in ( - self.inboxContextMenuToolbar, self.addressContextMenuToolbarYourIdentities, self.addressContextMenuToolbar, self.addressBookContextMenuToolbar, diff --git a/src/bitmessageqt/main.ui b/src/bitmessageqt/main.ui index f366a33d..028e4e61 100644 --- a/src/bitmessageqt/main.ui +++ b/src/bitmessageqt/main.ui @@ -993,68 +993,6 @@ p, li { white-space: pre-wrap; } - - - TopToolBarArea - - - false - - - - Reply to sender - - - - - Reply to channel - - - - - Add sender to your Address Book - - - - - Add sender to your Blacklist - - - - - Move to Trash - - - - - Undelete - - - - - View HTML code as formatted text - - - - - Save message as... - - - - - Mark Unread - - - - - - - - - - - - TopToolBarArea @@ -1713,38 +1651,6 @@ p, li { white-space: pre-wrap; } - - tableWidgetInboxSubscriptions - customContextMenuRequested(QPoint) - MainWindow - on_context_menuInbox() - - - 20 - 20 - - - 20 - 20 - - - - - tableWidgetInboxChans - customContextMenuRequested(QPoint) - MainWindow - on_context_menuInbox() - - - 20 - 20 - - - 20 - 20 - - - tableWidgetAddressBook customContextMenuRequested(QPoint) @@ -1793,150 +1699,6 @@ p, li { white-space: pre-wrap; } - - actionReply - triggered() - MainWindow - on_action_InboxReply - - - -1 - -1 - - - 20 - 20 - - - - - actionReplyChan - triggered() - MainWindow - on_action_InboxReplyChan - - - -1 - -1 - - - 20 - 20 - - - - - actionAddSenderToAddressBook - triggered() - MainWindow - on_action_InboxAddSenderToAddressBook - - - -1 - -1 - - - 20 - 20 - - - - - actionAddSenderToBlackList - triggered() - MainWindow - on_action_InboxAddSenderToBlackList - - - -1 - -1 - - - 20 - 20 - - - - - actionTrashInboxMessage - triggered() - MainWindow - on_action_InboxTrash - - - -1 - -1 - - - 20 - 20 - - - - - actionUndeleteTrashedMessage - triggered() - MainWindow - on_action_TrashUndelete - - - -1 - -1 - - - 20 - 20 - - - - - actionForceHtml - triggered() - MainWindow - on_action_InboxMessageForceHtml - - - -1 - -1 - - - 20 - 20 - - - - - actionSaveMessageAs - triggered() - MainWindow - on_action_InboxSaveMessageAs - - - -1 - -1 - - - 20 - 20 - - - - - actionMarkUnread - triggered() - MainWindow - on_action_InboxMarkUnread - - - -1 - -1 - - - 20 - 20 - - - actionNew triggered() diff --git a/src/bitmessageqt/messagelist.py b/src/bitmessageqt/messagelist.py index c912c88c..6266b01a 100644 --- a/src/bitmessageqt/messagelist.py +++ b/src/bitmessageqt/messagelist.py @@ -5,6 +5,8 @@ from PyQt4 import QtCore, QtGui import account import foldertree import l10n +import queues +import settingsmixin import widgets from bmconfigparser import BMConfigParser from debug import logger @@ -100,7 +102,7 @@ class InboxTableModel(QtCore.QAbstractTableModel): self.filter = InboxFilter(folder='*', fields=self.fields) self.fields = ','.join(self.fields) self.query = 'SELECT %%s FROM %s ' % self.table - self.sort = ' ORDER BY received DESC' + self.order = ' ORDER BY received DESC' self.column_count = len(self.header) def columnCount(self, parent=QtCore.QModelIndex()): @@ -118,7 +120,7 @@ class InboxTableModel(QtCore.QAbstractTableModel): font = QtGui.QFont() font.setBold( not sqlQuery( - '%s %s %s' % (self.query, self.filter, self.sort) % 'read' + '%s %s %s' % (self.query, self.filter, self.order) % 'read' )[index.row()][0] ) return font @@ -135,7 +137,7 @@ class InboxTableModel(QtCore.QAbstractTableModel): column = self.attributes[index.column() - len(self.header)] result = sqlQuery( - '%s %s %s' % (self.query, self.filter, self.sort) % column + '%s %s %s' % (self.query, self.filter, self.order) % column )[index.row()][0] try: @@ -151,6 +153,15 @@ class InboxTableModel(QtCore.QAbstractTableModel): if role == QtCore.Qt.DisplayRole: return self.header[column]['label'] + def sort(self, column, order): + current_order = self.order + self.order = ' ORDER BY %s %s' % ( + self.header[column]['field'], + 'ASC' if order == QtCore.Qt.AscendingOrder else 'DESC' + ) + if self.order != current_order: + self.emit(QtCore.SIGNAL("layoutChanged()")) + def setRead(self, row, read=True): msgid = self.data(self.createIndex(row, 3)) sqlExecute('UPDATE inbox SET read = ? WHERE msgid = ?', read, msgid) @@ -169,16 +180,24 @@ class InboxTableModel(QtCore.QAbstractTableModel): self.emit(QtCore.SIGNAL("layoutChanged()")) -class InboxMessagelist(QtGui.QTableView): +class InboxMessagelist(settingsmixin.STableView): def __init__(self, parent=None): super(InboxMessagelist, self).__init__(parent) self.setModel(InboxTableModel()) + self.horizontalHeader().setSortIndicator(2, QtCore.Qt.DescendingOrder) + self.loadSettings() + + def contextMenuEvent(self, event): + # model = self.model() + + self.menu.exec_(event.globalPos()) def currentChanged(self, cur_id, prev_id): row = cur_id.row() + logger.warning('currentChanged, row: %r', row) if row and row == prev_id.row(): + logger.warning('returning because row <= 0') return - # what if folder changed? self.model().setRead(row) msg = self.model().getMessage(row) self.emit(QtCore.SIGNAL("messageSelected(QString)"), msg) @@ -194,12 +213,92 @@ class InboxMessagelist(QtGui.QTableView): if cur_folder.address: update['toaddress'] = cur_folder.address self.model().updateFilter(update) + self.selectRow(0) + def init_context(self, control): + self.menu = QtGui.QMenu(self) + self.menu.addAction(control.actionForceHtml) + self.menu.addAction(control.actionMarkUnread) + self.menu.addSeparator() + if control.AccountType == foldertree.AccountMixin.CHAN: + self.menu.addAction(control.actionReplyChan) + self.menu.addAction(control.actionReply) + self.menu.addAction(control.actionAddSenderToAddressbook) + self.menu.addAction(control.actionClipboard) + self.menu.addSeparator() + self.menu.addAction(control.actionAddSenderToBlacklist) + self.menu.addSeparator() + self.menu.addAction(control.actionSaveMessageAs) + + # Menu action handlers + def on_actionReply(self, reply_type=None): + pass + + def on_actionReplyChan(self): + pass + + def on_actionAddSenderToAddressbook(self): + current = self.selectionModel().currentIndex() + model = self.model() + logger.warning( + 'Address at current row: %s', + model.data(model.createIndex(current.row(), 0))) + + def on_actionAddSenderToBlacklist(self): + pass + + def on_actionTrash(self): + selection = self.selectedIndexes() + model = self.model() + for i in selection: + logger.warning( + 'Should trash %s', model.data(model.createIndex(i.row(), 3))) + + def on_actionTrashUndelete(self): + pass + + def on_actionForceHtml(self): + pass + + def on_actionSaveMessageAs(self): + model = self.model() + current = self.selectionModel().currentIndex() + defaultFilename = ''.join( + c for c in model.data(model.createIndex(current.row(), 1)) + if c.isalnum() + ) + '.txt' + filename = QtGui.QFileDialog.getSaveFileName( + self, _translate("MainWindow", "Save As..."), defaultFilename, + "Text files (*.txt);;All files (*.*)") + if filename == '': + return + try: + with open(filename, 'w') as output: + output.write(model.getMessage(current.row())) + except Exception: + logger.exception('Message not saved', exc_info=True) + queues.UISignalQueue.put(( + 'updateStatusBar', + _translate("MainWindow", "Write error.") + )) + + def on_actionMarkUnread(self): + for i in self.selectedIndexes(): + if i.column(): + break + self.model().setRead(i.row(), False) + + def on_actionClipboard(self): + current = self.selectionModel().currentIndex() + clipboard = QtGui.QApplication.clipboard() + clipboard.setText(self.model().data(current)) + class TreeWidgetIdentities(QtGui.QTreeWidget): def __init__(self, parent): super(TreeWidgetIdentities, self).__init__(parent) + self.account_type = None folders = ('inbox', 'new', 'sent', 'trash') accounts = account.getSortedAccounts() + account.getSortedSubscriptions().keys() top = foldertree.Ui_AddressWidget(self, 0, None, 0, True) @@ -214,6 +313,7 @@ class TreeWidgetIdentities(QtGui.QTreeWidget): self.header().setSortIndicator(0, QtCore.Qt.AscendingOrder) def filterAccountType(self, account_type): + self.account_type = account_type header = self.headerItem() if account_type == foldertree.AccountMixin.CHAN: header.setText(0, _translate("MainWindow", "Chans")) @@ -223,9 +323,17 @@ class TreeWidgetIdentities(QtGui.QTreeWidget): header.setIcon(0, QtGui.QIcon(":/newPrefix/images/subscriptions.png")) for i in xrange(self.topLevelItemCount()): item = self.topLevelItem(i) + if not item.type and account_type in ( + foldertree.AccountMixin.ALL, + foldertree.AccountMixin.NORMAL + ): + continue if item.type != account_type: self.setItemHidden(item, True) + def add(self): + logger.warning('Should add an item of type %s', self.account_type) + class MessagelistControl(QtGui.QWidget): @QtCore.pyqtProperty(int) @@ -252,4 +360,15 @@ class MessagelistControl(QtGui.QWidget): self.verticalSplitter.setCollapsible(2, False) self.verticalSplitter.handle(1).setEnabled(False) + if self.AccountType == foldertree.AccountMixin.CHAN: + button_label = _translate("MainWindow", "Add Chan") + elif self.AccountType == foldertree.AccountMixin.SUBSCRIPTION: + button_label = _translate("MainWindow", "Add new Subscription") + else: + button_label = _translate("MainWindow", "New Identity") + self.buttonAdd.setText(button_label) + self.treeWidget.filterAccountType(self.AccountType) + + self.inboxContextMenuToolbar.setVisible(False) + self.messagelistInbox.init_context(self) diff --git a/src/bitmessageqt/messagelistcontrol.ui b/src/bitmessageqt/messagelistcontrol.ui index d9ead7ec..74dd7ee1 100644 --- a/src/bitmessageqt/messagelistcontrol.ui +++ b/src/bitmessageqt/messagelistcontrol.ui @@ -60,7 +60,7 @@ - + 200 @@ -68,7 +68,7 @@ - New + Add @@ -119,9 +119,6 @@ - - Qt::CustomContextMenu - QAbstractItemView::NoEditTriggers @@ -153,7 +150,7 @@ 27 - false + true true @@ -188,6 +185,74 @@ + + + false + + + false + + + + Reply to sender + + + + + Reply to channel + + + + + Add sender to your Addressbook + + + + + Add sender to your Blacklist + + + + + Move to Trash + + + + + Undelete + + + + + View HTML code as formatted text + + + + + Save message as... + + + + + Mark Unread + + + + + Copy to clipboard + + + + + + + + + + + + + @@ -221,85 +286,29 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + buttonAdd + clicked() + treeWidget + add + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - treeWidget @@ -307,22 +316,6 @@ messagelistInbox folderChanged - - - - - - - - - - - - - - - - messagelistInbox messageSelected(QString) @@ -339,5 +332,65 @@ + + actionReply + triggered() + messagelistInbox + on_actionReply + + + actionReplyChan + triggered() + messagelistInbox + on_actionReplyChan + + + actionAddSenderToAddressbook + triggered() + messagelistInbox + on_actionAddSenderToAddressbook + + + actionAddSenderToBlacklist + triggered() + messagelistInbox + on_actionAddSenderToBlacklist + + + actionTrash + triggered() + messagelistInbox + on_actionTrash + + + actionTrashUndelete + triggered() + messagelistInbox + on_actionTrashUndelete + + + actionForceHtml + triggered() + messagelistInbox + on_actionForceHtml + + + actionSaveMessageAs + triggered() + messagelistInbox + on_actionSaveMessageAs + + + actionMarkUnread + triggered() + messagelistInbox + on_actionMarkUnread + + + actionClipboard + triggered() + messagelistInbox + on_actionClipboard + diff --git a/src/bitmessageqt/settingsmixin.py b/src/bitmessageqt/settingsmixin.py index 3d5999e2..4766fda8 100644 --- a/src/bitmessageqt/settingsmixin.py +++ b/src/bitmessageqt/settingsmixin.py @@ -10,6 +10,14 @@ from PyQt4 import QtCore, QtGui class SettingsMixin(object): """Mixin for adding geometry and state saving between restarts.""" + # def __init__(self, *args): + # super(SettingsMixin, self).__init__(*args) + # self.loadSettings() + + # def __del__(self): + # self.saveSettings() + # super(SettingsMixin, self).__del__() + def warnIfNoObjectName(self): """ Handle objects which don't have a name. Currently it ignores them. Objects without a name can't have their @@ -81,6 +89,18 @@ class STableWidget(QtGui.QTableWidget, SettingsMixin): self.writeState(self.horizontalHeader()) +class STableView(QtGui.QTableView, SettingsMixin): + """Table widget with Settings functionality""" + # pylint: disable=too-many-ancestors + def loadSettings(self): + """Load table settings.""" + self.readState(self.horizontalHeader()) + + def saveSettings(self): + """Save table settings.""" + self.writeState(self.horizontalHeader()) + + class SSplitter(QtGui.QSplitter, SettingsMixin): """Splitter with Settings functionality.""" def loadSettings(self):