mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-02 18:47:01 -05:00 
			
		
		
		
	Implement #3793 (Viewer Key Assignments)
This commit is contained in:
		
							parent
							
								
									745a2effce
								
							
						
					
					
						commit
						244c9368c8
					
				
							
								
								
									
										266
									
								
								src/calibre/gui2/shortcuts.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								src/calibre/gui2/shortcuts.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,266 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
 | 
			
		||||
from __future__ import with_statement
 | 
			
		||||
 | 
			
		||||
__license__   = 'GPL v3'
 | 
			
		||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
 | 
			
		||||
__docformat__ = 'restructuredtext en'
 | 
			
		||||
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from PyQt4.Qt import QAbstractListModel, Qt, QKeySequence, QListView, \
 | 
			
		||||
        QHBoxLayout, QWidget, QApplication, QStyledItemDelegate, QStyle, \
 | 
			
		||||
        QVariant, QTextDocument, QRectF, QFrame, QSize, QFont, QKeyEvent
 | 
			
		||||
 | 
			
		||||
from calibre.gui2 import NONE, error_dialog
 | 
			
		||||
from calibre.utils.config import XMLConfig
 | 
			
		||||
from calibre.gui2.shortcuts_ui import Ui_Frame
 | 
			
		||||
 | 
			
		||||
DEFAULTS = Qt.UserRole
 | 
			
		||||
DESCRIPTION = Qt.UserRole + 1
 | 
			
		||||
CUSTOM = Qt.UserRole + 2
 | 
			
		||||
KEY = Qt.UserRole + 3
 | 
			
		||||
 | 
			
		||||
