~ubuntu-branches/ubuntu/karmic/calibre/karmic

« back to all changes in this revision

Viewing changes to src/calibre/gui2/dialogs/config.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-04-05 18:42:16 UTC
  • mfrom: (1.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20090405184216-cyb0x4edrwjcaw33
Tags: 0.5.9+dfsg-1
* New upstream release. (Closes: #525339)
* manpages-installation.patch: Encode generated manpages as UTF-8, to avoid
  UnicodeDecodeErrors when writing them out to files.
* debian/control: Demote calibre dependency of calibre-bin to Recommends:,
  which is sufficient and avoids a circular dependency. (Closes: #522059)
* debian/control: Drop build dependency help2man, current version does not
  need it any more.
* debian/control: Drop versioned build dependency on python-mechanize,
  current sid version is enough.
* debian/rules: Copy "setup.py install" command from cdbs'
  python-distutils.mk, since the current version broke this. This is a
  hackish workaround until #525436 gets fixed.
* debian/rules: Drop using $(wildcard ), use `ls`; the former does not work
  any more.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
__license__   = 'GPL v3'
2
2
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
3
 
import os, re, time, textwrap
 
3
import os, re, time, textwrap, sys, cStringIO
 
4
from binascii import hexlify, unhexlify
4
5
 
5
6
from PyQt4.Qt import    QDialog, QMessageBox, QListWidgetItem, QIcon, \
6
7
                        QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
7
 
                        QStringListModel, QAbstractItemModel, \
 
8
                        QStringListModel, QAbstractItemModel, QFont, \
8
9
                        SIGNAL, QTimer, Qt, QSize, QVariant, QUrl, \
9
 
                        QModelIndex, QInputDialog
 
10
                        QModelIndex, QInputDialog, QAbstractTableModel
10
11
 
11
12
from calibre.constants import islinux, iswindows
12
13
from calibre.gui2.dialogs.config_ui import Ui_Dialog
 
14
from calibre.gui2.dialogs.test_email_ui import Ui_Dialog as TE_Dialog
13
15
from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config, \
14
16
                         ALL_COLUMNS, NONE, info_dialog, choose_files
15
17
from calibre.utils.config import prefs
21
23
from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin, \
22
24
                                 disable_plugin, customize_plugin, \
23
25
                                 plugin_customization, add_plugin, remove_plugin
 
26
from calibre.utils.smtp import config as smtp_prefs
24
27
 
25
28
class PluginModel(QAbstractItemModel):
26
 
    
 
29
 
27
30
    def __init__(self, *args):
28
31
        QAbstractItemModel.__init__(self, *args)
29
32
        self.icon = QVariant(QIcon(':/images/plugins.svg'))
30
33
        self.populate()
31
 
        
 
34
 
32
35
    def populate(self):
33
36
        self._data = {}
34
37
        for plugin in initialized_plugins():
37
40
            else:
38
41
                self._data[plugin.type].append(plugin)
39
42
        self.categories = sorted(self._data.keys())
40
 
        
 
43
 
41
44
    def index(self, row, column, parent):
42
45
        if not self.hasIndex(row, column, parent):
43
46
            return QModelIndex()
44
 
        
 
47
 
45
48
        if parent.isValid():
46
49
            return self.createIndex(row, column, parent.row())
47
50
        else:
48
51
            return self.createIndex(row, column, -1)
49
 
        
 
52
 
50
53
    def parent(self, index):
51
54
        if not index.isValid() or index.internalId() == -1:
52
55
            return QModelIndex()
53
56
        return self.createIndex(index.internalId(), 0, -1)
54
 
    
 
57
 
55
58
    def rowCount(self, parent):
56
59
        if not parent.isValid():
57
60
            return len(self.categories)
59
62
            category = self.categories[parent.row()]
60
63
            return len(self._data[category])
61
64
        return 0
62
 
    
 
65
 
63
66
    def columnCount(self, parent):
64
67
        return 1
65
 
    
 
68
 
66
69
    def index_to_plugin(self, index):
67
70
        category = self.categories[index.parent().row()]
68
71
        return self._data[category][index.row()]
69
 
    
 
72
 
70
73
    def plugin_to_index(self, plugin):
71
74
        for i, category in enumerate(self.categories):
72
75
            parent = self.index(i, 0, QModelIndex())
74
77
                if plugin == p:
75
78
                    return self.index(j, 0, parent)
76
79
        return QModelIndex()
77
 
    
 
80
 
78
81
    def refresh_plugin(self, plugin, rescan=False):
79
82
        if rescan:
80
83
            self.populate()
81
84
        idx = self.plugin_to_index(plugin)
82
85
        self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), idx, idx)
83
 
    
 
86
 
84
87
    def flags(self, index):
85
88
        if not index.isValid():
86
89
            return 0
90
93
        if not is_disabled(self.data(index, Qt.UserRole)):
91
94
            flags |= Qt.ItemIsEnabled
92
95
        return flags
93
 
            
 
96
 
94
97
    def data(self, index, role):
95
98
        if not index.isValid():
96
99
            return NONE
113
116
            if role == Qt.UserRole:
114
117
                return plugin
115
118
        return NONE
116
 
                
117
 
            
 
119
 
 
120
 
118
121
 
119
122
class CategoryModel(QStringListModel):
120
 
    
 
123
 
121
124
    def __init__(self, *args):
122
125
        QStringListModel.__init__(self, *args)
123
 
        self.setStringList([_('General'), _('Interface'), _('Advanced'), 
124
 
                            _('Content\nServer'), _('Plugins')])
125
 
        self.icons = list(map(QVariant, map(QIcon, 
126
 
            [':/images/dialog_information.svg', ':/images/lookfeel.svg', 
127
 
             ':/images/view.svg', ':/images/network-server.svg',
128
 
             ':/images/plugins.svg'])))
129
 
    
 
126
        self.setStringList([_('General'), _('Interface'), _('Email\nDelivery'),
 
127
                            _('Advanced'), _('Content\nServer'), _('Plugins')])
 
128
        self.icons = list(map(QVariant, map(QIcon,
 
129
            [':/images/dialog_information.svg', ':/images/lookfeel.svg',
 
130
             ':/images/mail.svg', ':/images/view.svg',
 
131
             ':/images/network-server.svg', ':/images/plugins.svg'])))
 
132
 
130
133
    def data(self, index, role):
131
134
        if role == Qt.DecorationRole:
132
135
            return self.icons[index.row()]
133
136
        return QStringListModel.data(self, index, role)
134
 
            
 
137
 
 
138
class TestEmail(QDialog, TE_Dialog):
 
139
 
 
140
    def __init__(self, accounts, parent):
 
141
        QDialog.__init__(self, parent)
 
142
        TE_Dialog.__init__(self)
 
143
        self.setupUi(self)
 
144
        opts = smtp_prefs().parse()
 
145
        self.test_func = parent.test_email_settings
 
146
        self.connect(self.test_button, SIGNAL('clicked(bool)'), self.test)
 
147
        self.from_.setText(unicode(self.from_.text())%opts.from_)
 
148
        if accounts:
 
149
            self.to.setText(list(accounts.keys())[0])
 
150
        if opts.relay_host:
 
151
            self.label.setText(_('Using: %s:%s@%s:%s and %s encryption')%
 
152
                    (opts.relay_username, unhexlify(opts.relay_password),
 
153
                        opts.relay_host, opts.relay_port, opts.encryption))
 
154
 
 
155
    def test(self):
 
156
        self.log.setPlainText(_('Sending...'))
 
157
        self.test_button.setEnabled(False)
 
158
        try:
 
159
            tb = self.test_func(unicode(self.to.text()))
 
160
            if not tb:
 
161
                tb = _('Mail successfully sent')
 
162
            self.log.setPlainText(tb)
 
163
        finally:
 
164
            self.test_button.setEnabled(True)
 
165
 
 
166
class EmailAccounts(QAbstractTableModel):
 
167
 
 
168
    def __init__(self, accounts):
 
169
        QAbstractTableModel.__init__(self)
 
170
        self.accounts = accounts
 
171
        self.account_order = sorted(self.accounts.keys())
 
172
        self.headers  = map(QVariant, [_('Email'), _('Formats'), _('Auto send')])
 
173
        self.default_font = QFont()
 
174
        self.default_font.setBold(True)
 
175
        self.default_font = QVariant(self.default_font)
 
176
        self.tooltips =[NONE] + map(QVariant,
 
177
            [_('Formats to email. The first matching format will be sent.'),
 
178
             '<p>'+_('If checked, downloaded news will be automatically '
 
179
                     'mailed <br>to this email address '
 
180
                     '(provided it is in one of the listed formats).')])
 
181
 
 
182
    def rowCount(self, *args):
 
183
        return len(self.account_order)
 
184
 
 
185
    def columnCount(self, *args):
 
186
        return 3
 
187
 
 
188
    def headerData(self, section, orientation, role):
 
189
        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
 
190
            return self.headers[section]
 
191
        return NONE
 
192
 
 
193
    def data(self, index, role):
 
194
        row, col = index.row(), index.column()
 
195
        if row < 0 or row >= self.rowCount():
 
196
            return NONE
 
197
        account = self.account_order[row]
 
198
        if role == Qt.UserRole:
 
199
            return (account, self.accounts[account])
 
200
        if role == Qt.ToolTipRole:
 
201
            return self.tooltips[col]
 
202
        if role in [Qt.DisplayRole, Qt.EditRole]:
 
203
            if col == 0:
 
204
                return QVariant(account)
 
205
            if col ==  1:
 
206
                return QVariant(self.accounts[account][0])
 
207
        if role == Qt.FontRole and self.accounts[account][2]:
 
208
            return self.default_font
 
209
        if role == Qt.CheckStateRole and col == 2:
 
210
            return QVariant(Qt.Checked if self.accounts[account][1] else Qt.Unchecked)
 
211
        return NONE
 
212
 
 
213
    def flags(self, index):
 
214
        if index.column() == 2:
 
215
            return QAbstractTableModel.flags(self, index)|Qt.ItemIsUserCheckable
 
216
        else:
 
217
            return QAbstractTableModel.flags(self, index)|Qt.ItemIsEditable
 
218
 
 
219
    def setData(self, index, value, role):
 
220
        if not index.isValid():
 
221
            return False
 
222
        row, col = index.row(), index.column()
 
223
        account = self.account_order[row]
 
224
        if col == 2:
 
225
            self.accounts[account][1] ^= True
 
226
        elif col == 1:
 
227
            self.accounts[account][0] = unicode(value.toString()).upper()
 
228
        else:
 
229
            na = unicode(value.toString())
 
230
            from email.utils import parseaddr
 
231
            addr = parseaddr(na)[-1]
 
232
            if not addr:
 
233
                return False
 
234
            self.accounts[na] = self.accounts.pop(account)
 
235
            self.account_order[row] = na
 
236
            if '@kindle.com' in addr:
 
237
                self.accounts[na][0] = 'AZW, MOBI, TPZ, PRC, AZW1'
 
238
 
 
239
        self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'),
 
240
                self.index(index.row(), 0), self.index(index.row(), 2))
 
