Mercurial > libervia-web
comparison browser_side/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 | 971e3812903a |
children | 36927be51481 |
comparison
equal
deleted
inserted
replaced
322:971e3812903a | 323:0b7934e75e76 |
---|---|
29 from pyjamas.ui.TextArea import TextArea | 29 from pyjamas.ui.TextArea import TextArea |
30 from pyjamas.ui.Label import Label | 30 from pyjamas.ui.Label import Label |
31 from pyjamas.ui.Button import Button | 31 from pyjamas.ui.Button import Button |
32 from pyjamas.ui.HTML import HTML | 32 from pyjamas.ui.HTML import HTML |
33 from pyjamas.ui.Image import Image | 33 from pyjamas.ui.Image import Image |
34 from pyjamas.ui.PopupPanel import PopupPanel | |
35 from pyjamas.ui.StackPanel import StackPanel | |
36 from pyjamas.ui.ClickListener import ClickHandler | 34 from pyjamas.ui.ClickListener import ClickHandler |
37 from pyjamas.ui.FlowPanel import FlowPanel | 35 from pyjamas.ui.FlowPanel import FlowPanel |
38 from pyjamas.ui.KeyboardListener import KEY_ENTER, KEY_UP, KEY_DOWN, KeyboardHandler | 36 from pyjamas.ui.KeyboardListener import KEY_ENTER, KEY_UP, KEY_DOWN, KeyboardHandler |
39 from pyjamas.ui.Event import BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT | |
40 from pyjamas.ui.MouseListener import MouseHandler | 37 from pyjamas.ui.MouseListener import MouseHandler |
38 from pyjamas.ui.FocusListener import FocusHandler | |
41 from pyjamas.Timer import Timer | 39 from pyjamas.Timer import Timer |
42 from pyjamas import DOM | 40 from pyjamas import DOM |
41 from pyjamas import Window | |
42 from __pyjamas__ import doc | |
43 | |
44 from tools import html_sanitize, setPresenceStyle | |
45 from base_panels import ChatText, OccupantsList, PopupMenuPanel | |
46 from datetime import datetime | |
47 from time import time | |
43 from card_game import CardPanel | 48 from card_game import CardPanel |
44 from radiocol import RadioColPanel | 49 from radiocol import RadioColPanel |
45 from menu import Menu | 50 from menu import Menu |
46 from jid import JID | 51 from jid import JID |
47 from tools import html_sanitize, inlineRoot, setPresenceStyle | |
48 from sat_frontends.tools.strings import addURLToText | |
49 from datetime import datetime | |
50 from time import time | |
51 import dialog | 52 import dialog |
52 import base_widget | 53 import base_widget |
53 from dialog import ConfirmDialog | |
54 import richtext | 54 import richtext |
55 | |
56 from constants import Const | |
55 from plugin_xep_0085 import ChatStateMachine | 57 from plugin_xep_0085 import ChatStateMachine |
56 from pyjamas import Window | |
57 from __pyjamas__ import doc | |
58 from sat_frontends.tools.games import SYMBOLS | 58 from sat_frontends.tools.games import SYMBOLS |
59 from sat_frontends import constants | 59 from sat_frontends.tools.strings import addURLToText |
60 from pyjamas.ui.FocusListener import FocusHandler | 60 |
61 import logging | 61 import logging |
62 | |
63 | |
64 const = constants.Const # to directly import 'const' doesn't work | |
65 | 62 |
66 | 63 |
67 class UniBoxPanel(HorizontalPanel): | 64 class UniBoxPanel(HorizontalPanel): |
68 """Panel containing the UniBox""" | 65 """Panel containing the UniBox""" |
69 | 66 |
423 """Actually set the entry content (header, icons, bubble...)""" | 420 """Actually set the entry content (header, icons, bubble...)""" |
424 self.delete_label = self.update_label = self.comment_label = None | 421 self.delete_label = self.update_label = self.comment_label = None |
425 self.bubble = self.editbox = self._current_comment = None | 422 self.bubble = self.editbox = self._current_comment = None |
426 self._setHeader() | 423 self._setHeader() |
427 if self.empty: | 424 if self.empty: |
428 self.editable_content = ['', const.SYNTAX_XHTML] | 425 self.editable_content = ['', Const.SYNTAX_XHTML] |
429 else: | 426 else: |
430 self.editable_content = [self.xhtml, const.SYNTAX_XHTML] if self.xhtml else [self.content, None] | 427 self.editable_content = [self.xhtml, Const.SYNTAX_XHTML] if self.xhtml else [self.content, None] |
431 self.setEntryDialog() | 428 self.setEntryDialog() |
432 self._setIcons() | 429 self._setIcons() |
433 | 430 |
434 def _setHeader(self): | 431 def _setHeader(self): |
435 """Set the entry header""" | 432 """Set the entry header""" |
587 def confirm_cb(answer): | 584 def confirm_cb(answer): |
588 if answer: | 585 if answer: |
589 self._blog_panel.host.bridge.call('deleteMblog', None, self.pub_data, self.comments) | 586 self._blog_panel.host.bridge.call('deleteMblog', None, self.pub_data, self.comments) |
590 | 587 |
591 target = 'message and all its comments' if self.comments else 'comment' | 588 target = 'message and all its comments' if self.comments else 'comment' |
592 _dialog = ConfirmDialog(confirm_cb, text="Do you really want to delete this %s?" % target) | 589 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete this %s?" % target) |
593 _dialog.show() | 590 _dialog.show() |
594 | 591 |
595 def _comment(self): | 592 def _comment(self): |
596 """Add an empty entry for a new comment""" | 593 """Add an empty entry for a new comment""" |
597 if self._current_comment: | 594 if self._current_comment: |
968 self.presence_button = Label(u"◉") | 965 self.presence_button = Label(u"◉") |
969 self.presence_button.setStyleName("presence-button") | 966 self.presence_button.setStyleName("presence-button") |
970 self.status_panel = StatusPanel(host, status=status) | 967 self.status_panel = StatusPanel(host, status=status) |
971 self.setPresence(presence) | 968 self.setPresence(presence) |
972 entries = {} | 969 entries = {} |
973 for value in const.PRESENCE.keys(): | 970 for value in Const.PRESENCE.keys(): |
974 entries.update({const.PRESENCE[value]: {"value": value}}) | 971 entries.update({Const.PRESENCE[value]: {"value": value}}) |
975 | 972 |
976 def callback(sender, key): | 973 def callback(sender, key): |
977 self.setPresence(entries[key]["value"]) # order matters | 974 self.setPresence(entries[key]["value"]) # order matters |
978 self.host.send([("STATUS", None)], self.status_panel.status) | 975 self.host.send([("STATUS", None)], self.status_panel.status) |
979 | 976 |
994 def getPresence(self): | 991 def getPresence(self): |
995 return self.presence | 992 return self.presence |
996 | 993 |
997 def setPresence(self, presence): | 994 def setPresence(self, presence): |
998 status = self.status_panel.status | 995 status = self.status_panel.status |
999 if not status.strip() or status == " " or (self.presence in const.PRESENCE and status == const.PRESENCE[self.presence]): | 996 if not status.strip() or status == " " or (self.presence in Const.PRESENCE and status == Const.PRESENCE[self.presence]): |
1000 self.changeStatus(const.PRESENCE[presence]) | 997 self.changeStatus(Const.PRESENCE[presence]) |
1001 self.presence = presence | 998 self.presence = presence |
1002 setPresenceStyle(self.presence_button, self.presence) | 999 setPresenceStyle(self.presence_button, self.presence) |
1003 | 1000 |
1004 def changeStatus(self, new_status): | 1001 def changeStatus(self, new_status): |
1005 self.status_panel.changeStatus(new_status) | 1002 self.status_panel.changeStatus(new_status) |
1006 | 1003 |
1007 def onClick(self, sender): | 1004 def onClick(self, sender): |
1008 # As status is the default target of uniBar, we don't want to select anything if click on it | 1005 # As status is the default target of uniBar, we don't want to select anything if click on it |
1009 self.host.setSelected(None) | 1006 self.host.setSelected(None) |
1010 | |
1011 | |
1012 class ChatText(HTMLPanel): | |
1013 | |
1014 def __init__(self, timestamp, nick, mymess, msg, xhtml = None): | |
1015 _date = datetime.fromtimestamp(float(timestamp or time())) | |
1016 _msg_class = ["chat_text_msg"] | |
1017 if mymess: | |
1018 _msg_class.append("chat_text_mymess") | |
1019 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>" % | |
1020 {"timestamp": _date.strftime("%H:%M"), | |
1021 "nick": "[%s]" % html_sanitize(nick), | |
1022 "msg_class": ' '.join(_msg_class), | |
1023 "msg": addURLToText(html_sanitize(msg)) if not xhtml else inlineRoot(xhtml)} #FIXME: images and external links must be removed according to preferences | |
1024 ) | |
1025 self.setStyleName('chatText') | |
1026 | |
1027 | |
1028 class Occupant(HTML): | |
1029 """Occupant of a MUC room""" | |
1030 | |
1031 def __init__(self, nick, special=""): | |
1032 HTML.__init__(self) | |
1033 self.nick = nick | |
1034 self.special = special | |
1035 self._refresh() | |
1036 | |
1037 def __str__(self): | |
1038 return self.nick | |
1039 | |
1040 def addSpecial(self, special=""): | |
1041 if special not in self.special: | |
1042 self.special += special | |
1043 self._refresh() | |
1044 | |
1045 def _refresh(self): | |
1046 special = "" if len(self.special) == 0 else " %s" % self.special | |
1047 self.setHTML("<div class='occupant'>%s%s</div>" % (html_sanitize(self.nick), special)) | |
1048 | |
1049 | |
1050 class OccupantsList(AbsolutePanel): | |
1051 """Panel user to show occupants of a room""" | |
1052 | |
1053 def __init__(self): | |
1054 AbsolutePanel.__init__(self) | |
1055 self.occupants_list = {} | |
1056 self.setStyleName('occupantsList') | |
1057 | |
1058 def addOccupant(self, nick): | |
1059 _occupant = Occupant(nick) | |
1060 self.occupants_list[nick] = _occupant | |
1061 self.add(_occupant) | |
1062 | |
1063 def removeOccupant(self, nick): | |
1064 try: | |
1065 self.remove(self.occupants_list[nick]) | |
1066 except KeyError: | |
1067 print "ERROR: trying to remove an unexisting nick" | |
1068 | |
1069 def clear(self): | |
1070 self.occupants_list.clear() | |
1071 AbsolutePanel.clear(self) | |
1072 | |
1073 def addSpecials(self, occupants=[], html=""): | |
1074 index = 0 | |
1075 special = html | |
1076 for occupant in occupants: | |
1077 if occupant in self.occupants_list.keys(): | |
1078 if isinstance(html, list): | |
1079 special = html[index] | |
1080 index = (index + 1) % len(html) | |
1081 self.occupants_list[occupant].addSpecial(special) | |
1082 | 1007 |
1083 | 1008 |
1084 class ChatPanel(base_widget.LiberviaWidget): | 1009 class ChatPanel(base_widget.LiberviaWidget): |
1085 | 1010 |
1086 def __init__(self, host, target, type_='one2one'): | 1011 def __init__(self, host, target, type_='one2one'): |
1388 @param enable: boolean | 1313 @param enable: boolean |
1389 @return: UniBox instance or None if disabled | 1314 @return: UniBox instance or None if disabled |
1390 """ | 1315 """ |
1391 self.unibox_panel.setVisible(enable) | 1316 self.unibox_panel.setVisible(enable) |
1392 return self.unibox_panel.setUniBox(enable) | 1317 return self.unibox_panel.setUniBox(enable) |
1393 | |
1394 | |
1395 class PopupMenuPanel(PopupPanel): | |
1396 """This implementation of a popup menu (context menu) allow you to assign | |
1397 two special methods which are common to all the items, in order to hide | |
1398 certain items and also easily define their callbacks. The menu can be | |
1399 bound to any of the mouse button (left, middle, right). | |
1400 """ | |
1401 def __init__(self, entries, hide=None, callback=None, vertical=True, style={}, **kwargs): | |
1402 """ | |
1403 @param entries: a dict of dicts, where each sub-dict is representing | |
1404 one menu item: the sub-dict key can be used as the item text and | |
1405 description, but optional "title" and "desc" entries would be used | |
1406 if they exists. The sub-dicts may be extended later to do | |
1407 more complicated stuff or overwrite the common methods. | |
1408 @param hide: function with 2 args: widget, key as string and | |
1409 returns True if that item should be hidden from the context menu. | |
1410 @param callback: function with 2 args: sender, key as string | |
1411 @param vertical: True or False, to set the direction | |
1412 @param item_style: alternative CSS class for the menu items | |
1413 @param menu_style: supplementary CSS class for the sender widget | |
1414 """ | |
1415 PopupPanel.__init__(self, autoHide=True, **kwargs) | |
1416 self._entries = entries | |
1417 self._hide = hide | |
1418 self._callback = callback | |
1419 self.vertical = vertical | |
1420 self.style = {"selected": None, "menu": "recipientTypeMenu", "item": "popupMenuItem"} | |
1421 self.style.update(style) | |
1422 self._senders = {} | |
1423 | |
1424 def _show(self, sender): | |
1425 """Popup the menu relative to this sender's position. | |
1426 @param sender: the widget that has been clicked | |
1427 """ | |
1428 menu = VerticalPanel() if self.vertical is True else HorizontalPanel() | |
1429 menu.setStyleName(self.style["menu"]) | |
1430 | |
1431 def button_cb(item): | |
1432 """You can not put that method in the loop and rely | |
1433 on _key, because it is overwritten by each step. | |
1434 You can rely on item.key instead, which is copied | |
1435 from _key after the item creation. | |
1436 @param item: the menu item that has been clicked | |
1437 """ | |
1438 if self._callback is not None: | |
1439 self._callback(sender=sender, key=item.key) | |
1440 self.hide(autoClosed=True) | |
1441 | |
1442 for _key in self._entries.keys(): | |
1443 entry = self._entries[_key] | |
1444 if self._hide is not None and self._hide(sender=sender, key=_key) is True: | |
1445 continue | |
1446 title = entry["title"] if "title" in entry.keys() else _key | |
1447 item = Button(title, button_cb) | |
1448 item.key = _key | |
1449 item.setStyleName(self.style["item"]) | |
1450 item.setTitle(entry["desc"] if "desc" in entry.keys() else title) | |
1451 menu.add(item) | |
1452 if len(menu.getChildren()) == 0: | |
1453 return | |
1454 self.add(menu) | |
1455 if self.vertical is True: | |
1456 x = sender.getAbsoluteLeft() + sender.getOffsetWidth() | |
1457 y = sender.getAbsoluteTop() | |
1458 else: | |
1459 x = sender.getAbsoluteLeft() | |
1460 y = sender.getAbsoluteTop() + sender.getOffsetHeight() | |
1461 self.setPopupPosition(x, y) | |
1462 self.show() | |
1463 if self.style["selected"]: | |
1464 sender.addStyleDependentName(self.style["selected"]) | |
1465 | |
1466 def _onHide(popup): | |
1467 if self.style["selected"]: | |
1468 sender.removeStyleDependentName(self.style["selected"]) | |
1469 return PopupPanel.onHideImpl(self, popup) | |
1470 | |
1471 self.onHideImpl = _onHide | |
1472 | |
1473 def registerClickSender(self, sender, button=BUTTON_LEFT): | |
1474 """Bind the menu to the specified sender. | |
1475 @param sender: the widget to which the menu should be bound | |
1476 @param: BUTTON_LEFT, BUTTON_MIDDLE or BUTTON_RIGHT | |
1477 """ | |
1478 self._senders.setdefault(sender, []) | |
1479 self._senders[sender].append(button) | |
1480 | |
1481 if button == BUTTON_RIGHT: | |
1482 # WARNING: to disable the context menu is a bit tricky... | |
1483 # The following seems to work on Firefox 24.0, but: | |
1484 # TODO: find a cleaner way to disable the context menu | |
1485 sender.getElement().setAttribute("oncontextmenu", "return false") | |
1486 | |
1487 def _onBrowserEvent(event): | |
1488 button = DOM.eventGetButton(event) | |
1489 if DOM.eventGetType(event) == "mousedown" and button in self._senders[sender]: | |
1490 self._show(sender) | |
1491 return sender.__class__.onBrowserEvent(sender, event) | |
1492 | |
1493 sender.onBrowserEvent = _onBrowserEvent | |
1494 | |
1495 def registerMiddleClickSender(self, sender): | |
1496 self.registerClickSender(sender, BUTTON_MIDDLE) | |
1497 | |
1498 def registerRightClickSender(self, sender): | |
1499 self.registerClickSender(sender, BUTTON_RIGHT) | |
1500 | |
1501 | |
1502 class ToggleStackPanel(StackPanel): | |
1503 """This is a pyjamas.ui.StackPanel with modified behavior. All sub-panels ca be | |
1504 visible at the same time, clicking a sub-panel header will not display it and hide | |
1505 the others but only toggle its own visibility. The argument 'visibleStack' is ignored. | |
1506 Note that the argument 'visible' has been added to listener's 'onStackChanged' method. | |
1507 """ | |
1508 | |
1509 def __init__(self, **kwargs): | |
1510 StackPanel.__init__(self, **kwargs) | |
1511 | |
1512 def onBrowserEvent(self, event): | |
1513 if DOM.eventGetType(event) == "click": | |
1514 index = self.getDividerIndex(DOM.eventGetTarget(event)) | |
1515 if index != -1: | |
1516 self.toggleStack(index) | |
1517 | |
1518 def add(self, widget, stackText="", asHTML=False, visible=False): | |
1519 StackPanel.add(self, widget, stackText, asHTML) | |
1520 self.setStackVisible(self.getWidgetCount() - 1, visible) | |
1521 | |
1522 def toggleStack(self, index): | |
1523 if index >= self.getWidgetCount(): | |
1524 return | |
1525 visible = not self.getWidget(index).getVisible() | |
1526 self.setStackVisible(index, visible) | |
1527 for listener in self.stackListeners: | |
1528 listener.onStackChanged(self, index, visible) |