comparison 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
comparison
equal deleted inserted replaced
466:01880aa8ea2d 467:97c72fe4a5f2
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # Libervia: a Salut à Toi frontend
5 # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org>
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 import pyjd # this is dummy in pyjs
21 from sat.core.log import getLogger
22 log = getLogger(__name__)
23
24 from sat.core.i18n import _
25
26 from pyjamas.ui.SimplePanel import SimplePanel
27 from pyjamas.ui.MenuBar import MenuBar
28 from pyjamas.ui.MenuItem import MenuItem
29 from pyjamas.ui.HTML import HTML
30 from pyjamas.ui.Frame import Frame
31 from pyjamas import Window
32
33 import jid
34
35 import file_tools
36 import xmlui
37 import panels
38 import dialog
39 import contact_group
40
41
42 class MenuCmd:
43
44 def __init__(self, object_, handler):
45 self._object = object_
46 self._handler = handler
47
48 def execute(self):
49 handler = getattr(self._object, self._handler)
50 handler()
51
52
53 class PluginMenuCmd:
54
55 def __init__(self, host, action_id):
56 self.host = host
57 self.action_id = action_id
58
59 def execute(self):
60 self.host.launchAction(self.action_id, None)
61
62
63 class LiberviaMenuBar(MenuBar):
64
65 def __init__(self):
66 MenuBar.__init__(self, vertical=False)
67 self.setStyleName('gwt-MenuBar-horizontal') # XXX: workaround for the Pyjamas' class name fix (it's now "gwt-MenuBar gwt-MenuBar-horizontal")
68 # TODO: properly adapt CSS to the new class name
69
70 def doItemAction(self, item, fireCommand):
71 MenuBar.doItemAction(self, item, fireCommand)
72 if item == self.items[-1] and self.popup:
73 self.popup.setPopupPosition(Window.getClientWidth() -
74 self.popup.getOffsetWidth() - 22,
75 self.getAbsoluteTop() +
76 self.getOffsetHeight() - 1)
77 self.popup.addStyleName('menuLastPopup')
78
79
80 class AvatarUpload(file_tools.FileUploadPanel):
81 def __init__(self):
82 texts = {'ok_button': 'Upload avatar',
83 '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.',
84 'errback': "Can't open image... did you actually submit an image?",
85 'body_errback': 'Please select another image file.',
86 'callback': "Your new profile picture has been set!"}
87 file_tools.FileUploadPanel.__init__(self, 'upload_avatar', 'avatar_path', 2, texts)
88
89
90 class Menu(SimplePanel):
91
92 def __init__(self, host):
93 self.host = host
94 SimplePanel.__init__(self)
95 self.setStyleName('menuContainer')
96
97 def createMenus(self, add_menus):
98 _item_tpl = "<img src='media/icons/menu/%s_menu_red.png' />%s"
99 menus_dict = {}
100 menus_order = []
101
102 def addMenu(menu_name, menu_name_i18n, item_name_i18n, icon, menu_cmd):
103 """ add a menu to menu_dict """
104 log.info("addMenu: %s %s %s %s %s" % (menu_name, menu_name_i18n, item_name_i18n, icon, menu_cmd))
105 try:
106 menu_bar = menus_dict[menu_name]
107 except KeyError:
108 menu_bar = menus_dict[menu_name] = MenuBar(vertical=True)
109 menus_order.append((menu_name, menu_name_i18n, icon))
110 if item_name_i18n and menu_cmd:
111 menu_bar.addItem(item_name_i18n, menu_cmd)
112
113 addMenu("General", _("General"), _("Web widget"), 'home', MenuCmd(self, "onWebWidget"))
114 addMenu("General", _("General"), _("Disconnect"), 'home', MenuCmd(self, "onDisconnect"))
115 addMenu("Contacts", _("Contacts"), None, 'social', None)
116 addMenu("Groups", _("Groups"), _("Discussion"), 'social', MenuCmd(self, "onJoinRoom"))
117 addMenu("Groups", _("Groups"), _("Collective radio"), 'social', MenuCmd(self, "onCollectiveRadio"))
118 addMenu("Games", _("Games"), _("Tarot"), 'games', MenuCmd(self, "onTarotGame"))
119 addMenu("Games", _("Games"), _("Xiangqi"), 'games', MenuCmd(self, "onXiangqiGame"))
120
121 # additional menus
122 for action_id, type_, path, path_i18n in add_menus:
123 if not path:
124 log.warning("skipping menu without path")
125 continue
126 if len(path) != len(path_i18n):
127 log.error("inconsistency between menu paths")
128 continue
129 menu_name = path[0]
130 menu_name_i18n = path_i18n[0]
131 item_name = path[1:]
132 if not item_name:
133 log.warning("skipping menu with a path of lenght 1 [%s]" % path[0])
134 continue
135 item_name_i18n = ' | '.join(path_i18n[1:])
136 addMenu(menu_name, menu_name_i18n, item_name_i18n, 'plugins', PluginMenuCmd(self.host, action_id))
137
138 # menu items that should be displayed after the automatically added ones
139 addMenu("Contacts", _("Contacts"), _("Manage groups"), 'social', MenuCmd(self, "onManageContactGroups"))
140
141 menus_order.append(None) # we add separator
142
143 addMenu("Help", _("Help"), _("Social contract"), 'help', MenuCmd(self, "onSocialContract"))
144 addMenu("Help", _("Help"), _("About"), 'help', MenuCmd(self, "onAbout"))
145 addMenu("Settings", _("Settings"), _("Account"), 'settings', MenuCmd(self, "onAccount"))
146 addMenu("Settings", _("Settings"), _("Parameters"), 'settings', MenuCmd(self, "onParameters"))
147
148 # XXX: temporary, will change when a full profile will be managed in SàT
149 addMenu("Settings", _("Settings"), _("Upload avatar"), 'settings', MenuCmd(self, "onAvatarUpload"))
150
151 menubar = LiberviaMenuBar()
152
153 for menu_data in menus_order:
154 if menu_data is None:
155 _separator = MenuItem('', None)
156 _separator.setStyleName('menuSeparator')
157 menubar.addItem(_separator, None)
158 else:
159 menu_name, menu_name_i18n, icon = menu_data
160 menubar.addItem(MenuItem(_item_tpl % (icon, menu_name_i18n), True, menus_dict[menu_name]))
161
162 self.add(menubar)
163
164 #General menu
165 def onWebWidget(self):
166 web_panel = panels.WebPanel(self.host, "http://www.goffi.org")
167 self.host.addWidget(web_panel)
168 self.host.setSelected(web_panel)
169
170 def onDisconnect(self):
171 def confirm_cb(answer):
172 if answer:
173 log.info("disconnection")
174 self.host.bridge.call('disconnect', None)
175 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to disconnect ?")
176 _dialog.show()
177
178 def onSocialContract(self):
179 _frame = Frame('contrat_social.html')
180 _frame.setStyleName('infoFrame')
181 _dialog = dialog.GenericDialog("Contrat Social", _frame)
182 _dialog.setSize('80%', '80%')
183 _dialog.show()
184
185 def onAbout(self):
186 _about = HTML("""<b>Libervia</b>, a Salut &agrave; Toi project<br />
187 <br />
188 You can contact the author at <a href="mailto:goffi@goffi.org">goffi@goffi.org</a><br />
189 Blog available (mainly in french) at <a href="http://www.goffi.org" target="_blank">http://www.goffi.org</a><br />
190 Project page: <a href="http://sat.goffi.org"target="_blank">http://sat.goffi.org</a><br />
191 <br />
192 Any help welcome :)
193 <p style='font-size:small;text-align:center'>This project is dedicated to Roger Poisson</p>
194 """)
195 _dialog = dialog.GenericDialog("About", _about)
196 _dialog.show()
197
198 #Contact menu
199 def onManageContactGroups(self):
200 """Open the contact groups manager."""
201
202 def onCloseCallback():
203 pass
204
205 contact_group.ContactGroupEditor(self.host, None, onCloseCallback)
206
207 #Group menu
208 def onJoinRoom(self):
209
210 def invite(room_jid, contacts):
211 for contact in contacts:
212 self.host.bridge.call('inviteMUC', None, contact, room_jid)
213
214 def join(room_jid, contacts):
215 if self.host.whoami:
216 nick = self.host.whoami.node
217 if room_jid not in [room.bare for room in self.host.room_list]:
218 self.host.bridge.call('joinMUC', lambda room_jid: invite(room_jid, contacts), room_jid, nick)
219 else:
220 self.host.getOrCreateLiberviaWidget(panels.ChatPanel, (room_jid, "group"), True, jid.JID(room_jid).bare)
221 invite(room_jid, contacts)
222
223 dialog.RoomAndContactsChooser(self.host, join, ok_button="Join", visible=(True, False))
224
225 def onCollectiveRadio(self):
226 def callback(room_jid, contacts):
227 self.host.bridge.call('launchRadioCollective', None, contacts, room_jid)
228 dialog.RoomAndContactsChooser(self.host, callback, ok_button="Choose", title="Collective Radio", visible=(False, True))
229
230 #Game menu
231 def onTarotGame(self):
232 def onPlayersSelected(room_jid, other_players):
233 self.host.bridge.call('launchTarotGame', None, other_players, room_jid)
234 dialog.RoomAndContactsChooser(self.host, onPlayersSelected, 3, title="Tarot", title_invite="Please select 3 other players", visible=(False, True))
235
236 def onXiangqiGame(self):
237 Window.alert("A Xiangqi game is planed, but not available yet")
238
239 #Settings menu
240
241 def onAccount(self):
242 def gotUI(xml_ui):
243 if not xml_ui:
244 return
245 body = xmlui.XMLUI(self.host, xml_ui)
246 _dialog = dialog.GenericDialog("Manage your account", body, options=['NO_CLOSE'])
247 body.setCloseCb(_dialog.close)
248 _dialog.show()
249 self.host.bridge.call('getAccountDialogUI', gotUI)
250
251 def onParameters(self):
252 def gotParams(xml_ui):
253 if not xml_ui:
254 return
255 body = xmlui.XMLUI(self.host, xml_ui)
256 _dialog = dialog.GenericDialog("Parameters", body, options=['NO_CLOSE'])
257 body.setCloseCb(_dialog.close)
258 _dialog.setSize('80%', '80%')
259 _dialog.show()
260 self.host.bridge.call('getParamsUI', gotParams)
261
262 def removeItemParams(self):
263 """Remove the Parameters item from the Settings menu bar."""
264 self.menu_settings.removeItem(self.item_params)
265
266 def onAvatarUpload(self):
267 body = AvatarUpload()
268 _dialog = dialog.GenericDialog("Avatar upload", body, options=['NO_CLOSE'])
269 body.setCloseCb(_dialog.close)
270 _dialog.setWidth('40%')
271 _dialog.show()