241
        return True
 
242
 
 
243
    def make_default(self, index):
 
244
        if index.isValid():
 
245
            row = index.row()
 
246
            for x in self.accounts.values():
 
247
                x[2] = False
 
248
            self.accounts[self.account_order[row]][2] = True
 
249
            self.reset()
 
250
 
 
251
    def add(self):
 
252
        x = _('new email address')
 
253
        y = x
 
254
        c = 0
 
255
        while y in self.accounts:
 
256
            c += 1
 
257
            y = x + str(c)
 
258
        self.accounts[y] = ['MOBI, EPUB', True,
 
259
                                                len(self.account_order) == 0]
 
260
        self.account_order = sorted(self.accounts.keys())
 
261
        self.reset()
 
262
        return self.index(self.account_order.index(y), 0)
 
263
 
 
264
    def remove(self, index):
 
265
        if index.isValid():
 
266
            row = index.row()
 
267
            account = self.account_order[row]
 
268
            self.accounts.pop(account)
 
269
            self.account_order = sorted(self.accounts.keys())
 
270
            has_default = False
 
271
            for account in self.account_order:
 
272
                if self.accounts[account][2]:
 
273
                    has_default = True
 
274
                    break
 
275
            if not has_default and self.account_order:
 
276
                self.accounts[self.account_order[0]][2] = True
 
