home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_1315 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  28.0 KB  |  730 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __license__ = 'GPL v3'
  5. __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
  6. __docformat__ = 'restructuredtext en'
  7. import os
  8. from functools import partial
  9. from PyQt4.Qt import QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication, QPoint, QPixmap, QUrl, QImage, QPainter, QColor, QRect
  10. from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate
  11. from calibre.gui2.library.models import BooksModel, DeviceBooksModel
  12. from calibre.utils.config import tweaks, prefs
  13. from calibre.gui2 import error_dialog, gprefs
  14. from calibre.gui2.library import DEFAULT_SORT
  15. from calibre.constants import filesystem_encoding
  16. from calibre import force_unicode
  17.  
  18. class PreserveSelection(object):
  19.     
  20.     def __init__(self, view):
  21.         self.view = view
  22.         self.selected_ids = []
  23.  
  24.     
  25.     def __enter__(self):
  26.         self.selected_ids = self.view.get_selected_ids()
  27.  
  28.     
  29.     def __exit__(self, *args):
  30.         current = self.view.get_selected_ids()
  31.         if not current:
  32.             self.view.select_rows(self.selected_ids, using_ids = True)
  33.         
  34.  
  35.  
  36.  
  37. class BooksView(QTableView):
  38.     files_dropped = pyqtSignal(object)
  39.     add_column_signal = pyqtSignal()
  40.     
  41.     def __init__(self, parent, modelcls = BooksModel):
  42.         QTableView.__init__(self, parent)
  43.         self.drag_allowed = True
  44.         self.setDragEnabled(True)
  45.         self.setDragDropOverwriteMode(False)
  46.         self.setDragDropMode(self.DragDrop)
  47.         self.drag_start_pos = None
  48.         self.setAlternatingRowColors(True)
  49.         self.setSelectionBehavior(self.SelectRows)
  50.         self.setShowGrid(False)
  51.         self.setWordWrap(False)
  52.         self.rating_delegate = RatingDelegate(self)
  53.         self.timestamp_delegate = DateDelegate(self)
  54.         self.pubdate_delegate = PubDateDelegate(self)
  55.         self.tags_delegate = TagsDelegate(self)
  56.         self.authors_delegate = TextDelegate(self)
  57.         self.series_delegate = TextDelegate(self)
  58.         self.publisher_delegate = TextDelegate(self)
  59.         self.text_delegate = TextDelegate(self)
  60.         self.cc_text_delegate = CcTextDelegate(self)
  61.         self.cc_bool_delegate = CcBoolDelegate(self)
  62.         self.cc_comments_delegate = CcCommentsDelegate(self)
  63.         self.cc_template_delegate = CcTemplateDelegate(self)
  64.         self.display_parent = parent
  65.         self._model = modelcls(self)
  66.         self.setModel(self._model)
  67.         self.setSelectionBehavior(QAbstractItemView.SelectRows)
  68.         self.setSortingEnabled(True)
  69.         self.selectionModel().currentRowChanged.connect(self._model.current_changed)
  70.         self.preserve_selected_books = PreserveSelection(self)
  71.         self.can_add_columns = True
  72.         self.was_restored = False
  73.         self.column_header = self.horizontalHeader()
  74.         self.column_header.setMovable(True)
  75.         self.column_header.sectionMoved.connect(self.save_state)
  76.         self.column_header.setContextMenuPolicy(Qt.CustomContextMenu)
  77.         self.column_header.customContextMenuRequested.connect(self.show_column_header_context_menu)
  78.         self._model.database_changed.connect(self.database_changed)
  79.         hv = self.verticalHeader()
  80.         hv.setClickable(True)
  81.         hv.setCursor(Qt.PointingHandCursor)
  82.         self.selected_ids = []
  83.         self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
  84.         self._model.sorting_done.connect(self.sorting_done)
  85.  
  86.     
  87.     def column_header_context_handler(self, action = None, column = None):
  88.         if not action or not column:
  89.             return None
  90.         
  91.         try:
  92.             idx = self.column_map.index(column)
  93.         except:
  94.             not column
  95.             return None
  96.  
  97.         h = self.column_header
  98.         if action == 'hide':
  99.             h.setSectionHidden(idx, True)
  100.         elif action == 'show':
  101.             h.setSectionHidden(idx, False)
  102.             if h.sectionSize(idx) < 3:
  103.                 sz = h.sectionSizeHint(idx)
  104.                 h.resizeSection(idx, sz)
  105.             
  106.         elif action == 'ascending':
  107.             self.sortByColumn(idx, Qt.AscendingOrder)
  108.         elif action == 'descending':
  109.             self.sortByColumn(idx, Qt.DescendingOrder)
  110.         elif action == 'defaults':
  111.             self.apply_state(self.get_default_state())
  112.         elif action == 'addcustcol':
  113.             self.add_column_signal.emit()
  114.         elif action.startswith('align_'):
  115.             alignment = action.partition('_')[-1]
  116.             self._model.change_alignment(column, alignment)
  117.         
  118.         self.save_state()
  119.  
  120.     
  121.     def show_column_header_context_menu(self, pos):
  122.         idx = self.column_header.logicalIndexAt(pos)
  123.         if idx > -1 and idx < len(self.column_map):
  124.             col = self.column_map[idx]
  125.             name = unicode(self.model().headerData(idx, Qt.Horizontal, Qt.DisplayRole).toString())
  126.             self.column_header_context_menu = QMenu(self)
  127.             if col != 'ondevice':
  128.                 self.column_header_context_menu.addAction(_('Hide column %s') % name, partial(self.column_header_context_handler, action = 'hide', column = col))
  129.             
  130.             m = self.column_header_context_menu.addMenu(_('Sort on %s') % name)
  131.             a = m.addAction(_('Ascending'), partial(self.column_header_context_handler, action = 'ascending', column = col))
  132.             d = m.addAction(_('Descending'), partial(self.column_header_context_handler, action = 'descending', column = col))
  133.             if self._model.sorted_on[0] == col:
  134.                 ac = None if self._model.sorted_on[1] == Qt.AscendingOrder else d
  135.                 ac.setCheckable(True)
  136.                 ac.setChecked(True)
  137.             
  138.             if col not in ('ondevice', 'rating', 'inlibrary'):
  139.                 if not self.model().is_custom_column(col) or self.model().custom_columns[col]['datatype'] not in ('bool', 'rating'):
  140.                     m = self.column_header_context_menu.addMenu(_('Change text alignment for %s') % name)
  141.                     al = self._model.alignment_map.get(col, 'left')
  142.                     for x, t in (('left', _('Left')), ('right', _('Right')), ('center', _('Center'))):
  143.                         a = m.addAction(t, partial(self.column_header_context_handler, action = 'align_' + x, column = col))
  144.                         if al == x:
  145.                             a.setCheckable(True)
  146.                             a.setChecked(True)
  147.                             continue
  148.                     
  149.                 
  150.             hidden_cols = _[1]
  151.             
  152.             try:
  153.                 hidden_cols.remove('ondevice')
  154.             except:
  155.                 []
  156.                 []
  157.  
  158.             self.column_header_context_menu.addSeparator()
  159.             self.column_header_context_menu.addAction(_('Restore default layout'), partial(self.column_header_context_handler, action = 'defaults', column = col))
  160.             if self.can_add_columns:
  161.                 self.column_header_context_menu.addAction(QIcon(I('column.png')), _('Add your own columns'), partial(self.column_header_context_handler, action = 'addcustcol', column = col))
  162.             
  163.             self.column_header_context_menu.popup(self.column_header.mapToGlobal(pos))
  164.         
  165.  
  166.     
  167.     def about_to_be_sorted(self, idc):
  168.         selected_rows = [ r.row() for r in self.selectionModel().selectedRows() ]
  169.         self.selected_ids = [ idc(r) for r in selected_rows ]
  170.  
  171.     
  172.     def sorting_done(self, indexc):
  173.         self.selected_ids = []
  174.  
  175.     
  176.     def set_ondevice_column_visibility(self):
  177.         m = self._model
  178.         self.column_header.setSectionHidden(m.column_map.index('ondevice'), not (m.device_connected))
  179.  
  180.     
  181.     def set_device_connected(self, is_connected):
  182.         self._model.set_device_connected(is_connected)
  183.         self.set_ondevice_column_visibility()
  184.  
  185.     
  186.     def get_state(self):
  187.         h = self.column_header
  188.         cm = self.column_map
  189.         state = { }
  190.         state['hidden_columns'] = _[1]
  191.         state['sort_history'] = self.cleanup_sort_history(self.model().sort_history)
  192.         state['column_positions'] = { }
  193.         state['column_sizes'] = { }
  194.         state['column_alignment'] = self._model.alignment_map
  195.         for i in range(h.count()):
  196.             name = cm[i]
  197.             state['column_positions'][name] = h.visualIndex(i)
  198.             if name != 'ondevice':
  199.                 state['column_sizes'][name] = h.sectionSize(i)
  200.                 continue
  201.             []
  202.         
  203.         return state
  204.  
  205.     
  206.     def write_state(self, state):
  207.         db = getattr(self.model(), 'db', None)
  208.         name = unicode(self.objectName())
  209.         if name and db is not None:
  210.             db.prefs.set(name + ' books view state', state)
  211.         
  212.  
  213.     
  214.     def save_state(self):
  215.         if len(self.column_map) > 0 and self.was_restored:
  216.             state = self.get_state()
  217.             self.write_state(state)
  218.         
  219.  
  220.     
  221.     def cleanup_sort_history(self, sort_history):
  222.         history = []
  223.         for col, order in sort_history:
  224.             if col == 'date':
  225.                 col = 'timestamp'
  226.             
  227.             if col in self.column_map:
  228.                 if not history or history[0][0] != col:
  229.                     history.append([
  230.                         col,
  231.                         order])
  232.                     continue
  233.         
  234.         return history
  235.  
  236.     
  237.     def apply_sort_history(self, saved_history):
  238.         if not saved_history:
  239.             return None
  240.         for col, order in reversed(self.cleanup_sort_history(saved_history)[:3]):
  241.             self.sortByColumn(self.column_map.index(col), order)
  242.         
  243.  
  244.     
  245.     def apply_state(self, state):
  246.         h = self.column_header
  247.         cmap = { }
  248.         hidden = state.get('hidden_columns', [])
  249.         for i, c in enumerate(self.column_map):
  250.             cmap[c] = i
  251.             if c != 'ondevice':
  252.                 h.setSectionHidden(i, c in hidden)
  253.                 continue
  254.         
  255.         positions = state.get('column_positions', { })
  256.         pmap = { }
  257.         for col, pos in positions.items():
  258.             if col in cmap:
  259.                 pmap[pos] = col
  260.                 continue
  261.         
  262.         for pos in sorted(pmap.keys()):
  263.             col = pmap[pos]
  264.             idx = cmap[col]
  265.             current_pos = h.visualIndex(idx)
  266.             if current_pos != pos:
  267.                 h.moveSection(current_pos, pos)
  268.                 continue
  269.         
  270.         sizes = state.get('column_sizes', { })
  271.         for col, size in sizes.items():
  272.             if col in cmap:
  273.                 sz = sizes[col]
  274.                 if sz < 3:
  275.                     sz = h.sectionSizeHint(cmap[col])
  276.                 
  277.                 h.resizeSection(cmap[col], sz)
  278.                 continue
  279.         
  280.         self.apply_sort_history(state.get('sort_history', None))
  281.         for col, alignment in state.get('column_alignment', { }).items():
  282.             self._model.change_alignment(col, alignment)
  283.         
  284.         for i in range(h.count()):
  285.             if not h.isSectionHidden(i) and h.sectionSize(i) < 3:
  286.                 sz = h.sectionSizeHint(i)
  287.                 h.resizeSection(i, sz)
  288.                 continue
  289.         
  290.  
  291.     
  292.     def get_default_state(self):
  293.         old_state = {
  294.             'hidden_columns': [],
  295.             'sort_history': [
  296.                 DEFAULT_SORT],
  297.             'column_positions': { },
  298.             'column_sizes': { },
  299.             'column_alignment': {
  300.                 'size': 'center',
  301.                 'timestamp': 'center',
  302.                 'pubdate': 'center' } }
  303.         h = self.column_header
  304.         cm = self.column_map
  305.         for i in range(h.count()):
  306.             name = cm[i]
  307.             old_state['column_positions'][name] = i
  308.             if name != 'ondevice':
  309.                 old_state['column_sizes'][name] = min(350, max(self.sizeHintForColumn(i), h.sectionSizeHint(i)))
  310.                 if name == 'timestamp':
  311.                     old_state['column_sizes'][name] += 12
  312.                 
  313.             name == 'timestamp'
  314.         
  315.         return old_state
  316.  
  317.     
  318.     def get_old_state(self):
  319.         ans = None
  320.         name = unicode(self.objectName())
  321.         if name:
  322.             name += ' books view state'
  323.             db = getattr(self.model(), 'db', None)
  324.             if db is not None:
  325.                 ans = db.prefs.get(name, None)
  326.                 if ans is None:
  327.                     ans = gprefs.get(name, None)
  328.                     
  329.                     try:
  330.                         del gprefs[name]
  331.                     except:
  332.                         pass
  333.  
  334.                     if ans is not None:
  335.                         db.prefs[name] = ans
  336.                     
  337.                 
  338.             
  339.         
  340.         return ans
  341.  
  342.     
  343.     def restore_state(self):
  344.         old_state = self.get_old_state()
  345.         if old_state is None:
  346.             old_state = self.get_default_state()
  347.         
  348.         if tweaks['sort_columns_at_startup'] is not None:
  349.             old_state['sort_history'] = tweaks['sort_columns_at_startup']
  350.         
  351.         self.apply_state(old_state)
  352.         if self.model().rowCount(QModelIndex()) > 0:
  353.             self.resizeRowToContents(0)
  354.             self.verticalHeader().setDefaultSectionSize(self.rowHeight(0))
  355.         
  356.         self.was_restored = True
  357.  
  358.     
  359.     def set_database(self, db):
  360.         self.save_state()
  361.         self._model.set_database(db)
  362.         self.tags_delegate.set_database(db)
  363.         self.authors_delegate.set_auto_complete_function(db.all_authors)
  364.         self.series_delegate.set_auto_complete_function(db.all_series)
  365.         self.publisher_delegate.set_auto_complete_function(db.all_publishers)
  366.  
  367.     
  368.     def database_changed(self, db):
  369.         for i in range(self.model().columnCount(None)):
  370.             if self.itemDelegateForColumn(i) in (self.rating_delegate, self.timestamp_delegate, self.pubdate_delegate):
  371.                 self.setItemDelegateForColumn(i, self.itemDelegate())
  372.                 continue
  373.         
  374.         cm = self.column_map
  375.         for colhead in cm:
  376.             if self._model.is_custom_column(colhead):
  377.                 cc = self._model.custom_columns[colhead]
  378.                 if cc['datatype'] == 'datetime':
  379.                     delegate = CcDateDelegate(self)
  380.                     delegate.set_format(cc['display'].get('date_format', ''))
  381.                     self.setItemDelegateForColumn(cm.index(colhead), delegate)
  382.                 elif cc['datatype'] == 'comments':
  383.                     self.setItemDelegateForColumn(cm.index(colhead), self.cc_comments_delegate)
  384.                 elif cc['datatype'] in ('text', 'series'):
  385.                     if cc['is_multiple']:
  386.                         self.setItemDelegateForColumn(cm.index(colhead), self.tags_delegate)
  387.                     else:
  388.                         self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
  389.                 elif cc['datatype'] in ('int', 'float'):
  390.                     self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
  391.                 elif cc['datatype'] == 'bool':
  392.                     self.setItemDelegateForColumn(cm.index(colhead), self.cc_bool_delegate)
  393.                 elif cc['datatype'] == 'rating':
  394.                     self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate)
  395.                 elif cc['datatype'] == 'composite':
  396.                     self.setItemDelegateForColumn(cm.index(colhead), self.cc_template_delegate)
  397.                 
  398.             cc['datatype'] == 'datetime'
  399.             dattr = colhead + '_delegate'
  400.             delegate = None if hasattr(self, dattr) else 'text'
  401.             self.setItemDelegateForColumn(cm.index(colhead), getattr(self, delegate + '_delegate'))
  402.         
  403.         self.restore_state()
  404.         self.set_ondevice_column_visibility()
  405.  
  406.     
  407.     def set_context_menu(self, menu, edit_collections_action):
  408.         self.setContextMenuPolicy(Qt.DefaultContextMenu)
  409.         self.context_menu = menu
  410.         self.edit_collections_action = edit_collections_action
  411.  
  412.     
  413.     def contextMenuEvent(self, event):
  414.         self.context_menu.popup(event.globalPos())
  415.         event.accept()
  416.  
  417.     
  418.     def paths_from_event(cls, event):
  419.         md = event.mimeData()
  420.         if md.hasFormat('text/uri-list') and not md.hasFormat('application/calibre+from_library'):
  421.             urls = [ unicode(u.toLocalFile()) for u in md.urls() ]
  422.             return _[2]
  423.  
  424.     paths_from_event = classmethod(paths_from_event)
  425.     
  426.     def drag_icon(self, cover, multiple):
  427.         cover = cover.scaledToHeight(120, Qt.SmoothTransformation)
  428.         if multiple:
  429.             base_width = cover.width()
  430.             base_height = cover.height()
  431.             base = QImage(base_width + 21, base_height + 21, QImage.Format_ARGB32_Premultiplied)
  432.             base.fill(QColor(255, 255, 255, 0).rgba())
  433.             p = QPainter(base)
  434.             rect = QRect(20, 0, base_width, base_height)
  435.             p.fillRect(rect, QColor('white'))
  436.             p.drawRect(rect)
  437.             rect.moveLeft(10)
  438.             rect.moveTop(10)
  439.             p.fillRect(rect, QColor('white'))
  440.             p.drawRect(rect)
  441.             rect.moveLeft(0)
  442.             rect.moveTop(20)
  443.             p.fillRect(rect, QColor('white'))
  444.             p.save()
  445.             p.setCompositionMode(p.CompositionMode_SourceAtop)
  446.             p.drawImage(rect.topLeft(), cover)
  447.             p.restore()
  448.             p.drawRect(rect)
  449.             p.end()
  450.             cover = base
  451.         
  452.         return QPixmap.fromImage(cover)
  453.  
  454.     
  455.     def drag_data(self):
  456.         m = self.model()
  457.         db = m.db
  458.         rows = self.selectionModel().selectedRows()
  459.         selected = map(m.id, rows)
  460.         ids = ' '.join(map(str, selected))
  461.         md = QMimeData()
  462.         md.setData('application/calibre+from_library', ids)
  463.         fmt = prefs['output_format']
  464.         
  465.         def url_for_id(i):
  466.             ans = db.format_abspath(i, fmt, index_is_id = True)
  467.             if ans is None:
  468.                 fmts = db.formats(i, index_is_id = True)
  469.                 if fmts:
  470.                     fmts = fmts.split(',')
  471.                 else:
  472.                     fmts = []
  473.                 for f in fmts:
  474.                     ans = db.format_abspath(i, f, index_is_id = True)
  475.                     if ans is not None:
  476.                         break
  477.                         continue
  478.                 
  479.             
  480.             if ans is None:
  481.                 ans = db.abspath(i, index_is_id = True)
  482.             
  483.             return QUrl.fromLocalFile(ans)
  484.  
  485.         []([ url_for_id(i) for i in selected ])
  486.         drag = QDrag(self)
  487.         drag.setMimeData(md)
  488.         cover = self.drag_icon(m.cover(self.currentIndex().row()), len(selected) > 1)
  489.         drag.setHotSpot(QPoint(-15, -15))
  490.         drag.setPixmap(cover)
  491.         return drag
  492.  
  493.     
  494.     def event_has_mods(self, event = None):
  495.         mods = None if event is not None else QApplication.keyboardModifiers()
  496.         if not mods & Qt.ControlModifier:
  497.             pass
  498.         return mods & Qt.ShiftModifier
  499.  
  500.     
  501.     def mousePressEvent(self, event):
  502.         if event.button() == Qt.LeftButton and not self.event_has_mods():
  503.             self.drag_start_pos = event.pos()
  504.         
  505.         return QTableView.mousePressEvent(self, event)
  506.  
  507.     
  508.     def mouseMoveEvent(self, event):
  509.         if not self.drag_allowed:
  510.             return None
  511.         if self.drag_start_pos is None:
  512.             return QTableView.mouseMoveEvent(self, event)
  513.         if self.event_has_mods():
  514.             self.drag_start_pos = None
  515.             return None
  516.         if not (event.buttons() & Qt.LeftButton) or (event.pos() - self.drag_start_pos).manhattanLength() < QApplication.startDragDistance():
  517.             return None
  518.         index = self.indexAt(event.pos())
  519.         if not index.isValid():
  520.             return None
  521.         drag = self.drag_data()
  522.         drag.exec_(Qt.CopyAction)
  523.         self.drag_start_pos = None
  524.  
  525.     
  526.     def dragEnterEvent(self, event):
  527.         if int(event.possibleActions() & Qt.CopyAction) + int(event.possibleActions() & Qt.MoveAction) == 0:
  528.             return None
  529.         paths = self.paths_from_event(event)
  530.         if paths:
  531.             event.acceptProposedAction()
  532.         
  533.  
  534.     
  535.     def dragMoveEvent(self, event):
  536.         event.acceptProposedAction()
  537.  
  538.     
  539.     def dropEvent(self, event):
  540.         paths = self.paths_from_event(event)
  541.         event.setDropAction(Qt.CopyAction)
  542.         event.accept()
  543.         self.files_dropped.emit(paths)
  544.  
  545.     
  546.     def column_map(self):
  547.         return self._model.column_map
  548.  
  549.     column_map = property(column_map)
  550.     
  551.     def scrollContentsBy(self, dx, dy):
  552.         QTableView.scrollContentsBy(self, dx, dy)
  553.         if dy != 0:
  554.             self.column_header.update()
  555.         
  556.  
  557.     
  558.     def scroll_to_row(self, row):
  559.         if row > -1:
  560.             h = self.horizontalHeader()
  561.             for i in range(h.count()):
  562.                 if not h.isSectionHidden(i):
  563.                     self.scrollTo(self.model().index(row, i))
  564.                     break
  565.                     continue
  566.             
  567.         
  568.  
  569.     
  570.     def set_current_row(self, row, select = True):
  571.         pass
  572.  
  573.     
  574.     def select_rows(self, identifiers, using_ids = True, change_current = True, scroll = True):
  575.         rows = []([ _[1] if hasattr(x, 'row') else x for x in identifiers ])
  576.         if using_ids:
  577.             rows = set([])
  578.             identifiers = set(identifiers)
  579.             m = self.model()
  580.             for row in xrange(m.rowCount(QModelIndex())):
  581.                 if m.id(row) in identifiers:
  582.                     rows.add(row)
  583.                     continue
  584.                 set
  585.             
  586.         
  587.         rows = list(sorted(rows))
  588.         if rows:
  589.             row = rows[0]
  590.             if change_current:
  591.                 self.set_current_row(row, select = False)
  592.             
  593.             if scroll:
  594.                 self.scroll_to_row(row)
  595.             
  596.         
  597.         sm = self.selectionModel()
  598.         sel = QItemSelection()
  599.         m = self.model()
  600.         max_col = m.columnCount(QModelIndex()) - 1
  601.         for row in rows:
  602.             sel.select(m.index(row, 0), m.index(row, max_col))
  603.         
  604.         sm.select(sel, sm.ClearAndSelect)
  605.  
  606.     
  607.     def get_selected_ids(self):
  608.         ans = []
  609.         m = self.model()
  610.         for idx in self.selectedIndexes():
  611.             r = idx.row()
  612.             i = m.id(r)
  613.             if i not in ans:
  614.                 ans.append(i)
  615.                 continue
  616.         
  617.         return ans
  618.  
  619.     
  620.     def close(self):
  621.         self._model.close()
  622.  
  623.     
  624.     def set_editable(self, editable, supports_backloading):
  625.         self._model.set_editable(editable)
  626.  
  627.     
  628.     def connect_to_search_box(self, sb, search_done):
  629.         sb.search.connect(self._model.search)
  630.         self._search_done = search_done
  631.         self._model.searched.connect(self.search_done)
  632.  
  633.     
  634.     def connect_to_book_display(self, bd):
  635.         self._model.new_bookdisplay_data.connect(bd)
  636.  
  637.     
  638.     def search_done(self, ok):
  639.         self._search_done(self, ok)
  640.  
  641.     
  642.     def row_count(self):
  643.         return self._model.count()
  644.  
  645.  
  646.  
  647. class DeviceBooksView(BooksView):
  648.     
  649.     def __init__(self, parent):
  650.         BooksView.__init__(self, parent, DeviceBooksModel)
  651.         self.can_add_columns = False
  652.         self.columns_resized = False
  653.         self.resize_on_select = False
  654.         self.rating_delegate = None
  655.         for i in range(10):
  656.             self.setItemDelegateForColumn(i, TextDelegate(self))
  657.         
  658.         self.setDragDropMode(self.NoDragDrop)
  659.         self.setAcceptDrops(False)
  660.  
  661.     
  662.     def drag_data(self):
  663.         m = self.model()
  664.         rows = self.selectionModel().selectedRows()
  665.         paths = _[1]
  666.         md = QMimeData()
  667.         md.setData('application/calibre+from_device', 'dummy')
  668.         []([ QUrl.fromLocalFile(p) for p in paths ])
  669.         drag = QDrag(self)
  670.         drag.setMimeData(md)
  671.         cover = self.drag_icon(m.cover(self.currentIndex().row()), len(paths) > 1)
  672.         drag.setHotSpot(QPoint(-15, -15))
  673.         drag.setPixmap(cover)
  674.         return drag
  675.  
  676.     
  677.     def contextMenuEvent(self, event):
  678.         if callable(getattr(self._model.db, 'supports_collections', None)) and self._model.db.supports_collections():
  679.             pass
  680.         edit_collections = prefs['manage_device_metadata'] == 'manual'
  681.         self.edit_collections_action.setVisible(edit_collections)
  682.         self.context_menu.popup(event.globalPos())
  683.         event.accept()
  684.  
  685.     
  686.     def get_old_state(self):
  687.         ans = None
  688.         name = unicode(self.objectName())
  689.         if name:
  690.             name += ' books view state'
  691.             ans = gprefs.get(name, None)
  692.         
  693.         return ans
  694.  
  695.     
  696.     def write_state(self, state):
  697.         name = unicode(self.objectName())
  698.         if name:
  699.             gprefs.set(name + ' books view state', state)
  700.         
  701.  
  702.     
  703.     def set_database(self, db):
  704.         self._model.set_database(db)
  705.         self.restore_state()
  706.  
  707.     
  708.     def resizeColumnsToContents(self):
  709.         QTableView.resizeColumnsToContents(self)
  710.         self.columns_resized = True
  711.  
  712.     
  713.     def connect_dirtied_signal(self, slot):
  714.         self._model.booklist_dirtied.connect(slot)
  715.  
  716.     
  717.     def connect_upload_collections_signal(self, func = None, oncard = None):
  718.         self._model.upload_collections.connect(partial(func, view = self, oncard = oncard))
  719.  
  720.     
  721.     def dropEvent(self, *args):
  722.         error_dialog(self, _('Not allowed'), _('Dropping onto a device is not supported. First add the book to the calibre library.')).exec_()
  723.  
  724.     
  725.     def set_editable(self, editable, supports_backloading):
  726.         self._model.set_editable(editable)
  727.         self.drag_allowed = supports_backloading
  728.  
  729.  
  730.