diff browser_side/panels.py @ 33:e70521e6d803

browser side, misc stuffs - dropCell are now keep the dragover style if the mouse go on an other widget inside them - uniBox moved to panels.py - chatPanel now partially managed MUC Room - room joined open in a new tab (as Tarot games) - MainPanel & MainTabPanel are now dynamically sized in pixels - fixed stupid bug in json callbacks management
author Goffi <goffi@goffi.org>
date Sat, 14 May 2011 01:11:08 +0200
parents d89982865c57
children ed935f763cc8
line wrap: on
line diff
--- a/browser_side/panels.py	Sat May 14 00:58:11 2011 +0200
+++ b/browser_side/panels.py	Sat May 14 01:11:08 2011 +0200
@@ -21,24 +21,33 @@
 
 import pyjd # this is dummy in pyjs
 from pyjamas.ui.SimplePanel import SimplePanel
+from pyjamas.ui.FlowPanel import FlowPanel
+from pyjamas.ui.AbsolutePanel import AbsolutePanel
 from pyjamas.ui.VerticalPanel import VerticalPanel
 from pyjamas.ui.HorizontalPanel import HorizontalPanel
 from pyjamas.ui.ScrollPanel import ScrollPanel
 from pyjamas.ui.TabPanel import TabPanel
 from pyjamas.ui.HTMLPanel import HTMLPanel
+from pyjamas.ui.PopupPanel import PopupPanel
 from pyjamas.ui.Grid import Grid
+from pyjamas.ui.AutoComplete import AutoCompleteTextBox
 from pyjamas.ui.MenuBar import MenuBar
 from pyjamas.ui.MenuItem import MenuItem
 from pyjamas.ui.Label import Label
+from pyjamas.ui.HTML import HTML
 from pyjamas.ui.DropWidget import DropWidget
 from pyjamas.ui.ClickListener import ClickHandler
 from pyjamas.ui import HasAlignment
+from pyjamas.ui.KeyboardListener import KEY_ENTER
+from pyjamas.Timer import Timer
 from pyjamas import Window
 from pyjamas import DOM
+from __pyjamas__ import JS, doc
 
 from pyjamas.dnd import makeDraggable
 from pyjamas.ui.DragWidget import DragWidget, DragContainer
 from jid import JID
+from tools import html_sanitize
 from datetime import datetime
 from time import time
 from dialog import ContactsChooser
@@ -96,8 +105,13 @@
         DOM.eventPreventDefault(event)
 
     def onDragLeave(self, event):
-        print "drag leave"
-        self.removeStyleName('dragover')
+        print "\ndrag leave"
+        if event.clientX <= self.getAbsoluteLeft() or event.clientY <= self.getAbsoluteTop() or\
+           event.clientX >= self.getAbsoluteLeft() + self.getOffsetWidth()-1 or event.clientY >= self.getAbsoluteTop() + self.getOffsetHeight()-1:
+           #We check that we are inside widget's box, and we don't remove the style in this case because
+           #if the mouse is over a widget inside the DropWidget, if will leave the DropWidget, and we
+           #don't want that
+            self.removeStyleName('dragover')
 
     def onDragOver(self, event):
         DOM.eventPreventDefault(event)
@@ -150,9 +164,125 @@
         self.host = host
         _panel = HTMLPanel("&nbsp;")
         self.add(_panel)
-        self.setHeight('100%')
+        self.setHeight('50%')
         DropCell.__init__(self)
     
