home *** CD-ROM | disk | FTP | other *** search
- # Source Generated with Decompyle++
- # File: in.pyc (Python 2.6)
-
- __license__ = 'GPL v3'
- __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
- __docformat__ = 'restructuredtext en'
- import os
- from functools import partial
- from PyQt4.Qt import QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication, QPoint, QPixmap, QUrl, QImage, QPainter, QColor, QRect
- from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate
- from calibre.gui2.library.models import BooksModel, DeviceBooksModel
- from calibre.utils.config import tweaks, prefs
- from calibre.gui2 import error_dialog, gprefs
- from calibre.gui2.library import DEFAULT_SORT
- from calibre.constants import filesystem_encoding
- from calibre import force_unicode
-
- class PreserveSelection(object):
-
- def __init__(self, view):
- self.view = view
- self.selected_ids = []
-
-
- def __enter__(self):
- self.selected_ids = self.view.get_selected_ids()
-
-
- def __exit__(self, *args):
- current = self.view.get_selected_ids()
- if not current:
- self.view.select_rows(self.selected_ids, using_ids = True)
-
-
-
-
- class BooksView(QTableView):
- files_dropped = pyqtSignal(object)
- add_column_signal = pyqtSignal()
-
- def __init__(self, parent, modelcls = BooksModel):
- QTableView.__init__(self, parent)
- self.drag_allowed = True
- self.setDragEnabled(True)
- self.setDragDropOverwriteMode(False)
- self.setDragDropMode(self.DragDrop)
- self.drag_start_pos = None
- self.setAlternatingRowColors(True)
- self.setSelectionBehavior(self.SelectRows)
- self.setShowGrid(False)
- self.setWordWrap(False)
- self.rating_delegate = RatingDelegate(self)
- self.timestamp_delegate = DateDelegate(self)
- self.pubdate_delegate = PubDateDelegate(self)
- self.tags_delegate = TagsDelegate(self)
- self.authors_delegate = TextDelegate(self)
- self.series_delegate = TextDelegate(self)
- self.publisher_delegate = TextDelegate(self)
- self.text_delegate = TextDelegate(self)
- self.cc_text_delegate = CcTextDelegate(self)
- self.cc_bool_delegate = CcBoolDelegate(self)
- self.cc_comments_delegate = CcCommentsDelegate(self)
- self.cc_template_delegate = CcTemplateDelegate(self)
- self.display_parent = parent
- self._model = modelcls(self)
- self.setModel(self._model)
- self.setSelectionBehavior(QAbstractItemView.SelectRows)
- self.setSortingEnabled(True)
- self.selectionModel().currentRowChanged.connect(self._model.current_changed)
- self.preserve_selected_books = PreserveSelection(self)
- self.can_add_columns = True
- self.was_restored = False
- self.column_header = self.horizontalHeader()
- self.column_header.setMovable(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._model.database_changed.connect(self.database_changed)
- hv = self.verticalHeader()
- hv.setClickable(True)
- hv.setCursor(Qt.PointingHandCursor)
- self.selected_ids = []
- self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
- self._model.sorting_done.connect(self.sorting_done)
-
-
- def column_header_context_handler(self, action = None, column = None):
- if not action or not column:
- return None
-
- try:
- idx = self.column_map.index(column)
- except:
- not column
- return None
-
- h = self.column_header
- if action == 'hide':
- h.setSectionHidden(idx, True)
- elif action == 'show':
- h.setSectionHidden(idx, False)
- if h.sectionSize(idx) < 3:
- sz = h.sectionSizeHint(idx)
- h.resizeSection(idx, sz)
-
- elif action == 'ascending':
- self.sortByColumn(idx, Qt.AscendingOrder)
- elif action == 'descending':
- self.sortByColumn(idx, Qt.DescendingOrder)
- elif action == 'defaults':
- self.apply_state(self.get_default_state())
- elif action == 'addcustcol':
- self.add_column_signal.emit()
- elif action.startswith('align_'):
- alignment = action.partition('_')[-1]
- self._model.change_alignment(column, alignment)
-
- self.save_state()
-
-
- def show_column_header_context_menu(self, pos):
- idx = self.column_header.logicalIndexAt(pos)
- if idx > -1 and idx < len(self.column_map):
- col = self.column_map[idx]
- name = unicode(self.model().headerData(idx, Qt.Horizontal, Qt.DisplayRole).toString())
- self.column_header_context_menu = QMenu(self)
- if col != 'ondevice':
- self.column_header_context_menu.addAction(_('Hide column %s') % name, partial(self.column_header_context_handler, action = 'hide', column = col))
-
- m = self.column_header_context_menu.addMenu(_('Sort on %s') % name)
- a = m.addAction(_('Ascending'), partial(self.column_header_context_handler, action = 'ascending', column = col))
- d = m.addAction(_('Descending'), partial(self.column_header_context_handler, action = 'descending', column = col))
- if self._model.sorted_on[0] == col:
- ac = None if self._model.sorted_on[1] == Qt.AscendingOrder else d
- ac.setCheckable(True)
- ac.setChecked(True)
-
- if col not in ('ondevice', 'rating', 'inlibrary'):
- if not self.model().is_custom_column(col) or self.model().custom_columns[col]['datatype'] not in ('bool', 'rating'):
- m = self.column_header_context_menu.addMenu(_('Change text alignment for %s') % name)
- al = self._model.alignment_map.get(col, 'left')
- for x, t in (('left', _('Left')), ('right', _('Right')), ('center', _('Center'))):
- a = m.addAction(t, partial(self.column_header_context_handler, action = 'align_' + x, column = col))
- if al == x:
- a.setCheckable(True)
- a.setChecked(True)
- continue
-
-
- hidden_cols = _[1]
-
- try:
- hidden_cols.remove('ondevice')
- except:
- []
- []
-
- self.column_header_context_menu.addSeparator()
- self.column_header_context_menu.addAction(_('Restore default layout'), partial(self.column_header_context_handler, action = 'defaults', column = col))
- if self.can_add_columns:
- self.column_header_context_menu.addAction(QIcon(I('column.png')), _('Add your own columns'), partial(self.column_header_context_handler, action = 'addcustcol', column = col))
-
- self.column_header_context_menu.popup(self.column_header.mapToGlobal(pos))
-
-
-
- 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 ]
-
-
- def sorting_done(self, indexc):
- self.selected_ids = []
-
-
- def set_ondevice_column_visibility(self):
- m = self._model
- self.column_header.setSectionHidden(m.column_map.index('ondevice'), not (m.device_connected))
-
-
- def set_device_connected(self, is_connected):
- self._model.set_device_connected(is_connected)
- self.set_ondevice_column_visibility()
-
-
- def get_state(self):
- h = self.column_header
- cm = self.column_map
- state = { }
- state['hidden_columns'] = _[1]
- state['sort_history'] = 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)
- if name != 'ondevice':
- state['column_sizes'][name] = h.sectionSize(i)
- continue
- []
-
- return state
-
-
- def write_state(self, state):
- db = getattr(self.model(), 'db', None)
- name = unicode(self.objectName())
- if name and db is not None:
- db.prefs.set(name + ' books view state', state)
-
-
-
- def save_state(self):
- if len(self.column_map) > 0 and self.was_restored:
- state = self.get_state()
- self.write_state(state)
-
-
-
- def cleanup_sort_history(self, sort_history):
- history = []
- for col, order in sort_history:
- if col == 'date':
- col = 'timestamp'
-
- if col in self.column_map:
- if not history or history[0][0] != col:
- history.append([
- col,
- order])
- continue
-
- return history
-
-
- def apply_sort_history(self, saved_history):
- if not saved_history:
- return None
- for col, order in reversed(self.cleanup_sort_history(saved_history)[:3]):
- self.sortByColumn(self.column_map.index(col), order)
-
-
-
- def apply_state(self, state):
- h = self.column_header
- cmap = { }
- hidden = state.get('hidden_columns', [])
- for i, c in enumerate(self.column_map):
- cmap[c] = i
- if c != 'ondevice':
- h.setSectionHidden(i, c in hidden)
- continue
-
- positions = state.get('column_positions', { })
- pmap = { }
- for col, pos in positions.items():
- if col in cmap:
- pmap[pos] = col
- continue
-
- for pos in sorted(pmap.keys()):
- col = pmap[pos]
- idx = cmap[col]
- current_pos = h.visualIndex(idx)
- if current_pos != pos:
- h.moveSection(current_pos, pos)
- continue
-
- sizes = state.get('column_sizes', { })
- for col, size in sizes.items():
- if col in cmap:
- sz = sizes[col]
- if sz < 3:
- sz = h.sectionSizeHint(cmap[col])
-
- h.resizeSection(cmap[col], sz)
- continue
-
- self.apply_sort_history(state.get('sort_history', None))
- for col, alignment in state.get('column_alignment', { }).items():
- self._model.change_alignment(col, alignment)
-
- for i in range(h.count()):
- if not h.isSectionHidden(i) and h.sectionSize(i) < 3:
- sz = h.sectionSizeHint(i)
- h.resizeSection(i, sz)
- continue
-
-
-
- def get_default_state(self):
- old_state = {
- 'hidden_columns': [],
- 'sort_history': [
- DEFAULT_SORT],
- 'column_positions': { },
- '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] = i
- if name != 'ondevice':
- old_state['column_sizes'][name] = min(350, max(self.sizeHintForColumn(i), h.sectionSizeHint(i)))
- if name == 'timestamp':
- old_state['column_sizes'][name] += 12
-
- name == 'timestamp'
-
- return old_state
-
-
- def get_old_state(self):
- ans = None
- name = unicode(self.objectName())
- if name:
- name += ' books view state'
- db = getattr(self.model(), 'db', None)
- if db is not None:
- ans = db.prefs.get(name, None)
- if ans is None:
- ans = gprefs.get(name, None)
-
- try:
- del gprefs[name]
- except:
- pass
-
- if ans is not None:
- db.prefs[name] = ans
-
-
-
-
- return ans
-
-
- def restore_state(self):
- old_state = self.get_old_state()
- if old_state is None:
- old_state = self.get_default_state()
-
- if tweaks['sort_columns_at_startup'] is not None:
- old_state['sort_history'] = tweaks['sort_columns_at_startup']
-
- self.apply_state(old_state)
- if self.model().rowCount(QModelIndex()) > 0:
- self.resizeRowToContents(0)
- self.verticalHeader().setDefaultSectionSize(self.rowHeight(0))
-
- self.was_restored = True
-
-
- 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)):
- if self.itemDelegateForColumn(i) in (self.rating_delegate, self.timestamp_delegate, self.pubdate_delegate):
- self.setItemDelegateForColumn(i, self.itemDelegate())
- continue
-
- cm = self.column_map
- for colhead in cm:
- if self._model.is_custom_column(colhead):
- cc = self._model.custom_columns[colhead]
- if cc['datatype'] == 'datetime':
- delegate = CcDateDelegate(self)
- delegate.set_format(cc['display'].get('date_format', ''))
- self.setItemDelegateForColumn(cm.index(colhead), delegate)
- elif cc['datatype'] == 'comments':
- self.setItemDelegateForColumn(cm.index(colhead), self.cc_comments_delegate)
- elif cc['datatype'] in ('text', 'series'):
- if cc['is_multiple']:
- self.setItemDelegateForColumn(cm.index(colhead), self.tags_delegate)
- else:
- self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
- elif cc['datatype'] in ('int', 'float'):
- self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
- elif cc['datatype'] == 'bool':
- self.setItemDelegateForColumn(cm.index(colhead), self.cc_bool_delegate)
- elif cc['datatype'] == 'rating':
- self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate)
- elif cc['datatype'] == 'composite':
- self.setItemDelegateForColumn(cm.index(colhead), self.cc_template_delegate)
-
- cc['datatype'] == 'datetime'
- dattr = colhead + '_delegate'
- delegate = None if hasattr(self, dattr) else 'text'
- self.setItemDelegateForColumn(cm.index(colhead), getattr(self, delegate + '_delegate'))
-
- self.restore_state()
- self.set_ondevice_column_visibility()
-
-
- def set_context_menu(self, menu, edit_collections_action):
- self.setContextMenuPolicy(Qt.DefaultContextMenu)
- self.context_menu = menu
- self.edit_collections_action = edit_collections_action
-
-
- def contextMenuEvent(self, event):
- self.context_menu.popup(event.globalPos())
- event.accept()
-
-
- def paths_from_event(cls, event):
- md = event.mimeData()
- if md.hasFormat('text/uri-list') and not md.hasFormat('application/calibre+from_library'):
- urls = [ unicode(u.toLocalFile()) for u in md.urls() ]
- return _[2]
-
- paths_from_event = classmethod(paths_from_event)
-
- def drag_icon(self, cover, multiple):
- cover = cover.scaledToHeight(120, Qt.SmoothTransformation)
- if multiple:
- base_width = cover.width()
- base_height = cover.height()
- base = QImage(base_width + 21, base_height + 21, QImage.Format_ARGB32_Premultiplied)
- base.fill(QColor(255, 255, 255, 0).rgba())
- p = QPainter(base)
- rect = QRect(20, 0, base_width, base_height)
- p.fillRect(rect, QColor('white'))
- p.drawRect(rect)
- rect.moveLeft(10)
- rect.moveTop(10)
- p.fillRect(rect, QColor('white'))
- p.drawRect(rect)
- rect.moveLeft(0)
- rect.moveTop(20)
- p.fillRect(rect, QColor('white'))
- p.save()
- p.setCompositionMode(p.CompositionMode_SourceAtop)
- p.drawImage(rect.topLeft(), cover)
- p.restore()
- p.drawRect(rect)
- p.end()
- cover = base
-
- return QPixmap.fromImage(cover)
-
-
- def drag_data(self):
- m = self.model()
- db = m.db
- rows = self.selectionModel().selectedRows()
- selected = map(m.id, rows)
- ids = ' '.join(map(str, selected))
- md = QMimeData()
- md.setData('application/calibre+from_library', ids)
- fmt = prefs['output_format']
-
- def url_for_id(i):
- ans = db.format_abspath(i, fmt, index_is_id = True)
- if ans is None:
- fmts = db.formats(i, index_is_id = True)
- if fmts:
- fmts = fmts.split(',')
- else:
- fmts = []
- for f in fmts:
- ans = db.format_abspath(i, f, index_is_id = True)
- if ans is not None:
- break
- continue
-
-
- if ans is None:
- ans = db.abspath(i, index_is_id = True)
-
- return QUrl.fromLocalFile(ans)
-
- []([ url_for_id(i) for i in selected ])
- drag = QDrag(self)
- drag.setMimeData(md)
- cover = self.drag_icon(m.cover(self.currentIndex().row()), len(selected) > 1)
- drag.setHotSpot(QPoint(-15, -15))
- drag.setPixmap(cover)
- return drag
-
-
- def event_has_mods(self, event = None):
- mods = None if event is not None else QApplication.keyboardModifiers()
- if not mods & Qt.ControlModifier:
- pass
- return mods & Qt.ShiftModifier
-
-
- def mousePressEvent(self, event):
- if event.button() == Qt.LeftButton and not self.event_has_mods():
- self.drag_start_pos = event.pos()
-
- return QTableView.mousePressEvent(self, event)
-
-
- def mouseMoveEvent(self, event):
- if not self.drag_allowed:
- return None
- if self.drag_start_pos is None:
- return QTableView.mouseMoveEvent(self, event)
- if self.event_has_mods():
- self.drag_start_pos = None
- return None
- if not (event.buttons() & Qt.LeftButton) or (event.pos() - self.drag_start_pos).manhattanLength() < QApplication.startDragDistance():
- return None
- index = self.indexAt(event.pos())
- if not index.isValid():
- return None
- drag = self.drag_data()
- drag.exec_(Qt.CopyAction)
- self.drag_start_pos = None
-
-
- def dragEnterEvent(self, event):
- if int(event.possibleActions() & Qt.CopyAction) + int(event.possibleActions() & Qt.MoveAction) == 0:
- return None
- paths = self.paths_from_event(event)
- if paths:
- event.acceptProposedAction()
-
-
-
- def dragMoveEvent(self, event):
- event.acceptProposedAction()
-
-
- def dropEvent(self, event):
- paths = self.paths_from_event(event)
- event.setDropAction(Qt.CopyAction)
- event.accept()
- self.files_dropped.emit(paths)
-
-
- def column_map(self):
- return self._model.column_map
-
- column_map = property(column_map)
-
- def scrollContentsBy(self, dx, dy):
- QTableView.scrollContentsBy(self, dx, dy)
- if dy != 0:
- self.column_header.update()
-
-
-
- def scroll_to_row(self, row):
- if row > -1:
- h = self.horizontalHeader()
- for i in range(h.count()):
- if not h.isSectionHidden(i):
- self.scrollTo(self.model().index(row, i))
- break
- continue
-
-
-
-
- def set_current_row(self, row, select = True):
- pass
-
-
- def select_rows(self, identifiers, using_ids = True, change_current = True, scroll = True):
- rows = []([ _[1] if hasattr(x, 'row') else x for x in identifiers ])
- if using_ids:
- rows = set([])
- identifiers = set(identifiers)
- m = self.model()
- for row in xrange(m.rowCount(QModelIndex())):
- if m.id(row) in identifiers:
- rows.add(row)
- continue
- set
-
-
- rows = list(sorted(rows))
- if rows:
- row = rows[0]
- if change_current:
- self.set_current_row(row, select = False)
-
- if scroll:
- self.scroll_to_row(row)
-
-
- sm = self.selectionModel()
- sel = QItemSelection()
- m = self.model()
- max_col = m.columnCount(QModelIndex()) - 1
- for row in rows:
- sel.select(m.index(row, 0), m.index(row, max_col))
-
- sm.select(sel, sm.ClearAndSelect)
-
-
- def get_selected_ids(self):
- ans = []
- m = self.model()
- for idx in self.selectedIndexes():
- r = idx.row()
- i = m.id(r)
- if i not in ans:
- ans.append(i)
- continue
-
- return ans
-
-
- def close(self):
- self._model.close()
-
-
- def set_editable(self, editable, supports_backloading):
- self._model.set_editable(editable)
-
-
- def connect_to_search_box(self, sb, search_done):
- sb.search.connect(self._model.search)
- self._search_done = search_done
- self._model.searched.connect(self.search_done)
-
-
- def connect_to_book_display(self, bd):
- self._model.new_bookdisplay_data.connect(bd)
-
-
- def search_done(self, ok):
- self._search_done(self, ok)
-
-
- def row_count(self):
- return self._model.count()
-
-
-
- class DeviceBooksView(BooksView):
-
- def __init__(self, parent):
- BooksView.__init__(self, parent, DeviceBooksModel)
- self.can_add_columns = False
- self.columns_resized = False
- self.resize_on_select = False
- self.rating_delegate = None
- for i in range(10):
- self.setItemDelegateForColumn(i, TextDelegate(self))
-
- self.setDragDropMode(self.NoDragDrop)
- self.setAcceptDrops(False)
-
-
- def drag_data(self):
- m = self.model()
- rows = self.selectionModel().selectedRows()
- paths = _[1]
- md = QMimeData()
- md.setData('application/calibre+from_device', 'dummy')
- []([ QUrl.fromLocalFile(p) for p in paths ])
- drag = QDrag(self)
- drag.setMimeData(md)
- cover = self.drag_icon(m.cover(self.currentIndex().row()), len(paths) > 1)
- drag.setHotSpot(QPoint(-15, -15))
- drag.setPixmap(cover)
- return drag
-
-
- def contextMenuEvent(self, event):
- if callable(getattr(self._model.db, 'supports_collections', None)) and self._model.db.supports_collections():
- pass
- edit_collections = prefs['manage_device_metadata'] == 'manual'
- self.edit_collections_action.setVisible(edit_collections)
- self.context_menu.popup(event.globalPos())
- event.accept()
-
-
- def get_old_state(self):
- ans = None
- name = unicode(self.objectName())
- if name:
- name += ' books view state'
- ans = gprefs.get(name, None)
-
- return ans
-
-
- def write_state(self, state):
- name = unicode(self.objectName())
- if name:
- gprefs.set(name + ' books view state', state)
-
-
-
- def set_database(self, db):
- self._model.set_database(db)
- self.restore_state()
-
-
- def resizeColumnsToContents(self):
- QTableView.resizeColumnsToContents(self)
- self.columns_resized = True
-
-
- def connect_dirtied_signal(self, slot):
- self._model.booklist_dirtied.connect(slot)
-
-
- def connect_upload_collections_signal(self, func = None, oncard = None):
- self._model.upload_collections.connect(partial(func, view = self, oncard = oncard))
-
-
- def dropEvent(self, *args):
- error_dialog(self, _('Not allowed'), _('Dropping onto a device is not supported. First add the book to the calibre library.')).exec_()
-
-
- def set_editable(self, editable, supports_backloading):
- self._model.set_editable(editable)
- self.drag_allowed = supports_backloading
-
-
-