277
 
 
278
            self.reset()
 
279
 
135
280
 
136
281
class ConfigDialog(QDialog, Ui_Dialog):
137
282
 
141
286
        self.ICON_SIZES = {0:QSize(48, 48), 1:QSize(32,32), 2:QSize(24,24)}
142
287
        self.setupUi(self)
143
288
        self._category_model = CategoryModel()
144
 
        
145
 
        self.connect(self.category_view, SIGNAL('activated(QModelIndex)'), lambda i: self.stackedWidget.setCurrentIndex(i.row()))
146
 
        self.connect(self.category_view, SIGNAL('clicked(QModelIndex)'), lambda i: self.stackedWidget.setCurrentIndex(i.row()))
 
289
 
 
290
        self.category_view.currentChanged = \
 
291
            lambda n, p: self.stackedWidget.setCurrentIndex(n.row())
147
292
        self.category_view.setModel(self._category_model)
148
293
        self.db = db
149
294
        self.server = server
151
296
        self.location.setText(path if path else '')
152
297
        self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse)
153
298
        self.connect(self.compact_button, SIGNAL('clicked(bool)'), self.compact)
154
 
        
 
299
 
155
300
        dirs = config['frequently_used_directories']
156
301
        rn = config['use_roman_numerals_for_series_number']