+class UniBoxPanel(SimplePanel):
+    """Panel containing the UniBox"""
+
+    def __init__(self, host):
+        SimplePanel.__init__(self)
+        self.setStyleName('uniBoxPanel')
+        self.unibox = UniBox(host)
+        self.unibox.setWidth('100%')
+        self.add(self.unibox)
+
+class UniBox(AutoCompleteTextBox):
+    """This text box is used as a main typing point, for message, microblog, etc"""
+
+    def __init__(self, host):
+        AutoCompleteTextBox.__init__(self)
+        self._popup = None
+        self._timer = Timer(notify=self._timeCb)
+        self.host = host
+
+    def addKey(self, key):
+        self.getCompletionItems().completions.append(key)
+
+    def showWarning(self, target_data):
+        type, target = target_data
+        if type == "PUBLIC":
+            msg = "This message will be PUBLIC and everybody will be able to see it, even people you don't know"
+            style = "targetPublic"
+        elif type == "GROUP":
+            msg = "This message will be published for all the people of the group <span class='warningTarget'>%s</span>" % (target or '')
+            style = "targetGroup"
+        elif type == "STATUS":
+            msg = "This will be your new status message"
+            style = "targetStatus"
+        elif type == "ONE2ONE":
+            msg = "This message will be sent to your contact <span class='warningTarget'>%s</span>" % target
+            style = "targetOne2One"
+        else:
+            print "WARNING: undetermined target for this message"
+            return
+        contents = HTML(msg)
+
+        self._popup = PopupPanel(autoHide=False, modal=False)
+        self._popup.target_data = target_data
+        self._popup.add(contents)
+        self._popup.setStyleName("warningPopup")
+        if style:
+            self._popup.addStyleName(style)
+
+        left = 0
+        top  = 0 #max(0, self.getAbsoluteTop() - contents.getOffsetHeight() - 2)   
+        self._popup.setPopupPosition(left, top)
+        self._popup.setPopupPosition(left, top)
+        self._popup.show()
+
+    def _timeCb(self, timer):
+        if self._popup:
+            self._popup.hide()
+            del self._popup
+            self._popup = None
+
+    def _getTarget(self, txt):
+        """Say who will receive the messsage
+        Return a tuple (target_type, target info)"""
+        type = None
+        target = None
+        if txt.startswith('@@: '):
+            type = "PUBLIC"
+        elif txt.startswith('@'):
+            type = "GROUP"
+            _end = txt.find(': ')
+            if _end == -1:
+                type = "STATUS"
+            else:
+                target = txt[1:_end] #only one target group is managed for the moment
+                if not target in self.host.contact_panel.getGroups():
+                    target = None
+        elif self.host.selected == None:
+            type = "STATUS"
+        elif isinstance(self.host.selected, ChatPanel):
+            type = "ONE2ONE"
+            target = str(self.host.selected.target)
+        else:
+            print self.host.selected
+            type = "UNKNOWN"
+        return (type, target)
+
+    def onKeyPress(self, sender, keycode, modifiers):
+        _txt = self.getText()
+        if not self._popup:
+            self.showWarning(self._getTarget(_txt))
+        else:
+            _target = self._getTarget(_txt)
+            if _target != self._popup.target_data:
+                self._timeCb(None) #we remove the popup
+                self.showWarning(_target)
+
+        self._timer.schedule(2000)
+
+        if keycode == KEY_ENTER and not self.visible:
+            if _txt:
+                if _txt.startswith('@'):
+                    self.host.bridge.call('sendMblog', None, self.getText())
+                elif self.host.selected == None:
+                    self.host.bridge.call('setStatus', None, _txt)
+                elif isinstance(self.host.selected, ChatPanel):
+                    _chat = self.host.selected
+                    mess_type = "groupchat" if _chat.type=='group' else "chat"
+                    self.host.bridge.call('sendMessage', None, str(_chat.target), _txt, '', mess_type)
+            self.setText('')
+            self._timeCb(None) #we remove the popup
+
+    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 MicroblogEntry(SimplePanel):
 
     def __init__(self, body, author, timestamp):
@@ -185,7 +315,7 @@
         self.vpanel = VerticalPanel()
         self.vpanel.add(HTMLPanel("<div class='%s'>%s</div>" % (','.join(_class),title)))
         self.vpanel.setWidth('100%')
-        self.setHeight('100%')
+        self.setHeight('50%')
         self.setStyleName('microblogPanel')
         self.add(self.vpanel)
         DropCell.__init__(self)
@@ -252,8 +382,34 @@
             "msg": msg}
             )
         self.setStyleName('chatText')
-    
-class ChatPanel(DropCell, ClickHandler, ScrollPanel):
+
+class Occupant(HTML):
+    """Occupant of a MUC room"""
+
+    def __init__(self, nick):
+        self.nick = nick
+        HTML.__init__(self, "<div class='occupant'>%s</div>" % html_sanitize(nick)) 
+
+class OccupantsList(AbsolutePanel):
+    """Panel user to show occupants of a room"""
+
+    def __init__(self):
+        AbsolutePanel.__init__(self)
+        self.setStyleName('occupantsList')
+        self.occupants = set()
+        #self.setHeight('100%')
+
+    def addOccupant(self, nick):
+       _occupant = Occupant(nick)
+       print "addOccupant: nick", nick
+       print _occupant
+       self.occupants.add(_occupant)
+       self.add(_occupant)
+
+
+
+
+class ChatPanel(DropCell, ClickHandler, AbsolutePanel):
 
     def __init__(self, host, target, type='one2one'):
         """Panel used for conversation (one 2 one or group chat)
@@ -261,6 +417,8 @@
         @param target: entity (JID) with who we have a conversation (contact's jid for one 2 one chat, or MUC room)
         @param type: one2one for simple conversation, group for MUC"""
         self.host = host
