Example #1
0
    def __init__(self, parent):
        super(CharacterDialog, self).__init__(parent)
        self.setWindowTitle(_("KeepKey Seed Recovery"))
        self.character_pos = 0
        self.word_pos = 0
        self.loop = QEventLoop()
        self.word_help = QLabel()
        self.char_buttons = []

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(CHARACTER_RECOVERY))
        hbox = QHBoxLayout()
        hbox.addWidget(self.word_help)
        for i in range(4):
            char_button = CharacterButton('*')
            char_button.setMaximumWidth(36)
            self.char_buttons.append(char_button)
            hbox.addWidget(char_button)
        self.accept_button = CharacterButton(_("Accept Word"))
        self.accept_button.clicked.connect(partial(self.process_key, 32))
        self.rejected.connect(partial(self.loop.exit, 1))
        hbox.addWidget(self.accept_button)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        self.finished_button = QPushButton(_("Seed Entered"))
        self.cancel_button = QPushButton(_("Cancel"))
        self.finished_button.clicked.connect(partial(self.process_key,
                                                     Qt.Key_Return))
        self.cancel_button.clicked.connect(self.rejected)
        buttons = Buttons(self.finished_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Example #2
0
    def __init__(self, show_strength=True, parent=None):
        super(PinMatrixWidget, self).__init__(parent)
        
        self.password = QLineEdit()
        self.password.setValidator(QRegExpValidator(QRegExp('[1-9]+'), None))
        self.password.setEchoMode(QLineEdit.Password)
        QObject.connect(self.password, SIGNAL('textChanged(QString)'), self._password_changed)

        self.strength = QLabel()
        self.strength.setMinimumWidth(75)
        self.strength.setAlignment(Qt.AlignCenter)
        self._set_strength(0)

        grid = QGridLayout()
        grid.setSpacing(0)
        for y in range(3)[::-1]:
            for x in range(3):
                button = PinButton(self.password, x + y * 3 + 1)
                button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
                button.setFocusPolicy(Qt.NoFocus)
                grid.addWidget(button, 3 - y, x)

        hbox = QHBoxLayout()
        hbox.addWidget(self.password)
        if show_strength:
            hbox.addWidget(self.strength)

        vbox = QVBoxLayout()
        vbox.addLayout(grid)
        vbox.addLayout(hbox)
        self.setLayout(vbox)
Example #3
0
 def __init__(self, parent, modal=True, flags=Qt.WindowFlags(), caption="Select Tags", ok_button="Select"):
     QDialog.__init__(self, parent, flags)
     self.setModal(modal)
     self.setWindowTitle(caption)
     lo = QVBoxLayout(self)
     lo.setMargin(10)
     lo.setSpacing(5)
     # tag selector
     self.wtagsel = QListWidget(self)
     lo.addWidget(self.wtagsel)
     #    self.wtagsel.setColumnMode(QListBox.FitToWidth)
     self.wtagsel.setSelectionMode(QListWidget.MultiSelection)
     QObject.connect(self.wtagsel, SIGNAL("itemSelectionChanged()"), self._check_tag)
     # buttons
     lo.addSpacing(10)
     lo2 = QHBoxLayout()
     lo.addLayout(lo2)
     lo2.setContentsMargins(0, 0, 0, 0)
     lo2.setMargin(5)
     self.wokbtn = QPushButton(ok_button, self)
     self.wokbtn.setMinimumWidth(128)
     QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept)
     self.wokbtn.setEnabled(False)
     cancelbtn = QPushButton("Cancel", self)
     cancelbtn.setMinimumWidth(128)
     QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject)
     lo2.addWidget(self.wokbtn)
     lo2.addStretch(1)
     lo2.addWidget(cancelbtn)
     self.setMinimumWidth(384)
     self._tagnames = []
    def __init__(self):
        QGroupBox.__init__(self)

        self.treemodel = treemodel = TestNe1Model()

        self.view = view = ModelTree("Model tree", treemodel, self)
        view.mt_update()

        def thunk(str):
            def _thunk(str=str):
                print str
            return _thunk

        self.chunkNum = 2
        self.gbox = QGroupBox()
        vl = QVBoxLayout(self)
        vl.setSpacing(0)
        vl.setMargin(0)
        vl.addWidget(self.view)
        self.buttonLayout = hl = QHBoxLayout()
        hl.setSpacing(0)
        hl.setMargin(0)
        vl.addLayout(hl)
        self.buttonNum = 1
        for func in (self.addmol, self.addjig, self.selected):
            self.addButton(func)
    def _create_widgets(self, parent):

        # Layout
        GBuilder().set_props(self, None, 100, 100)  
        main_lo = QVBoxLayout()
        self.setLayout(main_lo)
        
        # Title
        main_lo.addWidget(GBuilder().label(parent, "<b>Description:</b>"))
        hl = QHBoxLayout()        
        self.desc_label = GBuilder().label(parent, "Add a new Standard Bus. This CAN Bus is a simple implementation of a automotive link.")
        self.desc_label.setFixedWidth(400)
        self.icon = GBuilder().image(parent, StdBusAddWidget.GUI_ICON, 2)        
        hl.addWidget(self.desc_label)
        hl.addWidget(self.icon)
        main_lo.addLayout(hl)
        
        line = GBuilder().hor_line(parent)
        main_lo.addWidget(line);
                
        # Constructor Inputs
        main_lo.addWidget(GBuilder().label(parent, "<b>General Information:</b>"))       
        lo0, self.id_list = GBuilder().label_text(parent, "List of IDs (optional):", label_width=120)
        lo1, self.nr_ecus_textedit = GBuilder().label_text(parent, "Number of Busses:", label_width=120)
        main_lo.addLayout(lo0)
        main_lo.addLayout(lo1)
class ConfigWidget(QWidget):

    config_help_message = 'Set your API keys and access information for online querying services'
    
    def __init__(self, plugin):
        super(ConfigWidget, self).__init__()
        
        self.vlayout = QVBoxLayout()
        self.setLayout(self.vlayout)

        self.dcinput = {}

        for key in prefs.defaults:
            hlayout = QHBoxLayout()
            txtlabel = QLabel(key.replace("_", " "))
            txtinput = QLineEdit()
            txtinput.setText(str(prefs[key]))
            txtlabel.setBuddy(txtinput)
            self.dcinput[key] = txtinput
            
            hlayout.addWidget(txtlabel)
            hlayout.addWidget(txtinput)
            self.vlayout.addLayout(hlayout)

    def save_settings(self):
        for key in prefs.defaults:
            prefs[key] = unicode(self.dcinput[key].text())
Example #7
0
    def set_viewer_group(self):
        
        ''' 1. Load Items from viewer factory '''
        view_fact = ViewerPluginFactory()
        self.viewer_stack = QtGui.QStackedWidget()        
        self.viewer_cbox = self.builder.combobox(self.groupb_viewer, [], self.set_cb_changed_event_view)
        self.set_start_button = self.builder.pushbutton(self.groupb_viewer, 'Start Simulation', self.create_aut_env)
        self.set_start_button.setFixedWidth(200)
        self.set_start_button.setFixedHeight(25)

        for viewer_plugin in view_fact.createable_objects():
            view = view_fact.make(viewer_plugin)
            if view != None:
                view.set_monitor(self.monitor)                 
                self.viewer_stack.addWidget(view.get_widget(self))
                self.viewer_cbox.addItem(view.get_combobox_name())                

        ''' layout '''
        v_layout = QVBoxLayout()                
        self.viewer_cbox.setFixedHeight(25)       
        v_layout.addWidget(self.viewer_stack)  
        
        hl = QHBoxLayout()
        hl.addWidget(self.viewer_cbox)
        hl.addWidget(self.set_start_button)
        v_layout.addLayout(hl)           
              
        self.groupb_viewer.setLayout(v_layout)    
    def _init_controls(self):
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        ml = QHBoxLayout()
        layout.addLayout(ml, 1)

        self.keys_list = QListWidget(self)
        self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection)
        self.keys_list.setFixedWidth(150)
        self.keys_list.setAlternatingRowColors(True)
        ml.addWidget(self.keys_list)
        self.value_text = QTextEdit(self)
        self.value_text.setTabStopWidth(24)
        self.value_text.setReadOnly(False)
        ml.addWidget(self.value_text, 1)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self._apply_changes)
        button_box.rejected.connect(self.reject)
        self.clear_button = button_box.addButton('Clear', QDialogButtonBox.ResetRole)
        self.clear_button.setIcon(get_icon('trash.png'))
        self.clear_button.setToolTip('Clear all settings for this plugin')
        self.clear_button.clicked.connect(self._clear_settings)
        layout.addWidget(button_box)
