Porting the application object to the qwrappers
[gc-dialer] / src / dialcentral_qt.py
1 #!/usr/bin/env python
2 # -*- coding: UTF8 -*-
3
4 from __future__ import with_statement
5
6 import os
7 import base64
8 import ConfigParser
9 import functools
10 import logging
11
12 from PyQt4 import QtGui
13 from PyQt4 import QtCore
14
15 import constants
16 from util import qtpie
17 from util import qwrappers
18 from util import qui_utils
19 from util import misc as misc_utils
20
21 import session
22
23
24 _moduleLogger = logging.getLogger(__name__)
25
26
27 class LedWrapper(object):
28
29         def __init__(self):
30                 self._ledHandler = None
31                 self._init = False
32
33         def off(self):
34                 self._lazy_init()
35                 if self._ledHandler is not None:
36                         self._ledHandler.off()
37
38         def _lazy_init(self):
39                 if self._init:
40                         return
41                 self._init = True
42                 try:
43                         import led_handler
44                         self._ledHandler = led_handler.LedHandler()
45                 except Exception, e:
46                         _moduleLogger.exception('Unable to initialize LED Handling: "%s"' % str(e))
47                         self._ledHandler = None
48
49
50 class Dialcentral(qwrappers.ApplicationWrapper):
51
52         _DATA_PATHS = [
53                 os.path.join(os.path.dirname(__file__), "../share"),
54                 os.path.join(os.path.dirname(__file__), "../data"),
55         ]
56
57         def __init__(self, app):
58                 self._dataPath = None
59                 self._aboutDialog = None
60                 self._ledHandler = LedWrapper()
61                 self.notifyOnMissed = False
62                 self.notifyOnVoicemail = False
63                 self.notifyOnSms = False
64
65                 try:
66                         import alarm_handler
67                         if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
68                                 self._alarmHandler = alarm_handler.AlarmHandler()
69                         else:
70                                 self._alarmHandler = None
71                 except (ImportError, OSError):
72                         self._alarmHandler = None
73                 except Exception:
74                         _moduleLogger.exception("Notification failure")
75                         self._alarmHandler = None
76                 if self._alarmHandler is None:
77                         _moduleLogger.info("No notification support")
78
79                 qwrappers.ApplicationWrapper.__init__(self, app, constants)
80
81         def load_settings(self):
82                 try:
83                         config = ConfigParser.SafeConfigParser()
84                         config.read(constants._user_settings_)
85                 except IOError, e:
86                         _moduleLogger.info("No settings")
87                         return
88                 except ValueError:
89                         _moduleLogger.info("Settings were corrupt")
90                         return
91                 except ConfigParser.MissingSectionHeaderError:
92                         _moduleLogger.info("Settings were corrupt")
93                         return
94                 except Exception:
95                         _moduleLogger.exception("Unknown loading error")
96
97                 blobs = "", ""
98                 isFullscreen = False
99                 isPortrait = qui_utils.screen_orientation() == QtCore.Qt.Vertical
100                 tabIndex = 0
101                 try:
102                         blobs = [
103                                 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
104                                 for i in xrange(len(self._mainWindow.get_default_credentials()))
105                         ]
106                         isFullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen")
107                         tabIndex = config.getint(constants.__pretty_app_name__, "tab")
108                         isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait")
109                 except ConfigParser.NoOptionError, e:
110                         _moduleLogger.info(
111                                 "Settings file %s is missing option %s" % (
112                                         constants._user_settings_,
113                                         e.option,
114                                 ),
115                         )
116                 except ConfigParser.NoSectionError, e:
117                         _moduleLogger.info(
118                                 "Settings file %s is missing section %s" % (
119                                         constants._user_settings_,
120                                         e.section,
121                                 ),
122                         )
123                 except Exception:
124                         _moduleLogger.exception("Unknown loading error")
125
126                 if self._alarmHandler is not None:
127                         try:
128                                 self._alarmHandler.load_settings(config, "alarm")
129                                 self.notifyOnMissed = config.getboolean("2 - Account Info", "notifyOnMissed")
130                                 self.notifyOnVoicemail = config.getboolean("2 - Account Info", "notifyOnVoicemail")
131                                 self.notifyOnSms = config.getboolean("2 - Account Info", "notifyOnSms")
132                         except ConfigParser.NoOptionError, e:
133                                 _moduleLogger.info(
134                                         "Settings file %s is missing option %s" % (
135                                                 constants._user_settings_,
136                                                 e.option,
137                                         ),
138                                 )
139                         except ConfigParser.NoSectionError, e:
140                                 _moduleLogger.info(
141                                         "Settings file %s is missing section %s" % (
142                                                 constants._user_settings_,
143                                                 e.section,
144                                         ),
145                                 )
146                         except Exception:
147                                 _moduleLogger.exception("Unknown loading error")
148
149                 creds = (
150                         base64.b64decode(blob)
151                         for blob in blobs
152                 )
153                 self._mainWindow.set_default_credentials(*creds)
154                 self._fullscreenAction.setChecked(isFullscreen)
155                 self._orientationAction.setChecked(isPortrait)
156                 self._mainWindow.set_current_tab(tabIndex)
157                 self._mainWindow.load_settings(config)
158
159         def save_settings(self):
160                 _moduleLogger.info("Saving settings")
161                 config = ConfigParser.SafeConfigParser()
162
163                 config.add_section(constants.__pretty_app_name__)
164                 config.set(constants.__pretty_app_name__, "tab", str(self._mainWindow.get_current_tab()))
165                 config.set(constants.__pretty_app_name__, "fullscreen", str(self._fullscreenAction.isChecked()))
166                 config.set(constants.__pretty_app_name__, "portrait", str(self._orientationAction.isChecked()))
167                 for i, value in enumerate(self._mainWindow.get_default_credentials()):
168                         blob = base64.b64encode(value)
169                         config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
170
171                 if self._alarmHandler is not None:
172                         config.add_section("alarm")
173                         self._alarmHandler.save_settings(config, "alarm")
174                 config.add_section("2 - Account Info")
175                 config.set("2 - Account Info", "notifyOnMissed", repr(self.notifyOnMissed))
176                 config.set("2 - Account Info", "notifyOnVoicemail", repr(self.notifyOnVoicemail))
177                 config.set("2 - Account Info", "notifyOnSms", repr(self.notifyOnSms))
178
179                 self._mainWindow.save_settings(config)
180
181                 with open(constants._user_settings_, "wb") as configFile:
182                         config.write(configFile)
183
184         def get_icon(self, name):
185                 if self._dataPath is None:
186                         for path in self._DATA_PATHS:
187                                 if os.path.exists(os.path.join(path, name)):
188                                         self._dataPath = path
189                                         break
190                 if self._dataPath is not None:
191                         icon = QtGui.QIcon(os.path.join(self._dataPath, name))
192                         return icon
193                 else:
194                         return None
195
196         def _close_windows(self):
197                 qwrappers.ApplicationWrapper._close_windows(self)
198                 if self._aboutDialog  is not None:
199                         self._aboutDialog.close()
200
201         @property
202         def fsContactsPath(self):
203                 return os.path.join(constants._data_path_, "contacts")
204
205         @property
206         def alarmHandler(self):
207                 return self._alarmHandler
208
209         @property
210         def ledHandler(self):
211                 return self._ledHandler
212
213         def _new_main_window(self):
214                 return MainWindow(None, self)
215
216         @QtCore.pyqtSlot()
217         @QtCore.pyqtSlot(bool)
218         @misc_utils.log_exception(_moduleLogger)
219         def _on_about(self, checked = True):
220                 with qui_utils.notify_error(self._errorLog):
221                         if self._aboutDialog is None:
222                                 import dialogs
223                                 self._aboutDialog = dialogs.AboutDialog(self)
224                         response = self._aboutDialog.run(self._mainWindow.window)
225
226
227 class DelayedWidget(object):
228
229         def __init__(self, app, settingsNames):
230                 self._layout = QtGui.QVBoxLayout()
231                 self._layout.setContentsMargins(0, 0, 0, 0)
232                 self._widget = QtGui.QWidget()
233                 self._widget.setContentsMargins(0, 0, 0, 0)
234                 self._widget.setLayout(self._layout)
235                 self._settings = dict((name, "") for name in settingsNames)
236
237                 self._child = None
238                 self._isEnabled = True
239
240         @property
241         def toplevel(self):
242                 return self._widget
243
244         def has_child(self):
245                 return self._child is not None
246
247         def set_child(self, child):
248                 if self._child is not None:
249                         self._layout.removeWidget(self._child.toplevel)
250                 self._child = child
251                 if self._child is not None:
252                         self._layout.addWidget(self._child.toplevel)
253
254                 self._child.set_settings(self._settings)
255
256                 if self._isEnabled:
257                         self._child.enable()
258                 else:
259                         self._child.disable()
260
261         def enable(self):
262                 self._isEnabled = True
263                 if self._child is not None:
264                         self._child.enable()
265
266         def disable(self):
267                 self._isEnabled = False
268                 if self._child is not None:
269                         self._child.disable()
270
271         def clear(self):
272                 if self._child is not None:
273                         self._child.clear()
274
275         def refresh(self, force=True):
276                 if self._child is not None:
277                         self._child.refresh(force)
278
279         def get_settings(self):
280                 if self._child is not None:
281                         return self._child.get_settings()
282                 else:
283                         return self._settings
284
285         def set_settings(self, settings):
286                 if self._child is not None:
287                         self._child.set_settings(settings)
288                 else:
289                         self._settings = settings
290
291
292 def _tab_factory(tab, app, session, errorLog):
293         import gv_views
294         return gv_views.__dict__[tab](app, session, errorLog)
295
296
297 class MainWindow(object):
298
299         KEYPAD_TAB = 0
300         RECENT_TAB = 1
301         MESSAGES_TAB = 2
302         CONTACTS_TAB = 3
303         MAX_TABS = 4
304
305         _TAB_TITLES = [
306                 "Dialpad",
307                 "History",
308                 "Messages",
309                 "Contacts",
310         ]
311         assert len(_TAB_TITLES) == MAX_TABS
312
313         _TAB_ICONS = [
314                 "dialpad.png",
315                 "history.png",
316                 "messages.png",
317                 "contacts.png",
318         ]
319         assert len(_TAB_ICONS) == MAX_TABS
320
321         _TAB_CLASS = [
322                 functools.partial(_tab_factory, "Dialpad"),
323                 functools.partial(_tab_factory, "History"),
324                 functools.partial(_tab_factory, "Messages"),
325                 functools.partial(_tab_factory, "Contacts"),
326         ]
327         assert len(_TAB_CLASS) == MAX_TABS
328
329         # Hack to allow delay importing/loading of tabs
330         _TAB_SETTINGS_NAMES = [
331                 (),
332                 ("filter", ),
333                 ("status", "type"),
334                 ("selectedAddressbook", ),
335         ]
336         assert len(_TAB_SETTINGS_NAMES) == MAX_TABS
337
338         def __init__(self, parent, app):
339                 self._app = app
340
341                 self._errorLog = qui_utils.QErrorLog()
342                 self._errorDisplay = qui_utils.ErrorDisplay(self._errorLog)
343
344                 self._session = session.Session(self._errorLog, constants._data_path_)
345                 self._session.error.connect(self._on_session_error)
346                 self._session.loggedIn.connect(self._on_login)
347                 self._session.loggedOut.connect(self._on_logout)
348                 self._session.draft.recipientsChanged.connect(self._on_recipients_changed)
349                 self._defaultCredentials = "", ""
350                 self._curentCredentials = "", ""
351                 self._currentTab = 0
352
353                 self._credentialsDialog = None
354                 self._smsEntryDialog = None
355                 self._accountDialog = None
356
357                 self._tabsContents = [
358                         DelayedWidget(self._app, self._TAB_SETTINGS_NAMES[i])
359                         for i in xrange(self.MAX_TABS)
360                 ]
361                 for tab in self._tabsContents:
362                         tab.disable()
363
364                 self._tabWidget = QtGui.QTabWidget()
365                 if qui_utils.screen_orientation() == QtCore.Qt.Vertical:
366                         self._tabWidget.setTabPosition(QtGui.QTabWidget.South)
367                 else:
368                         self._tabWidget.setTabPosition(QtGui.QTabWidget.West)
369                 defaultTabIconSize = self._tabWidget.iconSize()
370                 defaultTabIconWidth, defaultTabIconHeight = defaultTabIconSize.width(), defaultTabIconSize.height()
371                 for tabIndex, (tabTitle, tabIcon) in enumerate(
372                         zip(self._TAB_TITLES, self._TAB_ICONS)
373                 ):
374                         icon = self._app.get_icon(tabIcon)
375                         if constants.IS_MAEMO and icon is not None:
376                                 tabTitle = ""
377
378                         if icon is None:
379                                 self._tabWidget.addTab(self._tabsContents[tabIndex].toplevel, tabTitle)
380                         else:
381                                 iconSize = icon.availableSizes()[0]
382                                 defaultTabIconWidth = max(defaultTabIconWidth, iconSize.width())
383                                 defaultTabIconHeight = max(defaultTabIconHeight, iconSize.height())
384                                 self._tabWidget.addTab(self._tabsContents[tabIndex].toplevel, icon, tabTitle)
385                 defaultTabIconWidth = max(defaultTabIconWidth, 32)
386                 defaultTabIconHeight = max(defaultTabIconHeight, 32)
387                 self._tabWidget.setIconSize(QtCore.QSize(defaultTabIconWidth, defaultTabIconHeight))
388                 self._tabWidget.currentChanged.connect(self._on_tab_changed)
389                 self._tabWidget.setContentsMargins(0, 0, 0, 0)
390
391                 self._layout = QtGui.QVBoxLayout()
392                 self._layout.setContentsMargins(0, 0, 0, 0)
393                 self._layout.addWidget(self._errorDisplay.toplevel)
394                 self._layout.addWidget(self._tabWidget)
395
396                 centralWidget = QtGui.QWidget()
397                 centralWidget.setLayout(self._layout)
398                 centralWidget.setContentsMargins(0, 0, 0, 0)
399
400                 self._window = QtGui.QMainWindow(parent)
401                 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
402                 qui_utils.set_stackable(self._window, True)
403                 self._window.setWindowTitle("%s" % constants.__pretty_app_name__)
404                 self._window.setCentralWidget(centralWidget)
405
406                 self._loginTabAction = QtGui.QAction(None)
407                 self._loginTabAction.setText("Login")
408                 self._loginTabAction.triggered.connect(self._on_login_requested)
409
410                 self._importTabAction = QtGui.QAction(None)
411                 self._importTabAction.setText("Import")
412                 self._importTabAction.triggered.connect(self._on_import)
413
414                 self._accountTabAction = QtGui.QAction(None)
415                 self._accountTabAction.setText("Account")
416                 self._accountTabAction.triggered.connect(self._on_account)
417
418                 self._refreshTabAction = QtGui.QAction(None)
419                 self._refreshTabAction.setText("Refresh")
420                 self._refreshTabAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
421                 self._refreshTabAction.triggered.connect(self._on_refresh)
422
423                 self._closeWindowAction = QtGui.QAction(None)
424                 self._closeWindowAction.setText("Close")
425                 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
426                 self._closeWindowAction.triggered.connect(self._on_close_window)
427
428                 if constants.IS_MAEMO:
429                         fileMenu = self._window.menuBar().addMenu("&File")
430                         fileMenu.addAction(self._loginTabAction)
431                         fileMenu.addAction(self._refreshTabAction)
432
433                         toolsMenu = self._window.menuBar().addMenu("&Tools")
434                         toolsMenu.addAction(self._accountTabAction)
435                         toolsMenu.addAction(self._importTabAction)
436                         toolsMenu.addAction(self._app.aboutAction)
437
438                         self._window.addAction(self._closeWindowAction)
439                         self._window.addAction(self._app.quitAction)
440                         self._window.addAction(self._app.fullscreenAction)
441                 else:
442                         fileMenu = self._window.menuBar().addMenu("&File")
443                         fileMenu.addAction(self._loginTabAction)
444                         fileMenu.addAction(self._refreshTabAction)
445                         fileMenu.addAction(self._closeWindowAction)
446                         fileMenu.addAction(self._app.quitAction)
447
448                         viewMenu = self._window.menuBar().addMenu("&View")
449                         viewMenu.addAction(self._app.fullscreenAction)
450
451                         toolsMenu = self._window.menuBar().addMenu("&Tools")
452                         toolsMenu.addAction(self._accountTabAction)
453                         toolsMenu.addAction(self._importTabAction)
454                         toolsMenu.addAction(self._app.aboutAction)
455
456                 self._window.addAction(self._app.orientationAction)
457                 self._window.addAction(self._app.logAction)
458
459                 self._initialize_tab(self._tabWidget.currentIndex())
460                 self.set_fullscreen(self._app.fullscreenAction.isChecked())
461                 self.set_orientation(self._app.orientationAction.isChecked())
462
463         @property
464         def window(self):
465                 return self._window
466
467         def set_default_credentials(self, username, password):
468                 self._defaultCredentials = username, password
469
470         def get_default_credentials(self):
471                 return self._defaultCredentials
472
473         def walk_children(self):
474                 return ()
475
476         def start(self):
477                 assert self._session.state == self._session.LOGGEDOUT_STATE, "Initialization messed up"
478                 if self._defaultCredentials != ("", ""):
479                         username, password = self._defaultCredentials[0], self._defaultCredentials[1]
480                         self._curentCredentials = username, password
481                         self._session.login(username, password)
482                 else:
483                         self._prompt_for_login()
484
485         def close(self):
486                 for child in self.walk_children():
487                         child.window.destroyed.disconnect(self._on_child_close)
488                         child.close()
489                 for diag in (
490                         self._credentialsDialog,
491                         self._smsEntryDialog,
492                         self._accountDialog,
493                 ):
494                         if diag is not None:
495                                 diag.close()
496                 self._window.close()
497
498         def destroy(self):
499                 if self._session.state != self._session.LOGGEDOUT_STATE:
500                         self._session.logout()
501
502         def get_current_tab(self):
503                 return self._currentTab
504
505         def set_current_tab(self, tabIndex):
506                 self._tabWidget.setCurrentIndex(tabIndex)
507
508         def load_settings(self, config):
509                 backendId = 2 # For backwards compatibility
510                 for tabIndex, tabTitle in enumerate(self._TAB_TITLES):
511                         sectionName = "%s - %s" % (backendId, tabTitle)
512                         settings = self._tabsContents[tabIndex].get_settings()
513                         for settingName in settings.iterkeys():
514                                 try:
515                                         settingValue = config.get(sectionName, settingName)
516                                 except ConfigParser.NoOptionError, e:
517                                         _moduleLogger.info(
518                                                 "Settings file %s is missing section %s" % (
519                                                         constants._user_settings_,
520                                                         e.section,
521                                                 ),
522                                         )
523                                         return
524                                 except ConfigParser.NoSectionError, e:
525                                         _moduleLogger.info(
526                                                 "Settings file %s is missing section %s" % (
527                                                         constants._user_settings_,
528                                                         e.section,
529                                                 ),
530                                         )
531                                         return
532                                 except Exception:
533                                         _moduleLogger.exception("Unknown loading error")
534                                         return
535                                 settings[settingName] = settingValue
536                         self._tabsContents[tabIndex].set_settings(settings)
537
538         def save_settings(self, config):
539                 backendId = 2 # For backwards compatibility
540                 for tabIndex, tabTitle in enumerate(self._TAB_TITLES):
541                         sectionName = "%s - %s" % (backendId, tabTitle)
542                         config.add_section(sectionName)
543                         tabSettings = self._tabsContents[tabIndex].get_settings()
544                         for settingName, settingValue in tabSettings.iteritems():
545                                 config.set(sectionName, settingName, settingValue)
546
547         def show(self):
548                 self._window.show()
549                 for child in self.walk_children():
550                         child.show()
551
552         def hide(self):
553                 for child in self.walk_children():
554                         child.hide()
555                 self._window.hide()
556
557         def set_fullscreen(self, isFullscreen):
558                 if isFullscreen:
559                         self._window.showFullScreen()
560                 else:
561                         self._window.showNormal()
562                 for child in self.walk_children():
563                         child.set_fullscreen(isFullscreen)
564
565         def set_orientation(self, isPortrait):
566                 if isPortrait:
567                         self._tabWidget.setTabPosition(QtGui.QTabWidget.South)
568                         qui_utils.set_window_orientation(self.window, QtCore.Qt.Vertical)
569                 else:
570                         self._tabWidget.setTabPosition(QtGui.QTabWidget.West)
571                         qui_utils.set_window_orientation(self.window, QtCore.Qt.Horizontal)
572                 for child in (self._smsEntryDialog, ):
573                         if child is not None:
574                                 child.set_orientation(isPortrait)
575                 for child in self.walk_children():
576                         child.set_orientation(isPortrait)
577
578         def _initialize_tab(self, index):
579                 assert index < self.MAX_TABS, "Invalid tab"
580                 if not self._tabsContents[index].has_child():
581                         tab = self._TAB_CLASS[index](self._app, self._session, self._errorLog)
582                         self._tabsContents[index].set_child(tab)
583                         self._tabsContents[index].refresh(force=False)
584
585         def _prompt_for_login(self):
586                 if self._credentialsDialog is None:
587                         import dialogs
588                         self._credentialsDialog = dialogs.CredentialsDialog(self._app)
589                 username, password = self._credentialsDialog.run(
590                         self._defaultCredentials[0], self._defaultCredentials[1], self.window
591                 )
592                 self._curentCredentials = username, password
593                 self._session.login(username, password)
594
595         def _show_account_dialog(self):
596                 if self._accountDialog is None:
597                         import dialogs
598                         self._accountDialog = dialogs.AccountDialog(self._app)
599                         if self._app.alarmHandler is None:
600                                 self._accountDialog.setIfNotificationsSupported(False)
601                 if self._app.alarmHandler is not None:
602                         self._accountDialog.notifications = self._app.alarmHandler.isEnabled
603                         self._accountDialog.notificationTime = self._app.alarmHandler.recurrence
604                         self._accountDialog.notifyOnMissed = self._app.notifyOnMissed
605                         self._accountDialog.notifyOnVoicemail = self._app.notifyOnVoicemail
606                         self._accountDialog.notifyOnSms = self._app.notifyOnSms
607                 self._accountDialog.set_callbacks(
608                         self._session.get_callback_numbers(), self._session.get_callback_number()
609                 )
610                 self._accountDialog.accountNumber = self._session.get_account_number()
611                 response = self._accountDialog.run(self.window)
612                 if response == QtGui.QDialog.Accepted:
613                         if self._accountDialog.doClear:
614                                 self._session.logout_and_clear()
615                         else:
616                                 callbackNumber = self._accountDialog.selectedCallback
617                                 self._session.set_callback_number(callbackNumber)
618                         if self._app.alarmHandler is not None:
619                                 self._app.alarmHandler.apply_settings(self._accountDialog.notifications, self._accountDialog.notificationTime)
620                                 self._app.notifyOnMissed = self._accountDialog.notifyOnMissed
621                                 self._app.notifyOnVoicemail = self._accountDialog.notifyOnVoicemail
622                                 self._app.notifyOnSms = self._accountDialog.notifyOnSms
623                 elif response == QtGui.QDialog.Rejected:
624                         _moduleLogger.info("Cancelled")
625                 else:
626                         _moduleLogger.info("Unknown response")
627
628         @QtCore.pyqtSlot(str)
629         @misc_utils.log_exception(_moduleLogger)
630         def _on_session_error(self, message):
631                 with qui_utils.notify_error(self._errorLog):
632                         self._errorLog.push_error(message)
633
634         @QtCore.pyqtSlot()
635         @misc_utils.log_exception(_moduleLogger)
636         def _on_login(self):
637                 with qui_utils.notify_error(self._errorLog):
638                         changedAccounts = self._defaultCredentials != self._curentCredentials
639                         noCallback = not self._session.get_callback_number()
640                         if changedAccounts or noCallback:
641                                 self._show_account_dialog()
642
643                         self._defaultCredentials = self._curentCredentials
644
645                         for tab in self._tabsContents:
646                                 tab.enable()
647
648         @QtCore.pyqtSlot()
649         @misc_utils.log_exception(_moduleLogger)
650         def _on_logout(self):
651                 with qui_utils.notify_error(self._errorLog):
652                         for tab in self._tabsContents:
653                                 tab.disable()
654
655         @QtCore.pyqtSlot()
656         @misc_utils.log_exception(_moduleLogger)
657         def _on_recipients_changed(self):
658                 with qui_utils.notify_error(self._errorLog):
659                         if self._session.draft.get_num_contacts() == 0:
660                                 return
661
662                         if self._smsEntryDialog is None:
663                                 import dialogs
664                                 self._smsEntryDialog = dialogs.SMSEntryWindow(self.window, self._app, self._session, self._errorLog)
665
666         @QtCore.pyqtSlot()
667         @QtCore.pyqtSlot(bool)
668         @misc_utils.log_exception(_moduleLogger)
669         def _on_login_requested(self, checked = True):
670                 with qui_utils.notify_error(self._errorLog):
671                         self._prompt_for_login()
672
673         @QtCore.pyqtSlot(int)
674         @misc_utils.log_exception(_moduleLogger)
675         def _on_tab_changed(self, index):
676                 with qui_utils.notify_error(self._errorLog):
677                         self._currentTab = index
678                         self._initialize_tab(index)
679
680         @QtCore.pyqtSlot()
681         @QtCore.pyqtSlot(bool)
682         @misc_utils.log_exception(_moduleLogger)
683         def _on_refresh(self, checked = True):
684                 with qui_utils.notify_error(self._errorLog):
685                         self._tabsContents[self._currentTab].refresh(force=True)
686
687         @QtCore.pyqtSlot()
688         @QtCore.pyqtSlot(bool)
689         @misc_utils.log_exception(_moduleLogger)
690         def _on_import(self, checked = True):
691                 with qui_utils.notify_error(self._errorLog):
692                         csvName = QtGui.QFileDialog.getOpenFileName(self._window, caption="Import", filter="CSV Files (*.csv)")
693                         if not csvName:
694                                 return
695                         import shutil
696                         shutil.copy2(csvName, self._app.fsContactsPath)
697                         self._tabsContents[self.CONTACTS_TAB].update_addressbooks()
698
699         @QtCore.pyqtSlot()
700         @QtCore.pyqtSlot(bool)
701         @misc_utils.log_exception(_moduleLogger)
702         def _on_account(self, checked = True):
703                 with qui_utils.notify_error(self._errorLog):
704                         self._show_account_dialog()
705
706         @QtCore.pyqtSlot()
707         @QtCore.pyqtSlot(bool)
708         @misc_utils.log_exception(_moduleLogger)
709         def _on_close_window(self, checked = True):
710                 self.close()
711
712
713 def run():
714         app = QtGui.QApplication([])
715         handle = Dialcentral(app)
716         qtpie.init_pies()
717         return app.exec_()
718
719
720 if __name__ == "__main__":
721         import sys
722
723         logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s'
724         logging.basicConfig(level=logging.DEBUG, format=logFormat)
725         try:
726                 os.makedirs(constants._data_path_)
727         except OSError, e:
728                 if e.errno != 17:
729                         raise
730
731         val = run()
732         sys.exit(val)