Mercurial > libervia-web
view libervia.py @ 16:099c05a0dcab
browser side: microblog panel improvments
- panels can now be changed by DnD
- contacts panel's title can be used by DnD for the global microblog panel
- microblogs now appear in reverse order (from bottom to top)
- MicroblogPanel now use a ScrollPanel and 100% space
- user's message now appear in correct group when using groupblog
- MagicBox renamed to UniBox
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 15 Apr 2011 02:01:34 +0200 |
parents | 9bf8ed012adc |
children | c725b702e927 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ Libervia: a Salut à Toi frontend Copyright (C) 2011 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 pyjamas.ui.SimplePanel import SimplePanel from pyjamas.ui.RootPanel import RootPanel from pyjamas.ui.VerticalPanel import VerticalPanel from pyjamas.ui.HorizontalPanel import HorizontalPanel from pyjamas.ui.HTMLPanel import HTMLPanel from pyjamas.ui.ScrollPanel import ScrollPanel from pyjamas.ui.Grid import Grid from pyjamas.ui.Label import Label from pyjamas.ui import HasAlignment from pyjamas.ui.MenuBar import MenuBar from pyjamas.ui.MenuItem import MenuItem from pyjamas.ui.AutoComplete import AutoCompleteTextBox from pyjamas.ui.DropWidget import DropWidget from pyjamas import Window from pyjamas.JSONService import JSONProxy from pyjamas.ui.KeyboardListener import KEY_ENTER from register import RegisterPanel, RegisterBox from pyjamas import DOM from contact import ContactPanel from datetime import datetime class LiberviaJsonProxy(JSONProxy): def __init__(self, *args, **kwargs): JSONProxy.__init__(self, *args, **kwargs) self.handler=self self.cb={} def call(self, method, cb, *args): if cb: self.cb[method] = cb self.callMethod(method,args) def onRemoteResponse(self, response, request_info): if self.cb.has_key(request_info.method): self.cb[request_info.method](response) del self.cb[request_info.method] def onRemoteError(self, code, errobj, request_info): if code != 0: Window.alert("Internal server error") else: if isinstance(errobj['message'],dict): Window.alert("Error %s: %s" % (errobj['message']['faultCode'], errobj['message']['faultString'])) else: Window.alert("Error: %s" % errobj['message']) class RegisterCall(LiberviaJsonProxy): def __init__(self): LiberviaJsonProxy.__init__(self, "/register_api", ["isRegistered","isConnected","connect"]) self.handler=self self.cb={} class BridgeCall(LiberviaJsonProxy): def __init__(self): LiberviaJsonProxy.__init__(self, "/json_api", ["getContacts", "sendMblog", "getMblogNodes"]) class BridgeSignals(LiberviaJsonProxy): def __init__(self): LiberviaJsonProxy.__init__(self, "/json_signal_api", ["getSignals"]) class MenuCmd: def __init__(self, object, handler): self._object = object self._handler = handler def execute(self): handler = getattr(self._object, self._handler) handler() class Menu(SimplePanel): def __init__(self): SimplePanel.__init__(self) menu_general = MenuBar(vertical=True) menu_general.addItem("Properties", MenuCmd(self, "onProperties")) menu_games = MenuBar(vertical=True) menu_games.addItem("Tarot", MenuCmd(self, "onTarotGame")) menu_games.addItem("Xiangqi", MenuCmd(self, "onXiangqiGame")) menubar = MenuBar(vertical=False) menubar.addItem(MenuItem("General", menu_general)) menubar.addItem(MenuItem("Games", True, menu_games)) self.add(menubar) def onProperties(self): Window.alert("Properties selected") def onTarotGame(self): Window.alert("Tarot selected") def onXiangqiGame(self): Window.alert("Xiangqi selected") class UniBox(AutoCompleteTextBox): def __init__(self, host): AutoCompleteTextBox.__init__(self) self.host = host def addKey(self, key): self.getCompletionItems().completions.append(key) def onKeyPress(self, sender, keycode, modifiers): if keycode == KEY_ENTER and not self.visible: self.host.bridge.call('sendMblog', None, self.getText()) self.setText('') def complete(self): #self.visible=False #XXX: self.visible is not unset in pyjamas when ENTER is pressed and a completion is done #XXX: fixed directly on pyjamas, if the patch is accepted, no need to walk around this return AutoCompleteTextBox.complete(self) class DropCell(DropWidget): """Cell in the middle grid which replace itself with the dropped widget on DnD""" def __init__(self): DropWidget.__init__(self) def onDragEnter(self, event): print "drag enter" self.addStyleName('dragover') DOM.eventPreventDefault(event) def onDragLeave(self, event): print "drag leave" self.removeStyleName('dragover') def onDragOver(self, event): DOM.eventPreventDefault(event) def _getCellAndRow(self, grid, event): """Return cell and row index where the event is occuring""" cell = grid.getEventTargetCell(event) row = DOM.getParent(cell) return (row.rowIndex, cell.cellIndex) def onDrop(self, event): print "Empty Panel: onDrop" dt = event.dataTransfer #'text', 'text/plain', and 'Text' are equivalent. try: item = dt.getData("text/plain") item_type = dt.getData("type") print "message: %s" % item print "type: %s" % item_type except: print "no message found" item=' ' item_type = None DOM.eventPreventDefault(event) if item_type=="GROUP": _mblog = MicroblogPanel(self.host, item) _mblog.setAcceptedGroup(item) elif item_type=="CONTACT": _mblog = MicroblogPanel(self.host, accept_all=True) self.host.mpanels.remove(self) self.host.mpanels.append(_mblog) print "DEBUG" grid = self.getParent() row_idx, cell_idx = self._getCellAndRow(grid, event) self.removeFromParent() grid.setWidget(row_idx, cell_idx, _mblog) print "index:", row_idx, cell_idx #FIXME: delete object ? Check the right way with pyjamas #self.host.middle_panel.changePanel(self.data,self.host.mpanels[0]) class EmptyPanel(DropCell, SimplePanel): """Empty dropable panel""" def __init__(self, host): SimplePanel.__init__(self) self.host = host _panel = HTMLPanel(" ") self.add(_panel) self.setHeight('100%') DropCell.__init__(self) class MicroblogEntry(SimplePanel): def __init__(self, body, author, timestamp): SimplePanel.__init__(self) _datetime = datetime.fromtimestamp(timestamp) panel = HTMLPanel("<div class='mb_entry_header'><span class='mb_entry_author'>%(author)s</span> on <span class='mb_entry_timestamp'>%(timestamp)s</span></div><div class='mb_entry_body'>%(body)s</div>" % {"author": author, "timestamp": _datetime, "body": body} ) panel.setStyleName('microblogEntry') self.add(panel) class MicroblogPanel(DropCell, ScrollPanel): def __init__(self,host, title=' ', accept_all=False): """Panel used to show microblog @param title: title of the panel @param accept_all: if true, show every message, without filtering jids""" self.host = host self.accept_all = accept_all title=title.replace('<','<').replace('>','>') self.accepted_groups = [] _class = ['mb_panel_header'] if title == ' ': _class.append('empty_header') ScrollPanel.__init__(self) self.vpanel = VerticalPanel() self.vpanel.add(HTMLPanel("<div class='%s'>%s</div>" % (','.join(_class),title))) self.vpanel.setWidth('100%') self.setHeight('100%') self.setStyleName('microblogPanel') self.add(self.vpanel) DropCell.__init__(self) def addEntry(self, text, author=None, timestamp=None): """Add an entry to the panel @param text: main text of the entry @param author: who wrote the entry @param date: when the entry was written""" _entry = MicroblogEntry(text, author, timestamp) self.vpanel.insert(_entry,1) def setAcceptedGroup(self, group): """Set the group which can be displayed in this panel @param group: string of the group, or list of string """ if isinstance(group, list): self.accepted_groups.extend(group) else: self.accepted_groups.append(group) def isJidAccepted(self, jid): """Tell if a jid is actepted and show in this panel @param jid: jid @return: True if the jid is accepted""" if self.accept_all: return True for group in self.accepted_groups: if self.host.contactPanel.isContactInGroup(group, jid): return True return False class MiddlePannel(HorizontalPanel): def __init__(self, host): self.host=host HorizontalPanel.__init__(self) self._left = self.host.contactPanel self._right = Grid(1,3) self._right.setWidth('100%') self._right.setHeight('100%') self.add(self._left) self.setCellWidth(self._left, "15%") self.add(self._right) self.setCellWidth(self._right, "85%") self.setHeight('100%') def changePanel(self, idx, panel): print "panel:",panel print "idx:",idx self._right.setWidget(0,idx,panel) class MainPanel(VerticalPanel): def __init__(self, host): self.host=host VerticalPanel.__init__(self) self.setHorizontalAlignment(HasAlignment.ALIGN_LEFT) self.setVerticalAlignment(HasAlignment.ALIGN_TOP) menu = Menu() uni_box = host.uniBox self.middle_panel = MiddlePannel(self.host) self.middle_panel.setWidth('100%') self.add(menu) self.add(uni_box) self.add(self.middle_panel) self.setCellHeight(menu, "5%") self.setCellHeight(uni_box, "5%") self.setCellVerticalAlignment(uni_box, HasAlignment.ALIGN_CENTER) self.setCellHorizontalAlignment(uni_box, HasAlignment.ALIGN_CENTER) self.setCellHeight(self.middle_panel, "90%") self.setCellWidth(self.middle_panel, "100%") self.setWidth("100%") self.setHeight("100%") class SatWebFrontend: def onModuleLoad(self): self.bridge = BridgeCall() self.bridge_signals = BridgeSignals() self.uniBox = UniBox(self) self.uniBox.addKey("@@: ") self.contactPanel = ContactPanel(self) self.panel = MainPanel(self) self.middle_panel = self.panel.middle_panel self.mpanels = [EmptyPanel(self), MicroblogPanel(self, accept_all=True), EmptyPanel(self)] self.middle_panel.changePanel(0,self.mpanels[0]) self.middle_panel.changePanel(1,self.mpanels[1]) self.middle_panel.changePanel(2,self.mpanels[2]) self._dialog = None RootPanel().add(self.panel) self._register = RegisterCall() self._register.call('isRegistered',self._isRegisteredCB) def _isRegisteredCB(self, registered): if not registered: self._dialog = RegisterBox(self.logged,centered=True) self._dialog.show() else: self._register.call('isConnected', self._isConnectedCB) def _isConnectedCB(self, connected): if not connected: self._register.call('connect',self.logged) else: self.logged() def logged(self): if self._dialog: self._dialog.hide() del self._dialog # don't work if self._dialog is None #it's time to fill the page self.bridge.call('getContacts', self._getContactsCB) self.bridge_signals.call('getSignals', self._getSignalsCB) def _getContactsCB(self, contacts_data): for contact in contacts_data: jid, attributes, groups = contact self.contactPanel.addContact(jid, attributes, groups) def _getSignalsCB(self, signal_data): bridge_signals = BridgeSignals() bridge_signals.call('getSignals', self._getSignalsCB) print('Got signal ==> name: %s, params: %s' % (signal_data[0],signal_data[1])) name,args = signal_data if name == 'personalEvent': self._personalEventCb(*args) ## Signals callbacks ## def _personalEventCb(self, sender, event_type, data, profile): if event_type == "MICROBLOG": if not data.has_key('content'): print ("WARNING: No content found in microblog data") return print dir('') if data.has_key('groups'): _groups = set(data['groups'].split() if data['groups'] else []) else: _groups=None print "_groups=",_groups for panel in self.mpanels: if isinstance(panel,MicroblogPanel) and (panel.isJidAccepted(sender) or _groups == None or _groups.intersection(panel.accepted_groups)): print "sender:",sender content = data['content'] author = data.get('author') print "timestamp: %s" % data.get('timestamp') timestamp = float(data.get('timestamp',0)) #XXX: int doesn't work here panel.addEntry(content, author, timestamp) if __name__ == '__main__': pyjd.setup("http://localhost:8080/libervia.html") app = SatWebFrontend() app.onModuleLoad() pyjd.run()