Mercurial > libervia-web
comparison src/browser/sat_browser/main_panel.py @ 648:6d3142b782c3 frontends_multi_profiles
browser_side: classes reorganisation:
- moved widgets in dedicated modules (base, contact, editor, libervia) and a widget module for single classes
- same thing for panels (base, main, contact)
- libervia_widget mix main panels and widget and drag n drop for technical reasons (see comments)
- renamed WebPanel to WebWidget
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 26 Feb 2015 18:10:54 +0100 |
parents | src/browser/sat_browser/panels.py@7113d40533d6 |
children | 40c72f3b7638 |
comparison
equal
deleted
inserted
replaced
647:e0021d571eef | 648:6d3142b782c3 |
---|---|
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 """Panels used as main basis""" | |
21 | |
22 import pyjd # this is dummy in pyjs | |
23 from sat.core.log import getLogger | |
24 log = getLogger(__name__) | |
25 | |
26 from sat.core.i18n import _ | |
27 from sat_frontends.tools.strings import addURLToText | |
28 | |
29 from pyjamas.ui.AbsolutePanel import AbsolutePanel | |
30 from pyjamas.ui.HorizontalPanel import HorizontalPanel | |
31 from pyjamas.ui.Button import Button | |
32 from pyjamas.ui.HTML import HTML | |
33 from pyjamas.ui.ClickListener import ClickHandler | |
34 from pyjamas.Timer import Timer | |
35 from pyjamas import Window | |
36 from __pyjamas__ import doc | |
37 | |
38 | |
39 import base_menu | |
40 import menu | |
41 import dialog | |
42 import base_widget | |
43 import libervia_widget | |
44 import editor_widget | |
45 import contact_list | |
46 from constants import Const as C | |
47 | |
48 | |
49 ### Warning notification (visibility of message, and other warning data) ### | |
50 | |
51 | |
52 class WarningPopup(): | |
53 | |
54 def __init__(self): | |
55 self._popup = None | |
56 self._timer = Timer(notify=self._timeCb) | |
57 | |
58 def showWarning(self, type_=None, msg=None, duration=2000): | |
59 """Display a popup information message, e.g. to notify the recipient of a message being composed. | |
60 If type_ is None, a popup being currently displayed will be hidden. | |
61 @type_: a type determining the CSS style to be applied (see _showWarning) | |
62 @msg: message to be displayed | |
63 """ | |
64 if type_ is None: | |
65 self.__removeWarning() | |
66 return | |
67 if not self._popup: | |
68 self._showWarning(type_, msg) | |
69 elif (type_, msg) != self._popup.target_data: | |
70 self._timeCb(None) # we remove the popup | |
71 self._showWarning(type_, msg) | |
72 | |
73 self._timer.schedule(duration) | |
74 | |
75 def _showWarning(self, type_, msg): | |
76 """Display a popup information message, e.g. to notify the recipient of a message being composed. | |
77 @type_: a type determining the CSS style to be applied. For now the defined styles are | |
78 "NONE" (will do nothing), "PUBLIC", "GROUP", "STATUS" and "ONE2ONE". | |
79 @msg: message to be displayed | |
80 """ | |
81 if type_ == "NONE": | |
82 return | |
83 if not msg: | |
84 log.warning("no msg set uniBox warning") | |
85 return | |
86 if type_ == "PUBLIC": | |
87 style = "targetPublic" | |
88 elif type_ == "GROUP": | |
89 style = "targetGroup" | |
90 elif type_ == "STATUS": | |
91 style = "targetStatus" | |
92 elif type_ == "ONE2ONE": | |
93 style = "targetOne2One" | |
94 else: | |
95 log.error("unknown message type") | |
96 return | |
97 contents = HTML(msg) | |
98 | |
99 self._popup = dialog.PopupPanelWrapper(autoHide=False, modal=False) | |
100 self._popup.target_data = (type_, msg) | |
101 self._popup.add(contents) | |
102 self._popup.setStyleName("warningPopup") | |
103 if style: | |
104 self._popup.addStyleName(style) | |
105 | |
106 left = 0 | |
107 top = 0 # max(0, self.getAbsoluteTop() - contents.getOffsetHeight() - 2) | |
108 self._popup.setPopupPosition(left, top) | |
109 self._popup.show() | |
110 | |
111 def _timeCb(self, timer): | |
112 if self._popup: | |
113 self._popup.hide() | |
114 del self._popup | |
115 self._popup = None | |
116 | |
117 def __removeWarning(self): | |
118 """Remove the popup""" | |
119 self._timeCb(None) | |
120 | |
121 | |
122 ### Status ### | |
123 | |
124 | |
125 class StatusPanel(editor_widget.HTMLTextEditor): | |
126 | |
127 EMPTY_STATUS = '<click to set a status>' | |
128 | |
129 def __init__(self, host, status=''): | |
130 self.host = host | |
131 modifiedCb = lambda content: self.host.bridge.call('setStatus', None, self.host.status_panel.presence, content['text']) or True | |
132 editor_widget.HTMLTextEditor.__init__(self, {'text': status}, modifiedCb, options={'no_xhtml': True, 'listen_focus': True, 'listen_click': True}) | |
133 self.edit(False) | |
134 self.setStyleName('statusPanel') | |
135 | |
136 @property | |
137 def status(self): | |
138 return self._original_content['text'] | |
139 | |
140 def __cleanContent(self, content): | |
141 status = content['text'] | |
142 if status == self.EMPTY_STATUS or status in C.PRESENCE.values(): | |
143 content['text'] = '' | |
144 return content | |
145 | |
146 def getContent(self): | |
147 return self.__cleanContent(editor_widget.HTMLTextEditor.getContent(self)) | |
148 | |
149 def setContent(self, content): | |
150 content = self.__cleanContent(content) | |
151 editor_widget.BaseTextEditor.setContent(self, content) | |
152 | |
153 def setDisplayContent(self): | |
154 status = self._original_content['text'] | |
155 try: | |
156 presence = self.host.status_panel.presence | |
157 except AttributeError: # during initialization | |
158 presence = None | |
159 if not status: | |
160 if presence and presence in C.PRESENCE: | |
161 status = C.PRESENCE[presence] | |
162 else: | |
163 status = self.EMPTY_STATUS | |
164 self.display.setHTML(addURLToText(status)) | |
165 | |
166 | |
167 class PresenceStatusMenuBar(base_widget.WidgetMenuBar): | |
168 def __init__(self, parent): | |
169 styles = {'menu_bar': 'presence-button'} | |
170 base_widget.WidgetMenuBar.__init__(self, parent, parent.host, styles=styles) | |
171 self.button = self.addCategory(u"◉", u"◉", '') | |
172 for presence, presence_i18n in C.PRESENCE.items(): | |
173 html = u'<span class="%s">◉</span> %s' % (contact_list.buildPresenceStyle(presence), presence_i18n) | |
174 self.addMenuItem([u"◉", presence], [u"◉", html], '', base_menu.MenuCmd(self, 'changePresenceCb', presence), asHTML=True) | |
175 self.parent_panel = parent | |
176 | |
177 def changePresenceCb(self, presence): | |
178 """Callback to notice the backend of a new presence set by the user. | |
179 @param presence (str): the new presence is a value in ('', 'chat', 'away', 'dnd', 'xa') | |
180 """ | |
181 self.host.bridge.call('setStatus', None, presence, self.parent_panel.status_panel.status) | |
182 | |
183 @classmethod | |
184 def getCategoryHTML(cls, menu_name_i18n, type_): | |
185 return menu_name_i18n | |
186 | |
187 | |
188 class PresenceStatusPanel(HorizontalPanel, ClickHandler): | |
189 | |
190 def __init__(self, host, presence="", status=""): | |
191 self.host = host | |
192 HorizontalPanel.__init__(self, Width='100%') | |
193 self.menu = PresenceStatusMenuBar(self) | |
194 self.status_panel = StatusPanel(host, status=status) | |
195 self.setPresence(presence) | |
196 | |
197 panel = HorizontalPanel() | |
198 panel.add(self.menu) | |
199 panel.add(self.status_panel) | |
200 panel.setCellVerticalAlignment(self.menu, 'baseline') | |
201 panel.setCellVerticalAlignment(self.status_panel, 'baseline') | |
202 panel.setStyleName("marginAuto") | |
203 self.add(panel) | |
204 | |
205 self.status_panel.edit(False) | |
206 | |
207 ClickHandler.__init__(self) | |
208 self.addClickListener(self) | |
209 | |
210 @property | |
211 def presence(self): | |
212 return self._presence | |
213 | |
214 @property | |
215 def status(self): | |
216 return self.status_panel._original_content['text'] | |
217 | |
218 def setPresence(self, presence): | |
219 self._presence = presence | |
220 contact_list.setPresenceStyle(self.menu.button, self._presence) | |
221 | |
222 def setStatus(self, status): | |
223 self.status_panel.setContent({'text': status}) | |
224 self.status_panel.setDisplayContent() | |
225 | |
226 def onClick(self, sender): | |
227 # As status is the default target of uniBar, we don't want to select anything if click on it | |
228 self.host.setSelected(None) | |
229 | |
230 | |
231 ### Panels managing the main area ### | |
232 | |
233 | |
234 class MainPanel(AbsolutePanel): | |
235 """The panel which take the whole screen""" | |
236 | |
237 def __init__(self, host): | |
238 self.host = host | |
239 AbsolutePanel.__init__(self) | |
240 | |
241 # menu | |
242 self.menu = menu.MainMenuPanel(host) | |
243 | |
244 # # unibox | |
245 # self.unibox_panel = UniBoxPanel(host) | |
246 # self.unibox_panel.setVisible(False) | |
247 | |
248 # contacts | |
249 self._contacts = HorizontalPanel() | |
250 self._contacts.addStyleName('globalLeftArea') | |
251 self.contacts_switch = Button(u'«', self._contactsSwitch) | |
252 self.contacts_switch.addStyleName('contactsSwitch') | |
253 self._contacts.add(self.contacts_switch) | |
254 | |
255 # tabs | |
256 self.tab_panel = libervia_widget.MainTabPanel(host) | |
257 self.tab_panel.addWidgetsTab(_(u"Discussions"), select=True, locked=True) | |
258 | |
259 self.header = AbsolutePanel() | |
260 self.header.add(self.menu) | |
261 # self.header.add(self.unibox_panel) | |
262 self.header.add(self.host.status_panel) | |
263 self.header.setStyleName('header') | |
264 self.add(self.header) | |
265 | |
266 self._hpanel = HorizontalPanel() | |
267 self._hpanel.add(self._contacts) | |
268 self._hpanel.add(self.tab_panel) | |
269 self.add(self._hpanel) | |
270 | |
271 self.setWidth("100%") | |
272 Window.addWindowResizeListener(self) | |
273 | |
274 def addContactList(self, contact_list): | |
275 self._contacts.add(contact_list) | |
276 | |
277 def _contactsSwitch(self, btn=None): | |
278 """ (Un)hide contacts panel """ | |
279 if btn is None: | |
280 btn = self.contacts_switch | |
281 clist = self.host.contact_list | |
282 clist.setVisible(not clist.getVisible()) | |
283 btn.setText(u"«" if clist.getVisible() else u"»") | |
284 self.host.resize() | |
285 | |
286 def _contactsMove(self, parent): | |
287 """Move the contacts container (containing the contact list and | |
288 the "hide/show" button) to another parent, but always as the | |
289 first child position (insert at index 0). | |
290 """ | |
291 if self._contacts.getParent(): | |
292 if self._contacts.getParent() == parent: | |
293 return | |
294 self._contacts.removeFromParent() | |
295 parent.insert(self._contacts, 0) | |
296 | |
297 def onWindowResized(self, width, height): | |
298 _elts = doc().getElementsByClassName('gwt-TabBar') | |
299 if not _elts.length: | |
300 tab_bar_h = 0 | |
301 else: | |
302 tab_bar_h = _elts.item(0).offsetHeight | |
303 ideal_height = Window.getClientHeight() - tab_bar_h | |
304 self.setHeight("%s%s" % (ideal_height, "px")) | |
305 | |
306 def refresh(self): | |
307 """Refresh the main panel""" | |
308 self.unibox_panel.refresh() | |
309 self.host.contact_panel.refresh() | |
310 | |
311 |