view browser_side/menu.py @ 444:b64e528fb524

server side: use of bridge's getReady to be sure that backend is initialised at launch
author Goffi <goffi@goffi.org>
date Thu, 15 May 2014 20:27:52 +0200
parents d52f529a6d42
children
line wrap: on
line source

#!/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 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
from jid import JID
from file_tools import FileUploadPanel
from xmlui import XMLUI
from browser_side import panels
from browser_side import dialog
from contact_group import ContactGroupEditor
from sat.core.i18n import _


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(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!"}
        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

        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(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(xmlui):
            if not xmlui:
                return
            body = XMLUI(self.host, xmlui)
            _dialog = dialog.GenericDialog("Manage your XMPP account", body, options=['NO_CLOSE'])
            body.setCloseCb(_dialog.close)
            _dialog.show()
        self.host.bridge.call('getAccountDialogUI', gotUI)

    def onParameters(self):
        def gotParams(xmlui):
            if not xmlui:
                return
            body = XMLUI(self.host, xmlui)
            _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()