class Customize(QFrame, Ui_Frame):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, dup_check, parent=None):
 | 
			
		||||
        QFrame.__init__(self, parent)
 | 
			
		||||
        self.setupUi(self)
 | 
			
		||||
        self.setFocusPolicy(Qt.StrongFocus)
 | 
			
		||||
        self.setAutoFillBackground(True)
 | 
			
		||||
        self.custom.toggled.connect(self.custom_toggled)
 | 
			
		||||
        self.custom_toggled(False)
 | 
			
		||||
        self.capture = 0
 | 
			
		||||
        self.key = None
 | 
			
		||||
        self.shorcut1 = self.shortcut2 = None
 | 
			
		||||
        self.dup_check = dup_check
 | 
			
		||||
        for x in (1, 2):
 | 
			
		||||
            button = getattr(self, 'button%d'%x)
 | 
			
		||||
            button.clicked.connect(partial(self.capture_clicked, which=x))
 | 
			
		||||
            button.keyPressEvent = partial(self.key_press_event, which=x)
 | 
			
		||||
            clear = getattr(self, 'clear%d'%x)
 | 
			
		||||
            clear.clicked.connect(partial(self.clear_clicked, which=x))
 | 
			
		||||
 | 
			
		||||
    def clear_clicked(self, which=0):
 | 
			
		||||
         button = getattr(self, 'button%d'%which)
 | 
			
		||||
         button.setText(_('None'))
 | 
			
		||||
         setattr(self, 'shortcut%d'%which, None)
 | 
			
		||||
 | 
			
		||||
    def custom_toggled(self, checked):
 | 
			
		||||
        for w in ('1', '2'):
 | 
			
		||||
            for o in ('label', 'button', 'clear'):
 | 
			
		||||
                getattr(self, o+w).setEnabled(checked)
 | 
			
		||||
 | 
			
		||||
    def capture_clicked(self, which=1):
 | 
			
		||||
        self.capture = which
 | 
			
		||||
        button = getattr(self, 'button%d'%which)
 | 
			
		||||
        button.setText(_('Press a key...'))
 | 
			
		||||
        button.setFocus(Qt.OtherFocusReason)
 | 
			
		||||
        font = QFont()
 | 
			
		||||
        font.setBold(True)
 | 
			
		||||
        button.setFont(font)
 | 
			
		||||
 | 
			
		||||
    def key_press_event(self, ev, which=0):
 | 
			
		||||
        code = ev.key()
 | 
			
		||||
        if self.capture == 0 or code in (0, Qt.Key_unknown,
 | 
			
		||||
                Qt.Key_Shift, Qt.Key_Control, Qt.Key_Alt, Qt.Key_Meta,
 | 
			
		||||
                Qt.Key_AltGr, Qt.Key_CapsLock, Qt.Key_NumLock, Qt.Key_ScrollLock):
 | 
			
		||||
            return QWidget.keyPressEvent(self, ev)
 | 
			
		||||
        button = getattr(self, 'button%d'%which)
 | 
			
		||||
        font = QFont()
 | 
			
		||||
        button.setFont(font)
 | 
			
		||||
        sequence = QKeySequence(code|int(ev.modifiers()))
 | 
			
		||||
        button.setText(sequence.toString())
 | 
			
		||||
        self.capture = 0
 | 
			
		||||
        setattr(self, 'shortcut%d'%which, sequence)
 | 
			
		||||
        dup_desc = self.dup_check(sequence, self.key)
 | 
			
		||||
        if dup_desc is not None:
 | 
			
		||||
            error_dialog(self, _('Already assigned'),
 | 
			
		||||
                    unicode(sequence.toString()) + ' ' +
 | 
			
		||||
                    _('already assigned to') + ' ' + dup_desc, show=True)
 | 
			
		||||
            self.clear_clicked(which=which)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Delegate(QStyledItemDelegate):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        QStyledItemDelegate.__init__(self, parent)
 | 
			
		||||
        self.editing_indices = {}
 | 
			
		||||
 | 
			
		||||
    def to_doc(self, index):
 | 
			
		||||
        doc =  QTextDocument()
 | 
			
		||||
        doc.setHtml(index.data().toString())
 | 
			
		||||
        return doc
 | 
			
		||||
 | 
			
		||||
    def sizeHint(self, option, index):
 | 
			
		||||
        if index.row() in self.editing_indices:
 | 
			
		||||
            return QSize(200, 200)
 | 
			
		||||
        ans = self.to_doc(index).size().toSize()
 | 
			
		||||
        ans.setHeight(ans.height()+10)
 | 
			
		||||
        return ans
 | 
			
		||||
 | 
			
		||||
    def paint(self, painter, option, index):
 | 
			
		||||
        painter.save()
 | 
			
		||||
        painter.setClipRect(QRectF(option.rect))
 | 
			
		||||
        if hasattr(QStyle, 'CE_ItemViewItem'):
 | 
			
		||||
            QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter)
 | 
			
		||||
        elif option.state & QStyle.State_Selected:
 | 
			
		||||
            painter.fillRect(option.rect, option.palette.highlight())
 | 
			
		||||
        painter.translate(option.rect.topLeft())
 | 
			
		||||
        self.to_doc(index).drawContents(painter)
 | 
			
		||||
        painter.restore()
 | 
			
		||||
 | 
			
		||||
    def createEditor(self, parent, option, index):
 | 
			
		||||
        w = Customize(index.model().duplicate_check, parent=parent)
 | 
			
		||||
        self.editing_indices[index.row()] = w
 | 
			
		||||
        self.sizeHintChanged.emit(index)
 | 
			
		||||
        return w
 | 
			
		||||
 | 
			
		||||
    def setEditorData(self, editor, index):
 | 
			
		||||
        defs = index.data(DEFAULTS).toPyObject()
 | 
			
		||||
        defs = _(' or ').join([unicode(x.toString(x.NativeText)) for x in defs])
 | 
			
		||||
        editor.key = unicode(index.data(KEY).toString())
 | 
			
		||||
        editor.default_shortcuts.setText(_('&Default') + ': %s' % defs)
 | 
			
		||||
        editor.default_shortcuts.setChecked(True)
 | 
			
		||||
        editor.header.setText('<b>%s: %s</b>'%(_('Customize shortcuts for'),
 | 
			
		||||
            unicode(index.data(DESCRIPTION).toString())))
 | 
			
		||||
        custom = index.data(CUSTOM).toPyObject()
 | 
			
		||||
        if custom:
 | 
			
		||||
            editor.custom.setChecked(True)
 | 
			
		||||
            for x in (0, 1):
 | 
			
		||||
                button = getattr(editor, 'button%d'%(x+1))
 | 
			
		||||
                if len(custom) > x:
 | 
			
		||||
                    seq = QKeySequence(custom[x])
 | 
			
		||||
                    button.setText(seq.toString(seq.NativeText))
 | 
			
		||||
                    setattr(editor, 'shortcut%d'%(x+1), seq)
 | 
			
		||||
 | 
			
		||||
    def setModelData(self, editor, model, index):
 | 
			
		||||
        self.editing_indices.pop(index.row())
 | 
			
		||||
        self.sizeHintChanged.emit(index)
 | 
			
		||||
        self.closeEditor.emit(editor, self.NoHint)
 | 
			
		||||
        custom = []
 | 
			
		||||
        if editor.custom.isChecked():
 | 
			
		||||
            for x in ('1', '2'):
 | 
			
		||||
                sc = getattr(editor, 'shortcut'+x)
 | 
			
		||||
                if sc is not None:
 | 
			
		||||
                    custom.append(sc)
 | 
			
		||||
 | 
			
		||||
        model.set_data(index, custom)
 | 
			
		||||
 | 
			
		||||
    def updateEditorGeometry(self, editor, option, index):
 | 
			
		||||
        editor.setGeometry(option.rect)
 | 
			
		||||
 | 
			
		||||
