wcc_dialog.py :  » GUI » PmwContribD » PmwContribD-r2_0_2 » SampleApps » WhackyChat » Client » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » PmwContribD 
PmwContribD » PmwContribD r2_0_2 » SampleApps » WhackyChat » Client » wcc_dialog.py
#!/usr/bin/env python
#
# $Id: wcc_dialog.py,v 1.3 2001/11/03 11:05:22 doughellmann Exp $
#
# Copyright 2001 Doug Hellmann.
#
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

"""Messaging dialog.

"""

__rcs_info__ = {
    #
    #  Creation Information
    #
    'module_name'  : '$RCSfile: wcc_dialog.py,v $',
    'rcs_id'       : '$Id: wcc_dialog.py,v 1.3 2001/11/03 11:05:22 doughellmann Exp $',
    'creator'      : 'Doug Hellmann <doug@hellfly.net>',
    'project'      : 'PmwContribD',
    'created'      : 'Sun, 01-Apr-2001 13:20:32 EDT',

    #
    #  Current Information
    #
    'author'       : '$Author: doughellmann $',
    'version'      : '$Revision: 1.3 $',
    'date'         : '$Date: 2001/11/03 11:05:22 $',
}

#
# Import system modules
#
import string
import socket
import sys
import time
import re
import os

import Pmw
from Tkinter import *

#
# Import Local modules
#
import GuiAppD
import Table
import tkMessageBox

import wcc_protocol


#
# Module
#


def updateDialogSettings(**kw):
    """
    Works for:
        title
        ringbell
        <tag style attrs>
    """
    for dialog in _message_dialogs.values():
        #print 'Setting prefs %s for %s' % (kw, dialog)
        apply(dialog.configure, (), kw)
    return

def getMessageDialogIDFromUsers(users):
    ud={}
    for u in users:
        ud[u] = None
    users = ud.keys()
    users.sort()
    if len(users) > 1:
        dlg_id = 'Chatting with %s' % string.join( users[:-1],
                                                   ', ')
        dlg_id = dlg_id + ' and %s' % users[-1]
    else:
        dlg_id = 'Chatting with %s' % users[0]
    return dlg_id

_message_dialogs = {}
def getMessageDialog(
    parent, uid, uname, dialogID, addressees, addressee_ids, app,
    **kw):
    """
    Find a dialog using the given id, or create a new
    one and assign the id to it.
    """
    if _message_dialogs.has_key(dialogID):
        return _message_dialogs[dialogID]
    else:
        kw['dialogID'] = dialogID
        kw['addressMessagesToIDs'] = addressee_ids
        kw['addressMessagesToNames'] = addressees
        kw['userName'] = uname
        kw['userID'] = uid
        kw['app'] = app
        dlg = apply(MessageDialog, (parent,), kw)
        _message_dialogs[dialogID] = dlg
        return dlg

def _removeMessageDialog(dialogID):
    try:
        del _message_dialogs[dialogID]
    except KeyError:
        pass
    return

def getRangeAndText(w, x, y, tag_name):
    """
    Given the x,y coordinates on a text widget,
    and the name of a face type or tag, returns
    the range of text with that tag in which
    the x,y point occurs.
    """
    index_loc = '@%d,%d' % (x, y)
    clicked_on = w.index(index_loc)
    flat_range_list = w.tag_ranges(tag_name)
    range_list = []
    while flat_range_list:
        range_list.append( flat_range_list[0], flat_range_list[1] )
        flat_range_list = flat_range_list[2:]
    for first, last in range_list:
        if (w.compare(first, '<=', clicked_on) and
            w.compare(clicked_on, '<=', last)):
            return ( (first, last), w.get(first, last) )
    return None