Example #9
0
    def __init__(self, parent=None,):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        data_group_box = QGroupBox(u"", self)
        layout.addWidget(data_group_box)
        data_group_box_layout = QVBoxLayout()
        data_group_box.setLayout(data_group_box_layout)

        key_group = QHBoxLayout()
        data_group_box_layout.addLayout(key_group)
        key_group.addWidget(QLabel(u"EInk Kindle Serial Number:", self))
        self.key_ledit = QLineEdit("", self)
        self.key_ledit.setToolTip(u"Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
        key_group.addWidget(self.key_ledit)
        key_label = QLabel(_(''), self)
        key_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(key_label)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Example #10
0
    def __init__(self, parent=None,):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        data_group_box = QGroupBox(u"", self)
        layout.addWidget(data_group_box)
        data_group_box_layout = QVBoxLayout()
        data_group_box.setLayout(data_group_box_layout)

        key_group = QHBoxLayout()
        data_group_box_layout.addLayout(key_group)
        key_group.addWidget(QLabel(u"PID:", self))
        self.key_ledit = QLineEdit("", self)
        self.key_ledit.setToolTip(u"Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
        key_group.addWidget(self.key_ledit)
        key_label = QLabel(_(''), self)
        key_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(key_label)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
    def _create_widgets(self, parent):

        # Layout
        GBuilder().set_props(self, None, 100, 100)  
        main_lo = QVBoxLayout()
        self.setLayout(main_lo)
        
        # Title
        main_lo.addWidget(GBuilder().label(parent, "<b>Description:</b>"))
        hl = QHBoxLayout()        
        self.desc_label = GBuilder().label(parent, "Add a new SecureECU. This ECU resembles the ECU Part in a Lightweight Authentication Mechanism.")
        self.desc_label.setFixedWidth(400)
        self.icon = GBuilder().image(parent, SecLwAuthSecurityModuleAddWidget.GUI_ICON, 2)        
        hl.addWidget(self.desc_label)
        hl.addWidget(self.icon)
        main_lo.addLayout(hl)
        
        line = GBuilder().hor_line(parent)
        main_lo.addWidget(line);
                
        # Constructor Inputs
        main_lo.addWidget(GBuilder().label(parent, "<b>General Information:</b>"))
        lo0, self.id_list = GBuilder().label_text(parent, "List of IDs (optional):", label_width=120)
        lo1, self.send_buf_textedit = GBuilder().label_text(parent, "Sending BufferSize:", label_width=120)
        lo2, self.rec_buf_textedit = GBuilder().label_text(parent, "Receiving Buffer Size:", label_width=120)
        lo3, self.nr_ecus_textedit = GBuilder().label_text(parent, "Number of ECUs:", label_width=120)
        main_lo.addLayout(lo0)
        main_lo.addLayout(lo1)
        main_lo.addLayout(lo2)
        main_lo.addLayout(lo3)

        # ECU Settings
        items = self._get_ecu_settings()
        hl, self.ecu_set_cb, self.ecu_set_te = GBuilder().combobox_text(parent, items, self._set_cb_changed)
        self._cur_set_entry = self.ecu_set_cb.currentText()
        main_lo.addLayout(hl)
        
        # Timing Mapping 
        line = GBuilder().hor_line(parent)
        main_lo.addWidget(line)
        lab = GBuilder().label(parent, "<b>Timing and Certification:</b>")
        lab.setFixedHeight(20)
        main_lo.addWidget(lab)
        
        itm = StdSecurLwSecModTimingFunctions()
        avail_items = itm.available_tags
        items = itm.function_map.keys()        
        hl1 = QHBoxLayout()
        self.ecu_set_time_cb = GBuilder().combobox(parent, items, self._set_time_cb_changed)
        self._cur_set_time_entry = self.ecu_set_time_cb.currentText()   
        
        self.ecu_set_time_sel_cb = GBuilder().combobox(parent, avail_items, self._set_time_cb_changed)
        self._cur_set_time_sel_entry = self.ecu_set_time_sel_cb.currentText()
        hl1.addWidget(self.ecu_set_time_cb)
        hl1.addWidget(self.ecu_set_time_sel_cb)
        main_lo.addLayout(hl1)
Example #12
0
 def __init__(self, parent, flags=Qt.WindowFlags()):
     QDialog.__init__(self, parent, flags)
     self.setModal(False)
     self.setWindowTitle("Select sources by...")
     lo = QVBoxLayout(self)
     lo.setMargin(10)
     lo.setSpacing(5)
     # select by
     lo1 = QHBoxLayout()
     lo.addLayout(lo1)
     lo1.setContentsMargins(0, 0, 0, 0)
     #    lab = QLabel("Select:")
     #   lo1.addWidget(lab)
     self.wselby = QComboBox(self)
     lo1.addWidget(self.wselby, 0)
     QObject.connect(self.wselby, SIGNAL("activated(const QString &)"), self._setup_selection_by)
     # under/over
     self.wgele = QComboBox(self)
     lo1.addWidget(self.wgele, 0)
     self.wgele.addItems([">", ">=", "<=", "<", "sum<=", "sum>"])
     QObject.connect(self.wgele, SIGNAL("activated(const QString &)"), self._select_threshold)
     # threshold value
     self.wthreshold = QLineEdit(self)
     QObject.connect(self.wthreshold, SIGNAL("editingFinished()"), self._select_threshold)
     lo1.addWidget(self.wthreshold, 1)
     # min and max label
     self.wminmax = QLabel(self)
     lo.addWidget(self.wminmax)
     # selection slider
     lo1 = QHBoxLayout()
     lo.addLayout(lo1)
     self.wpercent = QSlider(self)
     self.wpercent.setTracking(False)
     QObject.connect(self.wpercent, SIGNAL("valueChanged(int)"), self._select_percentile)
     QObject.connect(self.wpercent, SIGNAL("sliderMoved(int)"), self._select_percentile_threshold)
     self.wpercent.setRange(0, 100)
     self.wpercent.setOrientation(Qt.Horizontal)
     lo1.addWidget(self.wpercent)
     self.wpercent_lbl = QLabel("0%", self)
     self.wpercent_lbl.setMinimumWidth(64)
     lo1.addWidget(self.wpercent_lbl)
     #    # hide button
     #    lo.addSpacing(10)
     #    lo2 = QHBoxLayout()
     #    lo.addLayout(lo2)
     #    lo2.setContentsMargins(0,0,0,0)
     #    hidebtn = QPushButton("Close",self)
     #    hidebtn.setMinimumWidth(128)
     #    QObject.connect(hidebtn,SIGNAL("clicked()"),self.hide)
     #    lo2.addStretch(1)
     #    lo2.addWidget(hidebtn)
     #    lo2.addStretch(1)
     #    self.setMinimumWidth(384)
     self._in_select_threshold = False
     self._sort_index = None
     self.qerrmsg = QErrorMessage(self)
Example #13
0
 def _create_widget(self):
     
     self.builder.set_props(self, False, 820, 300)
     self.setWindowIcon(QtGui.QIcon( os.getcwd() +r'/icons/tumcreatelogo2.png'))
     
     self.tit_label = self.builder.label(self, "Description: \nEdit the Database request and the conditions")
         
     self.left_label = self.builder.label(self, "Database request:")    
     
     self.db_textbox = QtGui.QTextEdit()
 
 
     self.cond_label = self.builder.label(self, "Conditions for Database with ID:") 
     self.condition_tab = self.builder.table(self, len(self.condition), 6, ["ID", 'Lookup DB', "Var Name", "Var Config", "Type", "Value"], False)
     self.condition_tab.verticalHeader().hide()
     self.condition_tab.setColumnWidth(2, 250)
     
     
     self.add_pb = self.builder.pushbutton(self, "Add Item", self.add_hit, icon_path = os.getcwd() +r'/icons/add.png')
     self.add_pb.setFixedHeight(30)
     self.add_pb.setFixedWidth(200)
     self.del_pb = self.builder.pushbutton(self, "Delete Item", self.delete_hit, icon_path = os.getcwd() +r'/icons/delete.png')
     self.del_pb.setFixedHeight(30)
     self.del_pb.setFixedWidth(200)
     
     
     self.space = self.builder.label(self, "")
     self.ok_pb = self.builder.pushbutton(self, "Ok", self.ok_hit)
     self.ok_pb.setFixedHeight(30)
     self.ok_pb.setFixedWidth(140)
     self.cancel_pb = self.builder.pushbutton(self, "Cancel", self.cancel_hit)
     self.cancel_pb.setFixedHeight(30)
     self.cancel_pb.setFixedWidth(140)
             
 
     ''' Layout '''
     v_lay = QVBoxLayout()
     
     v_lay.addWidget(self.tit_label)
     
     v_lay.addWidget(self.left_label)
 
     v_lay.addWidget(self.db_textbox)
     v_lay.addWidget(self.cond_label)
     v_lay.addWidget(self.condition_tab)
 
     hl = QHBoxLayout()
     hl.addWidget(self.add_pb)
     hl.addWidget(self.del_pb) 
     
     hl.addWidget(self.space)
     hl.addWidget(self.ok_pb)
     hl.addWidget(self.cancel_pb)   
     v_lay.addLayout(hl)
 
     self.setLayout(v_lay)
Example #14
0
class Dialog(QDialog):
    def __init__(self, title, widget=None, closeButton=True, keySequence=None, isDialog=False, icon=None):
        QDialog.__init__(self, ctx.mainScreen)
        self.setObjectName("dialog")

        self.isDialog = isDialog
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.wlayout= QHBoxLayout()

        if icon:
            self.setStyleSheet("""QDialog QLabel{ margin-left:16px;margin-right:10px}
                                  QDialog#dialog {background-image:url(':/images/%s.png');
                                                  background-repeat:no-repeat;
                                                  background-position: top left; padding-left:500px;} """ % icon)

        self.windowTitle = windowTitle(self, closeButton)
        self.setTitle(title)
        self.layout.setMargin(0)
        self.layout.addWidget(self.windowTitle)

        if widget:
            self.addWidget(widget)
            QObject.connect(widget, SIGNAL("finished(int)"), self.reject)
            QObject.connect(widget, SIGNAL("resizeDialog(int,int)"), self.resize)

        if closeButton:
            QObject.connect(self.windowTitle.pushButton, SIGNAL("clicked()"), self.reject)

        if keySequence:
            shortCut = QShortcut(keySequence, self)
            QObject.connect(shortCut, SIGNAL("activated()"), self.reject)

        QMetaObject.connectSlotsByName(self)
        self.resize(10,10)

    def setTitle(self, title):
        self.windowTitle.label.setText(title)

    def addWidget(self, widget):
        self.content = widget
        self.wlayout.addWidget(self.content)
        if self.isDialog:
            widget.setStyleSheet("QMessageBox { background:none }")
            self.layout.addItem(QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.layout.setContentsMargins(0, 0, 0, 8)
        self.layout.addLayout(self.wlayout)

    def setCentered(self):
        self.move(ctx.mainScreen.width()/2 - self.width()/2,
                  ctx.mainScreen.height()/2 - self.height()/2)

    def exec_(self):
        QTimer.singleShot(0, self.setCentered)
        return QDialog.exec_(self)
Example #15
0
 def message_dialog(self, msg, cancel_callback):
     # Called more than once during signing, to confirm output and fee
     self.clear_dialog()
     title = _('Please check your %s device') % self.device
     self.dialog = dialog = WindowModalDialog(self.window_stack[-1], title)
     l = QLabel(msg)
     vbox = QVBoxLayout(dialog)
     if cancel_callback:
         vbox.addLayout(Buttons(CancelButton(dialog)))
         dialog.connect(dialog, SIGNAL('rejected()'), cancel_callback)
     vbox.addWidget(l)
     dialog.show()
Example #16
0
 def pin_dialog(self, msg):
     # Needed e.g. when renaming label and haven't entered PIN
     dialog = WindowModalDialog(self.window_stack[-1], _("Enter PIN"))
     matrix = self.pin_matrix_widget_class()
     vbox = QVBoxLayout()
     vbox.addWidget(QLabel(msg))
     vbox.addWidget(matrix)
     vbox.addLayout(Buttons(CancelButton(dialog), OkButton(dialog)))
     dialog.setLayout(vbox)
     dialog.exec_()
     self.response = str(matrix.get_value())
     self.done.set()
Example #17
0
 def message_dialog(self, msg, on_cancel):
     # Called more than once during signing, to confirm output and fee
     self.clear_dialog()
     title = _('Please check your %s device') % self.device
     self.dialog = dialog = WindowModalDialog(self.top_level_window(), title)
     l = QLabel(msg)
     vbox = QVBoxLayout(dialog)
     vbox.addWidget(l)
     if on_cancel:
         dialog.rejected.connect(on_cancel)
         vbox.addLayout(Buttons(CancelButton(dialog)))
     dialog.show()
Example #18
0
    def setup_ui(self):
        self.l = l = QVBoxLayout(self)
        self.h = h = QHBoxLayout()
        l.addLayout(h)
        l.addWidget(self.bb)
        self.bb.clear(), self.bb.addButton(self.bb.Close)
        b = self.bb.addButton(_('&New dictionary'), self.bb.ActionRole)
        b.setIcon(QIcon(I('spell-check.png')))
        b.clicked.connect(self.new_dictionary)

        self.dictionaries = d = QListWidget(self)
        self.emph_font = f = QFont(self.font())
        f.setBold(True)
        self.build_dictionaries()
        d.currentItemChanged.connect(self.show_current_dictionary)
        h.addWidget(d)

        l = QVBoxLayout()
        h.addLayout(l)
        h = QHBoxLayout()
        self.remove_button = b = QPushButton(QIcon(I('trash.png')), _('&Remove dictionary'), self)
        b.clicked.connect(self.remove_dictionary)
        h.addWidget(b)
        self.rename_button = b = QPushButton(QIcon(I('modified.png')), _('Re&name dictionary'), self)
        b.clicked.connect(self.rename_dictionary)
        h.addWidget(b)
        self.dlabel = la = QLabel('')
        l.addWidget(la)
        l.addLayout(h)
        self.is_active = a = QCheckBox(_('Mark this dictionary as active'))
        self.is_active.stateChanged.connect(self.active_toggled)
        l.addWidget(a)
        self.la = la = QLabel(_('Words in this dictionary:'))
        l.addWidget(la)
        self.words = w = QListWidget(self)
        w.setSelectionMode(w.ExtendedSelection)
        l.addWidget(w)
        self.add_word_button = b = QPushButton(_('&Add word'), self)
        b.clicked.connect(self.add_word)
        b.setIcon(QIcon(I('plus.png')))
        l.h = h = QHBoxLayout()
        l.addLayout(h)
        h.addWidget(b)
        self.remove_word_button = b = QPushButton(_('&Remove selected words'), self)
        b.clicked.connect(self.remove_word)
        b.setIcon(QIcon(I('minus.png')))
        h.addWidget(b)
        self.import_words_button = b = QPushButton(_('&Import list of words'), self)
        b.clicked.connect(self.import_words)
        l.addWidget(b)

        self.show_current_dictionary()
Example #19
0
    def __init__(self, parent=None,):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        try:
            if iswindows or isosx:
                from calibre_plugins.dedrm.kindlekey import kindlekeys

                defaultkeys = kindlekeys()
            else: # linux
                from wineutils import WineGetKeys

                scriptpath = os.path.join(parent.parent.alfdir,u"kindlekey.py")
                defaultkeys = WineGetKeys(scriptpath, u".k4i",parent.getwineprefix())

            self.default_key = defaultkeys[0]
        except:
            traceback.print_exc()
            self.default_key = u""

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        if len(self.default_key)>0:
            data_group_box = QGroupBox(u"", self)
            layout.addWidget(data_group_box)
            data_group_box_layout = QVBoxLayout()
            data_group_box.setLayout(data_group_box_layout)

            key_group = QHBoxLayout()
            data_group_box_layout.addLayout(key_group)
            key_group.addWidget(QLabel(u"Unique Key Name:", self))
            self.key_ledit = QLineEdit(u"default_key", self)
            self.key_ledit.setToolTip(u"<p>Enter an identifying name for the current default Kindle for Mac/PC key.")
            key_group.addWidget(self.key_ledit)
            key_label = QLabel(_(''), self)
            key_label.setAlignment(Qt.AlignHCenter)
            data_group_box_layout.addWidget(key_label)
            self.button_box.accepted.connect(self.accept)
        else:
            default_key_error = QLabel(u"The default encryption key for Kindle for Mac/PC could not be found.", self)
            default_key_error.setAlignment(Qt.AlignHCenter)
            layout.addWidget(default_key_error)
            # if no default, bot buttons do the same
            self.button_box.accepted.connect(self.reject)

        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Example #20
0
 def pin_dialog(self, msg):
     # Needed e.g. when resetting a device
     self.clear_dialog()
     dialog = WindowModalDialog(self.top_level_window(), _("Enter PIN"))
     matrix = self.pin_matrix_widget_class()
     vbox = QVBoxLayout()
     vbox.addWidget(QLabel(msg))
     vbox.addWidget(matrix)
     vbox.addLayout(Buttons(CancelButton(dialog), OkButton(dialog)))
     dialog.setLayout(vbox)
     dialog.exec_()
     self.response = str(matrix.get_value())
     self.done.set()
Example #21
0
    def create_widgets(self):
        
        ''' Create Widgets'''
        self.builder.set_props(self, False, 1200, 740)

        self.desc_label = self.builder.label(self, "Description: \nEdit an existing Timing Mapping Parameter Set or create a new one")
        self.desc_label.setFixedHeight(50)
        
        self.set_cb = self.builder.combobox(self, [], self.set_cb_selection_changed)
        self.set_cb.setFixedHeight(22)
        
        self.set_plus_pb = self.builder.pushbutton(self, "Add", self.add_hit, os.getcwd() +r'/icons/add.png')
        self.set_plus_pb.setFixedWidth(100)
        self.set_plus_pb.setFixedHeight(30)
                
        self.elem_tab = self.builder.table(self, 0, 5, ["Affiliation", "Timing Variable", "Type", "Value", "Condition"])        
        self.elem_tab.cellDoubleClicked.connect(self._cell_double_clicked)
        
        ''' Save and Exit Buttons '''        
        self.space = self.builder.label(self, "")
#         self.space.setFixedWidth(300)
        self.ok_pb = self.builder.pushbutton(self, "Ok", self.ok_hit)
        self.ok_pb.setFixedHeight(30)
        self.ok_pb.setFixedWidth(140)
        self.apply_pb = self.builder.pushbutton(self, "Apply", self.apply_hit)
        self.apply_pb.setFixedHeight(30)
        self.apply_pb.setFixedWidth(140)
        self.cancel_pb = self.builder.pushbutton(self, "Cancel", self.cancel_hit)
        self.cancel_pb.setFixedHeight(30)
        self.cancel_pb.setFixedWidth(140)
                
        ''' Layout '''
        v_lay = QVBoxLayout()        
        v_lay.addWidget(self.desc_label)
        
        hl = QHBoxLayout()
        hl.addWidget(self.set_plus_pb)
        hl.addWidget(self.set_cb)
        v_lay.addLayout(hl)
        
        v_lay.addWidget(self.elem_tab)     
        
        hl = QHBoxLayout()
        hl.addWidget(self.space)
        hl.addWidget(self.ok_pb)
        hl.addWidget(self.cancel_pb)   
        hl.addWidget(self.apply_pb)
        v_lay.addLayout(hl)
        
        self.setLayout(v_lay)       
    def message_dialog(self):
        self.d = QDialog()
        self.d.setModal(1)
        self.d.setWindowTitle("Please Check KeepKey Device")
        self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
        l = QLabel(self.message)
        vbox = QVBoxLayout(self.d)
        vbox.addWidget(l)

        if self.messsage_code in (3, 8):
            vbox.addLayout(Buttons(CancelButton(self.d)))
            self.d.connect(self.d, SIGNAL("rejected()"), self.client.cancel)

        self.d.show()
Example #23
0
    def pin_dialog(self, msg):
        d = QDialog(None)
        d.setModal(1)
        d.setWindowTitle(_("Enter PIN"))
        matrix = PinMatrixWidget()

        vbox = QVBoxLayout()
        vbox.addWidget(QLabel(msg))
        vbox.addWidget(matrix)
        vbox.addLayout(ok_cancel_buttons(d))
        d.setLayout(vbox)

        if not d.exec_(): return
        return str(matrix.get_value())
 def pin_dialog(self):
     d = QDialog(None)
     d.setModal(1)
     d.setWindowTitle(_("Enter PIN"))
     d.setWindowFlags(d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
     matrix = PinMatrixWidget()
     vbox = QVBoxLayout()
     vbox.addWidget(QLabel(self.message))
     vbox.addWidget(matrix)
     vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
     d.setLayout(vbox)
     if not d.exec_():
         self.response = None
     self.response = str(matrix.get_value())
     self.done.set()
Example #25
0
class ConfigWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        self.l5 = QHBoxLayout()
        self.server_prefix_label = QLabel('Server prefix:')
        self.l5.addWidget(self.server_prefix_label)

        self.server_prefix = QLineEdit(self)
        self.server_prefix.setText(prefs['server_prefix'])
        self.l5.addWidget(self.server_prefix)
        self.server_prefix_label.setBuddy(self.server_prefix)
        self.l.addLayout(self.l5)

        self.ll = QHBoxLayout()
        self.server_label = QLabel('Server:')
        self.ll.addWidget(self.server_label)

        self.lsb_server = QLineEdit(self)
        self.lsb_server.setText(prefs['lsb_server'])
        self.ll.addWidget(self.lsb_server)
        self.server_label.setBuddy(self.lsb_server)
        self.l.addLayout(self.ll)

        self.lll = QHBoxLayout()
        self.librarian_label = QLabel('Librarian:')
        self.lll.addWidget(self.librarian_label)

        self.librarian = QLineEdit(self)
        self.librarian.setText(prefs['librarian']['name'])
        self.lll.addWidget(self.librarian)
        self.librarian_label.setBuddy(self.librarian)
        self.l.addLayout(self.lll)

        self.llll = QHBoxLayout()
        self.library_uuid_label = QLabel('Library ID:')
        self.llll.addWidget(self.library_uuid_label)
        self.library_uuid_button = QPushButton("New unique Library ID")
        self.library_uuid_button.clicked.connect(self.new_library_uuid)

        self.library_uuid = QLabel(self)
        self.library_uuid.setText(prefs['library_uuid'])
        self.llll.addWidget(self.library_uuid)
        self.llll.addWidget(self.library_uuid_button)
        self.library_uuid_label.setBuddy(self.library_uuid)
        self.l.addLayout(self.llll)

    def new_library_uuid(self):
        self.library_uuid.setText(str(uuid.uuid4()))

    def save_settings(self):
        prefs['lsb_server'] = unicode(self.lsb_server.text())
        prefs['server_prefix'] = unicode(self.server_prefix.text())
        prefs['librarian'] = {'name': unicode(self.librarian.text()),
                              'saved': True}
        prefs['library_uuid'] = self.library_uuid.text()
Example #26
0
 def passphrase_dialog(self, msg, confirm):
     # If confirm is true, require the user to enter the passphrase twice
     parent = self.top_level_window()
     if confirm:
         d = PasswordDialog(parent, None, msg, PW_PASSPHRASE)
         confirmed, p, passphrase = d.run()
     else:
         d = WindowModalDialog(parent, _("Enter Passphrase"))
         pw = QLineEdit()
         pw.setEchoMode(2)
         pw.setMinimumWidth(200)
         vbox = QVBoxLayout()
         vbox.addWidget(WWLabel(msg))
         vbox.addWidget(pw)
         vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
         d.setLayout(vbox)
         passphrase = unicode(pw.text()) if d.exec_() else None
     self.passphrase = passphrase
     self.done.set()
Example #27
0
class ConfigWidget(QWidget):

    def __init__(self):
        QWidget.__init__(self)
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        self.ll = QHBoxLayout()
        self.server_label = QLabel('Gphoto2 server address:')
        self.ll.addWidget(self.server_label)

        self.gphoto2_server = QLineEdit(self)
        self.gphoto2_server.setText(prefs['gphoto2_server'])
        self.ll.addWidget(self.gphoto2_server)
        self.server_label.setBuddy(self.gphoto2_server)
        self.l.addLayout(self.ll)
        
    def save_settings(self):
        prefs['gphoto2_server'] = self.gphoto2_server.text()
Example #28
0
class ConfigWidget(QWidget):

    def __init__(self):
        QWidget.__init__(self)
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        self.l5 = QHBoxLayout()
        self.server_prefix_label = QLabel('Server prefix:')
        self.l5.addWidget(self.server_prefix_label)

        self.server_prefix = QLineEdit(self)
        self.server_prefix.setText(prefs['server_prefix'])
        self.l5.addWidget(self.server_prefix)
        self.server_prefix_label.setBuddy(self.server_prefix)
        self.l.addLayout(self.l5)

        self.ll = QHBoxLayout()
        self.server_label = QLabel('Server:')
        self.ll.addWidget(self.server_label)

        self.lsb_server = QLineEdit(self)
        self.lsb_server.setText(prefs['lsb_server'])
        self.ll.addWidget(self.lsb_server)
        self.server_label.setBuddy(self.lsb_server)
        self.l.addLayout(self.ll)

        self.lll = QHBoxLayout()
        self.librarian_label = QLabel('Librarian:')
        self.lll.addWidget(self.librarian_label)

        self.librarian = QLabel(self)
        self.librarian.setText(prefs['librarian'])
        self.lll.addWidget(self.librarian)
        self.librarian_label.setBuddy(self.librarian)
        self.l.addLayout(self.lll)

        self.llll = QHBoxLayout()
        self.library_uuid_label = QLabel('Library ID:')
        self.llll.addWidget(self.library_uuid_label)

        self.library_uuid = QLabel(self)
        self.library_uuid.setText(prefs['library_uuid'])
        self.llll.addWidget(self.library_uuid)
        self.library_uuid_label.setBuddy(self.library_uuid)
        self.l.addLayout(self.llll)

    def save_settings(self):
        prefs['lsb_server'] = unicode(self.lsb_server.text())
        prefs['server_prefix'] = unicode(self.server_prefix.text())
        prefs['librarian'] = prefs['librarian']
        prefs['library_uuid'] = prefs['library_uuid']
    def __init__(self):
        QGroupBox.__init__(self)

        self.treemodel = treemodel = TestNe1Model()

        self.view = view = ModelTreeGui(TestMainWindow(), "Model tree", treemodel, self)
        view.mt_update()
        self.chunkNum = 2
        self.gbox = QGroupBox()
        vl = QVBoxLayout(self)
        vl.setSpacing(0)
        vl.setMargin(0)
        vl.addWidget(self.view)
        self.buttonLayout = hl = QHBoxLayout()
        hl.setSpacing(0)
        hl.setMargin(0)
        vl.addLayout(hl)
        self.buttonNum = 1
        for func in (self.addmol, self.addjig, self.selected):
            self.addButton(func)
Example #30
0
    def __init__(self, parent=None,):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        try:
            self.default_key = retrieve_kindle_keys()[0]
        except:
            self.default_key = u""

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        if len(self.default_key)>0:
            data_group_box = QGroupBox(u"", self)
            layout.addWidget(data_group_box)
            data_group_box_layout = QVBoxLayout()
            data_group_box.setLayout(data_group_box_layout)

            key_group = QHBoxLayout()
            data_group_box_layout.addLayout(key_group)
            key_group.addWidget(QLabel(u"Unique Key Name:", self))
            self.key_ledit = QLineEdit("", self)
            self.key_ledit.setToolTip(u"<p>Enter an identifying name for the current default Kindle for Mac/PC key.")
            key_group.addWidget(self.key_ledit)
            key_label = QLabel(_(''), self)
            key_label.setAlignment(Qt.AlignHCenter)
            data_group_box_layout.addWidget(key_label)
            self.button_box.accepted.connect(self.accept)
        else:
            default_key_error = QLabel(u"The default encryption key for Kindle for Mac/PC could not be found.", self)
            default_key_error.setAlignment(Qt.AlignHCenter)
            layout.addWidget(default_key_error)
            # if no default, bot buttons do the same
            self.button_box.accepted.connect(self.reject)

        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Example #31
0
class Dialog(QDialog):
    def __init__(self,
                 title,
                 widget=None,
                 closeButton=True,
                 keySequence=None,
                 isDialog=False,
                 icon=None):
        QDialog.__init__(self, ctx.mainScreen)
        self.setObjectName("dialog")

        self.isDialog = isDialog
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.wlayout = QHBoxLayout()

        if icon:
            self.setStyleSheet(
                """QDialog QLabel{ margin-left:16px;margin-right:10px}
                                  QDialog#dialog {background-image:url(':/images/%s.png');
                                                  background-repeat:no-repeat;
                                                  background-position: top left; padding-left:500px;} """
                % icon)

        self.windowTitle = windowTitle(self, closeButton)
        self.setTitle(title)
        self.layout.setMargin(0)
        self.layout.addWidget(self.windowTitle)

        if widget:
            self.addWidget(widget)
            QObject.connect(widget, SIGNAL("finished(int)"), self.reject)
            QObject.connect(widget, SIGNAL("resizeDialog(int,int)"),
                            self.resize)

        if closeButton:
            QObject.connect(self.windowTitle.pushButton, SIGNAL("clicked()"),
                            self.reject)

        if keySequence:
            shortCut = QShortcut(keySequence, self)
            QObject.connect(shortCut, SIGNAL("activated()"), self.reject)

        QMetaObject.connectSlotsByName(self)
        self.resize(10, 10)

    def setTitle(self, title):
        self.windowTitle.label.setText(title)

    def addWidget(self, widget):
        self.content = widget
        self.wlayout.addWidget(self.content)
        if self.isDialog:
            widget.setStyleSheet("QMessageBox { background:none }")
            self.layout.addItem(
                QSpacerItem(10, 10, QSizePolicy.Fixed,
                            QSizePolicy.MinimumExpanding))
            self.layout.setContentsMargins(0, 0, 0, 8)
        self.layout.addLayout(self.wlayout)

    def setCentered(self):
        self.move(ctx.mainScreen.width() / 2 - self.width() / 2,
                  ctx.mainScreen.height() / 2 - self.height() / 2)

    def exec_(self):
        QTimer.singleShot(0, self.setCentered)
        return QDialog.exec_(self)
Example #32
0
    def settings_dialog(self, window):

        def client():
            return self.client(wallet)

        def add_rows_to_layout(layout, rows):
            for row_num, items in enumerate(rows):
                for col_num, txt in enumerate(items):
                    widget = txt if isinstance(txt, QWidget) else QLabel(txt)
                    layout.addWidget(widget, row_num, col_num)

        def refresh():
            features = client().features
            bl_hash = features.bootloader_hash.encode('hex').upper()
            bl_hash = "%s...%s" % (bl_hash[:10], bl_hash[-10:])
            version = "%d.%d.%d" % (features.major_version,
                                    features.minor_version,
                                    features.patch_version)

            bl_hash_label.setText(bl_hash)
            device_label.setText(features.label)
            device_id_label.setText(features.device_id)
            initialized_label.setText(noyes[features.initialized])
            version_label.setText(version)
            pin_label.setText(noyes[features.pin_protection])
            passphrase_label.setText(noyes[features.passphrase_protection])
            language_label.setText(features.language)

            pin_button.setText(_("Change") if features.pin_protection
                               else _("Set"))
            clear_pin_button.setVisible(features.pin_protection)

        def rename():
            title = _("Set Device Label")
            msg = _("Enter new label:")
            response = QInputDialog().getText(dialog, title, msg)
            if not response[1]:
                return
            client().change_label(str(response[0]))
            refresh()

        def set_pin():
            client().set_pin(remove=False)
            refresh()

        def clear_pin():
            title = _("Confirm Clear PIN")
            msg = _("WARNING: if your clear your PIN, anyone with physical "
                    "access to your %s device can spend your bitcoins.\n\n"
                    "Are you certain you want to remove your PIN?") % device
            if not dialog.question(msg, title=title):
                return
            client().set_pin(remove=True)
            refresh()

        def wipe_device():
            title = _("Confirm Device Wipe")
            msg = _("Are you sure you want to wipe the device?  "
                    "You should make sure you have a copy of your recovery "
                    "seed and that your wallet holds no bitcoins.")
            if not dialog.question(msg, title=title):
                return
            if sum(wallet.get_balance()):
                title = _("Confirm Device Wipe")
                msg = _("Are you SURE you want to wipe the device?\n"
                        "Your wallet still has bitcoins in it!")
                if not dialog.question(msg, title=title,
                                       icon=QMessageBox.Critical):
                    return
            client().wipe_device()
            refresh()

        def slider_moved():
            mins = timeout_slider.sliderPosition()
            timeout_label.setText(_("%2d minutes") % mins)

        wallet = window.wallet
        handler = wallet.handler
        device = self.device

        info_tab = QWidget()
        tab_layout = QVBoxLayout(info_tab)
        info_layout = QGridLayout()
        noyes = [_("No"), _("Yes")]
        bl_hash_label = QLabel()
        device_label = QLabel()
        passphrase_label = QLabel()
        initialized_label = QLabel()
        device_id_label = QLabel()
        version_label = QLabel()
        pin_label = QLabel()
        language_label = QLabel()
        rename_button = QPushButton(_("Rename"))
        rename_button.clicked.connect(rename)
        pin_button = QPushButton()
        pin_button.clicked.connect(set_pin)
        clear_pin_button = QPushButton(_("Clear"))
        clear_pin_button.clicked.connect(clear_pin)

        add_rows_to_layout(info_layout, [
            (_("Device Label"), device_label, rename_button),
            (_("Has Passphrase"), passphrase_label),
            (_("Has PIN"), pin_label, pin_button, clear_pin_button),
            (_("Initialized"), initialized_label),
            (_("Device ID"), device_id_label),
            (_("Bootloader Hash"), bl_hash_label),
            (_("Firmware Version"), version_label),
            (_("Language"), language_label),
        ])
        tab_layout.addLayout(info_layout)

        timeout_layout = QHBoxLayout()
        timeout_label = QLabel()
        timeout_slider = QSlider(Qt.Horizontal)
        timeout_slider.setRange(1, 60)
        timeout_slider.setSingleStep(1)
        timeout_slider.setSliderPosition(wallet.session_timeout // 60)
        timeout_slider.setTickInterval(5)
        timeout_slider.setTickPosition(QSlider.TicksBelow)
        timeout_slider.setTracking(True)
        timeout_slider.valueChanged.connect(slider_moved)
        timeout_layout.addWidget(QLabel(_("Session Timeout")))
        timeout_layout.addWidget(timeout_slider)
        timeout_layout.addWidget(timeout_label)
        tab_layout.addLayout(timeout_layout)

        advanced_tab = QWidget()
        advanced_layout = QGridLayout(advanced_tab)
        wipe_device_button = QPushButton(_("Wipe Device"))
        wipe_device_button.clicked.connect(wipe_device)
        add_rows_to_layout(advanced_layout, [
            (wipe_device_button, ),
        ])

        dialog = WindowModalDialog(window, _("%s Settings") % device)
        vbox = QVBoxLayout()
        tabs = QTabWidget()
        tabs.addTab(info_tab, _("Information"))
        tabs.addTab(advanced_tab, _("Advanced"))
        vbox.addWidget(tabs)
        vbox.addStretch(1)
        vbox.addLayout(Buttons(CloseButton(dialog)))

        # Show values
        slider_moved()
        refresh()
        dialog.setLayout(vbox)
        handler.exec_dialog(dialog)
        wallet.set_session_timeout(timeout_slider.sliderPosition() * 60)
Example #33
0
    def __init__(self, window, plugin, keystore, device_id):
        title = _("%s Settings") % plugin.device
        super(SettingsDialog, self).__init__(window, title)
        self.setMaximumWidth(540)

        devmgr = plugin.device_manager()
        config = devmgr.config
        handler = keystore.handler
        thread = keystore.thread
        hs_rows, hs_cols = (64, 128)

        def invoke_client(method, *args, **kw_args):
            unpair_after = kw_args.pop('unpair_after', False)

            def task():
                client = devmgr.client_by_id(device_id)
                if not client:
                    raise RuntimeError("Device not connected")
                if method:
                    getattr(client, method)(*args, **kw_args)
                if unpair_after:
                    devmgr.unpair_id(device_id)
                return client.features

            thread.add(task, on_success=update)

        def update(features):
            self.features = features
            set_label_enabled()
            bl_hash = features.bootloader_hash.encode('hex')
            bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]])
            noyes = [_("No"), _("Yes")]
            endis = [_("Enable Passphrases"), _("Disable Passphrases")]
            disen = [_("Disabled"), _("Enabled")]
            setchange = [_("Set a PIN"), _("Change PIN")]

            version = "%d.%d.%d" % (features.major_version,
                                    features.minor_version,
                                    features.patch_version)
            coins = ", ".join(coin.coin_name for coin in features.coins)

            device_label.setText(features.label)
            pin_set_label.setText(noyes[features.pin_protection])
            passphrases_label.setText(disen[features.passphrase_protection])
            bl_hash_label.setText(bl_hash)
            label_edit.setText(features.label)
            device_id_label.setText(features.device_id)
            initialized_label.setText(noyes[features.initialized])
            version_label.setText(version)
            coins_label.setText(coins)
            clear_pin_button.setVisible(features.pin_protection)
            clear_pin_warning.setVisible(features.pin_protection)
            pin_button.setText(setchange[features.pin_protection])
            pin_msg.setVisible(not features.pin_protection)
            passphrase_button.setText(endis[features.passphrase_protection])
            language_label.setText(features.language)

        def set_label_enabled():
            label_apply.setEnabled(label_edit.text() != self.features.label)

        def rename():
            invoke_client('change_label', unicode(label_edit.text()))

        def toggle_passphrase():
            title = _("Confirm Toggle Passphrase Protection")
            currently_enabled = self.features.passphrase_protection
            if currently_enabled:
                msg = _("After disabling passphrases, you can only pair this "
                        "Electrum-DSR wallet if it had an empty passphrase.  "
                        "If its passphrase was not empty, you will need to "
                        "create a new wallet with the install wizard.  You "
                        "can use this wallet again at any time by re-enabling "
                        "passphrases and entering its passphrase.")
            else:
                msg = _(
                    "Your current Electrum-DSR wallet can only be used with "
                    "an empty passphrase.  You must create a separate "
                    "wallet with the install wizard for other passphrases "
                    "as each one generates a new set of addresses.")
            msg += "\n\n" + _("Are you sure you want to proceed?")
            if not self.question(msg, title=title):
                return
            invoke_client('toggle_passphrase', unpair_after=currently_enabled)

        def change_homescreen():
            from PIL import Image  # FIXME
            dialog = QFileDialog(self, _("Choose Homescreen"))
            filename = dialog.getOpenFileName()
            if filename:
                im = Image.open(str(filename))
                if im.size != (hs_cols, hs_rows):
                    raise Exception('Image must be 64 x 128 pixels')
                im = im.convert('1')
                pix = im.load()
                img = ''
                for j in range(hs_rows):
                    for i in range(hs_cols):
                        img += '1' if pix[i, j] else '0'
                img = ''.join(
                    chr(int(img[i:i + 8], 2)) for i in range(0, len(img), 8))
                invoke_client('change_homescreen', img)

        def clear_homescreen():
            invoke_client('change_homescreen', '\x00')

        def set_pin():
            invoke_client('set_pin', remove=False)

        def clear_pin():
            invoke_client('set_pin', remove=True)

        def wipe_device():
            wallet = window.wallet
            if wallet and sum(wallet.get_balance()):
                title = _("Confirm Device Wipe")
                msg = _("Are you SURE you want to wipe the device?\n"
                        "Your wallet still has Desire in it!")
                if not self.question(
                        msg, title=title, icon=QMessageBox.Critical):
                    return
            invoke_client('wipe_device', unpair_after=True)

        def slider_moved():
            mins = timeout_slider.sliderPosition()
            timeout_minutes.setText(_("%2d minutes") % mins)

        def slider_released():
            config.set_session_timeout(timeout_slider.sliderPosition() * 60)

        # Information tab
        info_tab = QWidget()
        info_layout = QVBoxLayout(info_tab)
        info_glayout = QGridLayout()
        info_glayout.setColumnStretch(2, 1)
        device_label = QLabel()
        pin_set_label = QLabel()
        passphrases_label = QLabel()
        version_label = QLabel()
        device_id_label = QLabel()
        bl_hash_label = QLabel()
        bl_hash_label.setWordWrap(True)
        coins_label = QLabel()
        coins_label.setWordWrap(True)
        language_label = QLabel()
        initialized_label = QLabel()
        rows = [
            (_("Device Label"), device_label),
            (_("PIN set"), pin_set_label),
            (_("Passphrases"), passphrases_label),
            (_("Firmware Version"), version_label),
            (_("Device ID"), device_id_label),
            (_("Bootloader Hash"), bl_hash_label),
            (_("Supported Coins"), coins_label),
            (_("Language"), language_label),
            (_("Initialized"), initialized_label),
        ]
        for row_num, (label, widget) in enumerate(rows):
            info_glayout.addWidget(QLabel(label), row_num, 0)
            info_glayout.addWidget(widget, row_num, 1)
        info_layout.addLayout(info_glayout)

        # Settings tab
        settings_tab = QWidget()
        settings_layout = QVBoxLayout(settings_tab)
        settings_glayout = QGridLayout()

        # Settings tab - Label
        label_msg = QLabel(
            _("Name this %s.  If you have mutiple devices "
              "their labels help distinguish them.") % plugin.device)
        label_msg.setWordWrap(True)
        label_label = QLabel(_("Device Label"))
        label_edit = QLineEdit()
        label_edit.setMinimumWidth(150)
        label_edit.setMaxLength(plugin.MAX_LABEL_LEN)
        label_apply = QPushButton(_("Apply"))
        label_apply.clicked.connect(rename)
        label_edit.textChanged.connect(set_label_enabled)
        settings_glayout.addWidget(label_label, 0, 0)
        settings_glayout.addWidget(label_edit, 0, 1, 1, 2)
        settings_glayout.addWidget(label_apply, 0, 3)
        settings_glayout.addWidget(label_msg, 1, 1, 1, -1)

        # Settings tab - PIN
        pin_label = QLabel(_("PIN Protection"))
        pin_button = QPushButton()
        pin_button.clicked.connect(set_pin)
        settings_glayout.addWidget(pin_label, 2, 0)
        settings_glayout.addWidget(pin_button, 2, 1)
        pin_msg = QLabel(
            _("PIN protection is strongly recommended.  "
              "A PIN is your only protection against someone "
              "stealing your Desire if they obtain physical "
              "access to your %s.") % plugin.device)
        pin_msg.setWordWrap(True)
        pin_msg.setStyleSheet("color: red")
        settings_glayout.addWidget(pin_msg, 3, 1, 1, -1)

        # Settings tab - Homescreen
        if plugin.device != 'KeepKey':  # Not yet supported by KK firmware
            homescreen_layout = QHBoxLayout()
            homescreen_label = QLabel(_("Homescreen"))
            homescreen_change_button = QPushButton(_("Change..."))
            homescreen_clear_button = QPushButton(_("Reset"))
            homescreen_change_button.clicked.connect(change_homescreen)
            homescreen_clear_button.clicked.connect(clear_homescreen)
            homescreen_msg = QLabel(
                _("You can set the homescreen on your "
                  "device to personalize it.  You must "
                  "choose a %d x %d monochrome black and "
                  "white image.") % (hs_rows, hs_cols))
            homescreen_msg.setWordWrap(True)
            settings_glayout.addWidget(homescreen_label, 4, 0)
            settings_glayout.addWidget(homescreen_change_button, 4, 1)
            settings_glayout.addWidget(homescreen_clear_button, 4, 2)
            settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1)

        # Settings tab - Session Timeout
        timeout_label = QLabel(_("Session Timeout"))
        timeout_minutes = QLabel()
        timeout_slider = QSlider(Qt.Horizontal)
        timeout_slider.setRange(1, 60)
        timeout_slider.setSingleStep(1)
        timeout_slider.setTickInterval(5)
        timeout_slider.setTickPosition(QSlider.TicksBelow)
        timeout_slider.setTracking(True)
        timeout_msg = QLabel(
            _("Clear the session after the specified period "
              "of inactivity.  Once a session has timed out, "
              "your PIN and passphrase (if enabled) must be "
              "re-entered to use the device."))
        timeout_msg.setWordWrap(True)
        timeout_slider.setSliderPosition(config.get_session_timeout() // 60)
        slider_moved()
        timeout_slider.valueChanged.connect(slider_moved)
        timeout_slider.sliderReleased.connect(slider_released)
        settings_glayout.addWidget(timeout_label, 6, 0)
        settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3)
        settings_glayout.addWidget(timeout_minutes, 6, 4)
        settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1)
        settings_layout.addLayout(settings_glayout)
        settings_layout.addStretch(1)

        # Advanced tab
        advanced_tab = QWidget()
        advanced_layout = QVBoxLayout(advanced_tab)
        advanced_glayout = QGridLayout()

        # Advanced tab - clear PIN
        clear_pin_button = QPushButton(_("Disable PIN"))
        clear_pin_button.clicked.connect(clear_pin)
        clear_pin_warning = QLabel(
            _("If you disable your PIN, anyone with physical access to your "
              "%s device can spend your Desire.") % plugin.device)
        clear_pin_warning.setWordWrap(True)
        clear_pin_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(clear_pin_button, 0, 2)
        advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5)

        # Advanced tab - toggle passphrase protection
        passphrase_button = QPushButton()
        passphrase_button.clicked.connect(toggle_passphrase)
        passphrase_msg = WWLabel(PASSPHRASE_HELP)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(passphrase_button, 3, 2)
        advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5)
        advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5)

        # Advanced tab - wipe device
        wipe_device_button = QPushButton(_("Wipe Device"))
        wipe_device_button.clicked.connect(wipe_device)
        wipe_device_msg = QLabel(
            _("Wipe the device, removing all data from it.  The firmware "
              "is left unchanged."))
        wipe_device_msg.setWordWrap(True)
        wipe_device_warning = QLabel(
            _("Only wipe a device if you have the recovery seed written down "
              "and the device wallet(s) are empty, otherwise the Desire "
              "will be lost forever."))
        wipe_device_warning.setWordWrap(True)
        wipe_device_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(wipe_device_button, 6, 2)
        advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5)
        advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5)
        advanced_layout.addLayout(advanced_glayout)
        advanced_layout.addStretch(1)

        tabs = QTabWidget(self)
        tabs.addTab(info_tab, _("Information"))
        tabs.addTab(settings_tab, _("Settings"))
        tabs.addTab(advanced_tab, _("Advanced"))
        dialog_vbox = QVBoxLayout(self)
        dialog_vbox.addWidget(tabs)
        dialog_vbox.addLayout(Buttons(CloseButton(self)))

        # Update information
        invoke_client(None)