+        self.type = type
+        self.nick = None
         if not target:
             print "ERROR: Empty target !"
             return
@@ -268,13 +426,33 @@
         title="%s" % target.bare
         title.replace('<','&lt;').replace('>','&gt;')
         _class = ['mb_panel_header']
-        ScrollPanel.__init__(self)
-        self.content = VerticalPanel()
-        self.content.add(HTMLPanel("<div class='%s'>%s</div>" % (','.join(_class),title)))
-        self.content.setWidth('100%')
-        self.setHeight('100%')
+        AbsolutePanel.__init__(self)
+        self.header = HTMLPanel("<div class='%s'>%s</div>" % (','.join(_class),title))
+        self.body = AbsolutePanel()
+        self.body.setStyleName('chatPanel_body')
+        #self.body.setWidth('100%')
+        #self.body.setHeight('50%')
+        chat_area = HorizontalPanel()
+        chat_area.setStyleName('chatArea')
+        #chat_area.setHeight('50%')
+        if type == 'group':
+            self.occupants_list = OccupantsList()
+            chat_area.add(self.occupants_list)
+        self.body.add(chat_area)
+        self.content = AbsolutePanel()
+        self.content.setStyleName('chatContent')
+        #_scrollp.setHeight('50%')
+        #_scrollp.setWidth('100%')
+        chat_area.add(ScrollPanel(self.content, Size=("100%", "100%")))
+        self.add(self.header)
+        self.add(self.body)
+        #self.content.setWidth('100%')
+        #self.main_panel.setVerticalAlignment(HasAlignment.ALIGN_TOP)
+        #self.body.setVerticalAlignment(HasAlignment.ALIGN_TOP)
+        #chat_area.setVerticalAlignment(HasAlignment.ALIGN_TOP)
+        #self.setWidth('100%')
+        #self.setHeight('50%')
         self.setStyleName('chatPanel')
-        self.add(self.content)
         DropCell.__init__(self)
         ClickHandler.__init__(self)
         self.addClickListener(self)
@@ -285,21 +463,27 @@
     def setUserNick(self, nick):
         """Set the nick of the user, usefull for e.g. change the color of the user"""
         self.nick = nick
-    
+   
+    def setPresents(self, nicks):
+        """Set the users presents in this room
+        @param occupants: list of nicks (string)"""
+        for nick in nicks:
+            self.occupants_list.addOccupant(nick)
+
     def historyPrint(self, size=20):
         """Print the initial history"""
         def getHistoryCB(history):
             stamps=history.keys()
             stamps.sort()
-            for stamp in stamps: 
+            for stamp in stamps:
                 self.printMessage(history[stamp][0], history[stamp][1], stamp)
         self.host.bridge.call('getHistory', getHistoryCB, self.host.whoami.bare, str(self.target), 20)
     
     def printMessage(self, from_jid, msg, timestamp=None):
         """Print message in chat window. Must be implemented by child class"""
         _jid=JID(from_jid)
-        nick = _jid.node 
-        mymess = _jid.bare == self.host.whoami.bare #mymess = True if message comes from local user
+        nick = _jid.node if self.type=='one2one' else _jid.resource 
+        mymess = _jid.resource == self.nick if self.type == "group" else  _jid.bare == self.host.whoami.bare #mymess = True if message comes from local user
         """if msg.startswith('/me '):
             self.printInfo('* %s %s' % (nick, msg[4:]),type='me')
             return"""
@@ -313,12 +497,12 @@
         self._left = self.host.contact_panel
         self._right = Grid(1,3)
         self._right.setWidth('100%')
-        self._right.setHeight('100%')
+        self._right.setHeight('50%')
         self.add(self._left)
         self.setCellWidth(self._left, "15%")
         self.add(self._right)
         self.setCellWidth(self._right, "85%")
-        self.setHeight('100%')
+        self.setHeight('50%')
 
     def changePanel(self, idx, panel):
         self._right.setWidget(0,idx,panel)
@@ -326,47 +510,116 @@
 class MainTabPannel(TabPanel):
 
     def __init__(self, host):
+        TabPanel.__init__(self)
         self.host=host
-        TabPanel.__init__(self)
+        TabPanel()
         self.tabBar.setVisible(False)
