Mercurial > libervia-web
comparison browser_side/base_panels.py @ 323:0b7934e75e76
misc: reorganization of the file panels.py + clean the modules import:
- some existing modules were not found during JS runtime (panels.py was too large?)
- the *Panel classes of panels.py that do not reference "host" have been moved to base_panels.py
- cleaned the import in various files
author | souliane <souliane@mailoo.org> |
---|---|
date | Sat, 04 Jan 2014 00:17:46 +0100 |
parents | browser_side/panels.py@971e3812903a |
children | 2067d6241927 |
comparison
equal
deleted
inserted
replaced
322:971e3812903a | 323:0b7934e75e76 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 Libervia: a Salut à Toi frontend | |
6 Copyright (C) 2011, 2012, 2013 Jérôme Poisson <goffi@goffi.org> | |
7 | |
8 This program is free software: you can redistribute it and/or modify | |
9 it under the terms of the GNU Affero General Public License as published by | |
10 the Free Software Foundation, either version 3 of the License, or | |
11 (at your option) any later version. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU Affero General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU Affero General Public License | |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 """ | |
21 | |
22 import pyjd # this is dummy in pyjs | |
23 from pyjamas.ui.AbsolutePanel import AbsolutePanel | |
24 from pyjamas.ui.VerticalPanel import VerticalPanel | |
25 from pyjamas.ui.HorizontalPanel import HorizontalPanel | |
26 from pyjamas.ui.HTMLPanel import HTMLPanel | |
27 from pyjamas.ui.Button import Button | |
28 from pyjamas.ui.HTML import HTML | |
29 from pyjamas.ui.PopupPanel import PopupPanel | |
30 from pyjamas.ui.StackPanel import StackPanel | |
31 from pyjamas.ui.Event import BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT | |
32 from pyjamas import DOM | |
33 | |
34 from datetime import datetime | |
35 from time import time | |
36 | |
37 from tools import html_sanitize, inlineRoot | |
38 | |
39 from sat_frontends.tools.strings import addURLToText | |
40 | |
41 | |
42 class ChatText(HTMLPanel): | |
43 | |
44 def __init__(self, timestamp, nick, mymess, msg, xhtml=None): | |
45 _date = datetime.fromtimestamp(float(timestamp or time())) | |
46 _msg_class = ["chat_text_msg"] | |
47 if mymess: | |
48 _msg_class.append("chat_text_mymess") | |
49 HTMLPanel.__init__(self, "<span class='chat_text_timestamp'>%(timestamp)s</span> <span class='chat_text_nick'>%(nick)s</span> <span class='%(msg_class)s'>%(msg)s</span>" % | |
50 {"timestamp": _date.strftime("%H:%M"), | |
51 "nick": "[%s]" % html_sanitize(nick), | |
52 "msg_class": ' '.join(_msg_class), | |
53 "msg": addURLToText(html_sanitize(msg)) if not xhtml else inlineRoot(xhtml)} # FIXME: images and external links must be removed according to preferences | |
54 ) | |
55 self.setStyleName('chatText') | |
56 | |
57 | |
58 class Occupant(HTML): | |
59 """Occupant of a MUC room""" | |
60 | |
61 def __init__(self, nick, special=""): | |
62 HTML.__init__(self) | |
63 self.nick = nick | |
64 self.special = special | |
65 self._refresh() | |
66 | |
67 def __str__(self): | |
68 return self.nick | |
69 | |
70 def addSpecial(self, special=""): | |
71 if special not in self.special: | |
72 self.special += special | |
73 self._refresh() | |
74 | |
75 def _refresh(self): | |
76 special = "" if len(self.special) == 0 else " %s" % self.special | |
77 self.setHTML("<div class='occupant'>%s%s</div>" % (html_sanitize(self.nick), special)) | |
78 | |
79 | |
80 class OccupantsList(AbsolutePanel): | |
81 """Panel user to show occupants of a room""" | |
82 | |
83 def __init__(self): | |
84 AbsolutePanel.__init__(self) | |
85 self.occupants_list = {} | |
86 self.setStyleName('occupantsList') | |
87 | |
88 def addOccupant(self, nick): | |
89 _occupant = Occupant(nick) | |
90 self.occupants_list[nick] = _occupant | |
91 self.add(_occupant) | |
92 | |
93 def removeOccupant(self, nick): | |
94 try: | |
95 self.remove(self.occupants_list[nick]) | |
96 except KeyError: | |
97 print "ERROR: trying to remove an unexisting nick" | |
98 | |
99 def clear(self): | |
100 self.occupants_list.clear() | |
101 AbsolutePanel.clear(self) | |
102 | |
103 def addSpecials(self, occupants=[], html=""): | |
104 index = 0 | |
105 special = html | |
106 for occupant in occupants: | |
107 if occupant in self.occupants_list.keys(): | |
108 if isinstance(html, list): | |
109 special = html[index] | |
110 index = (index + 1) % len(html) | |
111 self.occupants_list[occupant].addSpecial(special) | |
112 | |
113 | |
114 class PopupMenuPanel(PopupPanel): | |
115 """This implementation of a popup menu (context menu) allow you to assign | |
116 two special methods which are common to all the items, in order to hide | |
117 certain items and also easily define their callbacks. The menu can be | |
118 bound to any of the mouse button (left, middle, right). | |
119 """ | |
120 def __init__(self, entries, hide=None, callback=None, vertical=True, style={}, **kwargs): | |
121 """ | |
122 @param entries: a dict of dicts, where each sub-dict is representing | |
123 one menu item: the sub-dict key can be used as the item text and | |
124 description, but optional "title" and "desc" entries would be used | |
125 if they exists. The sub-dicts may be extended later to do | |
126 more complicated stuff or overwrite the common methods. | |
127 @param hide: function with 2 args: widget, key as string and | |
128 returns True if that item should be hidden from the context menu. | |
129 @param callback: function with 2 args: sender, key as string | |
130 @param vertical: True or False, to set the direction | |
131 @param item_style: alternative CSS class for the menu items | |
132 @param menu_style: supplementary CSS class for the sender widget | |
133 """ | |
134 PopupPanel.__init__(self, autoHide=True, **kwargs) | |
135 self._entries = entries | |
136 self._hide = hide | |
137 self._callback = callback | |
138 self.vertical = vertical | |
139 self.style = {"selected": None, "menu": "recipientTypeMenu", "item": "popupMenuItem"} | |
140 self.style.update(style) | |
141 self._senders = {} | |
142 | |
143 def _show(self, sender): | |
144 """Popup the menu relative to this sender's position. | |
145 @param sender: the widget that has been clicked | |
146 """ | |
147 menu = VerticalPanel() if self.vertical is True else HorizontalPanel() | |
148 menu.setStyleName(self.style["menu"]) | |
149 | |
150 def button_cb(item): | |
151 """You can not put that method in the loop and rely | |
152 on _key, because it is overwritten by each step. | |
153 You can rely on item.key instead, which is copied | |
154 from _key after the item creation. | |
155 @param item: the menu item that has been clicked | |
156 """ | |
157 if self._callback is not None: | |
158 self._callback(sender=sender, key=item.key) | |
159 self.hide(autoClosed=True) | |
160 | |
161 for _key in self._entries.keys(): | |
162 entry = self._entries[_key] | |
163 if self._hide is not None and self._hide(sender=sender, key=_key) is True: | |
164 continue | |
165 title = entry["title"] if "title" in entry.keys() else _key | |
166 item = Button(title, button_cb) | |
167 item.key = _key | |
168 item.setStyleName(self.style["item"]) | |
169 item.setTitle(entry["desc"] if "desc" in entry.keys() else title) | |
170 menu.add(item) | |
171 if len(menu.getChildren()) == 0: | |
172 return | |
173 self.add(menu) | |
174 if self.vertical is True: | |
175 x = sender.getAbsoluteLeft() + sender.getOffsetWidth() | |
176 y = sender.getAbsoluteTop() | |
177 else: | |
178 x = sender.getAbsoluteLeft() | |
179 y = sender.getAbsoluteTop() + sender.getOffsetHeight() | |
180 self.setPopupPosition(x, y) | |
181 self.show() | |
182 if self.style["selected"]: | |
183 sender.addStyleDependentName(self.style["selected"]) | |
184 | |
185 def _onHide(popup): | |
186 if self.style["selected"]: | |
187 sender.removeStyleDependentName(self.style["selected"]) | |
188 return PopupPanel.onHideImpl(self, popup) | |
189 | |
190 self.onHideImpl = _onHide | |
191 | |
192 def registerClickSender(self, sender, button=BUTTON_LEFT): | |
193 """Bind the menu to the specified sender. | |
194 @param sender: the widget to which the menu should be bound | |
195 @param: BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT | |
196 """ | |
197 self._senders.setdefault(sender, []) | |
198 self._senders[sender].append(button) | |
199 | |
200 if button == BUTTON_RIGHT: | |
201 # WARNING: to disable the context menu is a bit tricky... | |
202 # The following seems to work on Firefox 24.0, but: | |
203 # TODO: find a cleaner way to disable the context menu | |
204 sender.getElement().setAttribute("oncontextmenu", "return false") | |
205 | |
206 def _onBrowserEvent(event): | |
207 button = DOM.eventGetButton(event) | |
208 if DOM.eventGetType(event) == "mousedown" and button in self._senders[sender]: | |
209 self._show(sender) | |
210 return sender.__class__.onBrowserEvent(sender, event) | |
211 | |
212 sender.onBrowserEvent = _onBrowserEvent | |
213 | |
214 def registerMiddleClickSender(self, sender): | |
215 self.registerClickSender(sender, BUTTON_MIDDLE) | |
216 | |
217 def registerRightClickSender(self, sender): | |
218 self.registerClickSender(sender, BUTTON_RIGHT) | |
219 | |
220 | |
221 class ToggleStackPanel(StackPanel): | |
222 """This is a pyjamas.ui.StackPanel with modified behavior. All sub-panels ca be | |
223 visible at the same time, clicking a sub-panel header will not display it and hide | |
224 the others but only toggle its own visibility. The argument 'visibleStack' is ignored. | |
225 Note that the argument 'visible' has been added to listener's 'onStackChanged' method. | |
226 """ | |
227 | |
228 def __init__(self, **kwargs): | |
229 StackPanel.__init__(self, **kwargs) | |
230 | |
231 def onBrowserEvent(self, event): | |
232 if DOM.eventGetType(event) == "click": | |
233 index = self.getDividerIndex(DOM.eventGetTarget(event)) | |
234 if index != -1: | |
235 self.toggleStack(index) | |
236 | |
237 def add(self, widget, stackText="", asHTML=False, visible=False): | |
238 StackPanel.add(self, widget, stackText, asHTML) | |
239 self.setStackVisible(self.getWidgetCount() - 1, visible) | |
240 | |
241 def toggleStack(self, index): | |
242 if index >= self.getWidgetCount(): | |
243 return | |
244 visible = not self.getWidget(index).getVisible() | |
245 self.setStackVisible(index, visible) | |
246 for listener in self.stackListeners: | |
247 listener.onStackChanged(self, index, visible) |