diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index 5d564b5f0c..9ad7dbcc49 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -696,7 +696,7 @@ TagsView QTreeView -
tags.h
+
calibre/gui2/tag_view.h
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py new file mode 100644 index 0000000000..7d9f435fa9 --- /dev/null +++ b/src/calibre/gui2/tag_view.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' +__docformat__ = 'restructuredtext en' + +''' +Browsing book collection by tags. +''' +from PyQt4.Qt import QStandardItemModel, Qt, QTreeView, QStandardItem, \ + QFont, SIGNAL, QSize, QIcon, QPoint, QPixmap +from calibre.gui2 import config + +class TagsView(QTreeView): + + def __init__(self, *args): + QTreeView.__init__(self, *args) + self.setUniformRowHeights(True) + self.setCursor(Qt.PointingHandCursor) + self.setIconSize(QSize(30, 30)) + + def set_database(self, db, match_all, popularity): + self._model = TagsModel(db) + self.popularity = popularity + self.match_all = match_all + self.setModel(self._model) + self.connect(self, SIGNAL('clicked(QModelIndex)'), self.toggle) + self.popularity.setChecked(config['sort_by_popularity']) + self.connect(self.popularity, SIGNAL('stateChanged(int)'), self.sort_changed) + + def sort_changed(self, state): + config.set('sort_by_popularity', state == Qt.Checked) + self.model().refresh() + + def toggle(self, index): + if self._model.toggle(index): + self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), + self._model.tokens(), self.match_all.isChecked()) + + def recount(self, *args): + ci = self.currentIndex() + if not ci.isValid(): + ci = self.indexAt(QPoint(10, 10)) + self.model().refresh() + if ci.isValid(): + self.scrollTo(ci, QTreeView.PositionAtTop) + +class CategoryItem(QStandardItem): + + def __init__(self, category, display_text, tags, icon, font, icon_map): + self.category = category + self.tags = tags + QStandardItem.__init__(self, icon, display_text) + self.setFont(font) + self.setSelectable(False) + self.setSizeHint(QSize(100, 40)) + self.setEditable(False) + for tag in tags: + self.appendRow(TagItem(tag, icon_map)) + +class TagItem(QStandardItem): + + def __init__(self, tag, icon_map): + self.icon_map = icon_map + self.tag = tag + QStandardItem.__init__(self, tag.as_string()) + self.set_icon() + self.setEditable(False) + self.setSelectable(False) + + def toggle(self): + self.tag.state = (self.tag.state + 1)%3 + self.set_icon() + + def set_icon(self): + self.setIcon(self.icon_map[self.tag.state]) + + +class TagsModel(QStandardItemModel): + + categories = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('Tags')] + row_map = ['author', 'series', 'format', 'publisher', 'news', 'tag'] + + def __init__(self, db): + self.cmap = tuple(map(QIcon, [':/images/user_profile.svg', + ':/images/series.svg', ':/images/book.svg', ':/images/publisher.png', + ':/images/news.svg', ':/images/tags.svg'])) + p = QPixmap(30, 30) + p.fill(Qt.transparent) + self.icon_map = [QIcon(p), QIcon(':/images/plus.svg'), + QIcon(':/images/minus.svg')] + QStandardItemModel.__init__(self) + self.db = db + self.ignore_next_search = False + self._data = {} + self.bold_font = QFont() + self.bold_font.setBold(True) + self.refresh() + self.db.add_listener(self.database_changed) + + def database_changed(self, event, ids): + self.refresh() + + def refresh(self): + old_data = self._data + self._data = self.db.get_categories(config['sort_by_popularity']) + for key in old_data.keys(): + for tag in old_data[key]: + try: + index = self._data[key].index(tag) + if index > -1: + self._data[key][index].state = tag.state + except: + continue + self.clear() + root = self.invisibleRootItem() + for r, category in enumerate(self.row_map): + tags = self._data.get(category, []) + root.appendRow(CategoryItem(category, self.categories[r], + self._data[category], self.cmap[r], self.bold_font, self.icon_map)) + #self.reset() + + + def reinit(self, *args, **kwargs): + if not self.ignore_next_search: + for category in self._data.values(): + for tag in category: + tag.state = 0 + self.reset() + self.ignore_next_search = False + + def toggle(self, index): + if index.parent().isValid(): + category = self.row_map[index.parent().row()] + tag = self._data[category][index.row()] + self.invisibleRootItem().child(index.parent().row()).child(index.row()).toggle() + self.ignore_next_search = True + self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index) + return True + return False + + def tokens(self): + ans = [] + for key in self.row_map: + for tag in self._data[key]: + category = key if key != 'news' else 'tag' + if tag.state > 0: + prefix = ' not ' if tag.state == 2 else '' + ans.append('%s%s:"%s"'%(prefix, category, tag)) + return ans + + diff --git a/src/calibre/gui2/tags.py b/src/calibre/gui2/tags.py deleted file mode 100644 index a6772a3b44..0000000000 --- a/src/calibre/gui2/tags.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' -__docformat__ = 'restructuredtext en' - -''' -Browsing book collection by tags. -''' -from PyQt4.Qt import QAbstractItemModel, Qt, QVariant, QTreeView, QModelIndex, \ - QFont, SIGNAL, QSize, QColor, QIcon, QPoint -from calibre.gui2 import config -NONE = QVariant() - -class TagsView(QTreeView): - - def __init__(self, *args): - QTreeView.__init__(self, *args) - self.setUniformRowHeights(True) - self.setCursor(Qt.PointingHandCursor) - self.setIconSize(QSize(30, 30)) - - def set_database(self, db, match_all, popularity): - self._model = TagsModel(db) - self.popularity = popularity - self.match_all = match_all - self.setModel(self._model) - self.connect(self, SIGNAL('clicked(QModelIndex)'), self.toggle) - self.popularity.setChecked(config['sort_by_popularity']) - self.connect(self.popularity, SIGNAL('stateChanged(int)'), self.sort_changed) - - def sort_changed(self, state): - config.set('sort_by_popularity', state == Qt.Checked) - self.model().refresh() - - def toggle(self, index): - if self._model.toggle(index): - self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), - self._model.tokens(), self.match_all.isChecked()) - - def recount(self, *args): - ci = self.currentIndex() - if not ci.isValid(): - ci = self.indexAt(QPoint(10, 10)) - self.model().refresh() - if ci.isValid(): - self.scrollTo(ci, QTreeView.PositionAtTop) - -class TagsModel(QAbstractItemModel): - - categories = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('Tags')] - row_map = {0: 'author', 1:'series', 2:'format', 3:'publisher', 4:'news', 5:'tag'} - - def __init__(self, db): - QAbstractItemModel.__init__(self) - self.db = db - self.ignore_next_search = False - self._data = {} - self.refresh() - self.bold_font = QFont() - self.bold_font.setBold(True) - self.bold_font = QVariant(self.bold_font) - self.status_map = [QColor(200,200,200, 0), QIcon(':/images/plus.svg'), QIcon(':/images/minus.svg')] - self.status_map = list(map(QVariant, self.status_map)) - self.cmap = [QIcon(':/images/user_profile.svg'), QIcon(':/images/series.svg'), QIcon(':/images/book.svg'), QIcon(':/images/publisher.png'), QIcon(':/images/news.svg'), QIcon(':/images/tags.svg')] - self.cmap = list(map(QVariant, self.cmap)) - self.db.add_listener(self.database_changed) - - def database_changed(self, event, ids): - self.refresh() - - def refresh(self): - old_data = self._data - self._data = self.db.get_categories(config['sort_by_popularity']) - for key in old_data.keys(): - for tag in old_data[key]: - try: - index = self._data[key].index(tag) - if index > -1: - self._data[key][index].state = tag.state - except: - continue - self.reset() - - def reinit(self, *args, **kwargs): - if not self.ignore_next_search: - for category in self._data.values(): - for tag in category: - tag.state = 0 - self.reset() - self.ignore_next_search = False - - def toggle(self, index): - if index.parent().isValid(): - category = self.row_map[index.parent().row()] - tag = self._data[category][index.row()] - tag.state = (tag.state + 1)%3 - self.ignore_next_search = True - self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index) - return True - return False - - def tokens(self): - ans = [] - for key in self.row_map.values(): - for tag in self._data[key]: - category = key if key != 'news' else 'tag' - if tag.state > 0: - prefix = ' not ' if tag.state == 2 else '' - ans.append('%s%s:"%s"'%(prefix, category, tag)) - return ans - - def index(self, row, col, parent=QModelIndex()): - if parent.isValid(): - if parent.parent().isValid(): # parent is a tag - return QModelIndex() - try: - category = self.row_map[parent.row()] - except KeyError: - return QModelIndex() - if col == 0 and row < len(self._data[category]): - return self.createIndex(row, col, parent.row()) - return QModelIndex() - if col == 0 and row < len(self.categories): - return self.createIndex(row, col, -1) - return QModelIndex() - - def parent(self, index): - if not index.isValid() or index.internalId() < 0: - return QModelIndex() - return self.createIndex(index.internalId(), 0, -1) - - def rowCount(self, parent): - if not parent or not parent.isValid(): - return len(self.categories) - if not parent.parent().isValid(): - return len(self._data[self.row_map[parent.row()]]) - return 0 - - def columnCount(self, parent): - return 1 - - def flags(self, index): - if not index.isValid(): - return Qt.NoItemFlags - return Qt.ItemIsEnabled - - def category_data(self, index, role): - if role == Qt.DisplayRole: - row = index.row() - return QVariant(self.categories[row]) - if role == Qt.FontRole: - return self.bold_font - if role == Qt.SizeHintRole: - return QVariant(QSize(100, 40)) - if role == Qt.DecorationRole: - return self.cmap[index.row()] - return NONE - - def tag_data(self, index, role): - category = self.row_map[index.parent().row()] - if role == Qt.DisplayRole: - return QVariant(self._data[category][index.row()].as_string()) - if role == Qt.DecorationRole: - return self.status_map[self._data[category][index.row()].state] - return NONE - - - def data(self, index, role): - if not index.parent().isValid(): - return self.category_data(index, role) - if not index.parent().parent().isValid(): - return self.tag_data(index, role) - return NONE diff --git a/src/calibre/linux.py b/src/calibre/linux.py index 34f7abb195..11884a2e4e 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -334,7 +334,9 @@ def post_install(): os.chdir(config_dir) for f in os.listdir('.'): if os.stat(f).st_uid == 0: - os.unlink(f) + os.rmdir(f) if os.path.isdir(f) else os.unlink(f) + if os.stat(config_dir).st_uid == 0: + os.rmdir(config_dir) def binary_install(): manifest = os.path.join(getattr(sys, 'frozen_path'), 'manifest')