Due to crashes, temporarilly dropping missed call detection
[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 get_resource(self, name):
197                 if self._dataPath is None:
198                         for path in self._DATA_PATHS:
199                                 if os.path.exists(os.path.join(path, name)):
200                                         self._dataPath = path
201                                         break
202                 if self._dataPath is not None:
203                         return os.path.join(self._dataPath, name)
204                 else:
205                         return None
206
207         def _close_windows(self):
208                 qwrappers.ApplicationWrapper._close_windows(self)
209                 if self._aboutDialog  is not None:
210                         self._aboutDialog.close()
211
212         @property
213         def fsContactsPath(self):
214                 return os.path.join(constants._data_path_, "contacts")
215
216         @property
217         def alarmHandler(self):
218                 return self._alarmHandler
219
220         @property
221         def ledHandler(self):
222                 return self._ledHandler
223
224         def _new_main_window(self):
225                 return MainWindow(None, self)
226
227         @QtCore.pyqtSlot()
228         @QtCore.pyqtSlot(bool)
229         @misc_utils.log_exception(_moduleLogger)
230         def _on_about(self, checked = True):
231                 with qui_utils.notify_error(self._errorLog):
232                         if self._aboutDialog is None:
233                                 import dialogs
234                                 self._aboutDialog = dialogs.AboutDialog(self)
235                         response = self._aboutDialog.run(self._mainWindow.window)
236
237
238 class DelayedWidget(object):
239
240         def __init__(self, app, settingsNames):
241                 self._layout = QtGui.QVBoxLayout()
242                 self._layout.setContentsMargins(0, 0, 0, 0)
243                 self._widget = QtGui.QWidget()
244                 self._widget.setContentsMargins(0, 0, 0, 0)
245                 self._widget.setLayout(self._layout)
246                 self._settings = dict((name, "") for name in settingsNames)
247
248                 self._child = None
249                 self._isEnabled = True
250
251         @property
252         def toplevel(self):
253                 return self._widget
254
255         def has_child(self):
256                 return self._child is not None
257
258         def set_child(self, child):
259                 if self._child is not None:
260                         self._layout.removeWidget(self._child.toplevel)
261                 self._child = child
262                 if self._child is not None:
263                         self._layout.addWidget(self._child.toplevel)
264
265                 self._child.set_settings(self._settings)
266
267                 if self._isEnabled:
268                         self._child.enable()
269                 else:
270                         self._child.disable()
271
272         @property
273         def child(self):
274                 return self._child
275
276         def enable(self):
277                 self._isEnabled = True
278                 if self._child is not None:
279                         self._child.enable()
280
281         def disable(self):
282                 self._isEnabled = False
283                 if self._child is not None:
284                         self._child.disable()
285
286         def clear(self):
287                 if self._child is not None:
288                         self._child.clear()
289
290         def refresh(self, force=True):
291                 if self._child is not None:
292                         self._child.refresh(force)
293
294         def get_settings(self):
295                 if self._child is not None:
296                         return self._child.get_settings()
297                 else:
298                         return self._settings
299
300         def set_settings(self, settings):
301                 if self._child is not None:
302                         self._child.set_settings(settings)
303                 else:
304                         self._settings = settings
305
306
307 def _tab_factory(tab, app, session, errorLog):
308         import gv_views
309         return gv_views.__dict__[tab](app, session, errorLog)
310
311
312 class MainWindow(qwrappers.WindowWrapper):
313
314         KEYPAD_TAB = 0
315         RECENT_TAB = 1
316         MESSAGES_TAB = 2
317         CONTACTS_TAB = 3
318         MAX_TABS = 4
319
320         _TAB_TITLES = [
321                 "Dialpad",
322                 "History",
323                 "Messages",
324                 "Contacts",
325         ]
326         assert len(_TAB_TITLES) == MAX_TABS
327
328         _TAB_ICONS = [
329                 "dialpad.png",
330                 "history.png",
331                 "messages.png",
332                 "contacts.png",
333         ]
334         assert len(_TAB_ICONS) == MAX_TABS
335
336         _TAB_CLASS = [
337                 functools.partial(_tab_factory, "Dialpad"),
338                 functools.partial(_tab_factory, "History"),
339                 functools.partial(_tab_factory, "Messages"),
340                 functools.partial(_tab_factory, "Contacts"),
341         ]
342         assert len(_TAB_CLASS) == MAX_TABS
343
344         # Hack to allow delay importing/loading of tabs
345         _TAB_SETTINGS_NAMES = [
346                 (),
347                 ("filter", ),
348                 ("status", "type"),
349                 ("selectedAddressbook", ),
350         ]
351         assert len(_TAB_SETTINGS_NAMES) == MAX_TABS
352
353         def __init__(self, parent, app):
354                 qwrappers.WindowWrapper.__init__(self, parent, app)
355                 self._window.setWindowTitle("%s" % constants.__pretty_app_name__)
356                 self._errorLog = self._app.errorLog
357
358                 self._session = session.Session(self._errorLog, constants._data_path_)
359                 self._session.error.connect(self._on_session_error)
360                 self._session.loggedIn.connect(self._on_login)
361                 self._session.loggedOut.connect(self._on_logout)
362                 self._session.draft.recipientsChanged.connect(self._on_recipients_changed)
363                 self._session.newMessages.connect(self._on_new_message_alert)
364                 self._defaultCredentials = "", ""
365                 self._curentCredentials = "", ""
366                 self._currentTab = 0
367
368                 self._credentialsDialog = None
369                 self._smsEntryDialog = None
370                 self._accountDialog = None
371
372                 self._tabsContents = [
373                         DelayedWidget(self._app, self._TAB_SETTINGS_NAMES[i])
374                         for i in xrange(self.MAX_TABS)
375                 ]
376                 for tab in self._tabsContents:
377                         tab.disable()
378
379                 self._tabWidget = QtGui.QTabWidget()
380                 if qui_utils.screen_orientation() == QtCore.Qt.Vertical:
381                         self._tabWidget.setTabPosition(QtGui.QTabWidget.South)
382                 else:
383                         self._tabWidget.setTabPosition(QtGui.QTabWidget.West)
384                 defaultTabIconSize = self._tabWidget.iconSize()
385                 defaultTabIconWidth, defaultTabIconHeight = defaultTabIconSize.width(), defaultTabIconSize.height()
386                 for tabIndex, (tabTitle, tabIcon) in enumerate(
387                         zip(self._TAB_TITLES, self._TAB_ICONS)
388                 ):
389                         icon = self._app.get_icon(tabIcon)
390                         if constants.IS_MAEMO and icon is not None:
391                                 tabTitle = ""
392
393                         if icon is None:
394                                 self._tabWidget.addTab(self._tabsContents[tabIndex].toplevel, tabTitle)
395                         else:
396                                 iconSize = icon.availableSizes()[0]
397                                 defaultTabIconWidth = max(defaultTabIconWidth, iconSize.width())
398                                 defaultTabIconHeight = max(defaultTabIconHeight, iconSize.height())
399                                 self._tabWidget.addTab(self._tabsContents[tabIndex].toplevel, icon, tabTitle)
400                 defaultTabIconWidth = max(defaultTabIconWidth, 32)
401                 defaultTabIconHeight = max(defaultTabIconHeight, 32)
402                 self._tabWidget.setIconSize(QtCore.QSize(defaultTabIconWidth, defaultTabIconHeight))
403                 self._tabWidget.currentChanged.connect(self._on_tab_changed)
404                 self._tabWidget.setContentsMargins(0, 0, 0, 0)
405
406                 self._layout.addWidget(self._tabWidget)
407
408                 self._loginTabAction = QtGui.QAction(None)
409                 self._loginTabAction.setText("Login")
410                 self._loginTabAction.triggered.connect(self._on_login_requested)
411
412                 self._importTabAction = QtGui.QAction(None)
413                 self._importTabAction.setText("Import")
414                 self._importTabAction.triggered.connect(self._on_import)
415
416                 self._accountTabAction = QtGui.QAction(None)
417                 self._accountTabAction.setText("Account")
418                 self._accountTabAction.triggered.connect(self._on_account)
419
420                 self._refreshTabAction = QtGui.QAction(None)
421                 self._refreshTabAction.setText("Refresh")
422                 self._refreshTabAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
423                 self._refreshTabAction.triggered.connect(self._on_refresh)
424
425                 fileMenu = self._window.menuBar().addMenu("&File")
426                 fileMenu.addAction(self._loginTabAction)
427                 fileMenu.addAction(self._refreshTabAction)
428
429                 toolsMenu = self._window.menuBar().addMenu("&Tools")
430                 toolsMenu.addAction(self._accountTabAction)
431                 toolsMenu.addAction(self._importTabAction)
432                 toolsMenu.addAction(self._app.aboutAction)
433
434                 self._initialize_tab(self._tabWidget.currentIndex())
435                 self.set_fullscreen(self._app.fullscreenAction.isChecked())
436                 self.set_orientation(self._app.orientationAction.isChecked())
437
438         def set_default_credentials(self, username, password):
439                 self._defaultCredentials = username, password
440
441         def get_default_credentials(self):
442                 return self._defaultCredentials
443
444         def walk_children(self):
445                 if self._smsEntryDialog is not None:
446                         return (self._smsEntryDialog, )
447                 else:
448                         return ()
449
450         def start(self):
451                 qwrappers.WindowWrapper.start(self)
452                 assert self._session.state == self._session.LOGGEDOUT_STATE, "Initialization messed up"
453                 if self._defaultCredentials != ("", ""):
454                         username, password = self._defaultCredentials[0], self._defaultCredentials[1]
455                         self._curentCredentials = username, password
456                         self._session.login(username, password)
457                 else:
458                         self._prompt_for_login()
459
460         def close(self):
461                 for diag in (
462                         self._credentialsDialog,
463                         self._accountDialog,
464                 ):
465                         if diag is not None:
466                                 diag.close()
467                 for child in self.walk_children():
468                         child.window.destroyed.disconnect(self._on_child_close)
469                         child.window.closed.disconnect(self._on_child_close)
470                         child.close()
471                 self._window.close()
472
473         def destroy(self):
474                 qwrappers.WindowWrapper.destroy(self)
475                 if self._session.state != self._session.LOGGEDOUT_STATE:
476                         self._session.logout()
477
478         def get_current_tab(self):
479                 return self._currentTab
480
481         def set_current_tab(self, tabIndex):
482                 self._tabWidget.setCurrentIndex(tabIndex)
483
484         def load_settings(self, config):
485                 backendId = 2 # For backwards compatibility
486                 for tabIndex, tabTitle in enumerate(self._TAB_TITLES):
487                         sectionName = "%s - %s" % (backendId, tabTitle)
488                         settings = self._tabsContents[tabIndex].get_settings()
489                         for settingName in settings.iterkeys():
490                                 try:
491                                         settingValue = config.get(sectionName, settingName)
492                                 except ConfigParser.NoOptionError, e:
493                                         _moduleLogger.info(
494                                                 "Settings file %s is missing section %s" % (
495                                                         constants._user_settings_,
496                                                         e.section,
497                                                 ),
498                                         )
499                                         return
500                                 except ConfigParser.NoSectionError, e:
501                                         _moduleLogger.info(
502                                                 "Settings file %s is missing section %s" % (
503                                                         constants._user_settings_,
504                                                         e.section,
505                                                 ),
506                                         )
507                                         return
508                                 except Exception:
509                                         _moduleLogger.exception("Unknown loading error")
510                                         return
511                                 settings[settingName] = settingValue
512                         self._tabsContents[tabIndex].set_settings(settings)
513
514         def save_settings(self, config):
515                 backendId = 2 # For backwards compatibility
516                 for tabIndex, tabTitle in enumerate(self._TAB_TITLES):
517                         sectionName = "%s - %s" % (backendId, tabTitle)
518                         config.add_section(sectionName)
519                         tabSettings = self._tabsContents[tabIndex].get_settings()
520                         for settingName, settingValue in tabSettings.iteritems():
521                                 config.set(sectionName, settingName, settingValue)
522
523         def set_orientation(self, isPortrait):
524                 qwrappers.WindowWrapper.set_orientation(self, isPortrait)
525                 if isPortrait:
526                         self._tabWidget.setTabPosition(QtGui.QTabWidget.South)
527                 else:
528                         self._tabWidget.setTabPosition(QtGui.QTabWidget.West)
529
530         def _initialize_tab(self, index):
531                 assert index < self.MAX_TABS, "Invalid tab"
532                 if not self._tabsContents[index].has_child():
533                         tab = self._TAB_CLASS[index](self._app, self._session, self._errorLog)
534                         self._tabsContents[index].set_child(tab)
535                 self._tabsContents[index].refresh(force=False)
536
537         def _prompt_for_login(self):
538                 if self._credentialsDialog is None:
539                         import dialogs
540                         self._credentialsDialog = dialogs.CredentialsDialog(self._app)
541                 credentials = self._credentialsDialog.run(
542                         self._defaultCredentials[0], self._defaultCredentials[1], self.window
543                 )
544                 if credentials is None:
545                         return
546                 username, password = credentials
547                 self._curentCredentials = username, password
548                 self._session.login(username, password)
549
550         def _show_account_dialog(self):
551                 if self._accountDialog is None:
552                         import dialogs
553                         self._accountDialog = dialogs.AccountDialog(self._app)
554                         if self._app.alarmHandler is None:
555                                 self._accountDialog.setIfNotificationsSupported(False)
556                 if self._app.alarmHandler is not None:
557                         self._accountDialog.notifications = self._app.alarmHandler.isEnabled
558                         self._accountDialog.notificationTime = self._app.alarmHandler.recurrence
559                         self._accountDialog.notifyOnMissed = self._app.notifyOnMissed
560                         self._accountDialog.notifyOnVoicemail = self._app.notifyOnVoicemail
561                         self._accountDialog.notifyOnSms = self._app.notifyOnSms
562                 self._accountDialog.set_callbacks(
563                         self._session.get_callback_numbers(), self._session.get_callback_number()
564                 )
565                 accountNumberToDisplay = self._session.get_account_number()
566                 if not accountNumberToDisplay:
567                         accountNumberToDisplay = "Not Available (%s)" % self._session.state
568                 self._accountDialog.set_account_number(accountNumberToDisplay)
569                 response = self._accountDialog.run(self.window)
570                 if response == QtGui.QDialog.Accepted:
571                         if self._accountDialog.doClear:
572                                 self._session.logout_and_clear()
573                                 self._defaultCredentials = "", ""
574                                 self._curentCredentials = "", ""
575                                 for tab in self._tabsContents:
576                                         tab.disable()
577                         else:
578                                 callbackNumber = self._accountDialog.selectedCallback
579                                 self._session.set_callback_number(callbackNumber)
580                         if self._app.alarmHandler is not None:
581                                 self._app.alarmHandler.apply_settings(self._accountDialog.notifications, self._accountDialog.notificationTime)
582                                 self._app.notifyOnMissed = self._accountDialog.notifyOnMissed
583                                 self._app.notifyOnVoicemail = self._accountDialog.notifyOnVoicemail
584                                 self._app.notifyOnSms = self._accountDialog.notifyOnSms
585                                 self._app.save_settings()
586                 elif response == QtGui.QDialog.Rejected:
587                         _moduleLogger.info("Cancelled")
588                 else:
589                         _moduleLogger.info("Unknown response")
590
591         @QtCore.pyqtSlot()
592         @misc_utils.log_exception(_moduleLogger)
593         def _on_new_message_alert(self):
594                 with qui_utils.notify_error(self._errorLog):
595                         self._errorLog.push_message("New messages available")
596
597         @QtCore.pyqtSlot(str)
598         @misc_utils.log_exception(_moduleLogger)
599         def _on_session_error(self, message):
600                 with qui_utils.notify_error(self._errorLog):
601                         self._errorLog.push_error(message)
602
603         @QtCore.pyqtSlot()
604         @misc_utils.log_exception(_moduleLogger)
605         def _on_login(self):
606                 with qui_utils.notify_error(self._errorLog):
607                         changedAccounts = self._defaultCredentials != self._curentCredentials
608                         noCallback = not self._session.get_callback_number()
609                         if changedAccounts or noCallback:
610                                 self._show_account_dialog()
611
612                         self._defaultCredentials = self._curentCredentials
613
614                         for tab in self._tabsContents:
615                                 tab.enable()
616                         self._initialize_tab(self._currentTab)
617
618         @QtCore.pyqtSlot()
619         @misc_utils.log_exception(_moduleLogger)
620         def _on_logout(self):
621                 with qui_utils.notify_error(self._errorLog):
622                         for tab in self._tabsContents:
623                                 tab.disable()
624
625         @QtCore.pyqtSlot()
626         @misc_utils.log_exception(_moduleLogger)
627         def _on_recipients_changed(self):
628                 with qui_utils.notify_error(self._errorLog):
629                         if self._session.draft.get_num_contacts() == 0:
630                                 return
631
632                         if self._smsEntryDialog is None:
633                                 import dialogs
634                                 self._smsEntryDialog = dialogs.SMSEntryWindow(self.window, self._app, self._session, self._errorLog)
635                                 self._smsEntryDialog.window.destroyed.connect(self._on_child_close)
636                                 self._smsEntryDialog.window.closed.connect(self._on_child_close)
637                                 self._smsEntryDialog.window.show()
638
639         @misc_utils.log_exception(_moduleLogger)
640         def _on_child_close(self, obj = None):
641                 self._smsEntryDialog = None
642
643         @QtCore.pyqtSlot()
644         @QtCore.pyqtSlot(bool)
645         @misc_utils.log_exception(_moduleLogger)
646         def _on_login_requested(self, checked = True):
647                 with qui_utils.notify_error(self._errorLog):
648                         self._prompt_for_login()
649
650         @QtCore.pyqtSlot(int)
651         @misc_utils.log_exception(_moduleLogger)
652         def _on_tab_changed(self, index):
653                 with qui_utils.notify_error(self._errorLog):
654                         self._currentTab = index
655                         self._initialize_tab(index)
656
657         @QtCore.pyqtSlot()
658         @QtCore.pyqtSlot(bool)
659         @misc_utils.log_exception(_moduleLogger)
660         def _on_refresh(self, checked = True):
661                 with qui_utils.notify_error(self._errorLog):
662                         self._tabsContents[self._currentTab].refresh(force=True)
663
664         @QtCore.pyqtSlot()
665         @QtCore.pyqtSlot(bool)
666         @misc_utils.log_exception(_moduleLogger)
667         def _on_import(self, checked = True):
668                 with qui_utils.notify_error(self._errorLog):
669                         csvName = QtGui.QFileDialog.getOpenFileName(self._window, caption="Import", filter="CSV Files (*.csv)")
670                         csvName = unicode(csvName)
671                         if not csvName:
672                                 return
673                         import shutil
674                         shutil.copy2(csvName, self._app.fsContactsPath)
675                         if self._tabsContents[self.CONTACTS_TAB].has_child:
676                                 self._tabsContents[self.CONTACTS_TAB].child.update_addressbooks()
677
678         @QtCore.pyqtSlot()
679         @QtCore.pyqtSlot(bool)
680         @misc_utils.log_exception(_moduleLogger)
681         def _on_account(self, checked = True):
682                 with qui_utils.notify_error(self._errorLog):
683                         assert self._session.state == self._session.LOGGEDIN_STATE, "Must be logged in for settings"
684                         self._show_account_dialog()
685
686
687 def run():
688         app = QtGui.QApplication([])
689         handle = Dialcentral(app)
690         qtpie.init_pies()
691         return app.exec_()
692
693
694 if __name__ == "__main__":
695         import sys
696
697         logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s'
698         logging.basicConfig(level=logging.DEBUG, format=logFormat)
699         try:
700                 os.makedirs(constants._data_path_)
701         except OSError, e:
702                 if e.errno != 17:
703                         raise
704
705         val = run()
706         sys.exit(val)