Example #34
0
    def request_trezor_init_settings(self, wizard, method, device):
        vbox = QVBoxLayout()
        next_enabled = True
        label = QLabel(_("Enter a label to name your device:"))
        name = QLineEdit()
        hl = QHBoxLayout()
        hl.addWidget(label)
        hl.addWidget(name)
        hl.addStretch(1)
        vbox.addLayout(hl)

        def clean_text(widget):
            text = unicode(widget.toPlainText()).strip()
            return ' '.join(text.split())

        if method in [TIM_NEW, TIM_RECOVER]:
            gb = QGroupBox()
            hbox1 = QHBoxLayout()
            gb.setLayout(hbox1)
            # KeepKey recovery doesn't need a word count
            if method == TIM_NEW or self.device == 'TREZOR':
                vbox.addWidget(gb)
            gb.setTitle(_("Select your seed length:"))
            bg = QButtonGroup()
            for i, count in enumerate([12, 18, 24]):
                rb = QRadioButton(gb)
                rb.setText(_("%d words") % count)
                bg.addButton(rb)
                bg.setId(rb, i)
                hbox1.addWidget(rb)
                rb.setChecked(True)
            cb_pin = QCheckBox(_('Enable PIN protection'))
            cb_pin.setChecked(True)
        else:
            text = QTextEdit()
            text.setMaximumHeight(60)
            if method == TIM_MNEMONIC:
                msg = _("Enter your BIP39 mnemonic:")
            else:
                msg = _("Enter the master private key beginning with xprv:")

                def set_enabled():
                    from electrum_desire.keystore import is_xprv
                    wizard.next_button.setEnabled(is_xprv(clean_text(text)))

                text.textChanged.connect(set_enabled)
                next_enabled = False

            vbox.addWidget(QLabel(msg))
            vbox.addWidget(text)
            pin = QLineEdit()
            pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,10}')))
            pin.setMaximumWidth(100)
            hbox_pin = QHBoxLayout()
            hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
            hbox_pin.addWidget(pin)
            hbox_pin.addStretch(1)

        if method in [TIM_NEW, TIM_RECOVER]:
            vbox.addWidget(WWLabel(RECOMMEND_PIN))
            vbox.addWidget(cb_pin)
        else:
            vbox.addLayout(hbox_pin)

        passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        cb_phrase = QCheckBox(_('Enable passphrases'))
        cb_phrase.setChecked(False)
        vbox.addWidget(passphrase_msg)
        vbox.addWidget(passphrase_warning)
        vbox.addWidget(cb_phrase)

        wizard.exec_layout(vbox, next_enabled=next_enabled)

        if method in [TIM_NEW, TIM_RECOVER]:
            item = bg.checkedId()
            pin = cb_pin.isChecked()
        else:
            item = ' '.join(str(clean_text(text)).split())
            pin = str(pin.text())

        return (item, unicode(name.text()), pin, cb_phrase.isChecked())
