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