From ab33f4eb29e334a8feecebe5899db72f95da68a7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 18 May 2010 09:23:08 -0600 Subject: [PATCH] Allow user to set text alignment for columns. Cleanup and fix default column alignment --- src/calibre/gui2/library/models.py | 26 +++++++-- src/calibre/gui2/library/views.py | 87 +++++++++++++++++++++--------- 2 files changed, 84 insertions(+), 29 deletions(-) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index f5fbc822b8..802e23e90c 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -30,6 +30,9 @@ def human_readable(size, precision=1): TIME_FMT = '%d %b %Y' +ALIGNMENT_MAP = {'left': Qt.AlignLeft, 'right': Qt.AlignRight, 'center': + Qt.AlignHCenter} + class BooksModel(QAbstractTableModel): # {{{ about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted') @@ -64,6 +67,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.last_search = '' # The last search performed on this model self.column_map = [] self.headers = {} + self.alignment_map = {} self.buffer_size = buffer self.cover_cache = None self.bool_yes_icon = QIcon(I('ok.svg')) @@ -72,6 +76,19 @@ class BooksModel(QAbstractTableModel): # {{{ self.device_connected = False self.read_config() + def change_alignment(self, colname, alignment): + if colname in self.column_map and alignment in ('left', 'right', 'center'): + old = self.alignment_map.get(colname, 'left') + if old == alignment: + return + self.alignment_map.pop(colname, None) + if alignment != 'left': + self.alignment_map[colname] = alignment + col = self.column_map.index(colname) + for row in xrange(self.rowCount(QModelIndex())): + self.dataChanged.emit(self.index(row, col), self.index(row, + col)) + def is_custom_column(self, cc_label): return cc_label in self.custom_columns @@ -593,14 +610,17 @@ class BooksModel(QAbstractTableModel): # {{{ # the column map does not accurately represent the screen. In these cases, # we will get asked to display columns we don't know about. Must test for this. if col >= len(self.column_to_dc_map): - return None + return NONE if role in (Qt.DisplayRole, Qt.EditRole): return self.column_to_dc_map[col](index.row()) elif role == Qt.DecorationRole: if self.column_to_dc_decorator_map[col] is not None: return self.column_to_dc_decorator_map[index.column()](index.row()) - #elif role == Qt.TextAlignmentRole and self.column_map[index.column()] in ('size', 'timestamp'): - # return QVariant(Qt.AlignVCenter | Qt.AlignCenter) + elif role == Qt.TextAlignmentRole: + cname = self.column_map[index.column()] + ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname, + 'left')] + return QVariant(ans) #elif role == Qt.ToolTipRole and index.isValid(): # if self.column_map[index.column()] in self.editable_cols: # return QVariant(_("Double click to edit me

")) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 201f473b1e..d3c7be433e 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -59,6 +59,7 @@ class BooksView(QTableView): # {{{ self._model.about_to_be_sorted.connect(self.about_to_be_sorted) self._model.sorting_done.connect(self.sorting_done) + # Column Header Context Menu {{{ def column_header_context_handler(self, action=None, column=None): if not action or not column: return @@ -78,6 +79,9 @@ class BooksView(QTableView): # {{{ self.sortByColumn(idx, Qt.DescendingOrder) elif action == 'defaults': self.apply_state(self.get_default_state()) + elif action.startswith('align_'): + alignment = action.partition('_')[-1] + self._model.change_alignment(column, alignment) self.save_state() @@ -93,14 +97,22 @@ class BooksView(QTableView): # {{{ name, partial(self.column_header_context_handler, action='hide', column=col)) - self.column_header_context_menu.addAction( - _('Sort on column %s (ascending)') % name, + m = self.column_header_context_menu.addMenu( + _('Sort on %s') % name) + m.addAction(_('Ascending'), partial(self.column_header_context_handler, action='ascending', column=col)) - self.column_header_context_menu.addAction( - _('Sort on column %s (descending)') % name, + m.addAction(_('Descending'), partial(self.column_header_context_handler, action='descending', column=col)) + m = self.column_header_context_menu.addMenu( + _('Change text alignment for %s') % name) + for x, t in (('left', _('Left')), ('right', _('Right')), ('center', + _('Center'))): + m.addAction(t, + partial(self.column_header_context_handler, + action='align_'+x, column=col)) + hidden_cols = [self.column_map[i] for i in range(self.column_header.count()) if @@ -120,6 +132,7 @@ class BooksView(QTableView): # {{{ partial(self.column_header_context_handler, action='show', column=col)) + self.column_header_context_menu.addSeparator() self.column_header_context_menu.addAction( _('Restore default layout'), @@ -127,8 +140,9 @@ class BooksView(QTableView): # {{{ action='defaults', column=col)) self.column_header_context_menu.popup(self.column_header.mapToGlobal(pos)) + # }}} - + # Sorting {{{ def about_to_be_sorted(self, idc): selected_rows = [r.row() for r in self.selectionModel().selectedRows()] self.selected_ids = [idc(r) for r in selected_rows] @@ -141,13 +155,9 @@ class BooksView(QTableView): # {{{ for idx in indices: sm.select(idx, sm.Select|sm.Rows) self.selected_ids = [] + # }}} - def scrollContentsBy(self, dx, dy): - # Needed as Qt bug causes headerview to not always update when scrolling - QTableView.scrollContentsBy(self, dx, dy) - if dy != 0: - self.column_header.update() - + # Ondevice column {{{ def set_ondevice_column_visibility(self): m = self._model self.column_header.setSectionHidden(m.column_map.index('ondevice'), @@ -156,6 +166,7 @@ class BooksView(QTableView): # {{{ def set_device_connected(self, is_connected): self._model.set_device_connected(is_connected) self.set_ondevice_column_visibility() + # }}} # Save/Restore State {{{ def get_state(self): @@ -168,6 +179,7 @@ class BooksView(QTableView): # {{{ self.cleanup_sort_history(self.model().sort_history) state['column_positions'] = {} state['column_sizes'] = {} + state['column_alignment'] = self._model.alignment_map for i in range(h.count()): name = cm[i] state['column_positions'][name] = h.visualIndex(i) @@ -211,7 +223,7 @@ class BooksView(QTableView): # {{{ for col, pos in positions.items(): if col in cmap: pmap[pos] = col - for pos in sorted(pmap.keys(), reverse=True): + for pos in sorted(pmap.keys()): col = pmap[pos] idx = cmap[col] current_pos = h.visualIndex(idx) @@ -225,18 +237,28 @@ class BooksView(QTableView): # {{{ if sz < 3: sz = h.sectionSizeHint(cmap[col]) h.resizeSection(cmap[col], sz) + self.apply_sort_history(state.get('sort_history', None)) + for col, alignment in state.get('column_alignment', {}).items(): + self._model.change_alignment(col, alignment) + def get_default_state(self): - old_state = {'hidden_columns': [], + old_state = { + 'hidden_columns': [], 'sort_history':[DEFAULT_SORT], 'column_positions': {}, - 'column_sizes': {}} + 'column_sizes': {}, + 'column_alignment': { + 'size':'center', + 'timestamp':'center', + 'pubdate':'center'}, + } h = self.column_header cm = self.column_map for i in range(h.count()): name = cm[i] - old_state['column_positions'][name] = h.logicalIndex(i) + old_state['column_positions'][name] = i if name != 'ondevice': old_state['column_sizes'][name] = \ max(self.sizeHintForColumn(i), h.sectionSizeHint(i)) @@ -259,9 +281,15 @@ class BooksView(QTableView): # {{{ # }}} - @property - def column_map(self): - return self._model.column_map + # Initialization/Delegate Setup {{{ + + def set_database(self, db): + self.save_state() + self._model.set_database(db) + self.tags_delegate.set_database(db) + self.authors_delegate.set_auto_complete_function(db.all_authors) + self.series_delegate.set_auto_complete_function(db.all_series) + self.publisher_delegate.set_auto_complete_function(db.all_publishers) def database_changed(self, db): for i in range(self.model().columnCount(None)): @@ -299,7 +327,9 @@ class BooksView(QTableView): # {{{ self.restore_state() self.set_ondevice_column_visibility() + #}}} + # Context Menu {{{ def set_context_menu(self, edit_metadata, send_to_device, convert, view, save, open_folder, book_details, delete, similar_menu=None): self.setContextMenuPolicy(Qt.DefaultContextMenu) @@ -324,8 +354,9 @@ class BooksView(QTableView): # {{{ def contextMenuEvent(self, event): self.context_menu.popup(event.globalPos()) event.accept() + # }}} - + # Drag 'n Drop {{{ @classmethod def paths_from_event(cls, event): ''' @@ -354,13 +385,17 @@ class BooksView(QTableView): # {{{ event.accept() self.files_dropped.emit(paths) - def set_database(self, db): - self.save_state() - self._model.set_database(db) - self.tags_delegate.set_database(db) - self.authors_delegate.set_auto_complete_function(db.all_authors) - self.series_delegate.set_auto_complete_function(db.all_series) - self.publisher_delegate.set_auto_complete_function(db.all_publishers) + # }}} + + @property + def column_map(self): + return self._model.column_map + + def scrollContentsBy(self, dx, dy): + # Needed as Qt bug causes headerview to not always update when scrolling + QTableView.scrollContentsBy(self, dx, dy) + if dy != 0: + self.column_header.update() def close(self): self._model.close()