Example #35
0
    def __init__(self, handler, data):
        '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods.
        Use last method from settings, but support new pairing and downgrade.
        '''
        QDialog.__init__(self, handler.top_level_window())
        self.handler = handler
        self.txdata = data
        self.idxs = self.txdata[
            'keycardData'] if self.txdata['confirmationType'] > 1 else ''
        self.setMinimumWidth(600)
        self.setWindowTitle(_("Ledger Wallet Authentication"))
        self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg)
        self.dongle = self.handler.win.wallet.get_keystore().get_client(
        ).dongle
        self.ws = None
        self.pin = ''

        self.devmode = self.getDevice2FAMode()
        if self.devmode == 0x11 or self.txdata['confirmationType'] == 1:
            self.cfg['mode'] = 0

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        def on_change_mode(idx):
            if idx < 2 and self.ws:
                self.ws.stop()
                self.ws = None
            self.cfg[
                'mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1
            if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws:
                self.req_validation()
            if self.cfg['mode'] > 0:
                self.handler.win.wallet.get_keystore().cfg = self.cfg
                self.handler.win.wallet.save_keystore()
            self.update_dlg()

        def add_pairing():
            self.do_pairing()

        def return_pin():
            self.pin = self.pintxt.text(
            ) if self.txdata['confirmationType'] == 1 else self.cardtxt.text()
            if self.cfg['mode'] == 1:
                self.pin = ''.join(chr(int(str(i), 16)) for i in self.pin)
            self.accept()

        self.modebox = QWidget()
        modelayout = QHBoxLayout()
        self.modebox.setLayout(modelayout)
        modelayout.addWidget(QLabel(_("Method:")))
        self.modes = QComboBox()
        modelayout.addWidget(self.modes, 2)
        self.addPair = QPushButton(_("Pair"))
        self.addPair.setMaximumWidth(60)
        modelayout.addWidget(self.addPair)
        modelayout.addStretch(1)
        self.modebox.setMaximumHeight(50)
        vbox.addWidget(self.modebox)

        self.populate_modes()
        self.modes.currentIndexChanged.connect(on_change_mode)
        self.addPair.clicked.connect(add_pairing)

        self.helpmsg = QTextEdit()
        self.helpmsg.setStyleSheet(
            "QTextEdit { background-color: lightgray; }")
        self.helpmsg.setReadOnly(True)
        vbox.addWidget(self.helpmsg)

        self.pinbox = QWidget()
        pinlayout = QHBoxLayout()
        self.pinbox.setLayout(pinlayout)
        self.pintxt = QLineEdit()
        self.pintxt.setEchoMode(2)
        self.pintxt.setMaxLength(4)
        self.pintxt.returnPressed.connect(return_pin)
        pinlayout.addWidget(QLabel(_("Enter PIN:")))
        pinlayout.addWidget(self.pintxt)
        pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        pinlayout.addStretch(1)
        self.pinbox.setVisible(self.cfg['mode'] == 0)
        vbox.addWidget(self.pinbox)

        self.cardbox = QWidget()
        card = QVBoxLayout()
        self.cardbox.setLayout(card)
        self.addrtext = QTextEdit()
        self.addrtext.setStyleSheet(
            "QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }"
        )
        self.addrtext.setReadOnly(True)
        self.addrtext.setMaximumHeight(120)
        card.addWidget(self.addrtext)

        def pin_changed(s):
            if len(s) < len(self.idxs):
                i = self.idxs[len(s)]
                addr = self.txdata['address']
                addr = addr[:i] + '<u><b>' + addr[i:i +
                                                  1] + '</u></b>' + addr[i +
                                                                         1:]
                self.addrtext.setHtml(str(addr))
            else:
                self.addrtext.setHtml(_("Press Enter"))

        pin_changed('')
        cardpin = QHBoxLayout()
        cardpin.addWidget(QLabel(_("Enter PIN:")))
        self.cardtxt = QLineEdit()
        self.cardtxt.setEchoMode(2)
        self.cardtxt.setMaxLength(len(self.idxs))
        self.cardtxt.textChanged.connect(pin_changed)
        self.cardtxt.returnPressed.connect(return_pin)
        cardpin.addWidget(self.cardtxt)
        cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        cardpin.addStretch(1)
        card.addLayout(cardpin)
        self.cardbox.setVisible(self.cfg['mode'] == 1)
        vbox.addWidget(self.cardbox)

        self.pairbox = QWidget()
        pairlayout = QVBoxLayout()
        self.pairbox.setLayout(pairlayout)
        pairhelp = QTextEdit(helpTxt[5])
        pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }")
        pairhelp.setReadOnly(True)
        pairlayout.addWidget(pairhelp, 1)
        self.pairqr = QRCodeWidget()
        pairlayout.addWidget(self.pairqr, 4)
        self.pairbox.setVisible(False)
        vbox.addWidget(self.pairbox)
        self.update_dlg()

        if self.cfg['mode'] > 1 and not self.ws:
            self.req_validation()
Example #36
0
class MetadataSingleDialogBase(ResizableDialog):

    view_format = pyqtSignal(object, object)
    cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields']
    one_line_comments_toolbar = False
    use_toolbutton_for_config_metadata = True

    def __init__(self, db, parent=None):
        self.db = db
        self.changed = set()
        self.books_to_refresh = set()
        self.rows_to_refresh = set()
        ResizableDialog.__init__(self, parent)

    def setupUi(self, *args):  # {{{
        self.resize(990, 670)

        self.download_shortcut = QShortcut(self)
        self.download_shortcut.setKey(
            QKeySequence('Ctrl+D', QKeySequence.PortableText))
        p = self.parent()
        if hasattr(p, 'keyboard'):
            kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download'
            sc = p.keyboard.keys_map.get(kname, None)
            if sc:
                self.download_shortcut.setKey(sc[0])

        self.button_box = bb = QDialogButtonBox(self)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'),
                                       self)
        self.next_button.setShortcut(QKeySequence('Alt+Right'))
        self.next_button.clicked.connect(self.next_clicked)
        self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'),
                                       self)
        self.prev_button.setShortcut(QKeySequence('Alt+Left'))

        self.button_box.addButton(self.prev_button, bb.ActionRole)
        self.button_box.addButton(self.next_button, bb.ActionRole)
        self.prev_button.clicked.connect(self.prev_clicked)
        bb.setStandardButtons(bb.Ok | bb.Cancel)
        bb.button(bb.Ok).setDefault(True)

        self.scroll_area = QScrollArea(self)
        self.scroll_area.setFrameShape(QScrollArea.NoFrame)
        self.scroll_area.setWidgetResizable(True)
        self.central_widget = QTabWidget(self)
        self.scroll_area.setWidget(self.central_widget)

        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.l.addWidget(self.scroll_area)
        ll = self.button_box_layout = QHBoxLayout()
        self.l.addLayout(ll)
        ll.addSpacing(10)
        ll.addWidget(self.button_box)

        self.setWindowIcon(QIcon(I('edit_input.png')))
        self.setWindowTitle(BASE_TITLE)

        self.create_basic_metadata_widgets()

        if len(self.db.custom_column_label_map):
            self.create_custom_metadata_widgets()

        self.do_layout()
        geom = gprefs.get('metasingle_window_geometry3', None)
        if geom is not None:
            self.restoreGeometry(bytes(geom))

    # }}}

    def create_basic_metadata_widgets(self):  # {{{
        self.basic_metadata_widgets = []

        self.languages = LanguagesEdit(self)
        self.basic_metadata_widgets.append(self.languages)

        self.title = TitleEdit(self)
        self.title.textChanged.connect(self.update_window_title)
        self.deduce_title_sort_button = QToolButton(self)
        self.deduce_title_sort_button.setToolTip(
            _('Automatically create the title sort entry based on the current '
              'title entry.\nUsing this button to create title sort will '
              'change title sort from red to green.'))
        self.deduce_title_sort_button.setWhatsThis(
            self.deduce_title_sort_button.toolTip())
        self.title_sort = TitleSortEdit(self, self.title,
                                        self.deduce_title_sort_button,
                                        self.languages)
        self.basic_metadata_widgets.extend([self.title, self.title_sort])

        self.deduce_author_sort_button = b = QToolButton(self)
        b.setToolTip('<p>' + _(
            'Automatically create the author sort entry based on the current '
            'author entry. Using this button to create author sort will '
            'change author sort from red to green.  There is a menu of '
            'functions available under this button. Click and hold '
            'on the button to see it.') + '</p>')
        b.m = m = QMenu()
        ac = m.addAction(QIcon(I('forward.png')),
                         _('Set author sort from author'))
        ac2 = m.addAction(QIcon(I('back.png')),
                          _('Set author from author sort'))
        ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors'))
        ac4 = m.addAction(QIcon(I('next.png')),
                          _('Copy author to author sort'))
        ac5 = m.addAction(QIcon(I('previous.png')),
                          _('Copy author sort to author'))

        b.setMenu(m)
        self.authors = AuthorsEdit(self, ac3)
        self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac,
                                          ac2, ac4, ac5)
        self.basic_metadata_widgets.extend([self.authors, self.author_sort])

        self.swap_title_author_button = QToolButton(self)
        self.swap_title_author_button.setIcon(QIcon(I('swap.png')))
        self.swap_title_author_button.setToolTip(
            _('Swap the author and title'))
        self.swap_title_author_button.clicked.connect(self.swap_title_author)

        self.manage_authors_button = QToolButton(self)
        self.manage_authors_button.setIcon(QIcon(I('user_profile.png')))
        self.manage_authors_button.setToolTip(
            '<p>' + _('Manage authors. Use to rename authors and correct '
                      'individual author\'s sort values') + '</p>')
        self.manage_authors_button.clicked.connect(self.authors.manage_authors)

        self.series = SeriesEdit(self)
        self.clear_series_button = QToolButton(self)
        self.clear_series_button.setToolTip(_('Clear series'))
        self.clear_series_button.clicked.connect(self.series.clear)
        self.series_index = SeriesIndexEdit(self, self.series)
        self.basic_metadata_widgets.extend([self.series, self.series_index])

        self.formats_manager = FormatsManager(self, self.copy_fmt)
        # We want formats changes to be committed before title/author, as
        # otherwise we could have data loss if the title/author changed and the
        # user was trying to add an extra file from the old books directory.
        self.basic_metadata_widgets.insert(0, self.formats_manager)
        self.formats_manager.metadata_from_format_button.clicked.connect(
            self.metadata_from_format)
        self.formats_manager.cover_from_format_button.clicked.connect(
            self.cover_from_format)
        self.cover = Cover(self)
        self.cover.download_cover.connect(self.download_cover)
        self.basic_metadata_widgets.append(self.cover)

        self.comments = CommentsEdit(self, self.one_line_comments_toolbar)
        self.basic_metadata_widgets.append(self.comments)

        self.rating = RatingEdit(self)
        self.clear_ratings_button = QToolButton(self)
        self.clear_ratings_button.setToolTip(_('Clear rating'))
        self.clear_ratings_button.setIcon(QIcon(I('trash.png')))
        self.clear_ratings_button.clicked.connect(self.rating.zero)

        self.basic_metadata_widgets.append(self.rating)

        self.tags = TagsEdit(self)
        self.tags_editor_button = QToolButton(self)
        self.tags_editor_button.setToolTip(_('Open Tag Editor'))
        self.tags_editor_button.setIcon(QIcon(I('chapters.png')))
        self.tags_editor_button.clicked.connect(self.tags_editor)
        self.clear_tags_button = QToolButton(self)
        self.clear_tags_button.setToolTip(_('Clear all tags'))
        self.clear_tags_button.setIcon(QIcon(I('trash.png')))
        self.clear_tags_button.clicked.connect(self.tags.clear)
        self.basic_metadata_widgets.append(self.tags)

        self.identifiers = IdentifiersEdit(self)
        self.basic_metadata_widgets.append(self.identifiers)
        self.clear_identifiers_button = QToolButton(self)
        self.clear_identifiers_button.setIcon(QIcon(I('trash.png')))
        self.clear_identifiers_button.setToolTip(_('Clear Ids'))
        self.clear_identifiers_button.clicked.connect(self.identifiers.clear)
        self.paste_isbn_button = QToolButton(self)
        self.paste_isbn_button.setToolTip(
            '<p>' + _('Paste the contents of the clipboard into the '
                      'identifiers box prefixed with isbn:') + '</p>')
        self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png')))
        self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn)

        self.publisher = PublisherEdit(self)
        self.basic_metadata_widgets.append(self.publisher)

        self.timestamp = DateEdit(self)
        self.pubdate = PubdateEdit(self)
        self.basic_metadata_widgets.extend([self.timestamp, self.pubdate])

        self.fetch_metadata_button = QPushButton(_('&Download metadata'), self)
        self.fetch_metadata_button.clicked.connect(self.fetch_metadata)
        self.download_shortcut.activated.connect(
            self.fetch_metadata_button.click)
        font = self.fmb_font = QFont()
        font.setBold(True)
        self.fetch_metadata_button.setFont(font)

        if self.use_toolbutton_for_config_metadata:
            self.config_metadata_button = QToolButton(self)
            self.config_metadata_button.setIcon(QIcon(I('config.png')))
        else:
            self.config_metadata_button = QPushButton(self)
            self.config_metadata_button.setText(
                _('Configure download metadata'))
        self.config_metadata_button.setIcon(QIcon(I('config.png')))
        self.config_metadata_button.clicked.connect(self.configure_metadata)
        self.config_metadata_button.setToolTip(
            _('Change how calibre downloads metadata'))

    # }}}

    def create_custom_metadata_widgets(self):  # {{{
        self.custom_metadata_widgets_parent = w = QWidget(self)
        layout = QGridLayout()
        w.setLayout(layout)
        self.custom_metadata_widgets, self.__cc_spacers = \
            populate_metadata_page(layout, self.db, None, parent=w, bulk=False,
                two_column=self.cc_two_column)
        self.__custom_col_layouts = [layout]

    # }}}

    def set_custom_metadata_tab_order(self, before=None, after=None):  # {{{
        sto = QWidget.setTabOrder
        if getattr(self, 'custom_metadata_widgets', []):
            ans = self.custom_metadata_widgets
            for i in range(len(ans) - 1):
                if before is not None and i == 0:
                    pass
                if len(ans[i + 1].widgets) == 2:
                    sto(ans[i].widgets[-1], ans[i + 1].widgets[1])
                else:
                    sto(ans[i].widgets[-1], ans[i + 1].widgets[0])
                for c in range(2, len(ans[i].widgets), 2):
                    sto(ans[i].widgets[c - 1], ans[i].widgets[c + 1])
            if after is not None:
                pass

    # }}}

    def do_view_format(self, path, fmt):
        if path:
            self.view_format.emit(None, path)
        else:
            self.view_format.emit(self.book_id, fmt)

    def copy_fmt(self, fmt, f):
        self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True)

    def do_layout(self):
        raise NotImplementedError()

    def __call__(self, id_):
        self.book_id = id_
        self.books_to_refresh = set([])
        for widget in self.basic_metadata_widgets:
            widget.initialize(self.db, id_)
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.initialize(id_)
        if callable(self.set_current_callback):
            self.set_current_callback(id_)
        # Commented out as it doesn't play nice with Next, Prev buttons
        #self.fetch_metadata_button.setFocus(Qt.OtherFocusReason)

    # Miscellaneous interaction methods {{{
    def update_window_title(self, *args):
        title = self.title.current_val
        if len(title) > 50:
            title = title[:50] + u'\u2026'
        self.setWindowTitle(
            BASE_TITLE + ' - ' + title + ' - ' + _(' [%(num)d of %(tot)d]') %
            dict(num=self.current_row + 1, tot=len(self.row_list)))

    def swap_title_author(self, *args):
        title = self.title.current_val
        self.title.current_val = authors_to_string(self.authors.current_val)
        self.authors.current_val = string_to_authors(title)
        self.title_sort.auto_generate()
        self.author_sort.auto_generate()

    def tags_editor(self, *args):
        self.tags.edit(self.db, self.book_id)

    def metadata_from_format(self, *args):
        mi, ext = self.formats_manager.get_selected_format_metadata(
            self.db, self.book_id)
        if mi is not None:
            self.update_from_mi(mi)

    def get_pdf_cover(self):
        pdfpath = self.formats_manager.get_format_path(self.db, self.book_id,
                                                       'pdf')
        from calibre.gui2.metadata.pdf_covers import PDFCovers
        d = PDFCovers(pdfpath, parent=self)
        if d.exec_() == d.Accepted:
            cpath = d.cover_path
            if cpath:
                with open(cpath, 'rb') as f:
                    self.update_cover(f.read(), 'PDF')
        d.cleanup()

    def cover_from_format(self, *args):
        ext = self.formats_manager.get_selected_format()
        if ext is None: return
        if ext == 'pdf':
            return self.get_pdf_cover()
        try:
            mi, ext = self.formats_manager.get_selected_format_metadata(
                self.db, self.book_id)
        except (IOError, OSError) as err:
            if getattr(err, 'errno',
                       None) == errno.EACCES:  # Permission denied
                import traceback
                fname = err.filename if err.filename else 'file'
                error_dialog(self,
                             _('Permission denied'),
                             _('Could not open %s. Is it being used by another'
                               ' program?') % fname,
                             det_msg=traceback.format_exc(),
                             show=True)
                return
            raise
        if mi is None:
            return
        cdata = None
        if mi.cover and os.access(mi.cover, os.R_OK):
            cdata = open(mi.cover).read()
        elif mi.cover_data[1] is not None:
            cdata = mi.cover_data[1]
        if cdata is None:
            error_dialog(self, _('Could not read cover'),
                         _('Could not read cover from %s format') %
                         ext).exec_()
            return
        self.update_cover(cdata, ext)

    def update_cover(self, cdata, fmt):
        orig = self.cover.current_val
        self.cover.current_val = cdata
        if self.cover.current_val is None:
            self.cover.current_val = orig
            return error_dialog(self,
                                _('Could not read cover'),
                                _('The cover in the %s format is invalid') %
                                fmt,
                                show=True)
            return

    def update_from_mi(self, mi, update_sorts=True, merge_tags=True):
        if not mi.is_null('title'):
            self.title.current_val = mi.title
            if update_sorts:
                self.title_sort.auto_generate()
        if not mi.is_null('authors'):
            self.authors.current_val = mi.authors
        if not mi.is_null('author_sort'):
            self.author_sort.current_val = mi.author_sort
        elif update_sorts:
            self.author_sort.auto_generate()
        if not mi.is_null('rating'):
            try:
                self.rating.current_val = mi.rating
            except:
                pass
        if not mi.is_null('publisher'):
            self.publisher.current_val = mi.publisher
        if not mi.is_null('tags'):
            old_tags = self.tags.current_val
            tags = mi.tags if mi.tags else []
            if old_tags and merge_tags:
                ltags, lotags = {t.lower()
                                 for t in tags}, {t.lower()
                                                  for t in old_tags}
                tags = [t for t in tags if t.lower() in ltags - lotags
                        ] + old_tags
            self.tags.current_val = tags
        if not mi.is_null('identifiers'):
            current = self.identifiers.current_val
            current.update(mi.identifiers)
            self.identifiers.current_val = current
        if not mi.is_null('pubdate'):
            self.pubdate.current_val = mi.pubdate
        if not mi.is_null('series') and mi.series.strip():
            self.series.current_val = mi.series
            if mi.series_index is not None:
                self.series_index.reset_original()
                self.series_index.current_val = float(mi.series_index)
        if not mi.is_null('languages'):
            langs = [canonicalize_lang(x) for x in mi.languages]
            langs = [x for x in langs if x is not None]
            if langs:
                self.languages.current_val = langs
        if mi.comments and mi.comments.strip():
            self.comments.current_val = mi.comments

    def fetch_metadata(self, *args):
        d = FullFetch(self.cover.pixmap(), self)
        ret = d.start(title=self.title.current_val,
                      authors=self.authors.current_val,
                      identifiers=self.identifiers.current_val)
        if ret == d.Accepted:
            from calibre.ebooks.metadata.sources.prefs import msprefs
            mi = d.book
            dummy = Metadata(_('Unknown'))
            for f in msprefs['ignore_fields']:
                if ':' not in f:
                    setattr(mi, f, getattr(dummy, f))
            if mi is not None:
                pd = mi.pubdate
                if pd is not None:
                    # Put the downloaded published date into the local timezone
                    # as we discard time info and the date is timezone
                    # invariant. This prevents the as_local_timezone() call in
                    # update_from_mi from changing the pubdate
                    mi.pubdate = datetime(pd.year,
                                          pd.month,
                                          pd.day,
                                          tzinfo=local_tz)
                self.update_from_mi(mi)
            if d.cover_pixmap is not None:
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    def configure_metadata(self):
        from calibre.gui2.preferences import show_config_widget
        gui = self.parent()
        show_config_widget('Sharing',
                           'Metadata download',
                           parent=self,
                           gui=gui,
                           never_shutdown=True)

    def download_cover(self, *args):
        from calibre.gui2.metadata.single_download import CoverFetch
        d = CoverFetch(self.cover.pixmap(), self)
        ret = d.start(self.title.current_val, self.authors.current_val,
                      self.identifiers.current_val)
        if ret == d.Accepted:
            if d.cover_pixmap is not None:
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    # }}}

    def apply_changes(self):
        self.changed.add(self.book_id)
        if self.db is None:
            # break_cycles has already been called, don't know why this should
            # happen but a user reported it
            return True
        for widget in self.basic_metadata_widgets:
            try:
                if not widget.commit(self.db, self.book_id):
                    return False
                self.books_to_refresh |= getattr(widget, 'books_to_refresh',
                                                 set([]))
            except (IOError, OSError) as err:
                if getattr(err, 'errno',
                           None) == errno.EACCES:  # Permission denied
                    import traceback
                    fname = getattr(err, 'filename', None)
                    p = 'Locked file: %s\n\n' % fname if fname else ''
                    error_dialog(
                        self,
                        _('Permission denied'),
                        _('Could not change the on disk location of this'
                          ' book. Is it open in another program?'),
                        det_msg=p + traceback.format_exc(),
                        show=True)
                    return False
                raise
        for widget in getattr(self, 'custom_metadata_widgets', []):
            self.books_to_refresh |= widget.commit(self.book_id)

        self.db.commit()
        rows = self.db.refresh_ids(list(self.books_to_refresh))
        if rows:
            self.rows_to_refresh |= set(rows)

        return True

    def accept(self):
        self.save_state()
        if not self.apply_changes():
            return
        ResizableDialog.accept(self)

    def reject(self):
        self.save_state()
        ResizableDialog.reject(self)

    def save_state(self):
        try:
            gprefs['metasingle_window_geometry3'] = bytearray(
                self.saveGeometry())
        except:
            # Weird failure, see https://bugs.launchpad.net/bugs/995271
            import traceback
            traceback.print_exc()

    # Dialog use methods {{{
    def start(self,
              row_list,
              current_row,
              view_slot=None,
              set_current_callback=None):
        self.row_list = row_list
        self.current_row = current_row
        if view_slot is not None:
            self.view_format.connect(view_slot)
        self.set_current_callback = set_current_callback
        self.do_one(apply_changes=False)
        ret = self.exec_()
        self.break_cycles()
        return ret

    def next_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=1, apply_changes=False)

    def prev_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=-1, apply_changes=False)

    def do_one(self, delta=0, apply_changes=True):
        if apply_changes:
            self.apply_changes()
        self.current_row += delta
        prev = next_ = None
        if self.current_row > 0:
            prev = self.db.title(self.row_list[self.current_row - 1])
        if self.current_row < len(self.row_list) - 1:
            next_ = self.db.title(self.row_list[self.current_row + 1])

        if next_ is not None:
            tip = (_('Save changes and edit the metadata of %s') +
                   ' [Alt+Right]') % next_
            self.next_button.setToolTip(tip)
        self.next_button.setEnabled(next_ is not None)
        if prev is not None:
            tip = (_('Save changes and edit the metadata of %s') +
                   ' [Alt+Left]') % prev
            self.prev_button.setToolTip(tip)
        self.prev_button.setEnabled(prev is not None)
        self.button_box.button(self.button_box.Ok).setDefault(True)
        self.button_box.button(self.button_box.Ok).setFocus(
            Qt.OtherFocusReason)
        self(self.db.id(self.row_list[self.current_row]))

    def break_cycles(self):
        # Break any reference cycles that could prevent python
        # from garbage collecting this dialog
        self.set_current_callback = self.db = None

        def disconnect(signal):
            try:
                signal.disconnect()
            except:
                pass  # Fails if view format was never connected

        disconnect(self.view_format)
        for b in ('next_button', 'prev_button'):
            x = getattr(self, b, None)
            if x is not None:
                disconnect(x.clicked)
        for widget in self.basic_metadata_widgets:
            bc = getattr(widget, 'break_cycles', None)
            if bc is not None and callable(bc):
                bc()
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.break_cycles()
Example #37
0
    def __init__(self, parent, hide_on_close=False):
        QMainWindow.__init__(self, parent)
        self._hide_on_close = hide_on_close
        # replace the BusyIndicator class with a GUI-aware one
        Purr.BusyIndicator = BusyIndicator
        self._pounce = False
        # we keep a small stack of previously active purrers. This makes directory changes
        # faster (when going back and forth between dirs)
        # current purrer
        self.purrer = None
        self.purrer_stack = []
        # Purr pipes for receiving remote commands
        self.purrpipes = {}
        # init GUI
        self.setWindowTitle("PURR")
        self.setWindowIcon(pixmaps.purr_logo.icon())
        cw = QWidget(self)
        self.setCentralWidget(cw)
        cwlo = QVBoxLayout(cw)
        cwlo.setContentsMargins(0, 0, 0, 0)
        cwlo.setMargin(5)
        cwlo.setSpacing(0)
        toplo = QHBoxLayout()
        cwlo.addLayout(toplo)

        # About dialog
        self._about_dialog = QMessageBox(self)
        self._about_dialog.setWindowTitle("About PURR")
        self._about_dialog.setText(self.about_message + """
        <P>PURR is not watching any directories right now. You may need to restart it, and give it
  some directory names on the command line.</P>""")
        self._about_dialog.setIconPixmap(pixmaps.purr_logo.pm())
        # Log viewer dialog
        self.viewer_dialog = HTMLViewerDialog(
            self,
            config_name="log-viewer",
            buttons=
            [(pixmaps.blue_round_reload, "Regenerate",
              """<P>Regenerates your log's HTML code from scratch. This can be useful if
                                                        your PURR version has changed, or if there was an error of some kind
                                                        the last time the files were generated.</P>
                                                        """)])
        self._viewer_timestamp = None
        self.connect(self.viewer_dialog, SIGNAL("Regenerate"),
                     self._regenerateLog)
        self.connect(self.viewer_dialog, SIGNAL("viewPath"), self._viewPath)

        # Log title toolbar
        title_tb = QToolBar(cw)
        title_tb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        title_tb.setIconSize(QSize(16, 16))
        cwlo.addWidget(title_tb)
        title_label = QLabel("Purrlog title:", title_tb)
        title_tb.addWidget(title_label)
        self.title_editor = QLineEdit(title_tb)
        title_tb.addWidget(self.title_editor)
        self.connect(self.title_editor, SIGNAL("editingFinished()"),
                     self._titleChanged)
        tip = """<P>This is your current log title. To rename the log, enter new name here and press Enter.</P>"""
        title_label.setToolTip(tip)
        self.title_editor.setToolTip(tip)
        self.wviewlog = title_tb.addAction(pixmaps.openbook.icon(), "View",
                                           self._showViewerDialog)
        self.wviewlog.setToolTip(
            "Click to see an HTML rendering of your current log.")
        qa = title_tb.addAction(pixmaps.purr_logo.icon(), "About...",
                                self._about_dialog.exec_)
        qa.setToolTip(
            "<P>Click to see the About... dialog, which will tell you something about PURR.</P>"
        )

        self.wdirframe = QFrame(cw)
        cwlo.addWidget(self.wdirframe)
        self.dirs_lo = QVBoxLayout(self.wdirframe)
        self.dirs_lo.setMargin(5)
        self.dirs_lo.setContentsMargins(5, 0, 5, 5)
        self.dirs_lo.setSpacing(0)
        self.wdirframe.setFrameStyle(QFrame.Box | QFrame.Raised)
        self.wdirframe.setLineWidth(1)

        ## Directories toolbar
        dirs_tb = QToolBar(self.wdirframe)
        dirs_tb.setToolButtonStyle(Qt.ToolButtonIconOnly)
        dirs_tb.setIconSize(QSize(16, 16))
        self.dirs_lo.addWidget(dirs_tb)
        label = QLabel("Monitoring directories:", dirs_tb)
        self._dirs_tip = """<P>PURR can monitor your working directories for new or updated files. If there's a checkmark
      next to the directory name in this list, PURR is monitoring it.</P>

      <P>If the checkmark is grey, PURR is monitoring things unobtrusively. When a new or updated file is detected in he monitored directory,
      it is quietly added to the list of files in the "New entry" window, even if this window is not currently visible.</P>

      <P>If the checkmark is black, PURR will be more obtrusive. Whenever a new or updated file is detected, the "New entry" window will
      pop up automatically. This is called "pouncing", and some people find it annoying.</P>
      """
        label.setToolTip(self._dirs_tip)
        label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        dirs_tb.addWidget(label)

        # add directory list widget
        self.wdirlist = DirectoryListWidget(self.wdirframe)
        self.wdirlist.setToolTip(self._dirs_tip)
        QObject.connect(self.wdirlist, SIGNAL("directoryStateChanged"),
                        self._changeWatchedDirState)
        self.dirs_lo.addWidget(self.wdirlist)
        # self.wdirlist.setMaximumSize(1000000,64)

        # add directory button
        add = dirs_tb.addAction(pixmaps.list_add.icon(), "Add",
                                self._showAddDirectoryDialog)
        add.setToolTip(
            "<P>Click to add another directory to be monitored.</P>")

        # remove directory button
        delbtn = dirs_tb.addAction(pixmaps.list_remove.icon(), "Remove",
                                   self.wdirlist.removeCurrent)
        delbtn.setEnabled(False)
        delbtn.setToolTip(
            "<P>Click to removed the currently selected directory from the list.</P>"
        )
        QObject.connect(self.wdirlist, SIGNAL("hasSelection"),
                        delbtn.setEnabled)

        #    # qa = dirs_tb.addAction(pixmaps.blue_round_reload.icon(),"Rescan",self._forceRescan)
        #    # qa.setToolTip("Click to rescan the directories for any new or updated files.")
        #    self.wshownew = QCheckBox("show new files",dirs_tb)
        #    dirs_tb.addWidget(self.wshownew)
        #    self.wshownew.setCheckState(Qt.Checked)
        #    self.wshownew.setToolTip("""<P>If this is checked, the "New entry" window will pop up automatically whenever
        #  new or updated files are detected. If this is unchecked, the files will be added to the window quietly
        #        and unobtrusively; you can show the window manually by clicking on the "New entry..." button below.</P>""")
        #    self._dir_entries = {}

        cwlo.addSpacing(5)

        wlogframe = QFrame(cw)
        cwlo.addWidget(wlogframe)
        log_lo = QVBoxLayout(wlogframe)
        log_lo.setMargin(5)
        log_lo.setContentsMargins(5, 5, 5, 5)
        log_lo.setSpacing(0)
        wlogframe.setFrameStyle(QFrame.Box | QFrame.Raised)
        wlogframe.setLineWidth(1)

        # listview of log entries
        self.etw = LogEntryTree(cw)
        log_lo.addWidget(self.etw, 1)
        self.etw.header().setDefaultSectionSize(128)
        self.etw.header().setMovable(False)
        self.etw.setHeaderLabels(["date", "entry title", "comment"])
        if hasattr(QHeaderView, 'ResizeToContents'):
            self.etw.header().setResizeMode(0, QHeaderView.ResizeToContents)
        else:
            self.etw.header().setResizeMode(0, QHeaderView.Custom)
            self.etw.header().resizeSection(0, 120)
        self.etw.header().setResizeMode(1, QHeaderView.Interactive)
        self.etw.header().setResizeMode(2, QHeaderView.Stretch)
        self.etw.header().show()
        try:
            self.etw.setAllColumnsShowFocus(True)
        except AttributeError:
            pass
            # Qt 4.2+
        # self.etw.setShowToolTips(True)
        self.etw.setSortingEnabled(False)
        # self.etw.setColumnAlignment(2,Qt.AlignLeft|Qt.AlignTop)
        self.etw.setSelectionMode(QTreeWidget.ExtendedSelection)
        self.etw.setRootIsDecorated(True)
        self.connect(self.etw, SIGNAL("itemSelectionChanged()"),
                     self._entrySelectionChanged)
        self.connect(self.etw, SIGNAL("itemActivated(QTreeWidgetItem*,int)"),
                     self._viewEntryItem)
        self.connect(self.etw, SIGNAL("itemContextMenuRequested"),
                     self._showItemContextMenu)
        # create popup menu for data products
        self._archived_dp_menu = menu = QMenu(self)
        self._archived_dp_menu_title = QLabel()
        self._archived_dp_menu_title.setMargin(5)
        self._archived_dp_menu_title_wa = wa = QWidgetAction(self)
        wa.setDefaultWidget(self._archived_dp_menu_title)
        menu.addAction(wa)
        menu.addSeparator()
        menu.addAction(pixmaps.editcopy.icon(),
                       "Restore file(s) from archived copy",
                       self._restoreItemFromArchive)
        menu.addAction(pixmaps.editpaste.icon(),
                       "Copy pathname of archived copy to clipboard",
                       self._copyItemToClipboard)
        self._current_item = None
        # create popup menu for entries
        self._entry_menu = menu = QMenu(self)
        self._entry_menu_title = QLabel()
        self._entry_menu_title.setMargin(5)
        self._entry_menu_title_wa = wa = QWidgetAction(self)
        wa.setDefaultWidget(self._entry_menu_title)
        menu.addAction(wa)
        menu.addSeparator()
        menu.addAction(pixmaps.filefind.icon(), "View this log entry",
                       self._viewEntryItem)
        menu.addAction(pixmaps.editdelete.icon(), "Delete this log entry",
                       self._deleteSelectedEntries)
        # buttons at bottom
        log_lo.addSpacing(5)
        btnlo = QHBoxLayout()
        log_lo.addLayout(btnlo)
        self.wnewbtn = QPushButton(pixmaps.filenew.icon(), "New entry...", cw)
        self.wnewbtn.setToolTip("Click to add a new log entry.")
        # self.wnewbtn.setFlat(True)
        self.wnewbtn.setEnabled(False)
        btnlo.addWidget(self.wnewbtn)
        btnlo.addSpacing(5)
        self.weditbtn = QPushButton(pixmaps.filefind.icon(), "View entry...",
                                    cw)
        self.weditbtn.setToolTip(
            "Click to view or edit the selected log entry/")
        # self.weditbtn.setFlat(True)
        self.weditbtn.setEnabled(False)
        self.connect(self.weditbtn, SIGNAL("clicked()"), self._viewEntryItem)
        btnlo.addWidget(self.weditbtn)
        btnlo.addSpacing(5)
        self.wdelbtn = QPushButton(pixmaps.editdelete.icon(), "Delete", cw)
        self.wdelbtn.setToolTip(
            "Click to delete the selected log entry or entries.")
        # self.wdelbtn.setFlat(True)
        self.wdelbtn.setEnabled(False)
        self.connect(self.wdelbtn, SIGNAL("clicked()"),
                     self._deleteSelectedEntries)
        btnlo.addWidget(self.wdelbtn)
        # enable status line
        self.statusBar().show()
        Purr.progressMessage = self.message
        self._prev_msg = None
        # editor dialog for new entry
        self.new_entry_dialog = Purr.Editors.NewLogEntryDialog(self)
        self.connect(self.new_entry_dialog, SIGNAL("newLogEntry"),
                     self._newLogEntry)
        self.connect(self.new_entry_dialog, SIGNAL("filesSelected"),
                     self._addDPFiles)
        self.connect(self.wnewbtn, SIGNAL("clicked()"),
                     self.new_entry_dialog.show)
        self.connect(self.new_entry_dialog, SIGNAL("shown"),
                     self._checkPounceStatus)
        # entry viewer dialog
        self.view_entry_dialog = Purr.Editors.ExistingLogEntryDialog(self)
        self.connect(self.view_entry_dialog, SIGNAL("previous()"),
                     self._viewPrevEntry)
        self.connect(self.view_entry_dialog, SIGNAL("next()"),
                     self._viewNextEntry)
        self.connect(self.view_entry_dialog, SIGNAL("viewPath"),
                     self._viewPath)
        self.connect(self.view_entry_dialog, SIGNAL("filesSelected"),
                     self._addDPFilesToOldEntry)
        self.connect(self.view_entry_dialog, SIGNAL("entryChanged"),
                     self._entryChanged)
        # saving a data product to an older entry will automatically drop it from the
        # new entry dialog
        self.connect(self.view_entry_dialog, SIGNAL("creatingDataProduct"),
                     self.new_entry_dialog.dropDataProducts)
        # resize selves
        width = Config.getint('main-window-width', 512)
        height = Config.getint('main-window-height', 512)
        self.resize(QSize(width, height))
        # create timer for pouncing
        self._timer = QTimer(self)
        self.connect(self._timer, SIGNAL("timeout()"), self._rescan)
        # create dict mapping index.html paths to entry numbers
        self._index_paths = {}
Example #38
0
class TagBrowserWidget(QWidget):  # {{{
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.parent = parent
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        self._layout.setContentsMargins(0, 0, 0, 0)

        # Set up the find box & button
        search_layout = QHBoxLayout()
        self._layout.addLayout(search_layout)
        self.item_search = HistoryLineEdit(parent)
        self.item_search.setMinimumContentsLength(10)
        self.item_search.setSizeAdjustPolicy(
            self.item_search.AdjustToMinimumContentsLengthWithIcon)
        try:
            self.item_search.lineEdit().setPlaceholderText(
                _('Find item in tag browser'))
        except:
            pass  # Using Qt < 4.7
        self.item_search.setToolTip(
            _('Search for items. This is a "contains" search; items containing the\n'
              'text anywhere in the name will be found. You can limit the search\n'
              'to particular categories using syntax similar to search. For example,\n'
              'tags:foo will find foo in any tag, but not in authors etc. Entering\n'
              '*foo will filter all categories at once, showing only those items\n'
              'containing the text "foo"'))
        search_layout.addWidget(self.item_search)
        # Not sure if the shortcut should be translatable ...
        sc = QShortcut(QKeySequence(_('ALT+f')), parent)
        sc.activated.connect(self.set_focus_to_find_box)

        self.search_button = QToolButton()
        self.search_button.setText(_('F&ind'))
        self.search_button.setToolTip(_('Find the first/next matching item'))
        search_layout.addWidget(self.search_button)

        self.expand_button = QToolButton()
        self.expand_button.setText('-')
        self.expand_button.setToolTip(_('Collapse all categories'))
        search_layout.addWidget(self.expand_button)
        search_layout.setStretch(0, 10)
        search_layout.setStretch(1, 1)
        search_layout.setStretch(2, 1)

        self.current_find_position = None
        self.search_button.clicked.connect(self.find)
        self.item_search.initialize('tag_browser_search')
        self.item_search.lineEdit().returnPressed.connect(self.do_find)
        self.item_search.lineEdit().textEdited.connect(self.find_text_changed)
        self.item_search.activated[QString].connect(self.do_find)
        self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive)

        parent.tags_view = TagsView(parent)
        self.tags_view = parent.tags_view
        self.expand_button.clicked.connect(self.tags_view.collapseAll)
        self._layout.addWidget(parent.tags_view)

        # Now the floating 'not found' box
        l = QLabel(self.tags_view)
        self.not_found_label = l
        l.setFrameStyle(QFrame.StyledPanel)
        l.setAutoFillBackground(True)
        l.setText(
            '<p><b>' +
            _('No More Matches.</b><p> Click Find again to go to first match'))
        l.setAlignment(Qt.AlignVCenter)
        l.setWordWrap(True)
        l.resize(l.sizeHint())
        l.move(10, 20)
        l.setVisible(False)
        self.not_found_label_timer = QTimer()
        self.not_found_label_timer.setSingleShot(True)
        self.not_found_label_timer.timeout.connect(
            self.not_found_label_timer_event, type=Qt.QueuedConnection)

        parent.alter_tb = l = QPushButton(parent)
        l.setText(_('Alter Tag Browser'))
        l.setIcon(QIcon(I('tags.png')))
        l.m = QMenu()
        l.setMenu(l.m)
        self._layout.addWidget(l)

        sb = l.m.addAction(_('Sort by'))
        sb.m = l.sort_menu = QMenu(l.m)
        sb.setMenu(sb.m)
        sb.bg = QActionGroup(sb)

        # Must be in the same order as db2.CATEGORY_SORTS
        for i, x in enumerate((_('Sort by name'), _('Sort by popularity'),
                               _('Sort by average rating'))):
            a = sb.m.addAction(x)
            sb.bg.addAction(a)
            a.setCheckable(True)
            if i == 0: a.setChecked(True)
        sb.setToolTip(_('Set the sort order for entries in the Tag Browser'))
        sb.setStatusTip(sb.toolTip())

        ma = l.m.addAction(_('Match type'))
        ma.m = l.match_menu = QMenu(l.m)
        ma.setMenu(ma.m)
        ma.ag = QActionGroup(ma)

        # Must be in the same order as db2.MATCH_TYPE
        for i, x in enumerate((_('Match any'), _('Match all'))):
            a = ma.m.addAction(x)
            ma.ag.addAction(a)
            a.setCheckable(True)
            if i == 0: a.setChecked(True)
        ma.setToolTip(
            _('When selecting multiple entries in the Tag Browser '
              'match any or all of them'))
        ma.setStatusTip(ma.toolTip())

        mt = l.m.addAction(_('Manage authors, tags, etc'))
        mt.setToolTip(
            _('All of these category_managers are available by right-clicking '
              'on items in the tag browser above'))
        mt.m = l.manage_menu = QMenu(l.m)
        mt.setMenu(mt.m)

        # self.leak_test_timer = QTimer(self)
        # self.leak_test_timer.timeout.connect(self.test_for_leak)
        # self.leak_test_timer.start(5000)

    def set_pane_is_visible(self, to_what):
        self.tags_view.set_pane_is_visible(to_what)

    def find_text_changed(self, str):
        self.current_find_position = None

    def set_focus_to_find_box(self):
        self.item_search.setFocus()
        self.item_search.lineEdit().selectAll()

    def do_find(self, str=None):
        self.current_find_position = None
        self.find()

    def find(self):
        model = self.tags_view.model()
        model.clear_boxed()
        txt = unicode(self.item_search.currentText()).strip()

        if txt.startswith('*'):
            model.set_categories_filter(txt[1:])
            self.tags_view.recount()
            self.current_find_position = None
            return
        if model.get_categories_filter():
            model.set_categories_filter(None)
            self.tags_view.recount()
            self.current_find_position = None

        if not txt:
            return

        self.item_search.lineEdit().blockSignals(True)
        self.search_button.setFocus(True)
        self.item_search.lineEdit().blockSignals(False)

        key = None
        colon = txt.rfind(':') if len(txt) > 2 else 0
        if colon > 0:
            key = self.parent.library_view.model().db.\
                        field_metadata.search_term_to_field_key(txt[:colon])
            txt = txt[colon + 1:]

        self.current_find_position = \
            model.find_item_node(key, txt, self.current_find_position)

        if self.current_find_position:
            self.tags_view.show_item_at_path(self.current_find_position,
                                             box=True)
        elif self.item_search.text():
            self.not_found_label.setVisible(True)
            if self.tags_view.verticalScrollBar().isVisible():
                sbw = self.tags_view.verticalScrollBar().width()
            else:
                sbw = 0
            width = self.width() - 8 - sbw
            height = self.not_found_label.heightForWidth(width) + 20
            self.not_found_label.resize(width, height)
            self.not_found_label.move(4, 10)
            self.not_found_label_timer.start(2000)

    def not_found_label_timer_event(self):
        self.not_found_label.setVisible(False)
Example #39
0
    def __init__(
        self,
        parent=None,
    ):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Create New Barnes & Noble Key".format(
            PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        data_group_box = QGroupBox(u"", self)
        layout.addWidget(data_group_box)
        data_group_box_layout = QVBoxLayout()
        data_group_box.setLayout(data_group_box_layout)

        key_group = QHBoxLayout()
        data_group_box_layout.addLayout(key_group)
        key_group.addWidget(QLabel(u"Unique Key Name:", self))
        self.key_ledit = QLineEdit("", self)
        self.key_ledit.setToolTip(
            _(u"<p>Enter an identifying name for this new key.</p>" +
              u"<p>It should be something that will help you remember " +
              u"what personal information was used to create it."))
        key_group.addWidget(self.key_ledit)
        key_label = QLabel(_(''), self)
        key_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(key_label)

        name_group = QHBoxLayout()
        data_group_box_layout.addLayout(name_group)
        name_group.addWidget(QLabel(u"Your Name:", self))
        self.name_ledit = QLineEdit(u"", self)
        self.name_ledit.setToolTip(
            _(u"<p>Enter your name as it appears in your B&N " +
              u"account or on your credit card.</p>" +
              u"<p>It will only be used to generate this " +
              u"one-time key and won\'t be stored anywhere " +
              u"in calibre or on your computer.</p>" +
              u"<p>(ex: Jonathan Smith)"))
        name_group.addWidget(self.name_ledit)
        name_disclaimer_label = QLabel(
            _(u"(Will not be saved in configuration data)"), self)
        name_disclaimer_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(name_disclaimer_label)

        ccn_group = QHBoxLayout()
        data_group_box_layout.addLayout(ccn_group)
        ccn_group.addWidget(QLabel(u"Credit Card#:", self))
        self.cc_ledit = QLineEdit(u"", self)
        self.cc_ledit.setToolTip(
            _(u"<p>Enter the full credit card number on record " +
              u"in your B&N account.</p>" +
              u"<p>No spaces or dashes... just the numbers. " +
              u"This number will only be used to generate this " +
              u"one-time key and won\'t be stored anywhere in " +
              u"calibre or on your computer."))
        ccn_group.addWidget(self.cc_ledit)
        ccn_disclaimer_label = QLabel(
            _('(Will not be saved in configuration data)'), self)
        ccn_disclaimer_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(ccn_disclaimer_label)
        layout.addSpacing(10)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Example #40
0
    def __init__(
        self,
        parent=None,
    ):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Create New eReader Key".format(
            PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        data_group_box = QGroupBox(u"", self)
        layout.addWidget(data_group_box)
        data_group_box_layout = QVBoxLayout()
        data_group_box.setLayout(data_group_box_layout)

        key_group = QHBoxLayout()
        data_group_box_layout.addLayout(key_group)
        key_group.addWidget(QLabel(u"Unique Key Name:", self))
        self.key_ledit = QLineEdit("", self)
        self.key_ledit.setToolTip(
            u"<p>Enter an identifying name for this new key.\nIt should be something that will help you remember what personal information was used to create it."
        )
        key_group.addWidget(self.key_ledit)
        key_label = QLabel(_(''), self)
        key_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(key_label)

        name_group = QHBoxLayout()
        data_group_box_layout.addLayout(name_group)
        name_group.addWidget(QLabel(u"Your Name:", self))
        self.name_ledit = QLineEdit(u"", self)
        self.name_ledit.setToolTip(
            u"Enter the name for this eReader key, usually the name on your credit card.\nIt will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.\n(ex: Mr Jonathan Q Smith)"
        )
        name_group.addWidget(self.name_ledit)
        name_disclaimer_label = QLabel(
            _(u"(Will not be saved in configuration data)"), self)
        name_disclaimer_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(name_disclaimer_label)

        ccn_group = QHBoxLayout()
        data_group_box_layout.addLayout(ccn_group)
        ccn_group.addWidget(QLabel(u"Credit Card#:", self))
        self.cc_ledit = QLineEdit(u"", self)
        self.cc_ledit.setToolTip(
            u"<p>Enter the last 8 digits of credit card number for this eReader key.\nThey will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer."
        )
        ccn_group.addWidget(self.cc_ledit)
        ccn_disclaimer_label = QLabel(
            _('(Will not be saved in configuration data)'), self)
        ccn_disclaimer_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(ccn_disclaimer_label)
        layout.addSpacing(10)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Example #41
0
    def __init__(
        self,
        parent=None,
    ):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(
            u"{0} {1}: Getting Default Kindle for Mac/PC Key".format(
                PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        try:
            if iswindows or isosx:
                from calibre_plugins.dedrm.kindlekey import kindlekeys

                defaultkeys = kindlekeys()
            else:  # linux
                from wineutils import WineGetKeys

                scriptpath = os.path.join(parent.parent.alfdir,
                                          u"kindlekey.py")
                defaultkeys = WineGetKeys(scriptpath, u".k4i",
                                          parent.getwineprefix())

            self.default_key = defaultkeys[0]
        except:
            traceback.print_exc()
            self.default_key = u""

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)

        if len(self.default_key) > 0:
            data_group_box = QGroupBox(u"", self)
            layout.addWidget(data_group_box)
            data_group_box_layout = QVBoxLayout()
            data_group_box.setLayout(data_group_box_layout)

            key_group = QHBoxLayout()
            data_group_box_layout.addLayout(key_group)
            key_group.addWidget(QLabel(u"Unique Key Name:", self))
            self.key_ledit = QLineEdit(u"default_key", self)
            self.key_ledit.setToolTip(
                u"<p>Enter an identifying name for the current default Kindle for Mac/PC key."
            )
            key_group.addWidget(self.key_ledit)
            key_label = QLabel(_(''), self)
            key_label.setAlignment(Qt.AlignHCenter)
            data_group_box_layout.addWidget(key_label)
            self.button_box.accepted.connect(self.accept)
        else:
            default_key_error = QLabel(
                u"The default encryption key for Kindle for Mac/PC could not be found.",
                self)
            default_key_error.setAlignment(Qt.AlignHCenter)
            layout.addWidget(default_key_error)
            # if no default, bot buttons do the same
            self.button_box.accepted.connect(self.reject)

        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Example #42
0
    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                                        _('User Plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(
            self._filter_combo_changed)
        header_layout.addWidget(QLabel(
            _('Filter list of plugins') + ':', self))
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse
                                            | Qt.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description') + ':', self), 0,
                                 Qt.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(
            _('&Install'), QDialogButtonBox.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(
            ' ' + _('&Customize plugin ') + ' ', QDialogButtonBox.ResetRole)
        self.configure_button.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)
Example #43
0
    def __init__(self, plugin_path, alfdir):
        QWidget.__init__(self)

        self.plugin_path = plugin_path
        self.alfdir = alfdir

        # get the prefs
        self.dedrmprefs = prefs.DeDRM_Prefs()

        # make a local copy
        self.tempdedrmprefs = {}
        self.tempdedrmprefs['bandnkeys'] = self.dedrmprefs['bandnkeys'].copy()
        self.tempdedrmprefs['adeptkeys'] = self.dedrmprefs['adeptkeys'].copy()
        self.tempdedrmprefs['ereaderkeys'] = self.dedrmprefs[
            'ereaderkeys'].copy()
        self.tempdedrmprefs['kindlekeys'] = self.dedrmprefs['kindlekeys'].copy(
        )
        self.tempdedrmprefs['pids'] = list(self.dedrmprefs['pids'])
        self.tempdedrmprefs['serials'] = list(self.dedrmprefs['serials'])
        self.tempdedrmprefs['adobewineprefix'] = self.dedrmprefs[
            'adobewineprefix']
        self.tempdedrmprefs['kindlewineprefix'] = self.dedrmprefs[
            'kindlewineprefix']

        # Start Qt Gui dialog layout
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        help_layout = QHBoxLayout()
        layout.addLayout(help_layout)
        # Add hyperlink to a help file at the right. We will replace the correct name when it is clicked.
        help_label = QLabel('<a href="http://www.foo.com/">Plugin Help</a>',
                            self)
        help_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse
                                           | Qt.LinksAccessibleByKeyboard)
        help_label.setAlignment(Qt.AlignRight)
        help_label.linkActivated.connect(self.help_link_activated)
        help_layout.addWidget(help_label)

        keys_group_box = QGroupBox(_('Configuration:'), self)
        layout.addWidget(keys_group_box)
        keys_group_box_layout = QHBoxLayout()
        keys_group_box.setLayout(keys_group_box_layout)

        button_layout = QVBoxLayout()
        keys_group_box_layout.addLayout(button_layout)
        self.bandn_button = QtGui.QPushButton(self)
        self.bandn_button.setToolTip(
            _(u"Click to manage keys for Barnes and Noble ebooks"))
        self.bandn_button.setText(u"Barnes and Noble ebooks")
        self.bandn_button.clicked.connect(self.bandn_keys)
        self.kindle_serial_button = QtGui.QPushButton(self)
        self.kindle_serial_button.setToolTip(
            _(u"Click to manage eInk Kindle serial numbers for Kindle ebooks"))
        self.kindle_serial_button.setText(u"eInk Kindle ebooks")
        self.kindle_serial_button.clicked.connect(self.kindle_serials)
        self.kindle_key_button = QtGui.QPushButton(self)
        self.kindle_key_button.setToolTip(
            _(u"Click to manage keys for Kindle for Mac/PC ebooks"))
        self.kindle_key_button.setText(u"Kindle for Mac/PC ebooks")
        self.kindle_key_button.clicked.connect(self.kindle_keys)
        self.adept_button = QtGui.QPushButton(self)
        self.adept_button.setToolTip(
            _(u"Click to manage keys for Adobe Digital Editions ebooks"))
        self.adept_button.setText(u"Adobe Digital Editions ebooks")
        self.adept_button.clicked.connect(self.adept_keys)
        self.mobi_button = QtGui.QPushButton(self)
        self.mobi_button.setToolTip(
            _(u"Click to manage PIDs for Mobipocket ebooks"))
        self.mobi_button.setText(u"Mobipocket ebooks")
        self.mobi_button.clicked.connect(self.mobi_keys)
        self.ereader_button = QtGui.QPushButton(self)
        self.ereader_button.setToolTip(
            _(u"Click to manage keys for eReader ebooks"))
        self.ereader_button.setText(u"eReader ebooks")
        self.ereader_button.clicked.connect(self.ereader_keys)
        button_layout.addWidget(self.kindle_serial_button)
        button_layout.addWidget(self.bandn_button)
        button_layout.addWidget(self.mobi_button)
        button_layout.addWidget(self.ereader_button)
        button_layout.addWidget(self.adept_button)
        button_layout.addWidget(self.kindle_key_button)

        self.resize(self.sizeHint())
Example #44
0
    def __init__(self,
                 parent=None,
                 desc=None,
                 name=None,
                 modal=0,
                 fl=0,
                 env=None,
                 type="QDialog"):
        if env is None:
            import foundation.env as env  # this is a little weird... probably it'll be ok, and logically it seems correct.

        self.desc = desc

        self.typ = type
        if type == "QDialog":
            QDialog.__init__(self, parent, name, modal, fl)
        elif type == "QTextEdit":
            QTextEdit.__init__(self, parent, name)
        elif type == "QFrame":
            QFrame.__init__(self, parent, name)
        else:
            print "don't know about type == %r" % (type, )

        self.image1 = QPixmap()
        self.image1.loadFromData(image1_data,
                                 "PNG")  # should be: title_icon ####
        self.image3 = QPixmap()
        self.image3.loadFromData(image3_data, "PNG")
        self.image4 = QPixmap()
        self.image4.loadFromData(image4_data, "PNG")
        self.image5 = QPixmap()
        self.image5.loadFromData(image5_data, "PNG")
        self.image6 = QPixmap()
        self.image6.loadFromData(image6_data, "PNG")
        self.image7 = QPixmap()
        self.image7.loadFromData(image7_data, "PNG")
        self.image0 = QPixmap(image0_data)  # should be: border_icon ####
        self.image2 = QPixmap(image2_data)  # should be: sponsor_pixmap ####

        try:
            ####@@@@
            title_icon_name = self.desc.options.get('title_icon')
            border_icon_name = self.desc.options.get('border_icon')
            if title_icon_name:
                self.image1 = imagename_to_pixmap(
                    title_icon_name)  ###@@@ pass icon_path
                ###@@@ import imagename_to_pixmap or use env function
                # or let that func itself be an arg, or have an env arg for it
                ###e rename it icon_name_to_pixmap, or find_icon? (the latter only if it's ok if it returns an iconset)
                ###e use iconset instead?
            if border_icon_name:
                self.image0 = imagename_to_pixmap(border_icon_name)
        except:
            print_compact_traceback(
                "bug in icon-setting code, using fallback icons: ")
            pass

        if not name:
            self.setName("parameter_dialog_or_frame")  ###

        ###k guess this will need: if type == 'QDialog'
        self.setIcon(self.image0)  # should be: border_icon ####

        nanotube_dialogLayout = QVBoxLayout(self, 0, 0,
                                            "nanotube_dialogLayout")

        self.heading_frame = QFrame(self, "heading_frame")
        self.heading_frame.setPaletteBackgroundColor(QColor(122, 122, 122))
        self.heading_frame.setFrameShape(QFrame.NoFrame)
        self.heading_frame.setFrameShadow(QFrame.Plain)
        heading_frameLayout = QHBoxLayout(self.heading_frame, 0, 3,
                                          "heading_frameLayout")

        self.heading_pixmap = QLabel(self.heading_frame, "heading_pixmap")
        self.heading_pixmap.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed, 0, 0,
                        self.heading_pixmap.sizePolicy().hasHeightForWidth()))
        self.heading_pixmap.setPixmap(
            self.image1)  # should be: title_icon ####
        self.heading_pixmap.setScaledContents(1)
        heading_frameLayout.addWidget(self.heading_pixmap)

        self.heading_label = QLabel(self.heading_frame, "heading_label")
        self.heading_label.setPaletteForegroundColor(QColor(255, 255, 255))
        heading_label_font = QFont(self.heading_label.font())
        heading_label_font.setPointSize(12)
        heading_label_font.setBold(1)
        self.heading_label.setFont(heading_label_font)
        heading_frameLayout.addWidget(self.heading_label)
        nanotube_dialogLayout.addWidget(self.heading_frame)

        self.body_frame = QFrame(self, "body_frame")
        self.body_frame.setFrameShape(QFrame.StyledPanel)
        self.body_frame.setFrameShadow(QFrame.Raised)
        body_frameLayout = QVBoxLayout(self.body_frame, 3, 3,
                                       "body_frameLayout")

        self.sponsor_frame = QFrame(self.body_frame, "sponsor_frame")
        self.sponsor_frame.setPaletteBackgroundColor(QColor(255, 255, 255))
        self.sponsor_frame.setFrameShape(QFrame.StyledPanel)
        self.sponsor_frame.setFrameShadow(QFrame.Raised)
        sponsor_frameLayout = QHBoxLayout(self.sponsor_frame, 0, 0,
                                          "sponsor_frameLayout")

        self.sponsor_btn = QPushButton(self.sponsor_frame, "sponsor_btn")
        self.sponsor_btn.setAutoDefault(0)  #bruce 060703 bugfix
        self.sponsor_btn.setSizePolicy(
            QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred, 0, 0,
                        self.sponsor_btn.sizePolicy().hasHeightForWidth()))
        self.sponsor_btn.setPaletteBackgroundColor(QColor(255, 255, 255))
        self.sponsor_btn.setPixmap(
            self.image2
        )  # should be: sponsor_pixmap #### [also we'll need to support >1 sponsor]
        self.sponsor_btn.setFlat(1)
        sponsor_frameLayout.addWidget(self.sponsor_btn)
        body_frameLayout.addWidget(self.sponsor_frame)

        layout59 = QHBoxLayout(None, 0, 6, "layout59")
        left_spacer = QSpacerItem(20, 20, QSizePolicy.Expanding,
                                  QSizePolicy.Minimum)
        layout59.addItem(left_spacer)

        self.done_btn = QToolButton(self.body_frame, "done_btn")
        self.done_btn.setIcon(QIcon(self.image3))
        layout59.addWidget(self.done_btn)

        self.abort_btn = QToolButton(self.body_frame, "abort_btn")
        self.abort_btn.setIcon(QIcon(self.image4))
        layout59.addWidget(self.abort_btn)

        self.preview_btn = QToolButton(self.body_frame, "preview_btn")
        self.preview_btn.setIcon(QIcon(self.image5))
        layout59.addWidget(self.preview_btn)

        self.whatsthis_btn = QToolButton(self.body_frame, "whatsthis_btn")
        self.whatsthis_btn.setIcon(QIcon(self.image6))
        layout59.addWidget(self.whatsthis_btn)
        right_spacer = QSpacerItem(20, 20, QSizePolicy.Expanding,
                                   QSizePolicy.Minimum)
        layout59.addItem(right_spacer)
        body_frameLayout.addLayout(layout59)

        self.groups = []
        self.param_getters = {
        }  # map from param name to get-function (which gets current value out of its widget or controller)

        for group_desc in self.desc.kids('group'):

            # == start parameters_grpbox ### this will differ for Windows style

            header_refs = [
            ]  # keep python refcounted refs to all objects we make (at least the ones pyuic stored in self attrs)

            self.parameters_grpbox = QGroupBox(self.body_frame,
                                               "parameters_grpbox")
            self.parameters_grpbox.setFrameShape(QGroupBox.StyledPanel)
            self.parameters_grpbox.setFrameShadow(QGroupBox.Sunken)
            self.parameters_grpbox.setMargin(0)
            self.parameters_grpbox.setColumnLayout(0, Qt.Vertical)
            self.parameters_grpbox.layout().setSpacing(1)
            self.parameters_grpbox.layout().setMargin(4)
            parameters_grpboxLayout = QVBoxLayout(
                self.parameters_grpbox.layout())
            parameters_grpboxLayout.setAlignment(Qt.AlignTop)

            layout20 = QHBoxLayout(None, 0, 6, "layout20")

            self.nt_parameters_grpbtn = QPushButton(self.parameters_grpbox,
                                                    "nt_parameters_grpbtn")
            self.nt_parameters_grpbtn.setSizePolicy(
                QSizePolicy(
                    QSizePolicy.Minimum, QSizePolicy.Fixed, 0, 0,
                    self.nt_parameters_grpbtn.sizePolicy().hasHeightForWidth())
            )
            self.nt_parameters_grpbtn.setMaximumSize(QSize(16, 16))
            self.nt_parameters_grpbtn.setAutoDefault(0)
            self.nt_parameters_grpbtn.setIcon(QIcon(
                self.image7))  ### not always right, but doesn't matter
            self.nt_parameters_grpbtn.setFlat(1)
            layout20.addWidget(self.nt_parameters_grpbtn)

            self.parameters_grpbox_label = QLabel(self.parameters_grpbox,
                                                  "parameters_grpbox_label")
            self.parameters_grpbox_label.setSizePolicy(
                QSizePolicy(
                    QSizePolicy.Preferred, QSizePolicy.Minimum, 0, 0,
                    self.parameters_grpbox_label.sizePolicy().
                    hasHeightForWidth()))
            self.parameters_grpbox_label.setAlignment(QLabel.AlignVCenter)
            layout20.addWidget(self.parameters_grpbox_label)
            gbx_spacer1 = QSpacerItem(67, 16, QSizePolicy.Expanding,
                                      QSizePolicy.Minimum)
            layout20.addItem(gbx_spacer1)
            parameters_grpboxLayout.addLayout(layout20)

            nt_parameters_body_layout = QGridLayout(
                None, 1, 1, 0, 6, "nt_parameters_body_layout"
            )  ### what is 6 -- is it related to number of items???
            # is it 6 in all the ones we got, but that could be a designer error so i better look it up sometime.

            # == start its kids

            # will use from above: self.parameters_grpbox, nt_parameters_body_layout

            nextrow = 0  # which row of the QGridLayout to start filling next (loop variable)
            hidethese = [
            ]  # set of objects to hide or show, when this group is closed or opened

            for param in group_desc.kids('parameter'):
                # param (a group subobj desc) is always a parameter, but we already plan to extend this beyond that,
                # so we redundantly test for this here.
                getter = None
                paramname = None
                # set these for use by uniform code at the end (e.g. for tooltips)
                editfield = None
                label = None
                if param.isa('parameter'):
                    label = QLabel(self.parameters_grpbox, "members_label")
                    label.setAlignment(QLabel.AlignVCenter | QLabel.AlignRight)
                    nt_parameters_body_layout.addWidget(label, nextrow, 0)
                    hidethese.append(label)
                    thisrow = nextrow
                    nextrow += 1
                    #e following should be known in a place that knows the input language, not here
                    paramname = param.options.get('name') or (
                        param.args and param.args[0]) or "?"
                    paramlabel = param.options.get(
                        'label'
                    ) or paramname  ##e wrong, label "" or none ought to be possible
                    # QtGui.QApplication.translate(self.__class__.__name__, "xyz")
                    label.setText(
                        QtGui.QApplication.translate(self.__class__.__name__,
                                                     paramlabel))

                if param.isa('parameter',
                             widget='combobox',
                             type=('str', None)):
                    self.members_combox = QComboBox(
                        0, self.parameters_grpbox,
                        "members_combox")  ###k  what's 0?
                    editfield = self.members_combox
                    #### it probably needs a handler class, and then that could do this setup
                    self.members_combox.clear()
                    default = param.options.get(
                        'default', None)  # None is not equal to any string
                    thewidgetkid = param.kids(
                        'widget'
                    )[-1]  # kluge; need to think what the desc method for this should be
                    for item in thewidgetkid.kids('item'):
                        itemval = item.args[0]
                        itemtext = itemval
                        self.members_combox.insertItem(
                            QtGui.QApplication.translate(
                                self.__class__.__name__,
                                itemtext))  #k __tr ok??
                        if itemval == default:  #k or itemtext?
                            pass  ##k i find no setItem in our py code, so not sure yet what to do for this.
                    nt_parameters_body_layout.addWidget(
                        self.members_combox, thisrow, 1)
                    hidethese.append(self.members_combox)
                    getter = (lambda combobox=self.members_combox: str(
                        combobox.currentText()))
                    ##e due to __tr or non-str values, it might be better to use currentIndex and look it up in a table
                    # (though whether __tr is good here might depend on what it's used for)

                elif param.isa('parameter',
                               widget=('lineedit', None),
                               type=('str', None)):
                    # this covers explicit str|lineedit, and 3 default cases str, lineedit, neither.
                    # (i.e. if you say parameter and nothing else, it's str lineedit by default.)
                    self.length_linedit = QLineEdit(self.parameters_grpbox,
                                                    "length_linedit")
                    editfield = self.length_linedit
                    nt_parameters_body_layout.addWidget(
                        self.length_linedit, thisrow, 1)
                    hidethese.append(self.length_linedit)
                    default = str(param.options.get('default', ""))
                    self.length_linedit.setText(
                        QtGui.QApplication.translate(self.__class__.__name__,
                                                     default))  # __tr ok?
                    getter = (lambda lineedit=self.length_linedit: str(
                        lineedit.text()))

                elif param.isa('parameter',
                               widget=('lineedit', None),
                               type='float'):
                    self.length_linedit = QLineEdit(self.parameters_grpbox,
                                                    "length_linedit")
                    editfield = self.length_linedit
                    nt_parameters_body_layout.addWidget(
                        self.length_linedit, thisrow, 1)
                    hidethese.append(self.length_linedit)
                    controller = FloatLineeditController_Qt(
                        self, param, self.length_linedit)
                    header_refs.append(controller)
                    getter = controller.get_value

                elif param.isa('parameter', widget = ('spinbox', None), type = 'int') or \
                     param.isa('parameter', widget = ('spinbox'), type = None):
                    self.chirality_N_spinbox = QSpinBox(
                        self.parameters_grpbox, "chirality_N_spinbox"
                    )  # was chirality_m_spinbox, now chirality_N_spinbox
                    editfield = self.chirality_N_spinbox
                    ### seems like Qt defaults for min and max are 0,100 -- way too small a range!
                    if param.options.has_key('min') or 1:
                        self.chirality_N_spinbox.setMinimum(
                            param.options.get('min', -999999999))  # was 0
                    if param.options.has_key('max') or 1:
                        self.chirality_N_spinbox.setMaximum(
                            param.options.get(
                                'max',
                                +999999999))  # wasn't in egcode, but needed
                    self.chirality_N_spinbox.setValue(
                        param.options.get('default', 0))  # was 5
                    ##e note: i suspect this default 0 should come from something that knows this desc grammar.
                    suffix = param.options.get('suffix', '')
                    if suffix:
                        self.chirality_N_spinbox.setSuffix(
                            QtGui.QApplication.translate(
                                self.__class__.__name__, suffix))
                    else:
                        self.chirality_N_spinbox.setSuffix(
                            QString.null)  # probably not needed
                    nt_parameters_body_layout.addWidget(
                        self.chirality_N_spinbox, thisrow, 1)
                    hidethese.append(self.chirality_N_spinbox)
                    getter = self.chirality_N_spinbox.value  # note: it also has .text, which includes suffix

                else:
                    print "didn't match:", param  ###e improve this

                # things done the same way for all kinds of param-editing widgets
                if 1:  #bruce 060703 moved this down here, as bugfix
                    # set tooltip (same one for editfield and label)
                    tooltip = param.options.get('tooltip', '')
                    ###e do it for more kinds of params; share the code somehow; do it in controller, or setup-aid?
                    ###k QToolTip appropriateness; tooltip option might be entirely untested
                    if tooltip and label:
                        QToolTip.add(
                            label,
                            QtGui.QApplication.translate(
                                self.__class__.__name__, tooltip))
                    if tooltip and editfield:
                        QToolTip.add(
                            editfield,
                            QtGui.QApplication.translate(
                                self.__class__.__name__, tooltip)
                        )  ##k ok?? review once not all params have same-row labels.

                if getter and paramname and paramname != '?':
                    self.param_getters[paramname] = getter
                ### also bind these params to actions...
                continue  # next param

            header_refs.extend([
                self.parameters_grpbox, self.nt_parameters_grpbtn,
                self.parameters_grpbox_label
            ])

            # now create the logic/control object for the group
            group = CollapsibleGroupController_Qt(self, group_desc,
                                                  header_refs, hidethese,
                                                  self.nt_parameters_grpbtn)
            ### maybe ask env for the class to use for this?
            self.groups.append(
                group
            )  ### needed?? only for scanning the params, AFAIK -- oh, and to maintain a python refcount.

            # from languageChange:
            if 1:  # i don't know if these are needed:
                self.parameters_grpbox.setTitle(QString.null)
                self.nt_parameters_grpbtn.setText(QString.null)
            self.parameters_grpbox_label.setText(
                QtGui.QApplication.translate(
                    self.__class__.__name__,
                    group_desc.args[0]))  # was "Nanotube Parameters"
            ##e note that it's questionable in the syntax design for this property of a group (overall group label)
            # to be in that position (desc arg 0).

            # == end its kids

            parameters_grpboxLayout.addLayout(nt_parameters_body_layout)
            body_frameLayout.addWidget(self.parameters_grpbox)

            # == end parameters groupbox

            continue  # next group

        nanotube_dialogLayout.addWidget(self.body_frame)
        spacer14 = QSpacerItem(20, 20, QSizePolicy.Minimum,
                               QSizePolicy.Expanding)
        nanotube_dialogLayout.addItem(spacer14)

        layout42 = QHBoxLayout(None, 4, 6, "layout42")
        btm_spacer = QSpacerItem(59, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        layout42.addItem(btm_spacer)

        self.cancel_btn = QPushButton(self, "cancel_btn")
        self.cancel_btn.setAutoDefault(0)  #bruce 060703 bugfix
        layout42.addWidget(self.cancel_btn)

        self.ok_btn = QPushButton(self, "ok_btn")
        self.ok_btn.setAutoDefault(0)  #bruce 060703 bugfix
        layout42.addWidget(self.ok_btn)
        nanotube_dialogLayout.addLayout(layout42)

        self.languageChange()

        self.resize(
            QSize(246, 618).expandedTo(self.minimumSizeHint())
        )  ### this size will need to be adjusted (guess -- it's only place overall size is set)
        qt4todo('self.clearWState(Qt.WState_Polished)')

        ## self.connect(self.nt_parameters_grpbtn,SIGNAL("clicked()"),self.toggle_nt_parameters_grpbtn) ####

        # new:
        for button, methodname in (
            (self.sponsor_btn,
             'do_sponsor_btn'),  #e generalize to more than one sponsor button
            (self.done_btn, 'do_done_btn'),
            (self.abort_btn, 'do_abort_btn'),
            (self.preview_btn, 'do_preview_btn'),
            (self.whatsthis_btn, 'do_whatsthis_btn'),
            (self.cancel_btn, 'do_cancel_btn'),
            (self.ok_btn, 'do_ok_btn')):
            if hasattr(self, methodname):
                self.connect(button, SIGNAL("clicked()"),
                             getattr(self, methodname))
        return
Example #45
0
    def __init__(self,
                 parent,
                 key_type_name,
                 plugin_keys,
                 create_key,
                 keyfile_ext=u"",
                 wineprefix=None):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.key_type_name = key_type_name
        self.plugin_keys = plugin_keys
        self.create_key = create_key
        self.keyfile_ext = keyfile_ext
        self.import_key = (keyfile_ext != u"")
        self.binary_file = (keyfile_ext == u".der")
        self.json_file = (keyfile_ext == u".k4i")
        self.wineprefix = wineprefix

        self.setWindowTitle("{0} {1}: Manage {2}s".format(
            PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))

        # Start Qt Gui dialog layout
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        help_layout = QHBoxLayout()
        layout.addLayout(help_layout)
        # Add hyperlink to a help file at the right. We will replace the correct name when it is clicked.
        help_label = QLabel('<a href="http://www.foo.com/">Help</a>', self)
        help_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse
                                           | Qt.LinksAccessibleByKeyboard)
        help_label.setAlignment(Qt.AlignRight)
        help_label.linkActivated.connect(self.help_link_activated)
        help_layout.addWidget(help_label)

        keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self)
        layout.addWidget(keys_group_box)
        keys_group_box_layout = QHBoxLayout()
        keys_group_box.setLayout(keys_group_box_layout)

        self.listy = QListWidget(self)
        self.listy.setToolTip(
            u"{0}s that will be used to decrypt ebooks".format(
                self.key_type_name))
        self.listy.setSelectionMode(QAbstractItemView.SingleSelection)
        self.populate_list()
        keys_group_box_layout.addWidget(self.listy)

        button_layout = QVBoxLayout()
        keys_group_box_layout.addLayout(button_layout)
        self._add_key_button = QtGui.QToolButton(self)
        self._add_key_button.setToolTip(u"Create new {0}".format(
            self.key_type_name))
        self._add_key_button.setIcon(QIcon(I('plus.png')))
        self._add_key_button.clicked.connect(self.add_key)
        button_layout.addWidget(self._add_key_button)

        self._delete_key_button = QtGui.QToolButton(self)
        self._delete_key_button.setToolTip(_(u"Delete highlighted key"))
        self._delete_key_button.setIcon(QIcon(I('list_remove.png')))
        self._delete_key_button.clicked.connect(self.delete_key)
        button_layout.addWidget(self._delete_key_button)

        if type(self.plugin_keys) == dict and self.import_key:
            self._rename_key_button = QtGui.QToolButton(self)
            self._rename_key_button.setToolTip(_(u"Rename highlighted key"))
            self._rename_key_button.setIcon(QIcon(I('edit-select-all.png')))
            self._rename_key_button.clicked.connect(self.rename_key)
            button_layout.addWidget(self._rename_key_button)

            self.export_key_button = QtGui.QToolButton(self)
            self.export_key_button.setToolTip(
                u"Save highlighted key to a .{0} file".format(
                    self.keyfile_ext))
            self.export_key_button.setIcon(QIcon(I('save.png')))
            self.export_key_button.clicked.connect(self.export_key)
            button_layout.addWidget(self.export_key_button)
        spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
                                       QtGui.QSizePolicy.Expanding)
        button_layout.addItem(spacerItem)

        if self.wineprefix is not None:
            layout.addSpacing(5)
            wineprefix_layout = QHBoxLayout()
            layout.addLayout(wineprefix_layout)
            wineprefix_layout.setAlignment(Qt.AlignCenter)
            self.wp_label = QLabel(u"WINEPREFIX:")
            wineprefix_layout.addWidget(self.wp_label)
            self.wp_lineedit = QLineEdit(self)
            wineprefix_layout.addWidget(self.wp_lineedit)
            self.wp_label.setBuddy(self.wp_lineedit)
            self.wp_lineedit.setText(self.wineprefix)

        layout.addSpacing(5)
        migrate_layout = QHBoxLayout()
        layout.addLayout(migrate_layout)
        if self.import_key:
            migrate_layout.setAlignment(Qt.AlignJustify)
            self.migrate_btn = QPushButton(u"Import Existing Keyfiles", self)
            self.migrate_btn.setToolTip(
                u"Import *.{0} files (created using other tools).".format(
                    self.keyfile_ext))
            self.migrate_btn.clicked.connect(self.migrate_wrapper)
            migrate_layout.addWidget(self.migrate_btn)
        migrate_layout.addStretch()
        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.button_box.rejected.connect(self.close)
        migrate_layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Example #46
0
    def __init__(self, plugin):
        DefaultConfigWidget.__init__(self, plugin)
        c = plugin_prefs[STORE_NAME]
        all_tags = get_current_db().all_tags()

        self.gb.setMaximumHeight(80)
        genre_group_box = QGroupBox('CBDB genre to calibre tag mappings', self)
        self.l.addWidget(genre_group_box, self.l.rowCount(), 0, 1, 2)
        genre_group_box_layout = QVBoxLayout()
        genre_group_box.setLayout(genre_group_box_layout)

        tags_layout = QHBoxLayout()
        genre_group_box_layout.addLayout(tags_layout)

        self.edit_table = GenreTagMappingsTableWidget(self, all_tags)
        tags_layout.addWidget(self.edit_table)
        button_layout = QVBoxLayout()
        tags_layout.addLayout(button_layout)
        add_mapping_button = QtGui.QToolButton(self)
        add_mapping_button.setToolTip('Add genre mapping')
        add_mapping_button.setIcon(QIcon(I('plus.png')))
        add_mapping_button.clicked.connect(self.add_mapping)
        button_layout.addWidget(add_mapping_button)
        spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
                                        QtGui.QSizePolicy.Expanding)
        button_layout.addItem(spacerItem1)
        remove_mapping_button = QtGui.QToolButton(self)
        remove_mapping_button.setToolTip('Delete genre mapping')
        remove_mapping_button.setIcon(QIcon(I('minus.png')))
        remove_mapping_button.clicked.connect(self.delete_mapping)
        button_layout.addWidget(remove_mapping_button)
        spacerItem3 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
                                        QtGui.QSizePolicy.Expanding)
        button_layout.addItem(spacerItem3)
        rename_genre_button = QtGui.QToolButton(self)
        rename_genre_button.setToolTip('Rename CBDB genre')
        rename_genre_button.setIcon(QIcon(I('edit-undo.png')))
        rename_genre_button.clicked.connect(self.rename_genre)
        button_layout.addWidget(rename_genre_button)
        spacerItem2 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
                                        QtGui.QSizePolicy.Expanding)
        button_layout.addItem(spacerItem2)
        reset_defaults_button = QtGui.QToolButton(self)
        reset_defaults_button.setToolTip('Reset to plugin default mappings')
        reset_defaults_button.setIcon(QIcon(I('clear_left.png')))
        reset_defaults_button.clicked.connect(self.reset_to_defaults)
        button_layout.addWidget(reset_defaults_button)
        self.l.setRowStretch(self.l.rowCount() - 1, 2)

        other_group_box = QGroupBox('Other options', self)
        self.l.addWidget(other_group_box, self.l.rowCount(), 0, 1, 2)
        other_group_box_layout = QVBoxLayout()
        other_group_box.setLayout(other_group_box_layout)

        self.get_editions_checkbox = QCheckBox(
            'Scan multiple editions for title/author searches (slower)', self)
        self.get_editions_checkbox.setToolTip(
            'When checked will perform an additional search to scan the top ranked\n'
            'CBDB editions (if available) to exclude audiobook editions.\n'
            'Without this enabled you will get a faster search, using the "best".\n'
            'edition ranked by CBDB which can in some cases be an audiobook.')
        self.get_editions_checkbox.setChecked(c[KEY_GET_EDITIONS])
        other_group_box_layout.addWidget(self.get_editions_checkbox)
        self.all_authors_checkbox = QCheckBox(
            'Get all contributing authors (e.g. illustrators, series editors etc)',
            self)
        self.all_authors_checkbox.setToolTip(
            'CBDB for some books will list all of the contributing authors and\n'
            'the type of contribution like (Editor), (Illustrator) etc.\n\n'
            'When this option is checked, all contributing authors are retrieved.\n\n'
            'When unchecked (default) only the primary author(s) are returned which\n'
            'are those that either have no contribution type specified, or have the\n'
            'value of (CBDB Author).\n\n'
            'If there is no primary author then only those with the same contribution\n'
            'type as the first author are returned.\n'
            'e.g. "A, B (Illustrator)" will return author A\n'
            'e.g. "A (CBDB Author)" will return author A\n'
            'e.g. "A (Editor), B (Editor), C (Illustrator)" will return authors A & B\n'
            'e.g. "A (Editor), B (Series Editor)" will return author A\n')
        self.all_authors_checkbox.setChecked(c[KEY_GET_ALL_AUTHORS])
        other_group_box_layout.addWidget(self.all_authors_checkbox)

        self.edit_table.populate_table(c[KEY_GENRE_MAPPINGS])
Example #47
0
class CheckLibraryDialog(QDialog):

    def __init__(self, parent, db):
        QDialog.__init__(self, parent)
        self.db = db

        self.setWindowTitle(_('Check Library -- Problems Found'))
        self.setWindowIcon(QIcon(I('debug.png')))

        self._tl = QHBoxLayout()
        self.setLayout(self._tl)
        self.splitter = QSplitter(self)
        self.left = QWidget(self)
        self.splitter.addWidget(self.left)
        self.helpw = QTextEdit(self)
        self.splitter.addWidget(self.helpw)
        self._tl.addWidget(self.splitter)
        self._layout = QVBoxLayout()
        self.left.setLayout(self._layout)
        self.helpw.setReadOnly(True)
        self.helpw.setText(_('''\
        <h1>Help</h1>

        <p>calibre stores the list of your books and their metadata in a
        database. The actual book files and covers are stored as normal
        files in the calibre library folder. The database contains a list of the files
        and covers belonging to each book entry. This tool checks that the
        actual files in the library folder on your computer match the
        information in the database.</p>

        <p>The result of each type of check is shown to the left. The various
        checks are:
        </p>
        <ul>
        <li><b>Invalid titles</b>: These are files and folders appearing
        in the library where books titles should, but that do not have the
        correct form to be a book title.</li>
        <li><b>Extra titles</b>: These are extra files in your calibre
        library that appear to be correctly-formed titles, but have no corresponding
        entries in the database</li>
        <li><b>Invalid authors</b>: These are files appearing
        in the library where only author folders should be.</li>
        <li><b>Extra authors</b>: These are folders in the
        calibre library that appear to be authors but that do not have entries
        in the database</li>
        <li><b>Missing book formats</b>: These are book formats that are in
        the database but have no corresponding format file in the book's folder.
        <li><b>Extra book formats</b>: These are book format files found in
        the book's folder but not in the database.
        <li><b>Unknown files in books</b>: These are extra files in the
        folder of each book that do not correspond to a known format or cover
        file.</li>
        <li><b>Missing cover files</b>: These represent books that are marked
        in the database as having covers but the actual cover files are
        missing.</li>
        <li><b>Cover files not in database</b>: These are books that have
        cover files but are marked as not having covers in the database.</li>
        <li><b>Folder raising exception</b>: These represent folders in the
        calibre library that could not be processed/understood by this
        tool.</li>
        </ul>

        <p>There are two kinds of automatic fixes possible: <i>Delete
        marked</i> and <i>Fix marked</i>.</p>
        <p><i>Delete marked</i> is used to remove extra files/folders/covers that
        have no entries in the database. Check the box next to the item you want
        to delete. Use with caution.</p>

        <p><i>Fix marked</i> is applicable only to covers and missing formats
        (the three lines marked 'fixable'). In the case of missing cover files,
        checking the fixable box and pushing this button will tell calibre that
        there is no cover for all of the books listed. Use this option if you
        are not going to restore the covers from a backup. In the case of extra
        cover files, checking the fixable box and pushing this button will tell
        calibre that the cover files it found are correct for all the books
        listed. Use this when you are not going to delete the file(s). In the
        case of missing formats, checking the fixable box and pushing this
        button will tell calibre that the formats are really gone. Use this if
        you are not going to restore the formats from a backup.</p>

        '''))

        self.log = QTreeWidget(self)
        self.log.itemChanged.connect(self.item_changed)
        self.log.itemExpanded.connect(self.item_expanded_or_collapsed)
        self.log.itemCollapsed.connect(self.item_expanded_or_collapsed)
        self._layout.addWidget(self.log)

        self.check_button = QPushButton(_('&Run the check again'))
        self.check_button.setDefault(False)
        self.check_button.clicked.connect(self.run_the_check)
        self.copy_button = QPushButton(_('Copy &to clipboard'))
        self.copy_button.setDefault(False)
        self.copy_button.clicked.connect(self.copy_to_clipboard)
        self.ok_button = QPushButton(_('&Done'))
        self.ok_button.setDefault(True)
        self.ok_button.clicked.connect(self.accept)
        self.delete_button = QPushButton(_('Delete &marked'))
        self.delete_button.setToolTip(_('Delete marked files (checked subitems)'))
        self.delete_button.setDefault(False)
        self.delete_button.clicked.connect(self.delete_marked)
        self.fix_button = QPushButton(_('&Fix marked'))
        self.fix_button.setDefault(False)
        self.fix_button.setEnabled(False)
        self.fix_button.setToolTip(_('Fix marked sections (checked fixable items)'))
        self.fix_button.clicked.connect(self.fix_items)
        self.bbox = QDialogButtonBox(self)
        self.bbox.addButton(self.check_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.delete_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.fix_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.copy_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.ok_button, QDialogButtonBox.AcceptRole)

        h = QHBoxLayout()
        ln = QLabel(_('Names to ignore:'))
        h.addWidget(ln)
        self.name_ignores = QLineEdit()
        self.name_ignores.setText(db.prefs.get('check_library_ignore_names', ''))
        self.name_ignores.setToolTip(
            _('Enter comma-separated standard file name wildcards, such as synctoy*.dat'))
        ln.setBuddy(self.name_ignores)
        h.addWidget(self.name_ignores)
        le = QLabel(_('Extensions to ignore'))
        h.addWidget(le)
        self.ext_ignores = QLineEdit()
        self.ext_ignores.setText(db.prefs.get('check_library_ignore_extensions', ''))
        self.ext_ignores.setToolTip(
            _('Enter comma-separated extensions without a leading dot. Used only in book folders'))
        le.setBuddy(self.ext_ignores)
        h.addWidget(self.ext_ignores)
        self._layout.addLayout(h)

        self._layout.addWidget(self.bbox)
        self.resize(950, 500)
        self.bbox.setEnabled(True)

    def do_exec(self):
        self.run_the_check()

        probs = 0
        for c in self.problem_count:
            probs += self.problem_count[c]
        if probs == 0:
            return False
        self.exec_()
        return True

    def accept(self):
        self.db.prefs['check_library_ignore_extensions'] = \
                                            unicode(self.ext_ignores.text())
        self.db.prefs['check_library_ignore_names'] = \
                                            unicode(self.name_ignores.text())
        QDialog.accept(self)

    def box_to_list(self, txt):
        return [f.strip() for f in txt.split(',') if f.strip()]

    def run_the_check(self):
        checker = CheckLibrary(self.db.library_path, self.db)
        checker.scan_library(self.box_to_list(unicode(self.name_ignores.text())),
                             self.box_to_list(unicode(self.ext_ignores.text())))

        plaintext = []

        def builder(tree, checker, check):
            attr, h, checkable, fixable = check
            list = getattr(checker, attr, None)
            if list is None:
                self.problem_count[attr] = 0
                return
            else:
                self.problem_count[attr] = len(list)

            tl = Item()
            tl.setText(0, h)
            if fixable and list:
                tl.setText(1, _('(fixable)'))
                tl.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                tl.setCheckState(1, False)
            self.top_level_items[attr] = tl

            for problem in list:
                it = Item()
                if checkable:
                    it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                    it.setCheckState(1, False)
                else:
                    it.setFlags(Qt.ItemIsEnabled)
                it.setText(0, problem[0])
                it.setData(0, Qt.UserRole, problem[2])
                it.setText(1, problem[1])
                tl.addChild(it)
                self.all_items.append(it)
                plaintext.append(','.join([h, problem[0], problem[1]]))
            tree.addTopLevelItem(tl)

        t = self.log
        t.clear()
        t.setColumnCount(2)
        t.setHeaderLabels([_('Name'), _('Path from library')])
        self.all_items = []
        self.top_level_items = {}
        self.problem_count = {}
        for check in CHECKS:
            builder(t, checker, check)

        t.resizeColumnToContents(0)
        t.resizeColumnToContents(1)
        self.delete_button.setEnabled(False)
        self.text_results = '\n'.join(plaintext)

    def item_expanded_or_collapsed(self, item):
        self.log.resizeColumnToContents(0)
        self.log.resizeColumnToContents(1)

    def item_changed(self, item, column):
        self.fix_button.setEnabled(False)
        for it in self.top_level_items.values():
            if it.checkState(1):
                self.fix_button.setEnabled(True)

        self.delete_button.setEnabled(False)
        for it in self.all_items:
            if it.checkState(1):
                self.delete_button.setEnabled(True)
                return

    def delete_marked(self):
        if not confirm('<p>'+_('The marked files and folders will be '
               '<b>permanently deleted</b>. Are you sure?')
               +'</p>', 'check_library_editor_delete', self):
            return

        # Sort the paths in reverse length order so that we can be sure that
        # if an item is in another item, the sub-item will be deleted first.
        items = sorted(self.all_items,
                       key=lambda x: len(x.text(1)),
                       reverse=True)
        for it in items:
            if it.checkState(1):
                try:
                    p = os.path.join(self.db.library_path ,unicode(it.text(1)))
                    if os.path.isdir(p):
                        delete_tree(p)
                    else:
                        delete_file(p)
                except:
                    prints('failed to delete',
                            os.path.join(self.db.library_path,
                                unicode(it.text(1))))
        self.run_the_check()

    def fix_missing_formats(self):
        tl = self.top_level_items['missing_formats']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            all = self.db.formats(id, index_is_id=True, verify_formats=False)
            all = set([f.strip() for f in all.split(',')]) if all else set()
            valid = self.db.formats(id, index_is_id=True, verify_formats=True)
            valid = set([f.strip() for f in valid.split(',')]) if valid else set()
            for fmt in all-valid:
                self.db.remove_format(id, fmt, index_is_id=True, db_only=True)

    def fix_missing_covers(self):
        tl = self.top_level_items['missing_covers']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            self.db.set_has_cover(id, False)

    def fix_extra_covers(self):
        tl = self.top_level_items['extra_covers']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            self.db.set_has_cover(id, True)

    def fix_items(self):
        for check in CHECKS:
            attr = check[0]
            fixable = check[3]
            tl = self.top_level_items[attr]
            if fixable and tl.checkState(1):
                func = getattr(self, 'fix_' + attr, None)
                if func is not None and callable(func):
                    func()
        self.run_the_check()

    def copy_to_clipboard(self):
        QApplication.clipboard().setText(self.text_results)
Example #48
0
class PM_DockWidget(QDockWidget):
    """
    PM_DockWidget class provides a dockable widget that can either be docked
    inside a PropertyManager OR can be docked in the MainWindow depending
    on the <parentWidget> . see DnaSequenceEditor.py for an example.
    The dockWidget has its own layout and containerwidget which makes it easy
    to add various children widgets  similar to how its done in PM_GroupBox
    """
    def __init__(self, parentWidget, title="", showWidget=True):
        """
        Constructor for PM_dockWidget

        @param showWidget: If true, this class will show the widget
        immediately in the __init__ method itself. This is the default behavior
        and subclasses may pass appropriate value to this flag
        @type showWidget: bool
        """
        self.labelWidget = None
        self._title = ""
        self._widgetList = []
        self._rowCount = 0

        QDockWidget.__init__(self, parentWidget)

        self.parentWidget = parentWidget

        self._title = title

        self.label = ''
        self.labelColumn = 0
        self.spanWidth = True
        self.labelWidget = None

        if self.label:  # Create this widget's QLabel.
            self.labelWidget = QLabel()
            self.labelWidget.setText(self.label)

        self.setEnabled(True)
        self.setFloating(False)
        self.setVisible(showWidget)
        self.setWindowTitle(self._title)
        self.setAutoFillBackground(True)
        self.setPalette(getPalette(None, QPalette.Window, pmGrpBoxColor))

        self.parentWidget.addDockWidget(Qt.BottomDockWidgetArea, self)

        #Define layout
        self._containerWidget = QWidget()
        self.setWidget(self._containerWidget)

        # Create vertical box layout
        self.vBoxLayout = QVBoxLayout(self._containerWidget)
        self.vBoxLayout.setMargin(1)
        self.vBoxLayout.setSpacing(0)

        # Create grid layout
        self.gridLayout = QGridLayout()
        self.gridLayout.setMargin(1)
        self.gridLayout.setSpacing(1)

        # Insert grid layout in its own vBoxLayout
        self.vBoxLayout.addLayout(self.gridLayout)

        #self.parentWidget.addPmWidget(self)
        self._loadWidgets()

        try:
            self._addWhatsThisText()
        except:
            print_compact_traceback("Error loading whatsthis text for this " \
                                    "property manager dock widget.")

        try:
            self._addToolTipText()
        except:
            print_compact_traceback("Error loading tool tip text for this " \
                                    "property manager dock widget.")

    def _loadWidgets(self):
        """
        Subclasses should override this method. Default implementation does
        nothing.
        @see: DnaSequenceEditor._loadWidgets
        """
        pass

    def _addWhatsThisText(self):
        """
        Add 'What's This' help text for self and child widgets.
        Subclasses should override this method.
        """
        pass

    def _addToolTipText(self):
        """
        Add 'Tool tip' help text for self and child widgets.
        Subclasses should override this method.
        """
        pass

    def getPmWidgetPlacementParameters(self, pmWidget):
        """
        NOTE: This method is duplicated from PM_GroupBox

        Returns all the layout parameters needed to place
        a PM_Widget in the group box grid layout.

        @param pmWidget: The PM widget.
        @type  pmWidget: PM_Widget
        """
        row = self._rowCount

        #PM_CheckBox doesn't have a label. So do the following to decide the
        #placement of the checkbox. (can be placed either in column 0 or 1 ,
        #This also needs to be implemented for PM_RadioButton, but at present
        #the following code doesn't support PM_RadioButton.
        if isinstance(pmWidget, PM_CheckBox):
            # Set the widget's row and column parameters.
            widgetRow = row
            widgetColumn = pmWidget.widgetColumn
            widgetSpanCols = 1
            widgetAlignment = PM_LABEL_LEFT_ALIGNMENT
            rowIncrement = 1
            #set a virtual label
            labelRow = row
            labelSpanCols = 1
            labelAlignment = PM_LABEL_RIGHT_ALIGNMENT

            if widgetColumn == 0:
                labelColumn = 1
            elif widgetColumn == 1:
                labelColumn = 0

            return (widgetRow, widgetColumn, widgetSpanCols, widgetAlignment,
                    rowIncrement, labelRow, labelColumn, labelSpanCols,
                    labelAlignment)

        label = pmWidget.label
        labelColumn = pmWidget.labelColumn
        spanWidth = pmWidget.spanWidth

        if not spanWidth:
            # This widget and its label are on the same row
            labelRow = row
            labelSpanCols = 1
            labelAlignment = PM_LABEL_RIGHT_ALIGNMENT
            # Set the widget's row and column parameters.
            widgetRow = row
            widgetColumn = 1
            widgetSpanCols = 1
            widgetAlignment = PM_LABEL_LEFT_ALIGNMENT
            rowIncrement = 1

            if labelColumn == 1:
                widgetColumn = 0
                labelAlignment = PM_LABEL_LEFT_ALIGNMENT
                widgetAlignment = PM_LABEL_RIGHT_ALIGNMENT

        else:

            # This widget spans the full width of the groupbox
            if label:
                # The label and widget are on separate rows.
                # Set the label's row, column and alignment.
                labelRow = row
                labelColumn = 0
                labelSpanCols = 2

                # Set this widget's row and column parameters.
                widgetRow = row + 1  # Widget is below the label.
                widgetColumn = 0
                widgetSpanCols = 2

                rowIncrement = 2
            else:  # No label. Just the widget.
                labelRow = 0
                labelColumn = 0
                labelSpanCols = 0

                # Set the widget's row and column parameters.
                widgetRow = row
                widgetColumn = 0
                widgetSpanCols = 2
                rowIncrement = 1

            labelAlignment = PM_LABEL_LEFT_ALIGNMENT
            widgetAlignment = PM_LABEL_LEFT_ALIGNMENT

        return (widgetRow, widgetColumn, widgetSpanCols, widgetAlignment,
                rowIncrement, labelRow, labelColumn, labelSpanCols,
                labelAlignment)

    def addPmWidget(self, pmWidget):
        """
        Add a PM widget and its label to this group box.

        @param pmWidget: The PM widget to add.
        @type  pmWidget: PM_Widget
        """

        # Get all the widget and label layout parameters.
        widgetRow, \
        widgetColumn, \
        widgetSpanCols, \
        widgetAlignment, \
        rowIncrement, \
        labelRow, \
        labelColumn, \
        labelSpanCols, \
        labelAlignment = self.getPmWidgetPlacementParameters(pmWidget)

        if pmWidget.labelWidget:
            #Create Label as a pixmap (instead of text) if a valid icon path
            #is provided
            labelPath = str(pmWidget.label)
            if labelPath and labelPath.startswith(
                    "ui/"):  #bruce 080325 revised
                labelPixmap = getpixmap(labelPath)
                if not labelPixmap.isNull():
                    pmWidget.labelWidget.setPixmap(labelPixmap)
                    pmWidget.labelWidget.setText('')

            self.gridLayout.addWidget(pmWidget.labelWidget, labelRow,
                                      labelColumn, 1, labelSpanCols,
                                      labelAlignment)

        # The following is a workaround for a Qt bug. If addWidth()'s
        # <alignment> argument is not supplied, the widget spans the full
        # column width of the grid cell containing it. If <alignment>
        # is supplied, this desired behavior is lost and there is no
        # value that can be supplied to maintain the behavior (0 doesn't
        # work). The workaround is to call addWidget() without the <alignment>
        # argument. Mark 2007-07-27.
        if widgetAlignment == PM_LABEL_LEFT_ALIGNMENT:
            self.gridLayout.addWidget(pmWidget, widgetRow, widgetColumn, 1,
                                      widgetSpanCols)
            # aligment = 0 doesn't work.
        else:
            self.gridLayout.addWidget(pmWidget, widgetRow, widgetColumn, 1,
                                      widgetSpanCols, widgetAlignment)
        self._rowCount += rowIncrement
Example #49
0
        def __init__(self, dirname, purrlogs, parent=None, create=None, message=None):
            QWizardPage.__init__(self, parent)
            self.dirname = dirname
            self.purrlogs = purrlogs or []
            bg = QButtonGroup(self)
            lo = QVBoxLayout()
            self.setLayout(lo)
            # set page titles
            self.setTitle("Starting PURR")
            message and self.setSubTitle(message)
            if not purrlogs:
                self.rbs_log = []
            else:
                # add options for existing purrlogs
                self.rbs_log = [QRadioButton("Load %s" % Kittens.utils.collapseuser(log)) for log in purrlogs]
                for rb in self.rbs_log:
                    lo.addWidget(rb)
                    bg.addButton(rb)
                    QObject.connect(rb, SIGNAL("toggled(bool)"), self.checkCompleteness)
                self.rbs_log[0].setChecked(True)
            # add option to load another purrlog
            lo1 = QHBoxLayout()
            self.rb_other = QRadioButton("Load purrlog from:")
            lo1.addWidget(self.rb_other)
            bg.addButton(self.rb_other)
            self.wother = QLineEdit()
            self.wother.setReadOnly(True)
            lo1.addWidget(self.wother, 1)
            pb = QPushButton(pixmaps.folder_open.icon(), "")
            QObject.connect(pb, SIGNAL("clicked()"), self._select_other_dialog)
            QObject.connect(self.rb_other, SIGNAL("toggled(bool)"), pb.setEnabled)
            QObject.connect(self.rb_other, SIGNAL("toggled(bool)"), self.wother.setEnabled)
            QObject.connect(self.rb_other, SIGNAL("toggled(bool)"), self.checkCompleteness)
            pb.setEnabled(False)
            self.wother.setEnabled(False)
            lo1.addWidget(pb)
            lo.addLayout(lo1)
            self.load_path = None

            # add option to create new purrlog
            lo1 = QHBoxLayout()
            self.rb_create = QRadioButton("Create new purrlog:")
            lo1.addWidget(self.rb_create)
            bg.addButton(self.rb_create)
            self.wcreate = QLineEdit()
            lo1.addWidget(self.wcreate, 1)
            pb = QPushButton(pixmaps.folder_open.icon(), "")
            QObject.connect(pb, SIGNAL("clicked()"), self._select_create_dialog)
            QObject.connect(self.rb_create, SIGNAL("toggled(bool)"), pb.setEnabled)
            QObject.connect(self.rb_create, SIGNAL("toggled(bool)"), self.wcreate.setEnabled)
            QObject.connect(self.rb_create, SIGNAL("toggled(bool)"), self.checkCompleteness)
            QObject.connect(self.wcreate, SIGNAL("editingFinished()"), self._validate_create_filename)
            pb.setEnabled(False)
            self.wcreate.setEnabled(False)
            # this holds the last validated name
            self._validated_create_path = None
            self._validated_result = False
            # generate default name for a new purrlog
            self.create_path = os.path.join(dirname, "purrlog")
            num = 0
            while os.path.exists(self.create_path):
                self.create_path = os.path.join(dirname, "purrlog.%d" % num)
                num += 1
            # This will be not None as long as a valid name is entered
            self.create_path = Kittens.utils.collapseuser(os.path.normpath(self.create_path))
            if create:
                self.wcreate.setText(create or Kittens.utils.collapseuser(create))
                # this will emit checkCompleteness(), causing a _validate_create_filename() call, causing the content of the wcreate widget
                # to be validated and copied to create_path if valid, or reset from create_path if invalid
                self.rb_create.setChecked(True)
            else:
                self.wcreate.setText(self.create_path)

            lo1.addWidget(pb)
            lo.addLayout(lo1)

            # make create option default, if no purrlogs
            if not purrlogs:
                self.rb_create.setChecked(True)