+        self.addStyleName('mainTabPanel')
+        Window.addWindowResizeListener(self)
+
+    def onWindowResized(self, width, height):
+        print "onWindowResized"
+        tab_panel_elt = self.getElement()
+        _elts = doc().getElementsByClassName('gwt-TabBar')
+        if not _elts.length:
+            print ("ERROR: not TabBar found, it should exist !")
+            tab_bar_h = 0
+        else:
+            tab_bar_h = _elts.item(0).offsetHeight
+        ideal_height = Window.getClientHeight() - tab_panel_elt.offsetTop - tab_bar_h - 5
+        print "ideal_height =",ideal_height
+        self.setWidth("%s%s" % (width-5, "px"));
+        self.setHeight("%s%s" % (ideal_height, "px"));
 
     def add(self, widget, tabText=None, asHTML=False):
         TabPanel.add(self, widget, tabText, asHTML)
         if self.getWidgetCount()>1:
             self.tabBar.setVisible(True)
+            self.host.resize()
         
-class MainPanel(VerticalPanel):
+"""class MainTabPannel(FlowPanel):
+
+    def __init__(self, host):
+        FlowPanel.__init__(self)
+        self.host=host
+        self.tab_panel = TabPanel()
+        self.tab_panel.tabBar.setVisible(False)
+        self.addStyleName('mainTabPanel')
+        FlowPanel.add(self, self.tab_panel)
+        self.tab_panel.setWidth('100%')
+        self.tab_panel.setHeight('100%')
+        Window.addWindowResizeListener(self)
+
+    def onWindowResized(self, width, height):
+        print "onWindowResized"
+        tab_panel_elt = self.getElement()
+        _elts = doc().getElementsByClassName('gwt-TabBar')
+        if not _elts.length:
+            print ("ERROR: not TabBar found, it should exist !")
+            tab_bar_h = 0
+        else:
+            tab_bar_h = _elts.item(0).offsetHeight
+        ideal_height = Window.getClientHeight() - tab_panel_elt.offsetTop - tab_bar_h - 5
+        print "ideal_height =",ideal_height
+        self.setWidth("%s%s" % (width-5, "px"));
+        self.setHeight("%s%s" % (ideal_height, "px"));
+
+    def selectTab(self, idx):
+        self.tab_panel.selectTab(idx)
+
+    def add(self, widget, tabText=None, asHTML=False):
+        self.tab_panel.add(widget, tabText, asHTML)
+        if self.tab_panel.getWidgetCount()>1:
+            self.tab_panel.tabBar.setVisible(True)
+            self.host.resize()"""
+
+class MainPanel(AbsolutePanel):
 
     def __init__(self, host):
         self.host=host
-        VerticalPanel.__init__(self)
+        AbsolutePanel.__init__(self)
 
-        self.setHorizontalAlignment(HasAlignment.ALIGN_LEFT)
-        self.setVerticalAlignment(HasAlignment.ALIGN_TOP)
+        #self.setHorizontalAlignment(HasAlignment.ALIGN_LEFT)
+        #self.setVerticalAlignment(HasAlignment.ALIGN_TOP)
 
         menu = Menu(host)
-        uni_box = host.uni_box
+        unibox_panel = UniBoxPanel(host)
+        self.host.setUniBox(unibox_panel.unibox)
         status = host.status_panel
-        self.tab_panel = MainTabPannel(self)
-        self.tab_panel.setWidth('100%')
-        self.tab_panel.setHeight('100%')
+        self.tab_panel = MainTabPannel(host)
+        #self.tab_panel.setWidth('100%')
+        #self.tab_panel.setHeight('50%')
         self.discuss_panel = MainDiscussionPannel(self.host)
         self.discuss_panel.setWidth('100%')
-        self.discuss_panel.setHeight('100%')
+        self.discuss_panel.setHeight('50%')
         self.tab_panel.add(self.discuss_panel, "Discussions")
         self.tab_panel.selectTab(0)
 
         self.add(menu)
-        self.add(uni_box)
+        self.add(unibox_panel)
         self.add(status)
         self.add(self.tab_panel)
+        #self.tab_panel.onWindowResized(Window.getClientWidth(), Window.getClientHeight())
         
-        self.setCellHeight(menu, "5%")
+        """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.tab_panel, "90%")
-        self.setCellWidth(self.tab_panel, "100%")
+        self.setCellWidth(self.tab_panel, "100%")"""
 
         self.setWidth("100%")
-        self.setHeight("100%")
+        #self.setHeight("99%")
+        Window.addWindowResizeListener(self)
+
+    def onWindowResized(self, width, height):
+        print "resizing: %s %s" % (width, height)
+        #self.setWidth("%s%s" % (width, "px"));
+        _elts = doc().getElementsByClassName('gwt-TabBar')
+        if not _elts.length:
+            tab_bar_h = 0
+        else:
+            tab_bar_h = _elts.item(0).offsetHeight
+        ideal_height = Window.getClientHeight() - tab_bar_h
+        self.setHeight("%s%s" % (ideal_height, "px"));
+