''' Main window's main menu ''' import wx, sys from gui.uberwidgets.umenu import UMenuBar as MenuBar, UMenu as Menu from common import actions, profile, pref, setpref from common.statusmessage import StatusMessage from logging import getLogger; log = getLogger('blistmenu') from peak.util.plugins import Hook import config import traceback import gui.supportdigsby as support from gui.filetransfer import FileTransferDialog from gui.addcontactdialog import AddContactDialog from gui.imdialogs import ShowNewIMDialog from gui.visuallisteditor import VisualListEditor from util.diagnostic import do_diagnostic from gui.native.helpers import createEmail from peak.events import trellis import contacts from contacts.sort_model import GROUP_BY_CHOICES, SORT_BY_CHOICES def prefsdialog_show(name): import gui.pref.prefsdialog as prefsdialog return prefsdialog.show(name) def update_statuses(menu): # New Status # Edit Statuses # ---- (separator) # << STATUSES >> while len(menu) > 3: # ignore first three items. menu.RemoveItem(menu[3]) from gui.statuscombo import status_menu status_menu(menu, add_global = True, add_promote = True) def new_status_message(): from gui.status import new_custom_status new_custom_status(None, save_checkbox = True) def allow_rename(b): from contacts.buddylistfilters import OfflineGroup if b is None or getattr(b, 'iswidget', False) or isinstance(b, OfflineGroup): return False if not isinstance(b, contacts.renamable): return False if hasattr(b, '_disallow_actions'): return False return True class reset_checks_SortOptionWatcher(trellis.Component): model = trellis.attr(None) names = trellis.attr(None) view = trellis.attr(None) @trellis.perform def sync(self): model_keys = [v[0] for v in self.model.values] selection = self.model.selection for i, item in enumerate(self.view): if i < len(self.names): val_key = self.names[i][0] item.Check(val_key == model_keys[selection]) item.Enable(val_key in model_keys) # else: # item.Check(False) # item.Enable(True) class reset_check_AdvWatcher(trellis.Component): model = trellis.attr(None) view = trellis.attr(None) @trellis.perform def sync(self): selection = self.model.selection self.view.Check(selection > 0) # TODO: Remove this code once the kinks have been worked out in the # new menubar implementation. def create_main_menu(parent): 'Returns the main menu object.' menu = MenuBar(parent, skinkey = 'menubar') digsby = Menu(parent) view = Menu(parent) tools = Menu(parent) help = Menu(parent) def makeadd(m): return (lambda title, callback = None, id = -1: m.AddItem(title, callback = callback, id = id)) # # My Status # mystatus = Menu(parent, onshow = update_statuses); add = makeadd(mystatus) add(_('&New Status Message...'), new_status_message) add(_('&Edit Status Messages...'), lambda: prefsdialog_show('status')) mystatus.AddSep() from gui.protocols import add_group def add_chat(): import gui.chatgui gui.chatgui.join_chat() # # Digsby # digsby.AddSubMenu(mystatus, _('My Status')); add = makeadd(digsby) add(_('My &Accounts...'), lambda: prefsdialog_show('accounts')) digsby.AddSep() sendimitem = add(_('&New IM...\tCtrl+N'), ShowNewIMDialog) if pref('messaging.groupchat.enabled', False): groupchatitem = add(_('New Group C&hat...\tCtrl+Shift+N'), add_chat) else: groupchatitem = None digsby.AddSep() addcontactitem = add(_('Add &Contact...\tCtrl+A'),lambda : AddContactDialog.MakeOrShow()) addgroupitem = add(_('Add &Group...\tCtrl+Shift+A'), lambda: add_group()) renameitem = add(_('&Rename Selection'), lambda: parent.blist.rename_selected()) deleteitem = add(_('&Delete Selection...'), lambda: parent.blist.delete_blist_item(parent.blist.SelectedItem)) digsby.AddSep() add(_('Sign &Off Digsby (%s)') % profile.name, lambda: profile.signoff()) # wx.App handles this for proper shutdown. add(_('E&xit Digsby'), id = wx.ID_EXIT) def on_digsby_show(_m): b = parent.blist.SelectedItem allow_add = any(x.allow_contact_add for x in profile.account_manager.connected_accounts) for item in (sendimitem, groupchatitem, addcontactitem, addgroupitem): if item is not None: item.Enable(allow_add) if not allow_rename(b): renameitem.SetItemLabel(_('&Rename Selection')) deleteitem.SetItemLabel(_('&Delete Selection')) renameitem.Enable(False) deleteitem.Enable(False) else: renameitem.SetItemLabel(_('&Rename {name}').format(name=getattr(b, 'alias', b.name))) deleteitem.SetItemLabel(_('&Delete {name}').format(name=getattr(b, 'alias', b.name))) renameitem.Enable(True) deleteitem.Enable(True) # # View # add = makeadd(view) view.AddPrefCheck('buddylist.always_on_top', _('&Always On Top')) add(_('Skins...\tCtrl+S'), lambda: prefsdialog_show('appearance')) view.AddSep() view.AddPrefCheck('buddylist.show_menubar', _('&Menu Bar')) def on_menubar(val): if not val: wx.MessageBox(_('You can bring back the menubar by right clicking ' 'on the digsby icon in the task tray.'), _('Hide Menu Bar')) profile.prefs.link('buddylist.show_menubar', lambda val: wx.CallAfter(on_menubar, val), False, menu) view.AddPrefCheck('buddylist.show_status', _('Status Panel')) add(_('Arrange &Panels...'), callback = lambda *_a: edit_buddylist_order(parent)) view.AddSep() view.AddPrefCheck('buddylist.show_mobile', _('Show &Mobile Contacts\tCtrl+M')) view.AddPrefCheck('buddylist.show_offline', _('Show &Offline Contacts\tCtrl+O')) groupoffline = view.AddPrefCheck('buddylist.group_offline', _('&Group Offline Contacts\tCtrl+G')) hideoffline = view.AddPrefCheck('buddylist.hide_offline_groups', _('&Hide Offline Groups')) groupby = Menu(parent); add = makeadd(groupby) groupby.sorttypes = [] # sort by sortby = Menu(parent); add = makeadd(sortby) sortby.sorttypes = [] sort_models = profile.blist.sort_models group_by = sort_models[0] sort_by = sort_models[1] then_by = sort_models[2] def addsort(model, view, sortstr, title): def set(model = model, view = view, sortstr = sortstr): model.selection = [v[0] for v in model.values].index(sortstr) view[model.selection].Check(True) mi = view.AddCheckItem(title, set) view.sorttypes.append( sortstr ) return mi sort_names = dict((('none', _('&None')), ('status', _('&Status')), ('name', _('N&ame')), ('log', _('&Log Size')), ('service', _('Ser&vice')))) def addsorts(model, view, names): for name in names: addsort(model = model, view = view, sortstr = name, title = sort_names[name]) addsorts(model = group_by, view = groupby, names = [k for k,_v in GROUP_BY_CHOICES]) groupby.AddSep() groupby.AddItem(_('Advan&ced...'), callback = lambda: prefsdialog_show('contact_list')) addsorts(model = sort_by, view = sortby, names = [k for k,_v in SORT_BY_CHOICES]) sortby.AddSep() def sortby_adv_click(): sortby[-1].Check(then_by.selection > 0) prefsdialog_show('contact_list') sortby.AddCheckItem(_('Advan&ced...'), callback = sortby_adv_click) sortby_adv = sortby[-1] groupby.reset_watcher = reset_checks_SortOptionWatcher(model = group_by, view = groupby, names = GROUP_BY_CHOICES) sortby.reset_watcher = reset_checks_SortOptionWatcher(model = sort_by, view = sortby, names = SORT_BY_CHOICES) sortby.adv_watcher = reset_check_AdvWatcher(model = then_by, view = sortby_adv) view.AddSep() view.AddSubMenu(groupby, _('&Group By')) view.AddSubMenu(sortby, _('&Sort By')) # # Tools # add = makeadd(tools) add(_('&Preferences...\tCtrl+P'), id = wx.ID_PREFERENCES, callback = lambda: prefsdialog_show('accounts')) add(_('Buddy List &Search\tCtrl+F'), parent.start_search) add(_('&File Transfer History\tCtrl+J'), FileTransferDialog.Display) def pb_show(): from gui.pastbrowser import PastBrowser PastBrowser.MakeOrShow() add(_('&Chat History\tCtrl+H'), pb_show) # # Help # add = makeadd(help) add(_('&Documentation'), lambda: wx.LaunchDefaultBrowser('http://wiki.digsby.com')) add(_('Support &Forums'), lambda: wx.LaunchDefaultBrowser('http://forum.digsby.com')) help.AddSep() add(_('&Submit Bug Report'), do_diagnostic) add(_('Su&ggest a Feature'), send_features_email) help.AddSep() if getattr(sys, 'DEV', False) or pref('debug.console', False): add(_('Show Debug Console'), wx.GetApp().toggle_crust) help.AddSep() add(_('Su&pport Digsby'), lambda: support.SupportFrame.MakeOrShow(parent)) for hook in Hook("digsby.help.actions"): help_menu_items = hook() for item in help_menu_items: add(*item) def on_view_show(_m): sortstatus = pref('buddylist.sortby').startswith('*status') showoffline = pref('buddylist.show_offline') hidegroups = pref('buddylist.hide_offline_groups') groupoff = pref('buddylist.group_offline') groupoffline.Enable(not sortstatus and showoffline) groupoffline.Check(groupoff and (not sortstatus and showoffline)) hideoffline.Enable(not sortstatus) hideoffline.Check((sortstatus and not showoffline) or (not sortstatus and hidegroups)) digsbyMenuName = _('&Digsby') if config.platformName == "mac": digsbyMenuName = _("&File") menu.Append(digsby, digsbyMenuName, onshow = on_digsby_show) menu.Append(view, _('&View'), onshow = on_view_show) menu.Append(tools, _('&Tools'), onshow = lambda m: on_accounts_entries(parent, m)) menu.Append(help, _('&Help')) return menu buddylist_panel_names = dict(status = _('Status Panel'), blist = _('Buddy List'), elist = _('Email Accounts'), slist = _('Social Networks'), clist = _('Connections')) def edit_buddylist_order(parent, *_a): # show an existing one if already on screen if VisualListEditor.RaiseExisting(): return editor = VisualListEditor(parent, profile.prefs['buddylist.order'], buddylist_panel_names, lambda l: setpref('buddylist.order', l), _('Arrange Panels')) editor.Show() def im_account_menu(parent, menu, account): 'Builds a menu for an IM account.' menu.RemoveAllItems() # Sign On / Sign Off if account.connected: menu.AddItem(_('&Sign Off'), callback = account.disconnect) else: menu.AddItem(_('&Sign On'), callback = account.connect) # Edit Account menu.AddItem(_('&Edit Account...'), callback = lambda: profile.account_manager.edit(account)) # if connected, append more action menu items if account.connected: menu.AddSep() # grab actions for the connection (ignoring Connect and Disconnect methods, since # we already have "Sign On/Off" above) actions.menu(parent, account.connection, menu, filter = lambda func: func.__name__ not in ('Disconnect', 'Connect')) actions.menu(parent, account, menu) def status_setter(status): def _do_set_status(s = status): import hooks; hooks.notify('digsby.statistics.ui.select_status') return profile.set_status(s) return _do_set_status def add_status_menu_item(menu, statusmsg): return menu.Append(statusmsg.title, bitmap = StatusMessage.icon_for(statusmsg), callback = status_setter(statusmsg)) def on_accounts_entries(parent, menu): accounts = profile.account_manager.accounts if hasattr(menu, '_oldaccts'): if getattr(menu, '_oldaccts', []) == [(id(acct), acct.connected) for acct in accounts]: return # early exit if the accounts list hasn't changed if not hasattr(menu, '_account_items'): menu._account_items = [] item_ids = menu._account_items # first: remove old for itemid in item_ids: menu.Remove(itemid) item_ids[:] = [] parent.Unbind(wx.EVT_MENU_OPEN) if accounts and not menu[-1].IsSeparator(): menu.AddSep() for acct in accounts: accticon = acct.serviceicon bitmap = accticon.Greyed if not acct.connected else accticon mi = menu.AppendLazyMenu(acct.name, (lambda m, acct=acct: im_account_menu(parent, m, acct)), bitmap = bitmap) # add a (potentially greyed) service icon item_ids += [mi.Id] menu._oldaccts = [(id(acct), acct.connected) for acct in accounts] if menu[-1].IsSeparator(): menu.RemoveItem(menu[-1]) def yahoo_buddy_menu(menu, contact): ''' Appends a Yahoo-specific "Stealth Settings" submenu to "menu". account - YahooProtocol object menu - a UMenu selection - the selected Contact object or None ''' menu_title = _('&Stealth Settings') stealth_menu = Menu(menu.Window) enable = stealth_menu.Enable a = stealth_menu.AddCheckItem name = getattr(contact, 'name', _('Selection')) # append an item for each stealth option items = [a(_('Appear Online to {name}').format(name=name), callback = lambda: contact.set_stealth_session(False)), a(_('Appear Offline to {name}').format(name=name), callback = lambda: contact.set_stealth_session(True)), a(_('Appear Permanently Offline to {name}').format(name=name), callback = lambda: contact.set_stealth_perm(True))] # append a separator and a link to the webpage explaining stealth mode stealth_menu.AddSep() a(_('Learn More (URL)'), callback = lambda: wx.LaunchDefaultBrowser('http://messenger.yahoo.com/stealth.php')) online, offline, perm = items # items are disabled unless a yahoo buddy is selected if contact is None or contact.service != 'yahoo': for item in items: enable(item.Id, False) item.Check(False) else: invisible = profile.status.invisible enable(online.Id, True) enable(offline.Id, invisible) enable(perm.Id, True) if contact.stealth_perm: perm.Check(True) elif invisible and contact.stealth_session: offline.Check(True) elif not invisible or not contact.stealth_session: online.Check(True) menu.AddSubMenu(stealth_menu, menu_title) def jabber_buddy_menu(menu, contact): # offline contacts have no resources #don't need a menu if there's only one, and it is None if not contact.online or getattr(contact, 'iswidget', False) or \ (len(list(contact))==1 and list(contact)[0].jid.resource is None): return resource_menu = Menu(menu.Window) a = resource_menu.AddItem for resource in contact: # append a menu item for each resource a(resource.name, callback = lambda r=resource: r.chat()) menu.AddSubMenu(resource_menu, _('Chat with Resource')) def metacontact_buddy_menu(menu, mc): lenmc = len(mc) for contact in mc: # add a service specific menu for each contact in the metacontact contact_menu = Menu(menu.Window) # search_bases = False means we'll get the actions specific to the buddy's service actions.menu(menu.Window, contact, contact_menu, search_bases = False) if contact_menu[0].IsSeparator(): contact_menu.RemoveItem(contact_menu[0]) if lenmc > 2: contact_menu.AddSep() contact_menu.AddItem(_('&Remove from Merged Contact'), callback = lambda contact=contact: mc.manager.remove(mc, contact)) # use the contact's service icon for the submenu (greyed out if offline) icon = contact.serviceicon if not contact.online: icon = icon.Greyed menu.AddSubMenu(contact_menu, contact.name, bitmap = icon) def send_features_email(): try: createEmail('mailto:features@digsby.com') except Exception: traceback.print_exc() from common.emailaccount import mailclient_launch_error mailclient_launch_error()