1 # -*- coding: utf-8 -*-
3 # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 from ui_utils import *
29 from PyQt4.QtCore import *
30 from PyQt4.QtGui import *
33 from fabwindow_base import Ui_MainWindow
39 # This can fail on Python < 2.3 due to the datetime module
40 log.error("Fax address book disabled - Python 2.3+ required.")
45 class FABWindow(QMainWindow, Ui_MainWindow):
46 def __init__(self, parent):
47 QMainWindow.__init__(self, parent)
49 self.group = u'All' # current group
50 self.name = None # current name
51 self.updating_group = False
52 self.updating_name = False
54 self.user_settings = UserSettings()
55 self.user_settings.load()
56 self.user_settings.debug()
61 QTimer.singleShot(0, self.updateUi)
65 self.db = fax.FaxAddressBook()
67 # Fixup data from old-style database
68 data = self.db.get_all_records()
70 if u'All' not in data[d]['groups']:
71 data[d]['groups'].append(u'All')
74 self.db.set('__' + utils.gen_random_uuid(), '', '', '', '', [u'All'], '')
79 self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))
81 self.NewGroupAction.setIcon(QIcon(load_pixmap('new_group', '24x24')))
82 self.NewGroupFromSelectionAction.setIcon(QIcon(load_pixmap('new_group_from_selection', '24x24')))
83 self.RenameGroupAction.setIcon(QIcon(load_pixmap('rename_group', '24x24')))
84 self.RemoveGroupAction.setIcon(QIcon(load_pixmap('remove_group', '24x24')))
85 self.NewNameAction.setIcon(QIcon(load_pixmap('new_user', '24x24')))
86 self.RemoveNameAction.setIcon(QIcon(load_pixmap('remove_user', '24x24')))
87 self.AddToGroupAction.setIcon(QIcon(load_pixmap('add_to_group', '24x24')))
88 self.RemoveFromGroupAction.setIcon(QIcon(load_pixmap('remove_from_group', '24x24')))
90 self.connect(self.QuitAction, SIGNAL("triggered()"), self.close)
91 self.connect(self.NewGroupAction, SIGNAL("triggered()"), self.NewGroupAction_triggered)
92 self.connect(self.NewGroupFromSelectionAction, SIGNAL("triggered()"), self.NewGroupFromSelectionAction_triggered)
93 self.connect(self.RenameGroupAction, SIGNAL("triggered()"), self.RenameGroupAction_triggered)
94 self.connect(self.RemoveGroupAction, SIGNAL("triggered()"), self.RemoveGroupAction_triggered)
95 self.connect(self.NewNameAction, SIGNAL("triggered()"), self.NewNameAction_triggered)
96 self.connect(self.RemoveNameAction, SIGNAL("triggered()"), self.RemoveNameAction_triggered)
97 self.connect(self.ImportAction, SIGNAL("triggered()"), self.ImportAction_triggered)
98 self.connect(self.RemoveFromGroupAction, SIGNAL("triggered()"), self.RemoveFromGroupAction_triggered)
99 self.connect(self.AddToGroupAction, SIGNAL("triggered()"), self.AddToGroupAction_triggered)
101 self.GroupTableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
102 self.NameTableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
104 self.connect(self.Splitter, SIGNAL("splitterMoved(int, int)"), self.Splitter_splitterMoved)
105 self.Splitter.setChildrenCollapsible(False)
106 self.Splitter.setHandleWidth(self.Splitter.handleWidth()+2)
108 self.GroupTableWidget.verticalHeader().hide()
109 self.GroupTableWidget.setShowGrid(False)
110 self.GroupTableWidget.setSelectionMode(QAbstractItemView.SingleSelection)
111 self.NameTableWidget.verticalHeader().hide()
112 self.NameTableWidget.setShowGrid(False)
114 self.NameTableWidget.setDragEnabled(True)
115 self.GroupTableWidget.setAcceptDrops(True)
116 self.GroupTableWidget.setDropIndicatorShown(True)
118 self.connect(self.GroupTableWidget, SIGNAL("itemSelectionChanged()"),
119 self.GroupTableWidget_itemSelectionChanged)
120 self.connect(self.NameTableWidget, SIGNAL("itemSelectionChanged()"),
121 self.NameTableWidget_itemSelectionChanged)
122 self.connect(self.NameLineEdit, SIGNAL("editingFinished()"),
123 self.NameLineEdit_editingFinished)
124 self.connect(self.FaxNumberLineEdit, SIGNAL("editingFinished()"),
125 self.FaxNumberLineEdit_editingFinished)
126 self.connect(self.NotesTextEdit, SIGNAL("textChanged()"),
127 self.NotesTextEdit_textChanged)
128 self.connect(self.NotesTextEdit, SIGNAL("editingFinished()"),
129 self.NotesTextEdit_editingFinished)
130 self.connect(self.GroupTableWidget, SIGNAL("namesAddedToGroup"),
131 self.GroupTableWidget_namesAddedToGroup)
133 self.FaxNumberLineEdit.setValidator(PhoneNumValidator(self.FaxNumberLineEdit))
134 self.NameLineEdit.setValidator(AddressBookNameValidator(self.db, self.NameLineEdit))
136 self.GroupTableWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
137 self.GroupTableWidget.addAction(self.NewGroupAction)
138 self.GroupTableWidget.addAction(self.NewGroupFromSelectionAction)
139 self.GroupTableWidget.addAction(self.RenameGroupAction)
140 self.GroupTableWidget.addAction(self.RemoveGroupAction)
142 self.NameTableWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
143 self.NameTableWidget.addAction(self.NewNameAction)
144 self.NameTableWidget.addAction(self.AddToGroupAction)
145 self.NameTableWidget.addAction(self.RemoveFromGroupAction)
146 self.NameTableWidget.addAction(self.RemoveNameAction)
147 self.NameTableWidget.addAction(self.NewGroupFromSelectionAction)
149 self.GroupTableWidget.setDatabase(self.db)
154 FailureUI(self, self.__tr("<b>Fax support disabled.</b><p>Fax support requires Python 2.3."))
158 self.updateGroupList()
159 self.updateNameList()
160 self.updateDetailsFrame()
163 def closeEvent(self, e):
164 #self.NameLineEdit.emit(SIGNAL("editingFinished()"))
165 self.FaxNumberLineEdit.emit(SIGNAL("editingFinished()"))
166 self.NotesTextEdit.emit(SIGNAL("editingFinished()"))
170 def Splitter_splitterMoved(self, pos, index):
171 self.GroupTableWidget.setColumnWidth(0, self.GroupTableWidget.width())
172 self.NameTableWidget.setColumnWidth(0, self.NameTableWidget.width())
175 def updateGroupList(self):
176 self.updating_group = True
179 headerItem = QTableWidgetItem()
180 headerItem.setText(self.__tr("Group"))
181 self.GroupTableWidget.clear()
182 self.GroupTableWidget.setColumnCount(1)
183 self.GroupTableWidget.setHorizontalHeaderItem(0, headerItem)
184 self.GroupTableWidget.setColumnWidth(0, self.GroupTableWidget.width())
186 groups = self.db.get_all_groups()
188 self.GroupTableWidget.setRowCount(len(groups))
190 # Force All group to top of table
191 all = QTableWidgetItem(self.__tr("All"))
192 all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
193 self.GroupTableWidget.setItem(0, 0, all)
200 i = QTableWidgetItem(QString(g))
205 i.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDropEnabled)
206 self.GroupTableWidget.setItem(j, 0, i)
211 self.updating_group = False
216 elif all is not None:
217 all.setSelected(True)
220 def GroupTableWidget_itemSelectionChanged(self):
221 if not self.updating_group:
222 selected_items = self.GroupTableWidget.selectedItems()
224 self.group = unicode(selected_items[0].text())
225 self.RemoveGroupAction.setEnabled(self.group != u'All')
226 self.RenameGroupAction.setEnabled(self.group != u'All')
227 else: # shouldn't happen?!
228 self.RemoveGroupAction.setEnabled(False)
229 self.RenameGroupAction.setEnabled(False)
232 self.updateNameList()
235 def NameTableWidget_itemSelectionChanged(self):
236 if not self.updating_name:
237 selected_items = self.NameTableWidget.selectedItems()
238 num_selected_items = len(selected_items)
240 if num_selected_items == 0:
242 self.RemoveNameAction.setEnabled(False)
243 self.NewGroupFromSelectionAction.setEnabled(False)
244 self.RemoveFromGroupAction.setEnabled(False)
245 self.AddToGroupAction.setEnabled(False)
247 elif num_selected_items == 1:
248 self.name = unicode(selected_items[0].text())
249 self.RemoveNameAction.setEnabled(True)
250 self.NewGroupFromSelectionAction.setEnabled(True)
252 self.RemoveFromGroupAction.setEnabled(self.group != u'All')
253 self.AddToGroupAction.setEnabled(True) #self.group != u'All')
256 self.RemoveNameAction.setEnabled(True)
257 self.NewGroupFromSelectionAction.setEnabled(True)
258 self.RemoveFromGroupAction.setEnabled(self.group != u'All')
259 self.AddToGroupAction.setEnabled(True) #self.group != u'All')
262 self.updateDetailsFrame()
265 def updateNameList(self):
266 self.updating_name = True
269 headerItem = QTableWidgetItem()
270 headerItem.setText(self.__tr("Name"))
271 self.NameTableWidget.clear()
272 self.NameTableWidget.setColumnCount(1)
273 self.NameTableWidget.setHorizontalHeaderItem(0,headerItem)
274 self.NameTableWidget.setColumnWidth(0, self.NameTableWidget.width())
276 names = self.db.group_members(self.group)
277 filtered_names = [n for n in names if not n.startswith('__')]
278 filtered_names.sort()
279 self.NameTableWidget.setRowCount(len(filtered_names))
281 for j, n in enumerate(filtered_names):
282 i = QTableWidgetItem(QString(n))
283 i.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
284 self.NameTableWidget.setItem(j, 0, i)
293 self.updating_name = False
301 else: # no names, disable name frame and name actions
303 self.RemoveNameAction.setEnabled(False)
304 self.NewGroupFromSelectionAction.setEnabled(False)
305 self.RemoveFromGroupAction.setEnabled(False)
306 self.AddToGroupAction.setEnabled(False)
307 self.updateDetailsFrame()
310 def selectByName(self, name):
311 rows = self.NameTableWidget.rowCount()
312 for r in range(rows):
313 i = self.NameTableWidget.item(r, 0)
314 i.setSelected(name == unicode(i.text()))
317 def updateDetailsFrame(self):
318 if self.name is None:
319 self.NameFrame.setEnabled(False)
320 self.NameLineEdit.setText(QString())
321 self.FaxNumberLineEdit.setText(QString())
322 self.NotesTextEdit.setText(QString())
325 self.NameFrame.setEnabled(True)
326 data = self.db.get(self.name)
327 self.NameLineEdit.setText(self.name)
328 self.FaxNumberLineEdit.setText(data['fax'])
329 self.NotesTextEdit.setText(data['notes'])
332 def NameLineEdit_editingFinished(self):
333 if self.name is not None:
334 new_name = unicode(self.NameLineEdit.text())
335 if new_name != self.name:
336 if QMessageBox.question(self, self.__tr("Rename?"), self.__tr("Rename '%1' to '%2'?").arg(self.name).arg(new_name), \
337 QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
339 self.db.rename(self.name, new_name)
340 log.debug("Rename %s to %s" % (self.name, new_name))
341 self.emit(SIGNAL("databaseChanged"), FAB_NAME_RENAME, self.name, new_name)
342 self.updateNameList()
344 self.NameLineEdit.setText(self.name)
347 def FaxNumberLineEdit_editingFinished(self):
348 if self.name is not None:
349 self.db.set_key_value(self.name, 'fax', unicode(self.FaxNumberLineEdit.text()))
350 self.emit(SIGNAL("databaseChanged"), FAB_NAME_DETAILS_CHANGED, self.name)
353 def NotesTextEdit_textChanged(self):
354 if self.name is not None:
355 self.db.set_key_value(self.name, 'notes', unicode(self.NotesTextEdit.document().toPlainText()))
358 def NotesTextEdit_editingFinished(self):
359 if self.name is not None:
360 self.emit(SIGNAL("databaseChanged"), FAB_NAME_DETAILS_CHANGED, self.name)
363 def NewGroupAction_triggered(self):
365 g, ok = QInputDialog.getText(self, self.__tr("Enter New Group Name"), self.__tr("Name for New Group:"))
369 FailureUI(self, self.__tr("<b>Sorry, the group name cannot be 'All'.</b><p>Please choose a different name."))
373 self.db.set('__' + utils.gen_random_uuid(), '', '', '', '', [u'All', g], '')
375 log.debug("New empty group %s" % self.group)
376 self.emit(SIGNAL("databaseChanged"), FAB_GROUP_ADD, self.group)
377 self.updateGroupList()
380 def NewGroupFromSelectionAction_triggered(self):
381 selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
384 g, ok = QInputDialog.getText(self, self.__tr("Enter New Group Name"), self.__tr("Name for New Group:"))
387 groups = self.db.get_all_groups()
390 FailureUI(self, self.__tr("<b>Sorry, the group name cannot be the same as an existing group (or 'All').</b><p>Please choose a different name."))
394 self.db.update_groups(g, selected_names)
396 log.debug("New group %s with names %s" % (self.group, ','.join(selected_names)))
397 self.emit(SIGNAL("databaseChanged"), FAB_GROUP_ADD, self.group)
398 self.updateGroupList()
401 def RenameGroupAction_triggered(self):
402 selected_items = self.GroupTableWidget.selectedItems()
404 old_group = unicode(selected_items[0].text())
406 new_group, ok = QInputDialog.getText(self, self.__tr("Rename Group"), self.__tr("New Name for Group '%1':").arg(old_group))
407 new_group = unicode(new_group)
408 groups = self.db.get_all_groups()
410 if new_group in groups:
411 FailureUI(self, self.__tr("<b>Sorry, the group name cannot be the same as an existing group (or 'All').</b><p>Please choose a different name."))
415 self.db.rename_group(old_group, new_group)
416 log.debug("Rename group %s to %s" % (old_group, new_group))
417 self.emit(SIGNAL("databaseChanged"), FAB_GROUP_RENAME, old_group, new_group)
418 self.group = new_group
419 self.updateGroupList()
422 def RemoveGroupAction_triggered(self):
423 self.db.delete_group(self.group)
424 log.debug("Remove group %s" % self.group)
425 self.emit(SIGNAL("databaseChanged"), FAB_GROUP_REMOVE, self.group)
427 self.updateGroupList()
430 def NewNameAction_triggered(self):
432 t, ok = QInputDialog.getText(self, self.__tr("Enter New Name"), self.__tr("New Name:"))
438 def addName(self, name, fax=''):
439 if self.group == u'All':
442 g = [u'All', self.group]
444 self.db.set(name, '', '', '', fax, g, '')
446 log.debug("New name %s" % self.name)
447 self.emit(SIGNAL("databaseChanged"), FAB_NAME_ADD, self.name)
448 self.updateNameList()
451 def RemoveNameAction_triggered(self):
452 selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
454 for n in selected_names:
456 log.debug("Removing name %s" % n)
457 self.emit(SIGNAL("databaseChanged"), FAB_NAME_REMOVE, n)
460 self.updateNameList()
463 def RemoveFromGroupAction_triggered(self):
464 selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
466 log.debug("%s leaving group %s" % (','.join(selected_names), self.group))
467 self.db.remove_from_group(self.group, selected_names)
468 self.emit(SIGNAL("databaseChanged"), FAB_GROUP_MEMBERSHIP_CHANGED, self.group)
470 self.updateGroupList()
473 def GroupTableWidget_namesAddedToGroup(self, row, items): # drag n' drop handler
474 self.group = unicode(self.GroupTableWidget.item(row, 0).text())
475 self.db.add_to_group(self.group, items)
476 log.debug("Adding %s to group %s" % (','.join(items), self.group))
477 self.emit(SIGNAL("databaseChanged"), FAB_GROUP_MEMBERSHIP_CHANGED, self.group)
478 self.updateGroupList()
481 def AddToGroupAction_triggered(self):
482 selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
485 all_groups = self.db.get_all_groups()
488 all_groups = [g for g in all_groups if g != u'All']
491 dlg = JoinDialog(self, all_groups)
493 if dlg.exec_() == QDialog.Accepted:
496 self.db.add_to_group(group, selected_names)
498 log.debug("Adding %s to group %s" % (','.join(selected_names), self.group))
499 self.emit(SIGNAL("databaseChanged"), FAB_GROUP_MEMBERSHIP_CHANGED, self.group)
500 self.updateGroupList()
503 FailureUI(self, self.__tr("<b>There are no groups to join.</b><p>Use <i>New Group from Selection</i> to create a new group using these name(s)."))
506 def ImportAction_triggered(self):
507 result = unicode(QFileDialog.getOpenFileName(self,
508 self.__tr("Import fax addresses from LDIF or vCard"),
509 #user_conf.workingDirectory(),
510 self.user_settings.working_dir,
511 "vCard (*.vcf);;LDIF (*.ldif *.ldi)"))
514 working_directory = unicode(os.path.dirname(result))
515 log.debug("result: %s" % result)
516 #user_conf.setWorkingDirectory(working_directory)
517 self.user_settings.working_dir = working_directory
518 self.user_settings.save()
521 if result.endswith('.vcf'):
522 ok, error_str = self.db.import_vcard(result)
524 ok, error_str = self.db.import_ldif(result)
527 FailureUI(self, error_str)
533 def __tr(self,s,c = None):
534 return qApp.translate("FABWindow",s,c)
539 class JoinDialog(QDialog):
540 def __init__(self, parent, groups):
541 QDialog.__init__(self, parent)
546 def setupUi(self, groups):
547 self.setObjectName("Dialog")
548 self.resize(QSize(QRect(0,0,271,107).size()).expandedTo(self.minimumSizeHint()))
550 self.gridlayout = QGridLayout(self)
551 self.gridlayout.setObjectName("gridlayout")
553 self.hboxlayout = QHBoxLayout()
554 self.hboxlayout.setObjectName("hboxlayout")
556 self.label = QLabel(self)
557 self.label.setObjectName("label")
558 self.hboxlayout.addWidget(self.label)
560 self.GroupJoinComboBox = QComboBox(self)
562 sizePolicy = QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Fixed)
563 sizePolicy.setHorizontalStretch(0)
564 sizePolicy.setVerticalStretch(0)
565 sizePolicy.setHeightForWidth(self.GroupJoinComboBox.sizePolicy().hasHeightForWidth())
566 self.GroupJoinComboBox.setSizePolicy(sizePolicy)
567 self.GroupJoinComboBox.setObjectName("comboBox")
568 self.hboxlayout.addWidget(self.GroupJoinComboBox)
569 self.gridlayout.addLayout(self.hboxlayout,0,0,1,3)
571 spacerItem = QSpacerItem(20,40,QSizePolicy.Minimum,QSizePolicy.Expanding)
572 self.gridlayout.addItem(spacerItem,1,0,1,1)
574 spacerItem1 = QSpacerItem(231,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
575 self.gridlayout.addItem(spacerItem1,2,0,1,1)
577 self.JoinButton = QPushButton(self)
578 self.JoinButton.setObjectName("pushButton_2")
579 self.gridlayout.addWidget(self.JoinButton,2,1,1,1)
581 self.CancelButton = QPushButton(self)
582 self.CancelButton.setObjectName("pushButton")
583 self.gridlayout.addWidget(self.CancelButton,2,2,1,1)
585 self.connect(self.GroupJoinComboBox, SIGNAL("currentIndexChanged(int)"),
586 self.GroupJoinComboBox_currentIndexChanged)
588 for i, g in enumerate(groups):
591 self.GroupJoinComboBox.insertItem(i, g)
593 self.connect(self.JoinButton, SIGNAL("clicked()"), self.accept)
594 self.connect(self.CancelButton, SIGNAL("clicked()"), self.reject)
599 def GroupJoinComboBox_currentIndexChanged(self, i):
600 self.group = unicode(self.GroupJoinComboBox.currentText())
603 def retranslateUi(self):
604 self.setWindowTitle(QApplication.translate("Dialog", "Join Group", None, QApplication.UnicodeUTF8))
605 self.label.setText(QApplication.translate("Dialog", "Group to Join:", None, QApplication.UnicodeUTF8))
606 self.JoinButton.setText(QApplication.translate("Dialog", "Join", None, QApplication.UnicodeUTF8))
607 self.CancelButton.setText(QApplication.translate("Dialog", "Cancel", None, QApplication.UnicodeUTF8))