class Shortcuts(QAbstractListModel):
 | 
			
		||||
 | 
			
		||||
    TEMPLATE = '''
 | 
			
		||||
    <p><b>{0}</b><br>
 | 
			
		||||
    Keys: <code>{1}</code></p>
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, shortcuts, config_file_base_name, parent=None):
 | 
			
		||||
        QAbstractListModel.__init__(self, parent)
 | 
			
		||||
 | 
			
		||||
        self.descriptions = {}
 | 
			
		||||
        for k, v in shortcuts.items():
 | 
			
		||||
            self.descriptions[k] = v[-1]
 | 
			
		||||
        self.keys = {}
 | 
			
		||||
        for k, v in shortcuts.items():
 | 
			
		||||
            self.keys[k] = v[0]
 | 
			
		||||
        self.order = list(shortcuts)
 | 
			
		||||
        self.order.sort(cmp=lambda x,y : cmp(self.descriptions[x],
 | 
			
		||||
            self.descriptions[y]))
 | 
			
		||||
        self.sequences = {}
 | 
			
		||||
        for k, v in self.keys.items():
 | 
			
		||||
            self.sequences[k] = [QKeySequence(x) for x in v]
 | 
			
		||||
 | 
			
		||||
        self.custom = XMLConfig(config_file_base_name)
 | 
			
		||||
 | 
			
		||||
    def rowCount(self, parent):
 | 
			
		||||
        return len(self.order)
 | 
			
		||||
 | 
			
		||||
    def get_sequences(self, key):
 | 
			
		||||
        custom = self.custom.get(key, [])
 | 
			
		||||
        if custom:
 | 
			
		||||
            return [QKeySequence(x) for x in custom]
 | 
			
		||||
        return self.sequences[key]
 | 
			
		||||
 | 
			
		||||
    def get_match(self, event_or_sequence, ignore=tuple()):
 | 
			
		||||
        q = event_or_sequence
 | 
			
		||||
        if isinstance(q, QKeyEvent):
 | 
			
		||||
            q = QKeySequence(q.key()|int(q.modifiers()))
 | 
			
		||||
        for key in self.order:
 | 
			
		||||
            if key not in ignore:
 | 
			
		||||
                for seq in self.get_sequences(key):
 | 
			
		||||
                    if seq.matches(q) == QKeySequence.ExactMatch:
 | 
			
		||||
                        return key
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def duplicate_check(self, seq, ignore):
 | 
			
		||||
        key = self.get_match(seq, ignore=[ignore])
 | 
			
		||||
        if key is not None:
 | 
			
		||||
            return self.descriptions[key]
 | 
			
		||||
 | 
			
		||||
    def get_shortcuts(self, key):
 | 
			
		||||
        return [unicode(x.toString(x.NativeText)) for x in
 | 
			
		||||
                self.get_sequences(key)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def data(self, index, role):
 | 
			
		||||
        row = index.row()
 | 
			
		||||
        if row < 0 or row >= len(self.order):
 | 
			
		||||
            return NONE
 | 
			
		||||
        key = self.order[row]
 | 
			
		||||
        if role == Qt.DisplayRole:
 | 
			
		||||
            return QVariant(self.TEMPLATE.format(self.descriptions[key],
 | 
			
		||||
                    _(' or ').join(self.get_shortcuts(key))))
 | 
			
		||||
        if role == Qt.ToolTipRole:
 | 
			
		||||
            return QVariant(_('Double click to change'))
 | 
			
		||||
        if role == DEFAULTS:
 | 
			
		||||
            return QVariant(self.sequences[key])
 | 
			
		||||
        if role == DESCRIPTION:
 | 
			
		||||
            return QVariant(self.descriptions[key])
 | 
			
		||||
        if role == CUSTOM:
 | 
			
		||||
            if key in self.custom:
 | 
			
		||||
                return QVariant(self.custom[key])
 | 
			
		||||
            else:
 | 
			
		||||
                return QVariant([])
 | 
			
		||||
        if role == KEY:
 | 
			
		||||
            return QVariant(key)
 | 
			
		||||
        return NONE
 | 
			
		||||
 | 
			
		||||
    def set_data(self, index, custom):
 | 
			
		||||
        key = self.order[index.row()]
 | 
			
		||||
        if custom:
 | 
			
		||||
            self.custom[key] = [unicode(x.toString()) for x in custom]
 | 
			
		||||
        elif key in self.custom:
 | 
			
		||||
            del self.custom[key]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def flags(self, index):
 | 
			
		||||
        if not index.isValid():
 | 
			
		||||
            return Qt.ItemIsEnabled
 | 
			
		||||
        return QAbstractListModel.flags(self, index) | Qt.ItemIsEditable
 | 
			
		||||
 | 
			
		||||
class ShortcutConfig(QWidget):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, model, parent=None):
 | 
			
		||||
        QWidget.__init__(self, parent)
 | 
			
		||||
        self._layout = QHBoxLayout()
 | 
			
		||||
        self.setLayout(self._layout)
 | 
			
		||||
        self.view = QListView(self)
 | 
			
		||||
        self._layout.addWidget(self.view)
 | 
			
		||||
        self.view.setModel(model)
 | 
			
		||||
        self.delegate = Delegate()
 | 
			
		||||
        self.view.setItemDelegate(self.delegate)
 | 
			
		||||
        self.delegate.sizeHintChanged.connect(self.view.scrollTo)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    from calibre.gui2 import is_ok_to_use_qt
 | 
			
		||||
    from calibre.gui2.viewer.keys import SHORTCUTS
 | 
			
		||||
    is_ok_to_use_qt()
 | 
			
		||||
    model = Shortcuts(SHORTCUTS, 'shortcuts/viewer')
 | 
			
		||||
    conf = ShortcutConfig(model)
 | 
			
		||||
    conf.resize(400, 500)
 | 
			
		||||
    conf.show()
 | 
			
		||||
    QApplication.instance().exec_()
 | 
			
		||||
							
								
								
									
										135
									
								
								src/calibre/gui2/shortcuts.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/calibre/gui2/shortcuts.ui
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<ui version="4.0">
 | 
			
		||||
 <class>Frame</class>
 | 
			
		||||
 <widget class="QFrame" name="Frame">
 | 
			
		||||
  <property name="geometry">
 | 
			
		||||
   <rect>
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>400</width>
 | 
			
		||||
    <height>170</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="windowTitle">
 | 
			
		||||
   <string>Frame</string>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="frameShape">
 | 
			
		||||
   <enum>QFrame::StyledPanel</enum>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="frameShadow">
 | 
			
		||||
   <enum>QFrame::Raised</enum>
 | 
			
		||||
  </property>
 | 
			
		||||
  <layout class="QGridLayout" name="gridLayout">
 | 
			
		||||
   <item row="1" column="0" colspan="2">
 | 
			
		||||
    <widget class="QRadioButton" name="default_shortcuts">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>&Default</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="2" column="0" colspan="2">
 | 
			
		||||
    <widget class="QRadioButton" name="custom">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>&Custom</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="3" column="0" colspan="2">
 | 
			
		||||
    <layout class="QHBoxLayout" name="horizontalLayout">
 | 
			
		||||
     <property name="leftMargin">
 | 
			
		||||
      <number>25</number>
 | 
			
		||||
     </property>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QLabel" name="label1">
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>&Shortcut:</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="buddy">
 | 
			
		||||
        <cstring>button1</cstring>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QPushButton" name="button1">
 | 
			
		||||
       <property name="toolTip">
 | 
			
		||||
        <string>Click to change</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>None</string>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QToolButton" name="clear1">
 | 
			
		||||
       <property name="toolTip">
 | 
			
		||||
        <string>Clear</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>...</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="icon">
 | 
			
		||||
        <iconset resource="../../../resources/images.qrc">
 | 
			
		||||
         <normaloff>:/images/clear_left.svg</normaloff>:/images/clear_left.svg</iconset>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="4" column="0" colspan="2">
 | 
			
		||||
    <layout class="QHBoxLayout" name="horizontalLayout_3">
 | 
			
		||||
     <property name="leftMargin">
 | 
			
		||||
      <number>25</number>
 | 
			
		||||
     </property>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QLabel" name="label2">
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>&Alternate shortcut:</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="buddy">
 | 
			
		||||
        <cstring>button1</cstring>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QPushButton" name="button2">
 | 
			
		||||
       <property name="toolTip">
 | 
			
		||||
        <string>Click to change</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>None</string>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QToolButton" name="clear2">
 | 
			
		||||
       <property name="toolTip">
 | 
			
		||||
        <string>Clear</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>...</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="icon">
 | 
			
		||||
        <iconset resource="../../../resources/images.qrc">
 | 
			
		||||
         <normaloff>:/images/clear_left.svg</normaloff>:/images/clear_left.svg</iconset>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="0" column="0" colspan="2">
 | 
			
		||||
    <widget class="QLabel" name="header">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string/>
 | 
			
		||||
     </property>
 | 
			
		||||
     <property name="wordWrap">
 | 
			
		||||
      <bool>true</bool>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
  </layout>
 | 
			
		||||
 </widget>
 | 
			
		||||
 <resources>
 | 
			
		||||
  <include location="../../../resources/images.qrc"/>
 | 
			
		||||
 </resources>
 | 
			
		||||
 <connections/>
 | 
			
		||||
</ui>
 | 
			
		||||
@ -18,7 +18,27 @@
 | 
			
		||||
    <normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset>
 | 
			
		||||
  </property>
 | 
			
		||||
  <layout class="QGridLayout" name="gridLayout_4">
 | 
			
		||||
   <item row="1" column="0">
 | 
			
		||||
    <widget class="QDialogButtonBox" name="buttonBox">
 | 
			
		||||
     <property name="orientation">
 | 
			
		||||
      <enum>Qt::Horizontal</enum>
 | 
			
		||||
     </property>
 | 
			
		||||
     <property name="standardButtons">
 | 
			
		||||
      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="0" column="0">
 | 
			
		||||
    <widget class="QTabWidget" name="tabs">
 | 
			
		||||
     <property name="currentIndex">
 | 
			
		||||
      <number>0</number>
 | 
			
		||||
     </property>
 | 
			
		||||
     <widget class="QWidget" name="tab">
 | 
			
		||||
      <attribute name="title">
 | 
			
		||||
       <string>&General</string>
 | 
			
		||||
      </attribute>
 | 
			
		||||
      <layout class="QVBoxLayout" name="verticalLayout_2">
 | 
			
		||||
       <item>
 | 
			
		||||
        <widget class="QGroupBox" name="groupBox">
 | 
			
		||||
         <property name="title">
 | 
			
		||||
          <string>&Font options</string>
 | 
			
		||||
@ -215,18 +235,29 @@
 | 
			
		||||
         </layout>
 | 
			
		||||
        </widget>
 | 
			
		||||
       </item>
 | 
			
		||||
   <item row="1" column="0">
 | 
			
		||||
    <widget class="QDialogButtonBox" name="buttonBox">
 | 
			
		||||
     <property name="orientation">
 | 
			
		||||
      <enum>Qt::Horizontal</enum>
 | 
			
		||||
      </layout>
 | 
			
		||||
     </widget>
 | 
			
		||||
     <widget class="QWidget" name="tab_2">
 | 
			
		||||
      <attribute name="title">
 | 
			
		||||
       <string>&Keyboard shortcuts</string>
 | 
			
		||||
      </attribute>
 | 
			
		||||
      <layout class="QVBoxLayout" name="verticalLayout">
 | 
			
		||||
       <item>
 | 
			
		||||
        <widget class="QLabel" name="label_9">
 | 
			
		||||
         <property name="text">
 | 
			
		||||
          <string>Double click to change a keyborad shortcut</string>
 | 
			
		||||
         </property>
 | 
			
		||||
     <property name="standardButtons">
 | 
			
		||||
      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
 | 
			
		||||
         <property name="wordWrap">
 | 
			
		||||
          <bool>true</bool>
 | 
			
		||||
         </property>
 | 
			
		||||
        </widget>
 | 
			
		||||
       </item>
 | 
			
		||||
      </layout>
 | 
			
		||||
     </widget>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
  </layout>
 | 
			
		||||
 </widget>
 | 
			
		||||
 <tabstops>
 | 
			
		||||
  <tabstop>serif_family</tabstop>
 | 
			
		||||
  <tabstop>sans_family</tabstop>
 | 
			
		||||
 | 
			
		||||
@ -15,9 +15,11 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
 | 
			
		||||
from calibre.utils.config import Config, StringConfig
 | 
			
		||||
from calibre.utils.localization import get_language
 | 
			
		||||
from calibre.gui2.viewer.config_ui import Ui_Dialog
 | 
			
		||||
from calibre.gui2.shortcuts import Shortcuts, ShortcutConfig
 | 
			
		||||
from calibre.ptempfile import PersistentTemporaryFile
 | 
			
		||||
from calibre.constants import iswindows
 | 
			
		||||
from calibre import prints, guess_type
 | 
			
		||||
from calibre.gui2.viewer.keys import SHORTCUTS
 | 
			
		||||
 | 
			
		||||
bookmarks = referencing = hyphenation = jquery = jquery_scrollTo = hyphenator = None
 | 
			
		||||
 | 
			
		||||
@ -73,8 +75,8 @@ class PythonJS(QObject):
 | 
			
		||||
 | 
			
		||||
class ConfigDialog(QDialog, Ui_Dialog):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args):
 | 
			
		||||
        QDialog.__init__(self, *args)
 | 
			
		||||
    def __init__(self, shortcuts, parent=None):
 | 
			
		||||
        QDialog.__init__(self, parent)
 | 
			
		||||
        self.setupUi(self)
 | 
			
		||||
 | 
			
		||||
        opts = config().parse()
 | 
			
		||||
@ -104,6 +106,10 @@ class ConfigDialog(QDialog, Ui_Dialog):
 | 
			
		||||
        self.hyphenate_default_lang.setCurrentIndex(idx)
 | 
			
		||||
        self.hyphenate.setChecked(opts.hyphenate)
 | 
			
		||||
        self.hyphenate_default_lang.setEnabled(opts.hyphenate)
 | 
			
		||||
        self.shortcuts = shortcuts
 | 
			
		||||
        self.shortcut_config = ShortcutConfig(shortcuts, parent=self)
 | 
			
		||||
        p = self.tabs.widget(1)
 | 
			
		||||
        p.layout().addWidget(self.shortcut_config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def accept(self, *args):
 | 
			
		||||
@ -139,15 +145,15 @@ class Document(QWebPage):
 | 
			
		||||
        settings.setFontFamily(QWebSettings.FixedFont, opts.mono_family)
 | 
			
		||||
 | 
			
		||||
    def do_config(self, parent=None):
 | 
			
		||||
        d = ConfigDialog(parent)
 | 
			
		||||
        d = ConfigDialog(self.shortcuts, parent)
 | 
			
		||||
        if d.exec_() == QDialog.Accepted:
 | 
			
		||||
            self.set_font_settings()
 | 
			
		||||
            self.set_user_stylesheet()
 | 
			
		||||
            self.misc_config()
 | 
			
		||||
            self.triggerAction(QWebPage.Reload)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args):
 | 
			
		||||
        QWebPage.__init__(self, *args)
 | 
			
		||||
    def __init__(self, shortcuts, parent=None):
 | 
			
		||||
        QWebPage.__init__(self, parent)
 | 
			
		||||
        self.setObjectName("py_bridge")
 | 
			
		||||
        self.debug_javascript = False
 | 
			
		||||
        self.current_language = None
 | 
			
		||||
@ -155,6 +161,7 @@ class Document(QWebPage):
 | 
			
		||||
 | 
			
		||||
        self.setLinkDelegationPolicy(self.DelegateAllLinks)
 | 
			
		||||
        self.scroll_marks = []
 | 
			
		||||
        self.shortcuts = shortcuts
 | 
			
		||||
        pal = self.palette()
 | 
			
		||||
        pal.setBrush(QPalette.Background, QColor(0xee, 0xee, 0xee))
 | 
			
		||||
        self.setPalette(pal)
 | 
			
		||||
@ -366,13 +373,14 @@ class DocumentView(QWebView):
 | 
			
		||||
    def __init__(self, *args):
 | 
			
		||||
        QWebView.__init__(self, *args)
 | 
			
		||||
        self.debug_javascript = False
 | 
			
		||||
        self.shortcuts =  Shortcuts(SHORTCUTS, 'shortcuts/viewer')
 | 
			
		||||
        self.self_closing_pat = re.compile(r'<([a-z]+)\s+([^>]+)/>',
 | 
			
		||||
                re.IGNORECASE)
 | 
			
		||||
        self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
 | 
			
		||||
        self._size_hint = QSize(510, 680)
 | 
			
		||||
        self.initial_pos = 0.0
 | 
			
		||||
        self.to_bottom = False
 | 
			
		||||
        self.document = Document(self)
 | 
			
		||||
        self.document = Document(self.shortcuts, parent=self)
 | 
			
		||||
        self.setPage(self.document)
 | 
			
		||||
        self.manager = None
 | 
			
		||||
        self._reference_mode = False
 | 
			
		||||
@ -407,6 +415,7 @@ class DocumentView(QWebView):
 | 
			
		||||
        self.document.do_config(parent)
 | 
			
		||||
        if self.manager is not None:
 | 
			
		||||
            self.manager.set_max_width()
 | 
			
		||||
        self.setFocus(Qt.OtherFocusReason)
 | 
			
		||||
 | 
			
		||||
    def bookmark(self):
 | 
			
		||||
        return self.document.bookmark()
 | 
			
		||||
@ -663,30 +672,30 @@ class DocumentView(QWebView):
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def keyPressEvent(self, event):
 | 
			
		||||
        key = event.key()
 | 
			
		||||
        if key in [Qt.Key_PageDown, Qt.Key_Space, Qt.Key_Down]:
 | 
			
		||||
        key = self.shortcuts.get_match(event)
 | 
			
		||||
        if key == 'Next Page':
 | 
			
		||||
            self.next_page()
 | 
			
		||||
        elif key in [Qt.Key_PageUp, Qt.Key_Backspace, Qt.Key_Up]:
 | 
			
		||||
        elif key == 'Previous Page':
 | 
			
		||||
            self.previous_page()
 | 
			
		||||
        elif key in [Qt.Key_Home]:
 | 
			
		||||
        elif key == 'Section Top':
 | 
			
		||||
            if event.modifiers() & Qt.ControlModifier:
 | 
			
		||||
                if self.manager is not None:
 | 
			
		||||
                    self.manager.goto_start()
 | 
			
		||||
            else:
 | 
			
		||||
                self.scroll_to(0)
 | 
			
		||||
        elif key in [Qt.Key_End]:
 | 
			
		||||
        elif key == 'Section Bottom':
 | 
			
		||||
            if event.modifiers() & Qt.ControlModifier:
 | 
			
		||||
                if self.manager is not None:
 | 
			
		||||
                    self.manager.goto_end()
 | 
			
		||||
            else:
 | 
			
		||||
                self.scroll_to(1)
 | 
			
		||||
        elif key in [Qt.Key_J]:
 | 
			
		||||
        elif key == 'Down':
 | 
			
		||||
            self.scroll_by(y=15)
 | 
			
		||||
        elif key in [Qt.Key_K]:
 | 
			
		||||
        elif key == 'Up':
 | 
			
		||||
            self.scroll_by(y=-15)
 | 
			
		||||
        elif key in [Qt.Key_H]:
 | 
			
		||||
        elif key == 'Left':
 | 
			
		||||
            self.scroll_by(x=-15)
 | 
			
		||||
        elif key in [Qt.Key_L]:
 | 
			
		||||
        elif key == 'Right':
 | 
			
		||||
            self.scroll_by(x=15)
 | 
			
		||||
        else:
 | 
			
		||||
            return QWebView.keyPressEvent(self, event)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										47
									
								
								src/calibre/gui2/viewer/keys.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/calibre/gui2/viewer/keys.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
 | 
			
		||||
from __future__ import with_statement
 | 
			
		||||
 | 
			
		||||
__license__   = 'GPL v3'
 | 
			
		||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
 | 
			
		||||
__docformat__ = 'restructuredtext en'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SHORTCUTS = {
 | 
			
		||||
        'Next Page' : (['PgDown', 'Space'],
 | 
			
		||||
            _('Scroll to the next page')),
 | 
			
		||||
 | 
			
		||||
        'Previous Page' : (['PgUp', 'Backspace'],
 | 
			
		||||
            _('Scroll to the previous page')),
 | 
			
		||||
 | 
			
		||||
        'Next Section' : (['Ctrl+PgDown', 'Ctrl+Down'],
 | 
			
		||||
            _('Scroll to the next section')),
 | 
			
		||||
 | 
			
		||||
        'Previous Section' : (['Ctrl+PgUp', 'Ctrl+Up'],
 | 
			
		||||
            _('Scroll to the previous section')),
 | 
			
		||||
 | 
			
		||||
        'Section Bottom' : (['End'],
 | 
			
		||||
            _('Scroll to the bottom of the section')),
 | 
			
		||||
 | 
			
		||||
        'Section Top' : (['Home'],
 | 
			
		||||
            _('Scroll to the top of the section')),
 | 
			
		||||
 | 
			
		||||
        'Document Bottom' : (['Ctrl+End'],
 | 
			
		||||
            _('Scroll to the end of the document')),
 | 
			
		||||
 | 
			
		||||
        'Document Top' : (['Ctrl+Home'],
 | 
			
		||||
            _('Scroll to the start of the document')),
 | 
			
		||||
 | 
			
		||||
        'Down' : (['J', 'Down'],
 | 
			
		||||
            _('Scroll down')),
 | 
			
		||||
 | 
			
		||||
        'Up' : (['K', 'Up'],
 | 
			
		||||
            _('Scroll up')),
 | 
			
		||||
 | 
			
		||||
        'Left' : (['H', 'Left'],
 | 
			
		||||
            _('Scroll left')),
 | 
			
		||||
 | 
			
		||||
        'Right' : (['L', 'Right'],
 | 
			
		||||
            _('Scroll right')),
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -264,7 +264,7 @@ Why does |app| show only some of my fonts on OS X?
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
|app| embeds fonts in ebook files it creates. E-book files support embedding only TrueType (.ttf) fonts. Most fonts on OS X systems are in .dfont format, thus they cannot be embedded. |app| shows only TrueType fonts founf on your system. You can obtain many TrueType fonts on the web. Simply download the .ttf files and add them to the Library/Fonts directory in your home directory. 
 | 
			
		||||
 | 
			
		||||
The graphical user interface of |app| is not starting on Windows?
 | 
			
		||||
|app| is not starting on Windows?
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
There can be several causes for this:
 | 
			
		||||
 | 
			
		||||
@ -280,7 +280,19 @@ If it still wont launch, start a command prompt (press the windows key and R; th
 | 
			
		||||
 | 
			
		||||
Post any output you see in a help message on the `Forum <http://www.mobileread.com/forums/forumdisplay.php?f=166>`_.
 | 
			
		||||
 | 
			
		||||
My antivirus programs claims |app| is a virus/trojan?
 | 
			
		||||
|app| is not starting on OS X?
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
You can obtain debug output about why |app| is not starting by running `Console.app`. Debug output will
 | 
			
		||||
be printed to it. If the debug output contains a line that looks like::
 | 
			
		||||
 | 
			
		||||
    Qt: internal: -108: Error ATSUMeasureTextImage text/qfontengine_mac.mm
 | 
			
		||||
 | 
			
		||||
then the problem is a corrupted font cache. You can clear the cache by following these
 | 
			
		||||
`instructions <http://www.macworld.com/article/139383/2009/03/fontcacheclear.html>`_.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
My antivirus program claims |app| is a virus/trojan?
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
Your antivirus program is wrong. |app| is a completely open source product. You can actually browse the source code yourself (or hire someone to do it for you) to verify that it is not a virus. Please report the false identification to whatever company you buy your antivirus software from. If the antivirus program is preventing you from downloading/installing |app|, disable it temporarily, install |app| and then re-enable it.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -608,6 +608,10 @@ class XMLConfig(dict):
 | 
			
		||||
    def set(self, key, val):
 | 
			
		||||
        self.__setitem__(key, val)
 | 
			
		||||
 | 
			
		||||
    def __delitem__(self, key):
 | 
			
		||||
        dict.__delitem__(self, key)
 | 
			
		||||
        self.commit()
 | 
			
		||||
 | 
			
		||||
    def commit(self):
 | 
			
		||||
        if hasattr(self, 'file_path') and self.file_path:
 | 
			
		||||
            dpath = os.path.dirname(self.file_path)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user