157
302
        self.timeout.setValue(prefs['network_timeout'])
162
307
        self.connect(self.remove_button, SIGNAL('clicked(bool)'), self.remove_dir)
163
308
        if not islinux:
164
309
            self.dirs_box.setVisible(False)
165
 
        
 
310
 
166
311
        column_map = config['column_map']
167
312
        for col in column_map + [i for i in ALL_COLUMNS if i not in column_map]:
168
313
            item = QListWidgetItem(BooksModel.headers[col], self.columns)
169
314
            item.setData(Qt.UserRole, QVariant(col))
170
315
            item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
171
316
            item.setCheckState(Qt.Checked if col in column_map else Qt.Unchecked)
172
 
            
 
317
 
173
318
        self.connect(self.column_up, SIGNAL('clicked()'), self.up_column)
174
319
        self.connect(self.column_down, SIGNAL('clicked()'), self.down_column)
175
320
 
176
321
        self.filename_pattern = FilenamePattern(self)
177
322
        self.metadata_box.layout().insertWidget(0, self.filename_pattern)
178
 
        
 
323
 
179
324
        icons = config['toolbar_icon_size']
180
325
        self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2)
181
326
        self.show_toolbar_text.setChecked(config['show_text_in_toolbar'])
183
328
        self.book_exts = sorted(BOOK_EXTENSIONS)
184
329
        for ext in self.book_exts:
185
330
            self.single_format.addItem(ext.upper(), QVariant(ext))
186
 
        
 
331
 
187
332
        single_format = config['save_to_disk_single_format']
188
333
        self.single_format.setCurrentIndex(self.book_exts.index(single_format))
189
334
        self.cover_browse.setValue(config['cover_flow_queue_length'])
194
339
        lang = get_lang()
195
340
        if lang is not None and language_codes.has_key(lang):
196
341
            self.language.addItem(language_codes[lang], QVariant(lang))
197
 
        items = [(l, language_codes[l]) for l in translations.keys() if l != lang]
 
342
        else:
 
343
            lang = 'en'
 
344
            self.language.addItem('English', QVariant('en'))
 
345
        items = [(l, language_codes[l]) for l in translations.keys() \
 
346
                 if l != lang]