class MessageDialog(Pmw.Dialog):

    #
    # Compute some font information
    #
    precomputedFonts = {
        'Helvetica':Pmw.logicalfont('Helvetica', -1, weight='bold'),
        'Times':Pmw.logicalfont('Times', 0),
        'Courier':Pmw.logicalfont('Courier', -1),
        'CourierBold':Pmw.logicalfont('Courier', -1, weight='bold'),
        }

    SEND_BTN     = 'Send'
    ADD_USER_BTN = 'Invite'
    CANCEL_BTN   = 'Cancel'
    CLEAR_BTN    = 'Clear'
    CLOSE_BTN    = 'Close'
    
    button_ids = (
        SEND_BTN,
        ADD_USER_BTN,
        CANCEL_BTN,
        #CLEAR_BTN,
        CLOSE_BTN,
        )
    
    def __init__(self, parent=None,
                 dialogID='Unknown',
                 addressMessagesToIDs=(),
                 addressMessagesToNames=(),
                 userName='',
                 userID=0,
                 app=None,
                 **kw):
        """
        Create a dialog to show an incoming message.
        """
        self._dialog_id = dialogID
        self._address_messages_to = addressMessagesToIDs
        self._address_messages_to_names = addressMessagesToNames
        self._name_lut = {}
        for id, name in map(None,
                            addressMessagesToIDs, addressMessagesToNames):
            self._name_lut[id] = name
        self._user_name = userName
        self._uid = userID

        #
        # Get some service methods from the application
        #
        self._balloon = app.balloon()
        self._error_handler = app.showError
        
        margin1 = 10
        margin2 = 130
        helvetica = self.precomputedFonts['Helvetica']

        button_help = {
            self.SEND_BTN:'Send the current message',
            self.ADD_USER_BTN:'Add a user to this chat dialog',
            self.CANCEL_BTN:'Discard the current message',
            self.CLEAR_BTN:'Clear the entire chat dialog',
            self.CLOSE_BTN:'Close this dialog',
            }
        optiondefs = (
            # closing the window
            ('command', self.callback, Pmw.INITOPT),
            ('buttons', self.button_ids, self._buttons),
            ('defaultbutton', self.SEND_BTN, self._defaultButton),
            ('ringbell', 1, None),
            ('clearonclose', None, None),
            ('webbrowser', 'netscape -remote "openURL(%s, new-window)"', None),
            ('app', app, Pmw.INITOPT),
            
            # text widget to hold chats
            ('chat_text_height', 15, None),
            ('chat_text_width', 80, None),
            ('chat_text_background', 'lightgrey', None),
            ('chat_labelpos', 'nw', Pmw.INITOPT),
            ('chat_label_text', 'Chatting as %s' % self._user_name,
             Pmw.INITOPT),
            ('chat_text_wrap', 'word', None),
            ('chat_text_font', helvetica, None),

            # text widget to hold outgoing messages
            ('message_text_height', 5, None),
            ('message_text_width', 80, None),
            ('message_labelpos', 'nw', Pmw.INITOPT),
            ('message_label_text', 'Message:', Pmw.INITOPT),

            ('message_text_wrap', 'word', None),
            ('message_text_font', helvetica, None),
            ('message_text_background', 'white', None),

            # dialog title
            ('title', dialogID, self._settitle),
            
            # date tag style (how date is displayed)
            ('dateface', '(("Helvetica",),{"foreground":"green","background":"black",})', self._tagValueReconfigure),
            ('datelmargin1', margin1, None),
            ('datelmargin2', margin2, None),
            # from tag style (how from name is displayed)
            ('fromface', '(("Helvetica",),{"foreground":"red","background":"lightgrey",})', self._tagValueReconfigure),
            ('fromlmargin1', margin1, None),
            ('fromlmargin2', margin2, None),

            # message tag style (how the message is displayed)
            ('messageface', '(("Helvetica",),{"foreground":"black","background":"lightgrey",})', self._tagValueReconfigure),
            ('messagelmargin1', margin2, None),
            ('messagelmargin2', margin2, None),
            
            # url tag style (how the url is displayed)
            ('urlface', '(("Helvetica",),{"foreground":"blue","background":"lightgrey",})', self._tagValueReconfigure),
            ('urllmargin1', margin2, None),
            ('urllmargin2', margin2, None),
            )
        self.defineoptions(kw, optiondefs)
        Pmw.Dialog.__init__(self, parent=parent)
        #
        # Create the dialog internals
        #
        self.withdraw()
        self.bind('<Control-w>', self.closeCB)
        inside = self.component('dialogchildsite')
        #
        # Create a text widget to hold the chat
        #
        chat = self.createcomponent(
            'chat',
            (), None,
            Pmw.ScrolledText,
            (inside,),
            #label_text=dialogID,
            text_state='disabled',
            )
        #
        # Define the styles for the parts of the chats
        #
        self.configureStyles()
        chat.pack(
            side=TOP,
            expand=YES,
            fill=BOTH,
            )
        #
        # Create the text widget that is going to be
        # used for sending messages.
        #
        msg = self.createcomponent(
            'message',
            (), None,
            Pmw.ScrolledText,
            (inside,),
            )
        msg.pack(
            side=TOP,
            expand=NO,
            fill=BOTH,
            )
        #
        # Check keywords and initialise options.
        #
        self.initialiseoptions(self.__class__)
        #
        # Attach balloon help messages to all of the
        # button box buttons.
        #
        if app and app.balloon():
            bbox = self.component('buttonbox')
            balloon = self._balloon
            for id in self.button_ids:
                balloon.bind(bbox.component(id),
                             button_help[id], button_help[id])
        return

    def configureStyles(self):
        txt = self.component('chat_text')
        for tag_name in ( 'date', 'from', 'message', 'url' ):
            #print 'looking for %sfont' % tag_name
            #print '\tand ', self['%sfont' % tag_name]
            face = self['%sface' % tag_name]
            if type(face) == type(''):
                face = eval(face)
            #print 'face=', face
            #print 'type(face)=', type(face)
            if not face:
                continue
            fontspec, tagspec = face
            tagspec['font'] = fontspec
            apply(txt.tag_configure, (tag_name,), tagspec)
            txt.tag_configure(
                tag_name,
                lmargin1=self['%slmargin1' % tag_name],
                lmargin2=self['%slmargin2' % tag_name],
                )
            txt.tag_bind(tag_name, '<Button-1>',
                         lambda e, t=tag_name, s=self: s.tagCB_click(t, e))
            txt.tag_bind(tag_name, '<Enter>',
                         lambda e, t=tag_name, s=self: s.tagCB_enter(t, e))
            txt.tag_bind(tag_name, '<Leave>',
                         lambda e, t=tag_name, s=self: s.tagCB_leave(t, e))
        return

    ##
    ## Callbacks for text tags
    ##
    
    def tagCB(self, tag_name, event, event_name):
        """
        General dispatch method.
        """
        try:
            callback = getattr(self, 'tagCB_%s_%s' % (event_name, tag_name))
        except AttributeError:
            return
        range_and_text = getRangeAndText(event.widget,
                                         event.x, event.y,
                                         tag_name)
        if range_and_text:
            callback(event, range_and_text[0], range_and_text[1])
        return

    def tagCB_enter(self, tag_name, event):
        """
        Callback registered for enter events.
        """
        self.tagCB(tag_name, event, 'enter')
        return

    def tagCB_leave(self, tag_name, event):
        """
        Callback registered for leave events.
        """
        self.tagCB(tag_name, event, 'leave')
        return

    def tagCB_click(self, tag_name, event):
        """
        Callback registered for click events.
        """
        self.tagCB(tag_name, event, 'click')
        return

    #
    # Use the balloon manager to display the email
    # address for the user that sent this message.
    #
    def tagCB_enter_from(self, event, span, text):
        self.tagCB_leave_url(event, span, text)
        user = string.strip(text)[:-1]
        user_info = self['app'].statusInfo().infoFromName(user)
        email = user_info[-1]
        self._balloon._enter(event.widget, email, email, 1)
        return
    
    def tagCB_leave_from(self, event, span, text):
        self._balloon._leave(event)
        return
    
    #
    # Register a bunch of event handlers
    # to switch the cursor around.  We should
    # be able to just use enter and leave event
    # handlers on url text, but that does not
    # see sufficient.
    #
    def tagCB_enter_url(self, event, span, text):
        self.text_cursor = event.widget.cget('cursor')
        event.widget.configure(cursor='hand2')
        return

    def tagCB_leave_url(self, event, span, text):
        event.widget.configure(cursor='xterm')
        return
    tagCB_enter_message = tagCB_leave_url
    
    def tagCB_click_url(self, event, span, text):
        """
        Called when user clicks on url text.
        """
        #print 'clicked on ', span, text
        try:
            browsercmd = '%s &' % (self['webbrowser'] % text)
        except TypeError:
            browsercmd = '%s %s &' % (self['webbrowser'], text)
        #print 'browser command = ', browsercmd
        os.system(browsercmd)
        return

    def _tagValueReconfigure(self, *args, **kw):
        self.configureStyles()
        return

    ##
    ## Dialog level callbacks
    ##
    
    def errorMessage(self, message):
        """
        Display an error message, if we can.
        """
        if self._error_handler:
            self._error_handler(message)
        else:
            print 'ERROR MESSAGE: %s' % message
        return

    def callback(self, button):
        """
        This callback will be called when the window is
        deleted (passing None for the button) or when a button
        is pressed in the button box.
        """
        if (button is None) or (button == self.CLOSE_BTN):
            #
            # Window was closed
            #
            if self['clearonclose'] == 'Always Clear':
                clearwindow = 1
            elif self['clearonclose'] == 'Always Keep':
                clearwindow = 0
            else:
                response = tkMessageBox.askyesno(
                    title='Clear window?',
                    message='Clear this chat window now?',
                    )
                clearwindow = response
            if clearwindow:
                self.clear()
            self.withdraw()
        elif button == self.SEND_BTN:
            self.sendMessageCB()
        elif button == self.CANCEL_BTN:
            self.clearInputArea()
        elif button == self.CLEAR_BTN:
            self.clear()
        elif button == self.ADD_USER_BTN:
            inside = self.component('dialogchildsite')
            new_user_name, new_user_id = self['app'].pick_user(
                inside, self['app'].connection())
            if (new_user_name and new_user_id and
                new_user_id not in self._address_messages_to):
                # add user to our addressee list
                self._address_messages_to.append(new_user_id)
                self._address_messages_to_names.append(new_user_name)
                # update our id
                newId = getMessageDialogIDFromUsers(
                    self._address_messages_to_names)
                del _message_dialogs[self._dialog_id]
                self._dialog_id = newId
                _message_dialogs[newId] = self
                # reset our title
                self.configure(title=self._dialog_id)
        else:
            print 'button %s pressed' % button
        return

    def closeCB(self, *args):
        self.callback(self.CLOSE_BTN)
        return
    
    def clear(self):
        self.clearInputArea()
        self.component('chat').settext('')
        return
    
    def sendMessageCB(self, event=None):
        """
        This callback is called when the user is ready to
        send a message.
        """
        # get widgets
        msg = self.component('message')
        # get message text
        outgoing_message = string.rstrip(msg.get())
        self.clearInputArea()
        self.sendMessage(outgoing_message)
        #
        # Schedule an event to have the input area cleared.
        # We can't just do this, because if the user
        # pressed a key to get to this callback, the regular
        # binding for that key will happen after we clear
        # the body of the text here.  That means they get
        # an extra carriage return in the text widget when
        # they hit <Return>.
        #
        #self.after_idle(self.clearInputArea)
        return

    ##
    ## Service functions
    ##
    
    def sendMessage(self, outgoing_message):
        """
        This method is called to
        send a message.
        """
        #
        # Check to verify that there is a message.
        #
        if not outgoing_message:
            return
        #print 'OUTGOING Message: ', outgoing_message
        # add the prefix for sending messages to >1 person
        original_message = outgoing_message
        if len(self._address_messages_to) > 1:
            to_prefix = 'To: %s\r\n' \
                        % string.join(self._address_messages_to_names,
                                      ', ')
            outgoing_message = to_prefix + outgoing_message
        # add the message to the chat history
        self.addMessage(self._user_name, original_message, bell=0)
        self.update_idletasks()
        self.component('message').update_idletasks()
        # send the message
        if self._address_messages_to:
            for uid in self._address_messages_to:
                if str(uid) != str(self._uid):
                    try:
                        self['app'].connection().mesg(uid, outgoing_message)
                    except wcc_protocol.WackyProtocolError, msg:
                        self._handleProtocolError(msg)
        else:
            try:
                self['app'].connection().cast(outgoing_message)
            except wcc_protocol.WackyProtocolError, msg:
                self._handleProtocolError(msg)
        return

    def _handleProtocolError(self, exc):
        if exc[-1] == '-MESG Unknown user.':
            # User isn't there or logged off
            try:
                self.errorMessage('User %s is no longer available.' \
                                  % self._name_lut[exc[1]])
            except KeyError:
                self.errorMessage('User %s is no longer available.' \
                                  % exc[1])
        else:
            self.errorMessage('Unhandled protocol exception: %s' % str(exc))
        return
        
    def clearInputArea(self, event=None):
        """
        Clear the text from the input message area
        """
        self.component('message').clear()
        self.component('message').update_idletasks()
        return

    ##
    ## Regular expressions for special parts of
    ## messages
    ##
    urlre = re.compile('(\S+://\S+)', re.DOTALL | re.MULTILINE)
    urlre.face = 'url'
    
    def addMessage(self, fromName, message, bell=1):
        """
        Add a message to the chat history.
        """
        #
        # Clean up the incoming message text
        #
        message = string.rstrip(message)
        message = string.replace(message, '\r', ' ')
        #
        # Get the text widget, add time and user
        # info
        #
        widget = self.component('chat_text')
        widget.configure(state='normal')
        widget.insert('end', time.strftime('%a %I:%M:%S %p',
                                           time.localtime(time.time())),
                      'date')
        widget.insert('end', ' ', 'date')
        widget.insert('end', ' ')
        widget.insert('end', '%s:' % fromName, 'from')
        widget.insert('end', ' ')
        #
        # Insert the message
        #
        message_parts = []
        matchObj = 1
        while matchObj:
            for pattern in ( self.urlre, ):
                matchObj = pattern.search(message)
                if matchObj:
                    #print matchObj.re.face, matchObj.re.pattern,
                    #print ' matched ', matchObj.groups()
                    start, end = matchObj.span()
                    before = message[:start]
                    match = message[start:end]
                    message = message[end:]
                    if before:
                        message_parts.append( (before, 'message') )
                    message_parts.append( (match, matchObj.re.face) )
                    #print '\t', before
                    #print '\t', match
                    #print '\t', message
                    break
            #break
        if message:
            message_parts.append( (message, 'message') )
        #print 'parts:', message_parts
        for text, face in message_parts:
            widget.insert('end', text, face)
        widget.insert('end', '\n', 'message')
        widget.yview_pickplace('end')
        widget.configure(state='disabled')
        widget.update_idletasks()
        if bell and self['ringbell']:
            self._hull.bell()
        self.show()
        return

    def show(self):
        self.component('message_text').focus_set()
        Pmw.Dialog.show(self)
        return
    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.