diff src/browser/sat_browser/menu.py @ 467:97c72fe4a5f2

browser_side: import fixes: - moved browser modules in a sat_browser packages, to avoid import conflicts with std lib (e.g. logging), and let pyjsbuild work normaly - refactored bad import practices: classes are most of time not imported directly, module is imported instead.
author Goffi <goffi@goffi.org>
date Mon, 09 Jun 2014 22:15:26 +0200
parents src/browser/menu.py@bea9788f3170
children a7f5448a1bc3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/browser/sat_browser/menu.py	Mon Jun 09 22:15:26 2014 +0200
@@ -0,0 +1,271 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Libervia: a Salut à Toi frontend
+# Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import pyjd  # this is dummy in pyjs
+from sat.core.log import getLogger
+log = getLogger(__name__)
+
+from sat.core.i18n import _
+
+from pyjamas.ui.SimplePanel import SimplePanel
+from pyjamas.ui.MenuBar import MenuBar
+from pyjamas.ui.MenuItem import MenuItem
+from pyjamas.ui.HTML import HTML
+from pyjamas.ui.Frame import Frame
+from pyjamas import Window
+
+import jid
+
+import file_tools
+import xmlui
+import panels
+import dialog
+import contact_group
+
+
+class MenuCmd:
+
+    def __init__(self, object_, handler):
+        self._object = object_
+        self._handler = handler
+
+    def execute(self):
+        handler = getattr(self._object, self._handler)
+        handler()
+
+
+class PluginMenuCmd:
+
+    def __init__(self, host, action_id):
+        self.host = host
+        self.action_id = action_id
+
+    def execute(self):
+        self.host.launchAction(self.action_id, None)
+
+
+class LiberviaMenuBar(MenuBar):
+
+    def __init__(self):
+        MenuBar.__init__(self, vertical=False)
+        self.setStyleName('gwt-MenuBar-horizontal') # XXX: workaround for the Pyjamas' class name fix (it's now "gwt-MenuBar gwt-MenuBar-horizontal")
+                                                    # TODO: properly adapt CSS to the new class name
+
+    def doItemAction(self, item, fireCommand):
+        MenuBar.doItemAction(self, item, fireCommand)
+        if item == self.items[-1] and self.popup:
+            self.popup.setPopupPosition(Window.getClientWidth() -
+                                        self.popup.getOffsetWidth() - 22,
+                                        self.getAbsoluteTop() +
+                                        self.getOffsetHeight() - 1)
+            self.popup.addStyleName('menuLastPopup')
+
+
+class AvatarUpload(file_tools.FileUploadPanel):
+    def __init__(self):
+        texts = {'ok_button': 'Upload avatar',
+                 'body': 'Please select an image to show as your avatar...<br>Your picture must be a square and will be resized to 64x64 pixels if necessary.',
+                 'errback': "Can't open image... did you actually submit an image?",
+                 'body_errback': 'Please select another image file.',
+                 'callback': "Your new profile picture has been set!"}
+        file_tools.FileUploadPanel.__init__(self, 'upload_avatar', 'avatar_path', 2, texts)
+
+
+class Menu(SimplePanel):
+
+    def __init__(self, host):
+        self.host = host
+        SimplePanel.__init__(self)
+        self.setStyleName('menuContainer')
+
+    def createMenus(self, add_menus):
+        _item_tpl = "<img src='media/icons/menu/%s_menu_red.png' />%s"
+        menus_dict = {}
+        menus_order = []
+
+        def addMenu(menu_name, menu_name_i18n, item_name_i18n, icon, menu_cmd):
+            """ add a menu to menu_dict """
+            log.info("addMenu: %s %s %s %s %s" % (menu_name, menu_name_i18n, item_name_i18n, icon, menu_cmd))
+            try:
+                menu_bar = menus_dict[menu_name]
+            except KeyError:
+                menu_bar = menus_dict[menu_name] = MenuBar(vertical=True)
+                menus_order.append((menu_name, menu_name_i18n, icon))
+            if item_name_i18n and menu_cmd:
+                menu_bar.addItem(item_name_i18n, menu_cmd)
+
+        addMenu("General", _("General"), _("Web widget"), 'home', MenuCmd(self, "onWebWidget"))
+        addMenu("General", _("General"), _("Disconnect"), 'home', MenuCmd(self, "onDisconnect"))
+        addMenu("Contacts", _("Contacts"), None, 'social', None)
+        addMenu("Groups", _("Groups"), _("Discussion"), 'social', MenuCmd(self, "onJoinRoom"))
+        addMenu("Groups", _("Groups"), _("Collective radio"), 'social', MenuCmd(self, "onCollectiveRadio"))
+        addMenu("Games", _("Games"), _("Tarot"), 'games', MenuCmd(self, "onTarotGame"))
+        addMenu("Games", _("Games"), _("Xiangqi"), 'games', MenuCmd(self, "onXiangqiGame"))
+
+        # additional menus
+        for action_id, type_, path, path_i18n in add_menus:
+            if not path:
+                log.warning("skipping menu without path")
+                continue
+            if len(path) != len(path_i18n):
+                log.error("inconsistency between menu paths")
+                continue
+            menu_name = path[0]
+            menu_name_i18n = path_i18n[0]
+            item_name = path[1:]
+            if not item_name:
+                log.warning("skipping menu with a path of lenght 1 [%s]" % path[0])
+                continue
+            item_name_i18n = ' | '.join(path_i18n[1:])
+            addMenu(menu_name, menu_name_i18n, item_name_i18n, 'plugins', PluginMenuCmd(self.host, action_id))
+
+        # menu items that should be displayed after the automatically added ones
+        addMenu("Contacts", _("Contacts"), _("Manage groups"), 'social', MenuCmd(self, "onManageContactGroups"))
+
+        menus_order.append(None)  # we add separator
+
+        addMenu("Help", _("Help"), _("Social contract"), 'help', MenuCmd(self, "onSocialContract"))
+        addMenu("Help", _("Help"), _("About"), 'help', MenuCmd(self, "onAbout"))
+        addMenu("Settings", _("Settings"), _("Account"), 'settings', MenuCmd(self, "onAccount"))
+        addMenu("Settings", _("Settings"), _("Parameters"), 'settings', MenuCmd(self, "onParameters"))
+
+        # XXX: temporary, will change when a full profile will be managed in SàT
+        addMenu("Settings", _("Settings"), _("Upload avatar"), 'settings', MenuCmd(self, "onAvatarUpload"))
+
+        menubar = LiberviaMenuBar()
+
+        for menu_data in menus_order:
+            if menu_data is None:
+                _separator = MenuItem('', None)
+                _separator.setStyleName('menuSeparator')
+                menubar.addItem(_separator, None)
+            else:
+                menu_name, menu_name_i18n, icon = menu_data
+                menubar.addItem(MenuItem(_item_tpl % (icon, menu_name_i18n), True, menus_dict[menu_name]))
+
+        self.add(menubar)
+
+    #General menu
+    def onWebWidget(self):
+        web_panel = panels.WebPanel(self.host, "http://www.goffi.org")
+        self.host.addWidget(web_panel)
+        self.host.setSelected(web_panel)
+
+    def onDisconnect(self):
+        def confirm_cb(answer):
+            if answer:
+                log.info("disconnection")
+                self.host.bridge.call('disconnect', None)
+        _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to disconnect ?")
+        _dialog.show()
+
+    def onSocialContract(self):
+        _frame = Frame('contrat_social.html')
+        _frame.setStyleName('infoFrame')
+        _dialog = dialog.GenericDialog("Contrat Social", _frame)
+        _dialog.setSize('80%', '80%')
+        _dialog.show()
+
+    def onAbout(self):
+        _about = HTML("""<b>Libervia</b>, a Salut &agrave; Toi project<br />
+<br />
+You can contact the author at <a href="mailto:goffi@goffi.org">goffi@goffi.org</a><br />
+Blog available (mainly in french) at <a href="http://www.goffi.org" target="_blank">http://www.goffi.org</a><br />
+Project page: <a href="http://sat.goffi.org"target="_blank">http://sat.goffi.org</a><br />
+<br />
+Any help welcome :)
+<p style='font-size:small;text-align:center'>This project is dedicated to Roger Poisson</p>
+""")
+        _dialog = dialog.GenericDialog("About", _about)
+        _dialog.show()
+
+    #Contact menu
+    def onManageContactGroups(self):
+        """Open the contact groups manager."""
+
+        def onCloseCallback():
+            pass
+
+        contact_group.ContactGroupEditor(self.host, None, onCloseCallback)
+
+    #Group menu
+    def onJoinRoom(self):
+
+        def invite(room_jid, contacts):
+            for contact in contacts:
+                self.host.bridge.call('inviteMUC', None, contact, room_jid)
+
+        def join(room_jid, contacts):
+            if self.host.whoami:
+                nick = self.host.whoami.node
+                if room_jid not in [room.bare for room in self.host.room_list]:
+                    self.host.bridge.call('joinMUC', lambda room_jid: invite(room_jid, contacts), room_jid, nick)
+                else:
+                    self.host.getOrCreateLiberviaWidget(panels.ChatPanel, (room_jid, "group"), True, jid.JID(room_jid).bare)
+                    invite(room_jid, contacts)
+
+        dialog.RoomAndContactsChooser(self.host, join, ok_button="Join", visible=(True, False))
+
+    def onCollectiveRadio(self):
+        def callback(room_jid, contacts):
+            self.host.bridge.call('launchRadioCollective', None, contacts, room_jid)
+        dialog.RoomAndContactsChooser(self.host, callback, ok_button="Choose", title="Collective Radio", visible=(False, True))
+
+    #Game menu
+    def onTarotGame(self):
+        def onPlayersSelected(room_jid, other_players):
+            self.host.bridge.call('launchTarotGame', None, other_players, room_jid)
+        dialog.RoomAndContactsChooser(self.host, onPlayersSelected, 3, title="Tarot", title_invite="Please select 3 other players", visible=(False, True))
+
+    def onXiangqiGame(self):
+        Window.alert("A Xiangqi game is planed, but not available yet")
+
+    #Settings menu
+
+    def onAccount(self):
+        def gotUI(xml_ui):
+            if not xml_ui:
+                return
+            body = xmlui.XMLUI(self.host, xml_ui)
+            _dialog = dialog.GenericDialog("Manage your account", body, options=['NO_CLOSE'])
+            body.setCloseCb(_dialog.close)
+            _dialog.show()
+        self.host.bridge.call('getAccountDialogUI', gotUI)
+
+    def onParameters(self):
+        def gotParams(xml_ui):
+            if not xml_ui:
+                return
+            body = xmlui.XMLUI(self.host, xml_ui)
+            _dialog = dialog.GenericDialog("Parameters", body, options=['NO_CLOSE'])
+            body.setCloseCb(_dialog.close)
+            _dialog.setSize('80%', '80%')
+            _dialog.show()
+        self.host.bridge.call('getParamsUI', gotParams)
+
+    def removeItemParams(self):
+        """Remove the Parameters item from the Settings menu bar."""
+        self.menu_settings.removeItem(self.item_params)
+
+    def onAvatarUpload(self):
+        body = AvatarUpload()
+        _dialog = dialog.GenericDialog("Avatar upload", body, options=['NO_CLOSE'])
+        body.setCloseCb(_dialog.close)
+        _dialog.setWidth('40%')
+        _dialog.show()