198
347
        if lang != 'en':
199
348
            items.append(('en', 'English'))
200
349
        items.sort(cmp=lambda x, y: cmp(x[1], y[1]))
201
350
        for item in items:
202
351
            self.language.addItem(item[1], QVariant(item[0]))
203
 
            
 
352
 
204
353
        self.pdf_metadata.setChecked(prefs['read_file_metadata'])
205
 
        
 
354
 
206
355
        added_html = False
207
356
        for ext in self.book_exts:
208
357
            ext = ext.lower()
238
387
        self.priority.setCurrentIndex(p)
239
388
        self.priority.setVisible(iswindows)
240
389
        self.priority_label.setVisible(iswindows)
241
 
        self.category_view.setCurrentIndex(self._category_model.index(0))
242
390
        self._plugin_model = PluginModel()
243
391
        self.plugin_view.setModel(self._plugin_model)
244
392
        self.connect(self.toggle_plugin, SIGNAL('clicked()'), lambda : self.modify_plugin(op='toggle'))
247
395
        self.connect(self.button_plugin_browse, SIGNAL('clicked()'), self.find_plugin)
248
396
        self.connect(self.button_plugin_add, SIGNAL('clicked()'), self.add_plugin)
249
397
        self.separate_cover_flow.setChecked(config['separate_cover_flow'])
250
 
    
 
398
        self.setup_email_page()
 
399
        self.category_view.setCurrentIndex(self.category_view.model().index(0))
 
400
        self.delete_news.setEnabled(bool(self.sync_news.isChecked()))
 
401
        self.connect(self.sync_news, SIGNAL('toggled(bool)'),
 
402
                self.delete_news.setEnabled)
 
403
 
 
404
    def setup_email_page(self):
 
405
        opts = smtp_prefs().parse()
 
406
        if opts.from_:
 
407
            self.email_from.setText(opts.from_)
 
408
        self._email_accounts = EmailAccounts(opts.accounts)
 
409
        self.email_view.setModel(self._email_accounts)
 
410
        if opts.relay_host:
 
411
            self.relay_host.setText(opts.relay_host)
 
412
        self.relay_port.setValue(opts.relay_port)
 
413
        if opts.relay_username:
 
414
            self.relay_username.setText(opts.relay_username)
 
415
        if opts.relay_password:
 
416
            self.relay_password.setText(unhexlify(opts.relay_password))
 
