def __init__(self): if self: BlinkLogger().log_debug('Starting History Viewer') NSBundle.loadNibNamed_owner_("HistoryViewer", self) self.all_contacts = BlinkHistoryViewerContact('Any Address', name='All Contacts') self.bonjour_contact = BlinkHistoryViewerContact('bonjour.local', name='Bonjour Neighbours', icon=NSImage.imageNamed_("NSBonjour")) self.notification_center = NotificationCenter() self.notification_center.add_observer(self, name='ChatViewControllerDidDisplayMessage') self.notification_center.add_observer(self, name='AudioCallLoggedToHistory') self.notification_center.add_observer(self, name='BlinkContactsHaveChanged') self.notification_center.add_observer(self, name='BlinkTableViewSelectionChaged') self.notification_center.add_observer(self, name='BlinkConferenceContactPresenceHasChanged') self.notification_center.add_observer(self, name='BlinkShouldTerminate') self.searchText.cell().setSendsSearchStringImmediately_(True) self.searchText.cell().setPlaceholderString_(NSLocalizedString("Type text and press Enter", "Placeholder text")) self.chatViewController.setContentFile_(NSBundle.mainBundle().pathForResource_ofType_("ChatView", "html")) self.chatViewController.setHandleScrolling_(False) self.entriesView.setShouldCloseWithWindow_(False) for c in ('remote_uri', 'local_uri', 'date', 'type'): col = self.indexTable.tableColumnWithIdentifier_(c) descriptor = NSSortDescriptor.alloc().initWithKey_ascending_(c, True) col.setSortDescriptorPrototype_(descriptor) self.chat_history = ChatHistory() self.session_history = SessionHistory() self.setPeriod(1) self.selectedTableView = self.contactTable
def __init__(self): if self: BlinkLogger().log_debug('Starting History Viewer') NSBundle.loadNibNamed_owner_("HistoryViewer", self) self.all_contacts = BlinkHistoryViewerContact('Any Address', name=u'All Contacts') self.bonjour_contact = BlinkHistoryViewerContact('bonjour', name=u'Bonjour Neighbours', icon=NSImage.imageNamed_("NSBonjour")) self.notification_center = NotificationCenter() self.notification_center.add_observer(self, name='ChatViewControllerDidDisplayMessage') self.notification_center.add_observer(self, name='AudioCallLoggedToHistory') self.notification_center.add_observer(self, name='BlinkContactsHaveChanged') self.notification_center.add_observer(self, name='BlinkTableViewSelectionChaged') self.notification_center.add_observer(self, name='BlinkConferenceContactPresenceHasChanged') self.notification_center.add_observer(self, name='BlinkShouldTerminate') self.searchText.cell().setSendsSearchStringImmediately_(True) self.searchText.cell().setPlaceholderString_(NSLocalizedString("Type text and press Enter", "Placeholder text")) self.chatViewController.setContentFile_(NSBundle.mainBundle().pathForResource_ofType_("ChatView", "html")) self.chatViewController.setHandleScrolling_(False) self.entriesView.setShouldCloseWithWindow_(False) for c in ('remote_uri', 'local_uri', 'date', 'type'): col = self.indexTable.tableColumnWithIdentifier_(c) descriptor = NSSortDescriptor.alloc().initWithKey_ascending_(c, True) col.setSortDescriptorPrototype_(descriptor) self.chat_history = ChatHistory() self.session_history = SessionHistory() self.setPeriod(1) self.selectedTableView = self.contactTable
class HistoryViewer(NSWindowController): implements(IObserver) chatViewController = objc.IBOutlet() indexTable = objc.IBOutlet() contactTable = objc.IBOutlet() toolbar = objc.IBOutlet() entriesView = objc.IBOutlet() period = objc.IBOutlet() searchText = objc.IBOutlet() searchMedia = objc.IBOutlet() searchContactBox = objc.IBOutlet() paginationButton = objc.IBOutlet() foundMessagesLabel = objc.IBOutlet() queryDatabaseLabel = objc.IBOutlet() busyIndicator = objc.IBOutlet() contactMenu = objc.IBOutlet() # viewer sections contacts = [] dayly_entries = NSMutableArray.array() messages = [] # database handler history = None # search filters start = 0 search_text = None search_uris = None search_local = None search_media = None after_date = None before_date = None refresh_contacts_counter = 1 daily_order_fields = {'date': 'DESC', 'local_uri': 'ASC', 'remote_uri': 'ASC'} media_type_array = {0: None, 1: 'audio', 2: 'chat', 3: 'sms', 4: 'file-transfer', 5: 'audio-recording', 6: 'availability', 7: 'voicemail', 8: 'missed-call'} period_array = {0: None, 1: datetime.datetime.now()-datetime.timedelta(days=1), 2: datetime.datetime.now()-datetime.timedelta(days=7), 3: datetime.datetime.now()-datetime.timedelta(days=31), 4: datetime.datetime.now()-datetime.timedelta(days=90), 5: datetime.datetime.now()-datetime.timedelta(days=180), 6: datetime.datetime.now()-datetime.timedelta(days=365), -1: datetime.datetime.now()-datetime.timedelta(days=1), -2: datetime.datetime.now()-datetime.timedelta(days=7), -3: datetime.datetime.now()-datetime.timedelta(days=31), -4: datetime.datetime.now()-datetime.timedelta(days=90), -5: datetime.datetime.now()-datetime.timedelta(days=180), -6: datetime.datetime.now()-datetime.timedelta(days=365) } def format_media_type(self, media_type): if media_type == 'sms': return NSLocalizedString("Instant Messages", "Column title") elif media_type == 'chat': return NSLocalizedString("Chat Sessions", "Column title") elif media_type == 'audio': return NSLocalizedString("Audio Calls", "Column title") elif media_type == 'file-transfer': return NSLocalizedString("File Transfers", "Column title") else: return media_type.title() def __new__(cls, *args, **kwargs): return cls.alloc().init() def __init__(self): if self: BlinkLogger().log_debug('Starting History Viewer') NSBundle.loadNibNamed_owner_("HistoryViewer", self) self.all_contacts = BlinkHistoryViewerContact('Any Address', name=u'All Contacts') self.bonjour_contact = BlinkHistoryViewerContact('bonjour', name=u'Bonjour Neighbours', icon=NSImage.imageNamed_("NSBonjour")) self.notification_center = NotificationCenter() self.notification_center.add_observer(self, name='ChatViewControllerDidDisplayMessage') self.notification_center.add_observer(self, name='AudioCallLoggedToHistory') self.notification_center.add_observer(self, name='BlinkContactsHaveChanged') self.notification_center.add_observer(self, name='BlinkTableViewSelectionChaged') self.notification_center.add_observer(self, name='BlinkConferenceContactPresenceHasChanged') self.notification_center.add_observer(self, name='BlinkShouldTerminate') self.searchText.cell().setSendsSearchStringImmediately_(True) self.searchText.cell().setPlaceholderString_(NSLocalizedString("Type text and press Enter", "Placeholder text")) self.chatViewController.setContentFile_(NSBundle.mainBundle().pathForResource_ofType_("ChatView", "html")) self.chatViewController.setHandleScrolling_(False) self.entriesView.setShouldCloseWithWindow_(False) for c in ('remote_uri', 'local_uri', 'date', 'type'): col = self.indexTable.tableColumnWithIdentifier_(c) descriptor = NSSortDescriptor.alloc().initWithKey_ascending_(c, True) col.setSortDescriptorPrototype_(descriptor) self.chat_history = ChatHistory() self.session_history = SessionHistory() self.setPeriod(1) self.selectedTableView = self.contactTable def setPeriod(self, days): if days <= -365: tag = -6 elif days <= -180: tag = -5 elif days <= -90: tag = -4 elif days <= -31: tag = -3 elif days <= -7: tag = -2 elif days <= -1: tag = -1 elif days <= 1: tag = 1 elif days <= 7: tag = 2 elif days <= 31: tag = 3 elif days <= 90: tag = 4 elif days <= 180: tag = 5 elif days <= 365: tag = 6 else: tag = 0 if tag == 0: self.before_date = None self.after_date = None elif tag < 0: try: date = self.period_array[tag] except KeyError: date = None self.before_date = date self.after_date = None else: try: date = self.period_array[tag] except KeyError: date = None self.after_date = date self.before_date = None self.period.selectItemWithTag_(tag) def awakeFromNib(self): NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, "contactSelectionChanged:", NSTableViewSelectionDidChangeNotification, self.contactTable) timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(0.3, self, "refreshContactsTimer:", None, True) NSRunLoop.currentRunLoop().addTimer_forMode_(timer, NSModalPanelRunLoopMode) NSRunLoop.currentRunLoop().addTimer_forMode_(timer, NSDefaultRunLoopMode) self.contactTable.setDoubleAction_("doubleClick:") def close_(self, sender): self.window().close() @allocate_autorelease_pool @run_in_gui_thread def refreshViewer(self): self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() @run_in_green_thread def delete_messages(self, local_uri=None, remote_uri=None, date=None, after_date=None, before_date=None, media_type=None): self.chat_history.delete_messages(local_uri=local_uri, remote_uri=remote_uri, date=date, after_date=after_date, before_date=before_date, media_type=media_type) self.session_history.delete_entries(local_uri=local_uri, remote_uri=remote_uri, after_date=after_date, before_date=before_date) self.search_text = None self.search_uris = None self.search_local = None self.refreshViewer() @run_in_green_thread def refreshContacts(self): if self.chat_history: self.updateBusyIndicator(True) remote_uri = self.search_uris if self.search_uris else None media_type = self.search_media if self.search_media else None search_text = self.search_text if self.search_text else None after_date = self.after_date if self.after_date else None before_date = self.before_date if self.before_date else None results = self.chat_history.get_contacts(remote_uri=remote_uri, media_type=media_type, search_text=search_text, after_date=after_date, before_date=before_date) self.renderContacts(results) self.updateBusyIndicator(False) @allocate_autorelease_pool @run_in_gui_thread def renderContacts(self, results): index = 0 found_uris = [] for item in self.contacts: item.destroy() getFirstContactMatchingURI = NSApp.delegate().contactsWindowController.getFirstContactMatchingURI self.contacts = [self.all_contacts, self.bonjour_contact] if self.search_uris: for uri in self.search_uris: found_contact = getFirstContactMatchingURI(uri, exact_match=True) if found_contact: contact_exist = False for contact_uri in found_contact.uris: if contact_uri.uri in found_uris: contact_exist = True break if contact_exist: continue contact = BlinkHistoryViewerContact(found_contact.uri, name=found_contact.name, icon=found_contact.icon) for contact_uri in found_contact.uris: found_uris.append(contact_uri.uri) self.contacts.append(contact) if isinstance(found_contact, BlinkPresenceContact): contact.setPresenceContact_(found_contact) else: if uri in found_uris: continue found_uris.append(uri) contact = BlinkHistoryViewerContact(unicode(uri), name=unicode(uri)) try: index = self.contacts.index(contact) except ValueError: pass if results: for row in results: found_contact = getFirstContactMatchingURI(row[0], exact_match=True) if found_contact: contact_exist = False for contact_uri in found_contact.uris: if contact_uri.uri in found_uris: contact_exist = True break if contact_exist: continue contact = BlinkHistoryViewerContact(found_contact.uri, name=found_contact.name, icon=found_contact.icon, presence_contact=found_contact if isinstance(found_contact, BlinkPresenceContact) else None) for contact_uri in found_contact.uris: found_uris.append(contact_uri.uri) else: if row[0] in found_uris: continue found_uris.append(row[0]) contact = BlinkHistoryViewerContact(unicode(row[0]), name=unicode(row[0])) self.contacts.append(contact) self.contactTable.reloadData() self.contactTable.selectRowIndexes_byExtendingSelection_(NSIndexSet.indexSetWithIndex_(index), False) self.contactTable.scrollRowToVisible_(index) self.updateContactsColumnHeader() @run_in_green_thread def refreshDailyEntries(self, order_text=None): if self.chat_history: self.resetDailyEntries() self.updateBusyIndicator(True) search_text = self.search_text if self.search_text else None remote_uri = self.search_uris if self.search_uris else None local_uri = self.search_local if self.search_local else None media_type = self.search_media if self.search_media else None after_date = self.after_date if self.after_date else None before_date = self.before_date if self.before_date else None results = self.chat_history.get_daily_entries(local_uri=local_uri, remote_uri=remote_uri, media_type=media_type, search_text=search_text, order_text=order_text, after_date=after_date, before_date=before_date) self.renderDailyEntries(results) self.updateBusyIndicator(False) @allocate_autorelease_pool @run_in_gui_thread def resetDailyEntries(self): self.dayly_entries = NSMutableArray.array() self.indexTable.reloadData() @allocate_autorelease_pool @run_in_gui_thread def renderDailyEntries(self, results): getFirstContactMatchingURI = NSApp.delegate().contactsWindowController.getFirstContactMatchingURI self.dayly_entries = NSMutableArray.array() for result in results: contact = getFirstContactMatchingURI(result[2], exact_match=True) if contact: remote_uri = '%s <%s>' % (contact.name, result[2]) else: remote_uri = result[2] entry = NSDictionary.dictionaryWithObjectsAndKeys_(result[1], "local_uri", remote_uri, "remote_uri", result[2], "remote_uri_sql", result[0], 'date', result[3], 'type') self.dayly_entries.addObject_(entry) self.dayly_entries.sortUsingDescriptors_(self.indexTable.sortDescriptors()) self.indexTable.reloadData() if self.search_uris and not self.dayly_entries: self.contactTable.deselectAll_(True) @run_in_green_thread @allocate_autorelease_pool def refreshMessages(self, count=SQL_LIMIT, remote_uri=None, local_uri=None, media_type=None, date=None, after_date=None, before_date=None): self.updateBusyIndicator(True) if self.chat_history: search_text = self.search_text if self.search_text else None if not remote_uri: remote_uri = self.search_uris if self.search_uris else None if not local_uri: local_uri = self.search_local if self.search_local else None if not media_type: media_type = self.search_media if self.search_media else None if not after_date: after_date = self.after_date if self.after_date else None if not before_date: before_date = self.before_date if self.before_date else None results = self.chat_history.get_messages(count=count, local_uri=local_uri, remote_uri=remote_uri, media_type=media_type, date=date, search_text=search_text, after_date=after_date, before_date=before_date) # cache message for pagination self.messages=[] for e in reversed(results): self.messages.append(e) # reset pagination to the last page start = len(self.messages) - len(self.messages)%MAX_MESSAGES_PER_PAGE if len(self.messages) > MAX_MESSAGES_PER_PAGE else 0 self.renderMessages(start) self.updateBusyIndicator(False) @allocate_autorelease_pool @run_in_gui_thread def renderMessages(self, start=None): self.chatViewController.clear() self.chatViewController.resetRenderedMessages() start_from = start or self.start end = start_from + MAX_MESSAGES_PER_PAGE if start_from + MAX_MESSAGES_PER_PAGE < len(self.messages) else len(self.messages) for row in self.messages[start_from:end]: self.renderMessage(row) self.paginationButton.setEnabled_forSegment_(True if len(self.messages)>MAX_MESSAGES_PER_PAGE and start_from > MAX_MESSAGES_PER_PAGE else False, 0) self.paginationButton.setEnabled_forSegment_(True if start_from else False, 1) self.paginationButton.setEnabled_forSegment_(True if start_from+MAX_MESSAGES_PER_PAGE+1 < len(self.messages) else False, 2) self.paginationButton.setEnabled_forSegment_(True if len(self.messages)>MAX_MESSAGES_PER_PAGE and len(self.messages) - start_from > 2*MAX_MESSAGES_PER_PAGE else False, 3) text = u'No entry found' if len(self.messages): if len(self.messages) == 1: text = NSLocalizedString("Displaying 1 entry", "Text label") elif MAX_MESSAGES_PER_PAGE > len(self.messages): text = NSLocalizedString("Displaying %d entries" % end, "Text label") else: l = len(self.messages) text = NSLocalizedString("Displaying %d to "% start_from + 1, "Text label") + end + NSLocalizedString("out of %d entries" % l, "Text label") self.foundMessagesLabel.setStringValue_(text) @allocate_autorelease_pool @run_in_gui_thread def renderMessage(self, message): if message.direction == 'outgoing': icon = NSApp.delegate().contactsWindowController.iconPathForSelf() else: sender_uri = sipuri_components_from_string(message.cpim_from)[0] # TODO: How to render the icons from Address Book? Especially in sandbox mode we do not have access to other folders icon = NSApp.delegate().contactsWindowController.iconPathForURI(sender_uri) try: timestamp=ISOTimestamp(message.cpim_timestamp) except Exception: pass else: is_html = False if message.content_type == 'text' else True private = True if message.private == "1" else False self.chatViewController.showMessage(message.sip_callid, message.msgid, message.direction, message.cpim_from, icon, message.body, timestamp, is_private=private, recipient=message.cpim_to, state=message.status, is_html=is_html, history_entry=True, media_type=message.media_type, encryption=message.encryption if message.media_type == 'chat' else None) @objc.IBAction def paginateResults_(self, sender): if sender.selectedSegment() == 0: self.start = 0 elif sender.selectedSegment() == 1: next_start = self.start - MAX_MESSAGES_PER_PAGE self.start = next_start if next_start >= 0 else self.start elif sender.selectedSegment() == 2: next_start = self.start + MAX_MESSAGES_PER_PAGE self.start = next_start if next_start < len(self.messages)-1 else self.start elif sender.selectedSegment() == 3: self.start = len(self.messages) - len(self.messages)%MAX_MESSAGES_PER_PAGE if len(self.messages) > MAX_MESSAGES_PER_PAGE else 0 self.renderMessages() def tableView_deleteRow_(self, table, row): pass def tableView_sortDescriptorsDidChange_(self, table, odescr): self.dayly_entries.sortUsingDescriptors_(self.indexTable.sortDescriptors()) self.indexTable.reloadData() @objc.IBAction def printDocument_(self, sender): printInfo = NSPrintInfo.sharedPrintInfo() printInfo.setTopMargin_(30) printInfo.setBottomMargin_(30) printInfo.setLeftMargin_(10) printInfo.setRightMargin_(10) printInfo.setOrientation_(NSPortraitOrientation) printInfo.setHorizontallyCentered_(True) printInfo.setVerticallyCentered_(False) printInfo.setHorizontalPagination_(NSFitPagination) printInfo.setVerticalPagination_(NSFitPagination) NSPrintInfo.setSharedPrintInfo_(printInfo) # print the content of the web view self.entriesView.mainFrame().frameView().documentView().print_(self) @objc.IBAction def search_(self, sender): if self.chat_history: self.search_text = unicode(sender.stringValue()).lower() self.refreshContacts() row = self.indexTable.selectedRow() if row > 0: self.refreshDailyEntries() self.refreshMessages(local_uri=self.dayly_entries[row].objectForKey_("local_uri"), date=self.dayly_entries[row].objectForKey_("date"), media_type=self.dayly_entries[row].objectForKey_("type")) else: row = self.contactTable.selectedRow() if row > 0: self.refreshMessages() self.refreshDailyEntries() else: self.refreshDailyEntries() self.refreshMessages() @objc.IBAction def searchContacts_(self, sender): text = unicode(self.searchContactBox.stringValue().strip()) contacts = [contact for contact in self.contacts[2:] if text in contact] if text else self.contacts[2:] self.contacts = [self.all_contacts, self.bonjour_contact] + contacts self.contactTable.reloadData() self.contactTable.selectRowIndexes_byExtendingSelection_(NSIndexSet.indexSetWithIndex_(0), False) self.contactTable.scrollRowToVisible_(0) self.updateContactsColumnHeader() if not text: self.refreshContacts() def updateContactsColumnHeader(self): found_contacts = len(self.contacts)-2 if found_contacts == 1: title = NSLocalizedString("1 Contact Found", "Column title") elif found_contacts > 1: title = NSLocalizedString("%d Contacts Found" % found_contacts, "Column title") else: title = NSLocalizedString("Contacts", "Column title") self.contactTable.tableColumnWithIdentifier_('contacts').headerCell().setStringValue_(title) def tableViewSelectionDidChange_(self, notification): if self.chat_history: if notification.object() == self.contactTable: row = self.contactTable.selectedRow() if row < 0: return elif row == 0: self.search_local = None self.search_uris = None self.searchContactBox.setStringValue_('') self.refreshContacts() elif row == 1: self.search_local = 'bonjour' self.search_uris = None elif row > 1: self.search_local = None if self.contacts[row].presence_contact is not None: self.search_uris = list(unicode(contact_uri.uri) for contact_uri in self.contacts[row].presence_contact.uris) self.chatViewController.expandSmileys = not self.contacts[row].presence_contact.contact.disable_smileys self.chatViewController.toggleSmileys(self.chatViewController.expandSmileys) try: item = (item for item in self.toolbar.visibleItems() if item.itemIdentifier() == 'smileys').next() except StopIteration: pass else: item.setImage_(NSImage.imageNamed_("smiley_on" if self.chatViewController.expandSmileys else "smiley_off")) else: self.search_uris = (self.contacts[row].uri,) self.refreshDailyEntries() self.refreshMessages() else: row = self.indexTable.selectedRow() if row >= 0: self.refreshMessages(remote_uri=(self.dayly_entries[row].objectForKey_("remote_uri_sql"),), local_uri=self.dayly_entries[row].objectForKey_("local_uri"), date=self.dayly_entries[row].objectForKey_("date"), media_type=self.dayly_entries[row].objectForKey_("type")) def numberOfRowsInTableView_(self, table): if table == self.indexTable: return self.dayly_entries.count() elif table == self.contactTable: return len(self.contacts) return 0 def tableView_objectValueForTableColumn_row_(self, table, column, row): if table == self.indexTable: ident = column.identifier() if ident == 'type': return self.format_media_type(self.dayly_entries[row].objectForKey_(ident)) try: return unicode(self.dayly_entries[row].objectForKey_(ident)) except IndexError: return None elif table == self.contactTable: try: if type(self.contacts[row]) in (str, unicode): return self.contacts[row] else: return self.contacts[row].name except IndexError: return None return None def tableView_willDisplayCell_forTableColumn_row_(self, table, cell, tableColumn, row): if table == self.contactTable: try: if row < len(self.contacts): if type(self.contacts[row]) in (str, unicode): cell.setContact_(None) else: cell.setContact_(self.contacts[row]) except: pass def showWindow_(self, sender): self.window().makeKeyAndOrderFront_(None) @run_in_gui_thread def filterByURIs(self, uris=(), media_type=None): self.search_text = None self.search_local = None if media_type != self.search_media: for tag in self.media_type_array.keys(): if self.media_type_array[tag] == media_type: self.searchMedia.selectItemAtIndex_(tag) self.search_media = media_type self.search_uris = uris self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() @objc.IBAction def filterByMediaChanged_(self, sender): tag = sender.selectedItem().tag() self.search_media = self.media_type_array[tag] self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() @objc.IBAction def filterByPeriodChanged_(self, sender): tag = sender.selectedItem().tag() if tag == 0: self.before_date = None self.after_date = None elif tag < 0: try: date = self.period_array[tag] except KeyError: date = None self.before_date = date self.after_date = None else: try: date = self.period_array[tag] except KeyError: date = None self.after_date = date self.before_date = None self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() def validateToolbarItem_(self, item): if item.itemIdentifier() == NSToolbarPrintItemIdentifier and not self.messages: return False return True def toolbarWillAddItem_(self, notification): item = notification.userInfo()["item"] if item.itemIdentifier() == NSToolbarPrintItemIdentifier: item.setToolTip_("Print Current Entries") item.setTarget_(self) item.setAutovalidates_(True) @objc.IBAction def userClickedToolbarItem_(self, sender): if sender.itemIdentifier() == 'smileys': self.chatViewController.expandSmileys = not self.chatViewController.expandSmileys sender.setImage_(NSImage.imageNamed_("smiley_on" if self.chatViewController.expandSmileys else "smiley_off")) self.chatViewController.toggleSmileys(self.chatViewController.expandSmileys) row = self.contactTable.selectedRow() if row and row > 1 and self.contacts[row].presence_contact is not None: self.contacts[row].presence_contact.contact.disable_smileys = not self.contacts[row].presence_contact.contact.disable_smileys self.contacts[row].presence_contact.contact.save() elif sender.itemIdentifier() == 'delete': if self.selectedTableView == self.contactTable: try: row = self.contactTable.selectedRow() self.showDeleteConfirmationDialog(row) except IndexError: pass elif self.selectedTableView == self.indexTable: try: row = self.indexTable.selectedRow() local_uri = self.dayly_entries[row].objectForKey_("local_uri") remote_uri = self.dayly_entries[row].objectForKey_("remote_uri") remote_uri_sql = self.dayly_entries[row].objectForKey_("remote_uri_sql") date = self.dayly_entries[row].objectForKey_("date") media_type = self.dayly_entries[row].objectForKey_("type") label = NSLocalizedString("Please confirm the deletion of %s history entries" % media_type, "Text label") + NSLocalizedString(" from %s" % remote_uri, "SIP Address label") + NSLocalizedString(" on %s. " % date, "Date label") + NSLocalizedString("This operation cannot be undone. ", "Text label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(local_uri=local_uri, remote_uri=remote_uri_sql, media_type=media_type, date=date) except IndexError: pass def showDeleteConfirmationDialog(self, row): media_print = self.search_media or NSLocalizedString("all", "Text label") tag = self.period.selectedItem().tag() period = '%s %s' % (NSLocalizedString(" newer than", "Date label") if tag < 4 else NSLocalizedString(" older than", "Date label"), self.period_array[tag].strftime("%Y-%m-%d")) if tag else '' if row == 0: label = NSLocalizedString("Please confirm the deletion of %s history entries" % media_print, "Text label") + period + ". "+ NSLocalizedString("This operation cannot be undone. ", "Text label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(media_type=self.search_media, after_date=self.after_date, before_date=self.before_date) elif row == 1: remote_uri=self.contacts[row].uri label = NSLocalizedString("Please confirm the deletion of %s Bonjour history entries" % media_print, "Text label") + period + ". "+ NSLocalizedString("This operation cannot be undone. ", "Text label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(local_uri='bonjour', media_type=self.search_media, after_date=self.after_date, before_date=self.before_date) else: contact = self.contacts[row] if contact.presence_contact is not None: remote_uri = list(unicode(contact.uri) for contact in contact.presence_contact.uris) else: remote_uri = contact.uri label = NSLocalizedString("Please confirm the deletion of %s history entries" % media_print, "Text label") + NSLocalizedString(" from", "Text label") + " " + contact.name + period + ". "+ NSLocalizedString("This operation cannot be undone. ", "Text label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(remote_uri=remote_uri, media_type=self.search_media, after_date=self.after_date, before_date=self.before_date) @allocate_autorelease_pool @run_in_gui_thread def updateBusyIndicator(self, busy=False): if busy: self.queryDatabaseLabel.setHidden_(False) self.busyIndicator.setHidden_(False) self.busyIndicator.setIndeterminate_(True) self.busyIndicator.setStyle_(NSProgressIndicatorSpinningStyle) self.busyIndicator.startAnimation_(None) else: self.busyIndicator.stopAnimation_(None) self.busyIndicator.setHidden_(True) self.queryDatabaseLabel.setHidden_(True) @allocate_autorelease_pool def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_BlinkShouldTerminate(self, notification): if self.window(): self.window().orderOut_(self) def _NH_ChatViewControllerDidDisplayMessage(self, notification): if notification.data.local_party != 'bonjour': exists = any(contact for contact in self.contacts if notification.data.remote_party == contact.uri) if not exists: self.refreshContacts() def _NH_AudioCallLoggedToHistory(self, notification): if notification.data.local_party != 'bonjour': exists = any(contact for contact in self.contacts if notification.data.remote_party == contact.uri) if not exists: self.refreshContacts() def _NH_BlinkContactsHaveChanged(self, notification): self.refresh_contacts_counter += 1 def refreshContactsTimer_(self, timer): if self.refresh_contacts_counter: self.refresh_contacts_counter = 0 self.refreshContacts() self.toolbar.validateVisibleItems() def _NH_BlinkTableViewSelectionChaged(self, notification): self.selectedTableView = notification.sender self.toolbar.validateVisibleItems() def _NH_BlinkConferenceContactPresenceHasChanged(self, notification): try: contact = (contact for contact in self.contacts[2:] if contact == notification.sender).next() except StopIteration: return else: try: idx = self.contacts.index(contact) self.contactTable.reloadDataForRowIndexes_columnIndexes_(NSIndexSet.indexSetWithIndex_(idx), NSIndexSet.indexSetWithIndex_(0)) except ValueError: pass def contactSelectionChanged_(self, notification): pass def menuWillOpen_(self, menu): if menu == self.contactMenu: self.contactMenu.itemWithTag_(2).setEnabled_(False) self.contactMenu.itemWithTag_(3).setEnabled_(False) self.contactMenu.itemWithTag_(4).setEnabled_(False) try: row = self.contactTable.selectedRow() except: return if row < 2: return try: contact = self.contacts[row] except IndexError: return contact_exists = bool(contact.presence_contact is not None) self.contactMenu.itemWithTag_(2).setEnabled_(not is_anonymous(contact.uri)) self.contactMenu.itemWithTag_(3).setEnabled_(not contact_exists and not is_anonymous(contact.uri)) self.contactMenu.itemWithTag_(4).setEnabled_(contact_exists) @objc.IBAction def doubleClick_(self, sender): row = self.contactTable.selectedRow() if row < 2: return try: contact = self.contacts[row] except IndexError: return NSApp.delegate().contactsWindowController.startSessionWithTarget(contact.uri) @objc.IBAction def userClickedContactMenu_(self, sender): row = self.contactTable.selectedRow() try: contact = self.contacts[row] except IndexError: return tag = sender.tag() if tag == 1: self.showDeleteConfirmationDialog(row) elif tag == 2: NSApp.delegate().contactsWindowController.searchBox.setStringValue_(contact.uri) NSApp.delegate().contactsWindowController.searchContacts() NSApp.delegate().contactsWindowController.window().makeFirstResponder_(NSApp.delegate().contactsWindowController.searchBox) NSApp.delegate().contactsWindowController.window().deminiaturize_(sender) NSApp.delegate().contactsWindowController.window().makeKeyWindow() elif tag == 3: NSApp.delegate().contactsWindowController.addContact(contact.uri, contact.name) elif tag == 4 and contact.presence_contact is not None: NSApp.delegate().contactsWindowController.model.editContact(contact.presence_contact) @objc.IBAction def userClickedActionsButton_(self, sender): point = sender.window().convertScreenToBase_(NSEvent.mouseLocation()) event = NSEvent.mouseEventWithType_location_modifierFlags_timestamp_windowNumber_context_eventNumber_clickCount_pressure_( NSLeftMouseUp, point, 0, NSDate.timeIntervalSinceReferenceDate(), sender.window().windowNumber(), sender.window().graphicsContext(), 0, 1, 0) NSMenu.popUpContextMenu_withEvent_forView_(self.contactMenu, event, sender)
class HistoryViewer(NSWindowController): chatViewController = objc.IBOutlet() indexTable = objc.IBOutlet() contactTable = objc.IBOutlet() toolbar = objc.IBOutlet() entriesView = objc.IBOutlet() period = objc.IBOutlet() searchText = objc.IBOutlet() searchMedia = objc.IBOutlet() searchContactBox = objc.IBOutlet() paginationButton = objc.IBOutlet() foundMessagesLabel = objc.IBOutlet() queryDatabaseLabel = objc.IBOutlet() busyIndicator = objc.IBOutlet() contactMenu = objc.IBOutlet() # viewer sections contacts = [] dayly_entries = NSMutableArray.array() messages = [] # database handler history = None # search filters start = 0 search_text = None search_uris = None search_local = None search_media = None after_date = None before_date = None refresh_contacts_counter = 1 contact_cache = {} display_name_cache = {} refresh_in_progress = False daily_order_fields = {'date': 'DESC', 'local_uri': 'ASC', 'remote_uri': 'ASC'} media_type_array = {0: None, 1: ('audio', 'video'), 2: ('chat', 'sms'), 3: 'file-transfer', 4: 'audio-recording', 5: 'availability', 6: 'voicemail', 7: 'video-recording'} period_array = {0: None, 1: datetime.datetime.now()-datetime.timedelta(days=1), 2: datetime.datetime.now()-datetime.timedelta(days=7), 3: datetime.datetime.now()-datetime.timedelta(days=31), 4: datetime.datetime.now()-datetime.timedelta(days=90), 5: datetime.datetime.now()-datetime.timedelta(days=180), 6: datetime.datetime.now()-datetime.timedelta(days=365), -1: datetime.datetime.now()-datetime.timedelta(days=1), -2: datetime.datetime.now()-datetime.timedelta(days=7), -3: datetime.datetime.now()-datetime.timedelta(days=31), -4: datetime.datetime.now()-datetime.timedelta(days=90), -5: datetime.datetime.now()-datetime.timedelta(days=180), -6: datetime.datetime.now()-datetime.timedelta(days=365) } @objc.python_method def format_media_type(self, media_type): if media_type == 'sms': media_type_formated = NSLocalizedString("Short Messages", "Label") elif media_type == 'chat': media_type_formated = NSLocalizedString("Chat Sessions", "Label") elif media_type == 'audio': media_type_formated = NSLocalizedString("Audio Calls", "Label") elif media_type == 'file-transfer': media_type_formated = NSLocalizedString("File Transfers", "Label") elif media_type == 'availability': media_type_formated = NSLocalizedString("Availability", "Label") elif media_type == 'missed-call': media_type_formated = NSLocalizedString("Missed Call", "Label") elif media_type == 'voicemail': media_type_formated = NSLocalizedString("Voicemail", "Label") else: media_type_formated = media_type.title() return media_type_formated def __new__(cls, *args, **kwargs): return cls.alloc().init() def __init__(self): if self: BlinkLogger().log_debug('Starting History Viewer') NSBundle.loadNibNamed_owner_("HistoryViewer", self) self.all_contacts = BlinkHistoryViewerContact('Any Address', name='All Contacts') self.bonjour_contact = BlinkHistoryViewerContact('bonjour.local', name='Bonjour Neighbours', icon=NSImage.imageNamed_("NSBonjour")) self.notification_center = NotificationCenter() self.notification_center.add_observer(self, name='ChatViewControllerDidDisplayMessage') self.notification_center.add_observer(self, name='AudioCallLoggedToHistory') self.notification_center.add_observer(self, name='BlinkContactsHaveChanged') self.notification_center.add_observer(self, name='BlinkTableViewSelectionChaged') self.notification_center.add_observer(self, name='BlinkConferenceContactPresenceHasChanged') self.notification_center.add_observer(self, name='BlinkShouldTerminate') self.searchText.cell().setSendsSearchStringImmediately_(True) self.searchText.cell().setPlaceholderString_(NSLocalizedString("Type text and press Enter", "Placeholder text")) self.chatViewController.setContentFile_(NSBundle.mainBundle().pathForResource_ofType_("ChatView", "html")) self.chatViewController.setHandleScrolling_(False) self.entriesView.setShouldCloseWithWindow_(False) for c in ('remote_uri', 'local_uri', 'date', 'type'): col = self.indexTable.tableColumnWithIdentifier_(c) descriptor = NSSortDescriptor.alloc().initWithKey_ascending_(c, True) col.setSortDescriptorPrototype_(descriptor) self.chat_history = ChatHistory() self.session_history = SessionHistory() self.setPeriod(1) self.selectedTableView = self.contactTable @objc.python_method def setPeriod(self, days): if days <= -365: tag = -6 elif days <= -180: tag = -5 elif days <= -90: tag = -4 elif days <= -31: tag = -3 elif days <= -7: tag = -2 elif days <= -1: tag = -1 elif days <= 1: tag = 1 elif days <= 7: tag = 2 elif days <= 31: tag = 3 elif days <= 90: tag = 4 elif days <= 180: tag = 5 elif days <= 365: tag = 6 else: tag = 0 if tag == 0: self.before_date = None self.after_date = None elif tag < 0: try: date = self.period_array[tag] except KeyError: date = None self.before_date = date self.after_date = None else: try: date = self.period_array[tag] except KeyError: date = None self.after_date = date self.before_date = None self.period.selectItemWithTag_(tag) def awakeFromNib(self): NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, "contactSelectionChanged:", NSTableViewSelectionDidChangeNotification, self.contactTable) timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(1, self, "refreshContactsTimer:", None, True) NSRunLoop.currentRunLoop().addTimer_forMode_(timer, NSModalPanelRunLoopMode) NSRunLoop.currentRunLoop().addTimer_forMode_(timer, NSDefaultRunLoopMode) self.contactTable.setDoubleAction_("doubleClick:") def close_(self, sender): self.window().close() @objc.python_method @run_in_green_thread def delete_messages(self, local_uri=None, remote_uri=None, date=None, after_date=None, before_date=None, media_type=None): block_on(self.chat_history.delete_messages(local_uri=local_uri, remote_uri=remote_uri, date=date, after_date=after_date, before_date=before_date, media_type=media_type)) block_on(self.session_history.delete_entries(local_uri=local_uri, remote_uri=remote_uri, after_date=after_date, before_date=before_date)) self.search_text = None self.search_uris = None self.search_local = None self.refreshViewer() @objc.python_method def refreshViewer(self): self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() @objc.python_method @run_in_green_thread def refreshContacts(self): if self.refresh_in_progress: return self.refresh_in_progress = True self.refresh_contacts_counter = 0 if self.chat_history: self.updateBusyIndicator(True) remote_uri = self.search_uris if self.search_uris else None media_type = self.search_media if self.search_media else None search_text = self.search_text if self.search_text else None after_date = self.after_date if self.after_date else None before_date = self.before_date if self.before_date else None results = self.chat_history.get_contacts(remote_uri=remote_uri, media_type=media_type, search_text=search_text, after_date=after_date, before_date=before_date) self.renderContacts(results) self.updateBusyIndicator(False) @objc.python_method @run_in_gui_thread def renderContacts(self, results): index = 0 found_uris = [] uris_without_display_name = [] for item in self.contacts: item.destroy() getFirstContactMatchingURI = NSApp.delegate().contactsWindowController.getFirstContactMatchingURI self.contacts = [self.all_contacts, self.bonjour_contact] if self.search_uris: for uri in self.search_uris: try: found_contact = self.contact_cache[uri] except KeyError: found_contact = getFirstContactMatchingURI(uri, exact_match=True) self.contact_cache[uri] = found_contact if found_contact: contact_exist = False for contact_uri in found_contact.uris: if contact_uri.uri in found_uris: contact_exist = True break if contact_exist: continue contact = BlinkHistoryViewerContact(found_contact.uri, name=found_contact.name, icon=found_contact.icon) for contact_uri in found_contact.uris: found_uris.append(contact_uri.uri) self.contacts.append(contact) if isinstance(found_contact, BlinkPresenceContact): contact.setPresenceContact_(found_contact) else: if uri in found_uris: continue found_uris.append(uri) contact = BlinkHistoryViewerContact(str(uri), name=str(uri)) try: index = self.contacts.index(contact) except ValueError: pass if results: for row in results: try: found_contact = self.contact_cache[row[0]] except KeyError: found_contact = getFirstContactMatchingURI(row[0], exact_match=True) self.contact_cache[row[0]] = found_contact if found_contact: contact_exist = False for contact_uri in found_contact.uris: if contact_uri.uri in found_uris: contact_exist = True break if contact_exist: continue contact = BlinkHistoryViewerContact(found_contact.uri, name=found_contact.name, icon=found_contact.icon, presence_contact=found_contact if isinstance(found_contact, BlinkPresenceContact) else None) for contact_uri in found_contact.uris: found_uris.append(contact_uri.uri) else: if row[0] in found_uris: continue found_uris.append(row[0]) try: display_name = self.display_name_cache[row[0]] except KeyError: display_name = str(row[0]) uris_without_display_name.append(row[0]) contact = BlinkHistoryViewerContact(str(row[0]), name=display_name) self.contacts.append(contact) self.update_display_names(uris_without_display_name) self.contactTable.reloadData() self.contactTable.selectRowIndexes_byExtendingSelection_(NSIndexSet.indexSetWithIndex_(index), False) self.contactTable.scrollRowToVisible_(index) self.updateContactsColumnHeader() self.refresh_in_progress = False @objc.python_method @run_in_green_thread def update_display_names(self, uris_without_display_name): results = self.session_history.get_display_names(uris_without_display_name) self.updateDisplayNames(results) @objc.python_method @run_in_gui_thread def updateDisplayNames(self, results): must_reload = False for result in results: self.display_name_cache[result[0]]=result[1] for contact in self.contacts: if contact.uri == result[0] and contact.name != result[1]: contact.name = result[1] must_reload = True if must_reload: self.contactTable.reloadData() must_reload = False for entry in self.dayly_entries: if entry['remote_uri_sql'] == entry['remote_uri']: try: display_name = self.display_name_cache[entry['remote_uri_sql']] except KeyError: pass else: entry['display_name'] = display_name entry['remote_uri'] = '%s <%s>' % (display_name, entry['remote_uri_sql']) if '@' in entry['remote_uri_sql'] else display_name must_reload = True self.dayly_entries.sortUsingDescriptors_(self.indexTable.sortDescriptors()) self.indexTable.reloadData() @objc.python_method @run_in_green_thread def refreshDailyEntries(self, order_text=None): if self.chat_history: self.resetDailyEntries() self.updateBusyIndicator(True) search_text = self.search_text if self.search_text else None remote_uri = self.search_uris if self.search_uris else None local_uri = self.search_local if self.search_local else None media_type = self.search_media if self.search_media else None after_date = self.after_date if self.after_date else None before_date = self.before_date if self.before_date else None results = self.chat_history.get_daily_entries(local_uri=local_uri, remote_uri=remote_uri, media_type=media_type, search_text=search_text, order_text=order_text, after_date=after_date, before_date=before_date) self.renderDailyEntries(results) self.updateBusyIndicator(False) @objc.python_method @run_in_gui_thread def resetDailyEntries(self): self.dayly_entries = NSMutableArray.array() self.indexTable.reloadData() @objc.python_method @run_in_gui_thread def renderDailyEntries(self, results): getFirstContactMatchingURI = NSApp.delegate().contactsWindowController.getFirstContactMatchingURI self.dayly_entries = NSMutableArray.array() for result in results: display_name = None try: found_contact = self.contact_cache[result[2]] except KeyError: found_contact = getFirstContactMatchingURI(result[2], exact_match=True) self.contact_cache[result[2]] = found_contact if found_contact: display_name = found_contact.name remote_uri = '%s <%s>' % (display_name, result[2]) if '@' in result[2] else display_name else: try: display_name = self.display_name_cache[result[2]] except KeyError: remote_uri = result[2] else: remote_uri = '%s <%s>' % (display_name, result[2]) if '@' in result[2] else display_name entry = NSMutableDictionary.dictionaryWithObjectsAndKeys_(result[1], "local_uri", remote_uri, "remote_uri", result[2], "remote_uri_sql", result[0], 'date', result[3], 'type', display_name, "display_name") self.dayly_entries.addObject_(entry) self.dayly_entries.sortUsingDescriptors_(self.indexTable.sortDescriptors()) self.indexTable.reloadData() if self.search_uris and not self.dayly_entries: self.contactTable.deselectAll_(True) @objc.python_method @run_in_green_thread def refreshMessages(self, count=SQL_LIMIT, remote_uri=None, local_uri=None, media_type=None, date=None, after_date=None, before_date=None): if self.chat_history: self.updateBusyIndicator(True) search_text = self.search_text if self.search_text else None if not remote_uri: remote_uri = self.search_uris if self.search_uris else None if not local_uri: local_uri = self.search_local if self.search_local else None if not media_type: media_type = self.search_media if self.search_media else None if not after_date: after_date = self.after_date if self.after_date else None if not before_date: before_date = self.before_date if self.before_date else None results = self.chat_history.get_messages(count=count, local_uri=local_uri, remote_uri=remote_uri, media_type=media_type, date=date, search_text=search_text, after_date=after_date, before_date=before_date) self.renderMessages(results) self.updateBusyIndicator(False) @objc.python_method @run_in_gui_thread def renderMessages(self, messages=None): self.chatViewController.clear() self.chatViewController.resetRenderedMessages() self.chatViewController.last_sender = None if messages is not None: # new message list. cache for pagination and reset pagination to the last page self.messages = list(reversed(messages)) message_count = len(messages) start = message_count // MAX_MESSAGES_PER_PAGE * MAX_MESSAGES_PER_PAGE end = message_count else: message_count = len(self.messages) start = self.start end = min(start + MAX_MESSAGES_PER_PAGE, message_count) for row in self.messages[start:end]: self.renderMessage(row) self.paginationButton.setEnabled_forSegment_(start > MAX_MESSAGES_PER_PAGE, 0) self.paginationButton.setEnabled_forSegment_(start > 0, 1) self.paginationButton.setEnabled_forSegment_(start + MAX_MESSAGES_PER_PAGE + 1 < message_count, 2) self.paginationButton.setEnabled_forSegment_(start + MAX_MESSAGES_PER_PAGE * 2 < message_count, 3) if message_count == 0: text = NSLocalizedString("No entry found", "Label") elif message_count == 1: text = NSLocalizedString("Displaying 1 entry", "Label") elif message_count < MAX_MESSAGES_PER_PAGE: text = NSLocalizedString("Displaying {} entries".format(end), "Label") else: text = NSLocalizedString("Displaying {} to {} out of {} entries", "Label").format(start+1, end, message_count) self.foundMessagesLabel.setStringValue_(text) @objc.python_method @run_in_gui_thread def renderMessage(self, message): if message.direction == 'outgoing': icon = NSApp.delegate().contactsWindowController.iconPathForSelf() else: sender_uri = sipuri_components_from_string(message.cpim_from)[0] # TODO: How to render the icons from Address Book? Especially in sandbox mode we do not have access to other folders icon = NSApp.delegate().contactsWindowController.iconPathForURI(sender_uri) try: timestamp=ISOTimestamp(message.cpim_timestamp) except Exception: pass else: is_html = False if message.content_type == 'text' else True private = True if message.private == "1" else False self.chatViewController.showMessage(message.sip_callid, message.msgid, message.direction, message.cpim_from, icon, message.body, timestamp, is_private=private, recipient=message.cpim_to, state=message.status, is_html=is_html, history_entry=True, media_type=message.media_type, encryption=message.encryption if message.media_type == 'chat' else None) @objc.IBAction def paginateResults_(self, sender): if sender.selectedSegment() == 0: self.start = 0 elif sender.selectedSegment() == 1: next_start = self.start - MAX_MESSAGES_PER_PAGE self.start = next_start if next_start >= 0 else self.start elif sender.selectedSegment() == 2: next_start = self.start + MAX_MESSAGES_PER_PAGE self.start = next_start if next_start < len(self.messages)-1 else self.start elif sender.selectedSegment() == 3: self.start = len(self.messages) - len(self.messages)%MAX_MESSAGES_PER_PAGE if len(self.messages) > MAX_MESSAGES_PER_PAGE else 0 self.renderMessages() def tableView_deleteRow_(self, table, row): pass def tableView_sortDescriptorsDidChange_(self, table, odescr): self.dayly_entries.sortUsingDescriptors_(self.indexTable.sortDescriptors()) self.indexTable.reloadData() @objc.IBAction def printDocument_(self, sender): printInfo = NSPrintInfo.sharedPrintInfo() printInfo.setTopMargin_(30) printInfo.setBottomMargin_(30) printInfo.setLeftMargin_(10) printInfo.setRightMargin_(10) printInfo.setOrientation_(NSPortraitOrientation) printInfo.setHorizontallyCentered_(True) printInfo.setVerticallyCentered_(False) printInfo.setHorizontalPagination_(NSFitPagination) printInfo.setVerticalPagination_(NSFitPagination) NSPrintInfo.setSharedPrintInfo_(printInfo) # print the content of the web view self.entriesView.mainFrame().frameView().documentView().print_(self) @objc.IBAction def search_(self, sender): if self.chat_history: self.search_text = str(sender.stringValue()).lower() self.refreshContacts() row = self.indexTable.selectedRow() if row > 0: self.refreshDailyEntries() self.refreshMessages(local_uri=self.dayly_entries[row].objectForKey_("local_uri"), date=self.dayly_entries[row].objectForKey_("date"), media_type=self.dayly_entries[row].objectForKey_("type")) else: row = self.contactTable.selectedRow() if row > 0: self.refreshMessages() self.refreshDailyEntries() else: self.refreshDailyEntries() self.refreshMessages() @objc.IBAction def searchContacts_(self, sender): text = str(self.searchContactBox.stringValue().strip()) contacts = [contact for contact in self.contacts[2:] if text in contact] if text else self.contacts[2:] self.contacts = [self.all_contacts, self.bonjour_contact] + contacts self.contactTable.reloadData() self.contactTable.selectRowIndexes_byExtendingSelection_(NSIndexSet.indexSetWithIndex_(0), False) self.contactTable.scrollRowToVisible_(0) self.updateContactsColumnHeader() if not text: self.refreshContacts() @objc.python_method def updateContactsColumnHeader(self): found_contacts = len(self.contacts)-2 if found_contacts == 1: title = NSLocalizedString("1 contact found", "Label") elif found_contacts > 1: title = NSLocalizedString("%d contacts found", "Label") % found_contacts else: title = NSLocalizedString("Contacts", "Label") self.contactTable.tableColumnWithIdentifier_('contacts').headerCell().setStringValue_(title) def tableViewSelectionDidChange_(self, notification): if self.chat_history: if notification.object() == self.contactTable: row = self.contactTable.selectedRow() if row < 0: return elif row == 0: self.search_local = None self.search_uris = None self.searchContactBox.setStringValue_('') self.refreshContacts() elif row == 1: self.search_local = 'bonjour.local' self.search_uris = None elif row > 1: self.search_local = None if self.contacts[row].presence_contact is not None: self.search_uris = list(str(contact_uri.uri) for contact_uri in self.contacts[row].presence_contact.uris) self.chatViewController.expandSmileys = not self.contacts[row].presence_contact.contact.disable_smileys self.chatViewController.toggleSmileys(self.chatViewController.expandSmileys) try: item = next((item for item in self.toolbar.visibleItems() if item.itemIdentifier() == 'smileys')) except StopIteration: pass else: item.setImage_(NSImage.imageNamed_("smiley_on" if self.chatViewController.expandSmileys else "smiley_off")) else: self.search_uris = (self.contacts[row].uri,) self.refreshDailyEntries() self.refreshMessages() else: row = self.indexTable.selectedRow() if row >= 0: self.refreshMessages(remote_uri=(self.dayly_entries[row].objectForKey_("remote_uri_sql"),), local_uri=self.dayly_entries[row].objectForKey_("local_uri"), date=self.dayly_entries[row].objectForKey_("date"), media_type=self.dayly_entries[row].objectForKey_("type")) def numberOfRowsInTableView_(self, table): if table == self.indexTable: return self.dayly_entries.count() elif table == self.contactTable: return len(self.contacts) return 0 def tableView_objectValueForTableColumn_row_(self, table, column, row): if table == self.indexTable: ident = column.identifier() if ident == 'type': return self.format_media_type(self.dayly_entries[row].objectForKey_(ident)) try: return str(self.dayly_entries[row].objectForKey_(ident)) except IndexError: return None elif table == self.contactTable: try: if type(self.contacts[row]) in (str, str): return self.contacts[row] else: return self.contacts[row].name except IndexError: return None return None def tableView_willDisplayCell_forTableColumn_row_(self, table, cell, tableColumn, row): if table == self.contactTable: try: if row < len(self.contacts): if type(self.contacts[row]) in (str, str): cell.setContact_(None) else: cell.setContact_(self.contacts[row]) except: pass def showWindow_(self, sender): self.window().makeKeyAndOrderFront_(None) @objc.python_method @run_in_gui_thread def filterByURIs(self, uris=(), media_type=None): self.search_text = None self.search_local = None if media_type != self.search_media: for tag in list(self.media_type_array.keys()): if self.media_type_array[tag] == media_type: self.searchMedia.selectItemAtIndex_(tag) self.search_media = media_type self.search_uris = uris self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() @objc.IBAction def filterByMediaChanged_(self, sender): tag = sender.selectedItem().tag() self.search_media = self.media_type_array[tag] self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() @objc.IBAction def filterByPeriodChanged_(self, sender): tag = sender.selectedItem().tag() if tag == 0: self.before_date = None self.after_date = None elif tag < 0: try: date = self.period_array[tag] except KeyError: date = None self.before_date = date self.after_date = None else: try: date = self.period_array[tag] except KeyError: date = None self.after_date = date self.before_date = None self.refreshContacts() self.refreshDailyEntries() self.refreshMessages() def validateToolbarItem_(self, item): if item.itemIdentifier() == NSToolbarPrintItemIdentifier and not self.messages: return False return True def toolbarWillAddItem_(self, notification): item = notification.userInfo()["item"] if item.itemIdentifier() == NSToolbarPrintItemIdentifier: item.setToolTip_("Print Current Entries") item.setTarget_(self) item.setAutovalidates_(True) @objc.IBAction def userClickedToolbarItem_(self, sender): if sender.itemIdentifier() == 'smileys': self.chatViewController.expandSmileys = not self.chatViewController.expandSmileys sender.setImage_(NSImage.imageNamed_("smiley_on" if self.chatViewController.expandSmileys else "smiley_off")) self.chatViewController.toggleSmileys(self.chatViewController.expandSmileys) row = self.contactTable.selectedRow() if row and row > 1 and self.contacts[row].presence_contact is not None: self.contacts[row].presence_contact.contact.disable_smileys = not self.contacts[row].presence_contact.contact.disable_smileys self.contacts[row].presence_contact.contact.save() elif sender.itemIdentifier() == 'delete': if self.selectedTableView == self.contactTable: try: row = self.contactTable.selectedRow() self.showDeleteConfirmationDialog(row) except IndexError: pass elif self.selectedTableView == self.indexTable: try: row = self.indexTable.selectedRow() local_uri = self.dayly_entries[row].objectForKey_("local_uri") remote_uri = self.dayly_entries[row].objectForKey_("remote_uri") remote_uri_sql = self.dayly_entries[row].objectForKey_("remote_uri_sql") date = self.dayly_entries[row].objectForKey_("date") media_type = self.dayly_entries[row].objectForKey_("type") label = NSLocalizedString("Please confirm the deletion of %s history entries", "Label") % media_type + NSLocalizedString(" from %s", "SIP Address label") % remote_uri + NSLocalizedString(" on %s. ", "Date label") % date + NSLocalizedString("This operation cannot be undone. ", "Label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(local_uri=local_uri, remote_uri=remote_uri_sql, media_type=media_type, date=date) except IndexError: pass @objc.python_method def showDeleteConfirmationDialog(self, row): media_print = self.search_media or NSLocalizedString("all", "Label") tag = self.period.selectedItem().tag() period = '%s %s' % (NSLocalizedString(" newer than", "Date label") if tag < 4 else NSLocalizedString(" older than", "Date label"), self.period_array[tag].strftime("%Y-%m-%d")) if tag else '' if row == 0: label = NSLocalizedString("Please confirm the deletion of %s history entries", "Label") % media_print + period + ". "+ NSLocalizedString("This operation cannot be undone. ", "Label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(media_type=self.search_media, after_date=self.after_date, before_date=self.before_date) elif row == 1: remote_uri=self.contacts[row].uri label = NSLocalizedString("Please confirm the deletion of %s Bonjour history entries", "Label") % media_print + period + ". "+ NSLocalizedString("This operation cannot be undone. ", "Label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(local_uri='bonjour.local', media_type=self.search_media, after_date=self.after_date, before_date=self.before_date) else: contact = self.contacts[row] if contact.presence_contact is not None: remote_uri = list(str(contact.uri) for contact in contact.presence_contact.uris) else: remote_uri = contact.uri label = NSLocalizedString("Please confirm the deletion of %s history entries", "Label") % media_print + NSLocalizedString(" from ", "Label") + contact.name + period + ". "+ NSLocalizedString("This operation cannot be undone. ", "Label") ret = NSRunAlertPanel(NSLocalizedString("Purge History Entries", "Window title"), label, NSLocalizedString("Confirm", "Button title"), NSLocalizedString("Cancel", "Button title"), None) if ret == NSAlertDefaultReturn: self.delete_messages(remote_uri=remote_uri, media_type=self.search_media, after_date=self.after_date, before_date=self.before_date) @objc.python_method @run_in_gui_thread def updateBusyIndicator(self, busy=False): if busy: self.queryDatabaseLabel.setHidden_(False) self.busyIndicator.setHidden_(False) self.busyIndicator.setIndeterminate_(True) self.busyIndicator.setStyle_(NSProgressIndicatorSpinningStyle) self.busyIndicator.startAnimation_(None) else: self.busyIndicator.stopAnimation_(None) self.busyIndicator.setHidden_(True) self.queryDatabaseLabel.setHidden_(True) @objc.python_method @run_in_gui_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) @objc.python_method def _NH_BlinkShouldTerminate(self, notification): if self.window(): self.window().orderOut_(self) @objc.python_method def _NH_ChatViewControllerDidDisplayMessage(self, notification): if notification.data.local_party != 'bonjour.local': exists = any(contact for contact in self.contacts if notification.data.remote_party == contact.uri) if not exists: self.refreshContacts() @objc.python_method def _NH_AudioCallLoggedToHistory(self, notification): if notification.data.local_party != 'bonjour.local': exists = any(contact for contact in self.contacts if notification.data.remote_party == contact.uri) if not exists: self.refreshContacts() @objc.python_method def _NH_BlinkContactsHaveChanged(self, notification): self.refresh_contacts_counter += 1 def refreshContactsTimer_(self, timer): if self.refresh_contacts_counter: self.refreshContacts() self.toolbar.validateVisibleItems() @objc.python_method def _NH_BlinkTableViewSelectionChaged(self, notification): self.selectedTableView = notification.sender self.toolbar.validateVisibleItems() @objc.python_method def _NH_BlinkConferenceContactPresenceHasChanged(self, notification): try: contact = next((contact for contact in self.contacts[2:] if contact == notification.sender)) except StopIteration: return else: try: idx = self.contacts.index(contact) self.contactTable.reloadDataForRowIndexes_columnIndexes_(NSIndexSet.indexSetWithIndex_(idx), NSIndexSet.indexSetWithIndex_(0)) except ValueError: pass def contactSelectionChanged_(self, notification): pass def menuWillOpen_(self, menu): if menu == self.contactMenu: self.contactMenu.itemWithTag_(2).setEnabled_(False) self.contactMenu.itemWithTag_(3).setEnabled_(False) self.contactMenu.itemWithTag_(4).setEnabled_(False) try: row = self.contactTable.selectedRow() except: return if row < 2: return try: contact = self.contacts[row] except IndexError: return contact_exists = bool(contact.presence_contact is not None) if '@' in contact.uri: self.contactMenu.itemWithTag_(2).setEnabled_(not is_anonymous(contact.uri)) self.contactMenu.itemWithTag_(3).setEnabled_(not contact_exists and not is_anonymous(contact.uri)) self.contactMenu.itemWithTag_(4).setEnabled_(contact_exists) else: bonjour_contact = NSApp.delegate().contactsWindowController.model.getBonjourContactMatchingDeviceId(contact.uri) self.contactMenu.itemWithTag_(2).setEnabled_(bool(bonjour_contact)) self.contactMenu.itemWithTag_(3).setEnabled_(False) self.contactMenu.itemWithTag_(4).setEnabled_(False) @objc.IBAction def doubleClick_(self, sender): row = self.contactTable.selectedRow() if row < 2: return try: contact = self.contacts[row] except IndexError: return if '@' in contact.uri: NSApp.delegate().contactsWindowController.startSessionWithTarget(contact.uri) else: bonjour_contact = NSApp.delegate().contactsWindowController.model.getBonjourContactMatchingDeviceId(contact.uri) if not bonjour_contact: BlinkLogger().log_info("Bonjour neighbour %s was not found on this network" % contact.name) message = NSLocalizedString("Bonjour neighbour %s was not found on this network. ", "label") % contact.name NSRunAlertPanel(NSLocalizedString("Error", "Window title"), message, NSLocalizedString("OK", "Button title"), None, None) return NSApp.delegate().contactsWindowController.startSessionWithTarget(bonjour_contact.uri) @objc.IBAction def userClickedContactMenu_(self, sender): row = self.contactTable.selectedRow() try: contact = self.contacts[row] except IndexError: return tag = sender.tag() if tag == 1: self.showDeleteConfirmationDialog(row) elif tag == 2: NSApp.delegate().contactsWindowController.searchBox.setStringValue_(contact.uri) NSApp.delegate().contactsWindowController.searchContacts() NSApp.delegate().contactsWindowController.window().makeFirstResponder_(NSApp.delegate().contactsWindowController.searchBox) NSApp.delegate().contactsWindowController.window().deminiaturize_(sender) NSApp.delegate().contactsWindowController.window().makeKeyWindow() elif tag == 3: NSApp.delegate().contactsWindowController.addContact(uris=[(contact.uri, 'sip')], name=contact.name) elif tag == 4 and contact.presence_contact is not None: NSApp.delegate().contactsWindowController.model.editContact(contact.presence_contact) @objc.IBAction def userClickedActionsButton_(self, sender): point = sender.window().convertScreenToBase_(NSEvent.mouseLocation()) event = NSEvent.mouseEventWithType_location_modifierFlags_timestamp_windowNumber_context_eventNumber_clickCount_pressure_( NSLeftMouseUp, point, 0, NSDate.timeIntervalSinceReferenceDate(), sender.window().windowNumber(), sender.window().graphicsContext(), 0, 1, 0) NSMenu.popUpContextMenu_withEvent_forView_(self.contactMenu, event, sender)