diff --git a/src/calibre/ebooks/docx/names.py b/src/calibre/ebooks/docx/names.py index 29a7f0eb81..91b051d691 100644 --- a/src/calibre/ebooks/docx/names.py +++ b/src/calibre/ebooks/docx/names.py @@ -45,8 +45,13 @@ namespaces = { 'dcterms': 'http://purl.org/dc/terms/' } +xpath_cache = {} + def XPath(expr): - return X(expr, namespaces=namespaces) + ans = xpath_cache.get(expr, None) + if ans is None: + xpath_cache[expr] = ans = X(expr, namespaces=namespaces) + return ans def is_tag(x, q): tag = getattr(x, 'tag', x) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 6c856a48b9..1cb095adf0 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -10,9 +10,9 @@ from functools import partial from future_builtins import map from collections import OrderedDict -from PyQt4.Qt import (QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, - QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication, - QPoint, QPixmap, QUrl, QImage, QPainter, QColor, QRect) +from PyQt4.Qt import (QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, QFont, + QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication, QStyle, + QPoint, QPixmap, QUrl, QImage, QPainter, QColor, QRect, QHeaderView, QStyleOptionHeader) from calibre.gui2.library.delegates import (RatingDelegate, PubDateDelegate, TextDelegate, DateDelegate, CompleteDelegate, CcTextDelegate, @@ -25,7 +25,54 @@ from calibre.gui2.library import DEFAULT_SORT from calibre.constants import filesystem_encoding from calibre import force_unicode -class PreserveViewState(object): # {{{ +class HeaderView(QHeaderView): # {{{ + + def __init__(self, *args): + QHeaderView.__init__(self, *args) + self.hover = -1 + self.current_font = QFont(self.font()) + self.current_font.setBold(True) + + def event(self, e): + if e.type() in (e.HoverMove, e.HoverEnter): + self.hover = self.logicalIndexAt(e.pos()) + elif e.type() in (e.Leave, e.HoverLeave): + self.hover = -1 + return QHeaderView.event(self, e) + + def paintSection(self, painter, rect, logical_index): + opt = QStyleOptionHeader() + self.initStyleOption(opt) + opt.rect = rect + opt.section = logical_index + opt.orientation = self.orientation() + opt.textAlignment = Qt.AlignHCenter | Qt.AlignVCenter + model = self.parent().model() + opt.text = model.headerData(logical_index, opt.orientation, Qt.DisplayRole).toString() + if self.isSortIndicatorShown() and self.sortIndicatorSection() == logical_index: + opt.sortIndicator = QStyleOptionHeader.SortDown if self.sortIndicatorOrder() == Qt.AscendingOrder else QStyleOptionHeader.SortUp + opt.text = opt.fontMetrics.elidedText(opt.text, Qt.ElideRight, rect.width() - 4) + if self.isEnabled(): + opt.state |= QStyle.State_Enabled + if self.window().isActiveWindow(): + opt.state |= QStyle.State_Active + if self.hover == logical_index: + opt.state |= QStyle.State_MouseOver + sm = self.selectionModel() + if opt.orientation == Qt.Vertical: + if sm.isRowSelected(logical_index, QModelIndex()): + opt.state |= QStyle.State_Sunken + + painter.save() + if ( + (opt.orientation == Qt.Horizontal and sm.currentIndex().column() == logical_index) or + (opt.orientation == Qt.Vertical and sm.currentIndex().row() == logical_index)): + painter.setFont(self.current_font) + self.style().drawControl(QStyle.CE_Header, opt, painter, self) + painter.restore() +# }}} + +class PreserveViewState(object): # {{{ ''' Save the set of selected books at enter time. If at exit time there are no @@ -72,13 +119,14 @@ class PreserveViewState(object): # {{{ return {x:getattr(self, x) for x in ('selected_ids', 'current_id', 'vscroll', 'hscroll')} def fset(self, state): - for k, v in state.iteritems(): setattr(self, k, v) + for k, v in state.iteritems(): + setattr(self, k, v) self.__exit__() return property(fget=fget, fset=fset) # }}} -class BooksView(QTableView): # {{{ +class BooksView(QTableView): # {{{ files_dropped = pyqtSignal(object) add_column_signal = pyqtSignal() @@ -152,12 +200,16 @@ class BooksView(QTableView): # {{{ # {{{ Column Header setup self.can_add_columns = True self.was_restored = False - self.column_header = self.horizontalHeader() + self.column_header = HeaderView(Qt.Horizontal, self) + self.setHorizontalHeader(self.column_header) self.column_header.setMovable(True) + self.column_header.setClickable(True) self.column_header.sectionMoved.connect(self.save_state) self.column_header.setContextMenuPolicy(Qt.CustomContextMenu) self.column_header.customContextMenuRequested.connect(self.show_column_header_context_menu) self.column_header.sectionResized.connect(self.column_resized, Qt.QueuedConnection) + self.row_header = HeaderView(Qt.Vertical, self) + self.setVerticalHeader(self.row_header) # }}} self._model.database_changed.connect(self.database_changed) @@ -242,7 +294,7 @@ class BooksView(QTableView): # {{{ ac.setCheckable(True) ac.setChecked(True) if col not in ('ondevice', 'inlibrary') and \ - (not self.model().is_custom_column(col) or \ + (not self.model().is_custom_column(col) or self.model().custom_columns[col]['datatype'] not in ('bool', )): m = self.column_header_context_menu.addMenu( @@ -284,7 +336,6 @@ 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( _('Shrink column if it is too wide to fit'), @@ -373,7 +424,7 @@ class BooksView(QTableView): # {{{ h = self.column_header cm = self.column_map state = {} - state['hidden_columns'] = [cm[i] for i in range(h.count()) + state['hidden_columns'] = [cm[i] for i in range(h.count()) if h.isSectionHidden(i) and cm[i] != 'ondevice'] state['last_modified_injected'] = True state['languages_injected'] = True @@ -521,7 +572,6 @@ class BooksView(QTableView): # {{{ db.prefs[name] = ans return ans - def restore_state(self): old_state = self.get_old_state() if old_state is None: @@ -844,7 +894,8 @@ class BooksView(QTableView): # {{{ ids = frozenset(ids) m = self.model() for row in xrange(m.rowCount(QModelIndex())): - if len(row_map) >= len(ids): break + if len(row_map) >= len(ids): + break c = m.id(row) if c in ids: row_map[c] = row @@ -904,7 +955,8 @@ class BooksView(QTableView): # {{{ pass return None def fset(self, val): - if val is None: return + if val is None: + return m = self.model() for row in xrange(m.rowCount(QModelIndex())): if m.id(row) == val: @@ -926,7 +978,8 @@ class BooksView(QTableView): # {{{ column = ci.column() for i in xrange(ci.row()+1, self.row_count()): - if i in selected_rows: continue + if i in selected_rows: + continue try: return self.model().id(self.model().index(i, column)) except: @@ -934,7 +987,8 @@ class BooksView(QTableView): # {{{ # No unselected rows after the current row, look before for i in xrange(ci.row()-1, -1, -1): - if i in selected_rows: continue + if i in selected_rows: + continue try: return self.model().id(self.model().index(i, column)) except: @@ -982,7 +1036,7 @@ class BooksView(QTableView): # {{{ # }}} -class DeviceBooksView(BooksView): # {{{ +class DeviceBooksView(BooksView): # {{{ def __init__(self, parent): BooksView.__init__(self, parent, DeviceBooksModel,