417
        (self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
 
418
        self.connect(self.relay_use_gmail, SIGNAL('clicked(bool)'),
 
419
                     self.create_gmail_relay)
 
420
        self.connect(self.relay_show_password, SIGNAL('stateChanged(int)'),
 
421
         lambda
 
422
         state:self.relay_password.setEchoMode(self.relay_password.Password if
 
423
             state == 0 else self.relay_password.Normal))
 
424
        self.connect(self.email_add, SIGNAL('clicked(bool)'),
 
425
                     self.add_email_account)
 
426
        self.connect(self.email_make_default, SIGNAL('clicked(bool)'),
 
427
             lambda c: self._email_accounts.make_default(self.email_view.currentIndex()))
 
428
        self.email_view.resizeColumnsToContents()
 
429
        self.connect(self.test_email_button, SIGNAL('clicked(bool)'),
 
430
                self.test_email)
 
431
        self.connect(self.email_remove, SIGNAL('clicked()'),
 
432
                self.remove_email_account)
 
433
 
 
434
    def add_email_account(self, checked):
 
435
        index = self._email_accounts.add()
 
436
        self.email_view.setCurrentIndex(index)
 
437
        self.email_view.resizeColumnsToContents()
 
438
        self.email_view.edit(index)
 
439
 
 
440
    def remove_email_account(self, *args):
 
441
        idx = self.email_view.currentIndex()
 
442
        self._email_accounts.remove(idx)
 
443
 
 
444
    def create_gmail_relay(self, *args):
 
445
        self.relay_username.setText('@gmail.com')
 
446
        self.relay_password.setText('')
 
447
        self.relay_host.setText('smtp.gmail.com')
 
448
        self.relay_port.setValue(587)
 
449
        self.relay_tls.setChecked(True)
 
450
 
 
451
        info_dialog(self, _('Finish gmail setup'),
 
452
            _('Dont forget to enter your gmail username and password')).exec_()
 
453
        self.relay_username.setFocus(Qt.OtherFocusReason)
 
454
        self.relay_username.setCursorPosition(0)
 
455
 
 
456
    def set_email_settings(self):
 
457
        from_ = unicode(self.email_from.text()).strip()
 
458
        if self._email_accounts.accounts and not from_:
 
459
            error_dialog(self, _('Bad configuration'),
 
460
                         _('You must set the From email address')).exec_()
 
461
            return False
 
462
        username = unicode(self.relay_username.text()).strip()
 
463
        password = unicode(self.relay_password.text()).strip()
 
464
        host = unicode(self.relay_host.text()).strip()
 
465
        if host and not (username and password):
 
466
            error_dialog(self, _('Bad configuration'),
 
467
                         _('You must set the username and password for '
 
468
                           'the mail server.')).exec_()
 
469
            return False
 
470
        conf = smtp_prefs()
 
471
        conf.set('from_', from_)
 
472
        conf.set('accounts', self._email_accounts.accounts)
 
473
        conf.set('relay_host', host if host else None)
 
474
        conf.set('relay_port', self.relay_port.value())
 
475
        conf.set('relay_username', username if username else None)
 
476
        conf.set('relay_password', hexlify(password))
 
477
        conf.set('encryption', 'TLS' if self.relay_tls.isChecked() else 'SSL')
 
478
        return True
 
479
 
 
480
    def test_email(self, *args):
 
481
        if self.set_email_settings():
 
482
          TestEmail(self._email_accounts.accounts, self).exec_()
 
483
 
 
484
    def test_email_settings(self, to):
 
485
        opts = smtp_prefs().parse()
 
486
        from calibre.utils.smtp import sendmail, create_mail
 
487
        buf = cStringIO.StringIO()
 
488
        oout, oerr = sys.stdout, sys.stderr
 
489
        sys.stdout = sys.stderr = buf
 
490
        tb = None
 
491
        try:
 
492
            msg = create_mail(opts.from_, to, 'Test mail from calibre',
 
493
                    'Test mail from calibre')
 
494
            sendmail(msg, from_=opts.from_, to=[to],
 
495
                verbose=3, timeout=30, relay=opts.relay_host,
 
496
                username=opts.relay_username,
 
497
                password=unhexlify(opts.relay_password),
 
498
                encryption=opts.encryption, port=opts.relay_port)
 
499
        except:
 
500
            import traceback
 
501
            tb = traceback.format_exc()
 
502
            tb += '\n\nLog:\n' + buf.getvalue()
 
503
        finally:
 
504
            sys.stdout, sys.stderr = oout, oerr
 
505
        return tb
 
506
 
251
507
    def add_plugin(self):
252
508
        path = unicode(self.plugin_path.text())
253
509
        if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'):
255
511
            self._plugin_model.populate()
256
512
            self._plugin_model.reset()
257
513
        else:
258
 
            error_dialog(self, _('No valid plugin path'), 
 
514
            error_dialog(self, _('No valid plugin path'),
259
515
                         _('%s is not a valid plugin path')%path).exec_()
260
 
    
 
516
 
261
517
    def find_plugin(self):
262
518
        path = choose_files(self, 'choose plugin dialog', _('Choose plugin'),
263
 
                            filters=[('Plugins', ['zip'])], all_files=False, 
 
519
                            filters=[('Plugins', ['zip'])], all_files=False,
264
520
                            select_only_single_file=True)
265
521
        if path:
266
522
            self.plugin_path.setText(path[0])
267
 
    
 
523
 
268
524
    def modify_plugin(self, op=''):
269
525
        index = self.plugin_view.currentIndex()
270
526
        if index.isValid():
271
527
            plugin = self._plugin_model.index_to_plugin(index)
272
528
            if not plugin.can_be_disabled:
273
 
                error_dialog(self,_('Plugin cannot be disabled'), 
 
529
                error_dialog(self,_('Plugin cannot be disabled'),
274
530
                             _('The plugin: %s cannot be disabled')%plugin.name).exec_()
275
531
                return
276
532
            if op == 'toggle':
282
538
            if op == 'customize':
283
539
                if not plugin.is_customizable():
284
540
                    info_dialog(self, _('Plugin not customizable'),
285
 
                                _('Plugin: %s does not need customization')%plugin.name).exec_()
 
541
                        _('Plugin: %s does not need customization')%plugin.name).exec_()
286
542
                    return
287
543
                help = plugin.customization_help()
288
544
                text, ok = QInputDialog.getText(self, _('Customize %s')%plugin.name,
295
551
                    self._plugin_model.populate()
296
552
                    self._plugin_model.reset()
297
553
                else:
298
 
                    error_dialog(self, _('Cannot remove builtin plugin'), 
299
 
                                 plugin.name + _(' cannot be removed. It is a builtin plugin. Try disabling it instead.')).exec_()
300
 
            
301
 
    
 
554
                    error_dialog(self, _('Cannot remove builtin plugin'),
 
555
                         plugin.name + _(' cannot be removed. It is a '
 
556
                         'builtin plugin. Try disabling it instead.')).exec_()
 
557
 
 
558
 
302
559
    def up_column(self):
303
560
        idx = self.columns.currentRow()
304
561
        if idx > 0:
305
562
            self.columns.insertItem(idx-1, self.columns.takeItem(idx))
306
563
            self.columns.setCurrentRow(idx-1)
307
 
            
 
564
 
308
565
    def down_column(self):
309
566
        idx = self.columns.currentRow()
310
567
        if idx < self.columns.count()-1:
311
568
            self.columns.insertItem(idx+1, self.columns.takeItem(idx))
312
569
            self.columns.setCurrentRow(idx+1)
313
 
    
 
570
 
314
571
    def view_server_logs(self):
315
572
        from calibre.library.server import log_access_file, log_error_file
316
573
        d = QDialog(self)
320
577
        layout.addWidget(QLabel(_('Error log:')))
321
578
        el = QPlainTextEdit(d)
322
579
        layout.addWidget(el)
323
 
        el.setPlainText(open(log_error_file, 'rb').read().decode('utf8', 'replace'))
 
580
        try:
 
581
            el.setPlainText(open(log_error_file, 'rb').read().decode('utf8', 'replace'))
 
582
        except IOError:
 
583
            el.setPlainText('No error log found')
324
584
        layout.addWidget(QLabel(_('Access log:')))
325
585
        al = QPlainTextEdit(d)
326
586
        layout.addWidget(al)
327
 
        al.setPlainText(open(log_access_file, 'rb').read().decode('utf8', 'replace'))
 
587
        try:
 
588
            al.setPlainText(open(log_access_file, 'rb').read().decode('utf8', 'replace'))
 
589
        except IOError:
 
590
            el.setPlainText('No access log found')
328
591
        d.show()
329
 
    
 
592
 
330
593
    def set_server_options(self):
331
594
        c = server_config()
332
595
        c.set('port', self.port.value())
335
598
        if not p:
336
599
            p = None
337
600
        c.set('password', p)
338
 
            
 
601
 
339
602
    def start_server(self):
340
603
        self.set_server_options()
341
604
        from calibre.library.server import start_threaded_server
343
606
        while not self.server.is_running and self.server.exception is None:
344
607
            time.sleep(1)
345
608
        if self.server.exception is not None:
346
 
            error_dialog(self, _('Failed to start content server'), 
 
609
            error_dialog(self, _('Failed to start content server'),
347
610
                         unicode(self.server.exception)).exec_()
348
611
            return
349
612
        self.start.setEnabled(False)
350
613
        self.test.setEnabled(True)
351
614
        self.stop.setEnabled(True)
352
 
        
 
615
 
353
616
    def stop_server(self):
354
617
        from calibre.library.server import stop_threaded_server
355
618
        stop_threaded_server(self.server)
357
620
        self.start.setEnabled(True)
358
621
        self.test.setEnabled(False)
359
622
        self.stop.setEnabled(False)
360
 
        
 
623
 
361
624
    def test_server(self):
362
625
        QDesktopServices.openUrl(QUrl('http://127.0.0.1:'+str(self.port.value())))
363
 
        
 
626
 
364
627
    def compact(self, toggled):
365
628
        d = Vacuum(self, self.db)
366
629
        d.exec_()
367
630
 
368
631
    def browse(self):
369
 
        dir = choose_dir(self, 'database location dialog', 'Select database location')
 
632
        dir = choose_dir(self, 'database location dialog',
 
633
                         _('Select database location'))
370
634
        if dir:
371
635
            self.location.setText(dir)
372
636
 
383
647
    def accept(self):
384
648
        mcs = unicode(self.max_cover_size.text()).strip()
385
649
        if not re.match(r'\d+x\d+', mcs):
386
 
            error_dialog(self, _('Invalid size'), _('The size %s is invalid. must be of the form widthxheight')%mcs).exec_()
 
650
            error_dialog(self, _('Invalid size'),
 
651
             _('The size %s is invalid. must be of the form widthxheight')%mcs).exec_()
 
652
            return
 
653
        if not self.set_email_settings():
387
654
            return
388
655
        config['use_roman_numerals_for_series_number'] = bool(self.roman_numerals.isChecked())
389
656
        config['new_version_notification'] = bool(self.new_version_notification.isChecked())
419
686
            if self.viewer.item(i).checkState() == Qt.Checked:
420
687
                fmts.append(str(self.viewer.item(i).text()))
421
688
        config['internally_viewed_formats'] = fmts
422
 
        
 
689
 
423
690
        if not path or not os.path.exists(path) or not os.path.isdir(path):
424
691
            d = error_dialog(self, _('Invalid database location'),
425
 
                             _('Invalid database location ')+path+_('<br>Must be a directory.'))
 
692
                             _('Invalid database location ')+path+
 
693
                             _('<br>Must be a directory.'))
426
694
            d.exec_()
427
695
        elif not os.access(path, os.W_OK):
428
696
            d = error_dialog(self, _('Invalid database location'),
429
 
                             _('Invalid database location.<br>Cannot write to ')+path)
 
697
                     _('Invalid database location.<br>Cannot write to ')+path)
430
698
            d.exec_()
431
699
        else:
432
700
            self.database_location = os.path.abspath(path)
433
 
            self.directories = [qstring_to_unicode(self.directory_list.item(i).text()) for i in range(self.directory_list.count())]
 
701
            self.directories = [
 
702
              qstring_to_unicode(self.directory_list.item(i).text()) for i in \
 
703
                    range(self.directory_list.count())]
434
704
            config['frequently_used_directories'] =  self.directories
435
705
            QDialog.accept(self)
436
706
 
438
708
 
439
709
    def __init__(self, parent, db):
440
710
        self.db = db
441
 
        QMessageBox.__init__(self, QMessageBox.Information, _('Compacting...'), _('Compacting database. This may take a while.'),
 
711
        QMessageBox.__init__(self, QMessageBox.Information, _('Compacting...'),
 
712
                             _('Compacting database. This may take a while.'),
442
713
                             QMessageBox.NoButton, parent)
443
714
        QTimer.singleShot(200, self.vacuum)
444
715
 
446
717
        self.db.vacuum()
447
718
        self.accept()
448
719
 
 
720
if __name__ == '__main__':
 
721
    from calibre.library.database2 import LibraryDatabase2
 
722
    from PyQt4.Qt import QApplication
 
723
    app = QApplication([])
 
724
    d=ConfigDialog(None, LibraryDatabase2('/tmp'))
 
725
    d.category_view.setCurrentIndex(d.category_view.model().index(2))
 
726
    d.show()
 
727
    app.exec_()