[orca] Fix for bgo#615489 - Migrate Pidgin script to new chat.py



commit 7d767c9e6f27c2b76fd99d75c33c3fb9c668104a
Author: Joanmarie Diggs <joanmarie diggs gmail com>
Date:   Sun Jun 13 10:14:46 2010 -0400

    Fix for bgo#615489 - Migrate Pidgin script to new chat.py

 src/orca/chat.py                                 |    2 +-
 src/orca/scripts/apps/pidgin/Makefile.am         |    2 +-
 src/orca/scripts/apps/pidgin/__init__.py         |    1 -
 src/orca/scripts/apps/pidgin/chat.py             |   64 ++
 src/orca/scripts/apps/pidgin/script.py           |  754 ++--------------------
 src/orca/scripts/apps/pidgin/script_settings.py  |   47 --
 src/orca/scripts/apps/pidgin/script_utilities.py |    4 +-
 src/orca/scripts/apps/pidgin/speech_generator.py |    4 +-
 8 files changed, 132 insertions(+), 746 deletions(-)
---
diff --git a/src/orca/chat.py b/src/orca/chat.py
index 2749545..1dad68f 100644
--- a/src/orca/chat.py
+++ b/src/orca/chat.py
@@ -704,7 +704,7 @@ class Chat:
             #
             return True
 
-        elif self.isTypingStatusChangedEvent(event.source):
+        elif self.isTypingStatusChangedEvent(event):
             self.presentTypingStatusChange(event, event.any_data)
             return True
 
diff --git a/src/orca/scripts/apps/pidgin/Makefile.am b/src/orca/scripts/apps/pidgin/Makefile.am
index 10ea6d4..9df9c24 100644
--- a/src/orca/scripts/apps/pidgin/Makefile.am
+++ b/src/orca/scripts/apps/pidgin/Makefile.am
@@ -2,8 +2,8 @@ orca_pathdir=$(pyexecdir)
 
 orca_python_PYTHON = \
 	__init__.py \
+	chat.py \
 	script.py \
-	script_settings.py \
 	script_utilities.py \
 	speech_generator.py
 
diff --git a/src/orca/scripts/apps/pidgin/__init__.py b/src/orca/scripts/apps/pidgin/__init__.py
index 6f2d03c..1eb5b8d 100644
--- a/src/orca/scripts/apps/pidgin/__init__.py
+++ b/src/orca/scripts/apps/pidgin/__init__.py
@@ -18,4 +18,3 @@
 # Boston MA  02110-1301 USA.
 
 from script import Script
-import script_settings
diff --git a/src/orca/scripts/apps/pidgin/chat.py b/src/orca/scripts/apps/pidgin/chat.py
new file mode 100644
index 0000000..7e344cf
--- /dev/null
+++ b/src/orca/scripts/apps/pidgin/chat.py
@@ -0,0 +1,64 @@
+# Orca
+#
+# Copyright 2010 Joanmarie Diggs.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA  02110-1301 USA.
+
+"""Custom chat module for Pidgin."""
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2010 Joanmarie Diggs."
+__license__   = "LGPL"
+
+import orca.chat as chat
+
+########################################################################
+#                                                                      #
+# The Pidgin chat class.                                               #
+#                                                                      #
+########################################################################
+
+class Chat(chat.Chat):
+
+    def __init__(self, script, buddyListAncestries):
+
+        chat.Chat.__init__(self, script, buddyListAncestries)
+
+    def isTypingStatusChangedEvent(self, event):
+        """Returns True if event is associated with a change in typing status.
+
+        Arguments:
+        - event: the accessible event being examined
+        """
+
+        if not event.type.startswith("object:text-changed:insert"):
+            return False
+
+        # Bit of a hack. Pidgin inserts text into the chat history when the
+        # user is typing. We seem able to (more or less) reliably distinguish
+        # this text via its attributes because these attributes are absent
+        # from user inserted text -- no matter how that text is formatted.
+        #
+        attr, start, end = \
+            self._script.utilities.textAttributes(event.source, event.detail1)
+
+        if float(attr.get('scale', '1')) < 1 \
+           or int(attr.get('weight', '400')) < 400:
+            return True
+
+        return False
diff --git a/src/orca/scripts/apps/pidgin/script.py b/src/orca/scripts/apps/pidgin/script.py
index ce2a675..5d5c7db 100644
--- a/src/orca/scripts/apps/pidgin/script.py
+++ b/src/orca/scripts/apps/pidgin/script.py
@@ -1,6 +1,7 @@
 # Orca
 #
 # Copyright 2004-2008 Sun Microsystems Inc.
+# Copyright 2010 Joanmarie Diggs
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Library General Public
@@ -17,92 +18,33 @@
 # Free Software Foundation, Inc., Franklin Street, Fifth Floor,
 # Boston MA  02110-1301 USA.
 
-"""Custom script for pidgin.  This provides the ability for Orca to
-monitor both the IM input and IM output text areas at the same time.
-
-The following script specific key sequences are supported:
-
-  Insert-h      -  Toggle whether we prefix chat room messages with
-                   the name of the chat room.
-  Insert-[1-9]  -  Speak and braille a previous chat room message.
-"""
+"""Custom script for pidgin."""
 
 __id__        = "$Id$"
 __version__   = "$Revision$"
 __date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
+__copyright__ = "Copyright (c) 2010 Joanmarie Diggs."
 __license__   = "LGPL"
 
-import gtk
 import pyatspi
 
-import orca.debug as debug
 import orca.default as default
-import orca.input_event as input_event
-import orca.keybindings as keybindings
-import orca.orca_state as orca_state
-import orca.settings as settings
 import orca.speech as speech
 
 from orca.orca_i18n import _
 
+from chat import Chat
 from script_utilities import Utilities
 from speech_generator import SpeechGenerator
-import script_settings
 
 ########################################################################
 #                                                                      #
-# Ring List. A fixed size circular list by Flavio Catalani             #
-# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/435902       #
-#                                                                      #
-########################################################################
-
-class RingList:
-    def __init__(self, length):
-        self.__data__ = []
-        self.__full__ = 0
-        self.__max__ = length
-        self.__cur__ = 0
-
-    def append(self, x):
-        if self.__full__ == 1:
-            for i in range (0, self.__cur__ - 1):
-                self.__data__[i] = self.__data__[i + 1]
-            self.__data__[self.__cur__ - 1] = x
-        else:
-            self.__data__.append(x)
-            self.__cur__ += 1
-            if self.__cur__ == self.__max__:
-                self.__full__ = 1
-
-    def get(self):
-        return self.__data__
-
-    def remove(self):
-        if (self.__cur__ > 0):
-            del self.__data__[self.__cur__ - 1]
-            self.__cur__ -= 1
-
-    def size(self):
-        return self.__cur__
-
-    def maxsize(self):
-        return self.__max__
-
-    def __str__(self):
-        return ''.join(self.__data__)
-
-
-########################################################################
-#                                                                      #
-# The Gaim script class.                                               #
+# The Pidgin script class.                                             #
 #                                                                      #
 ########################################################################
 
 class Script(default.Script):
 
-    MESSAGE_LIST_LENGTH = 9
-
     def __init__(self, app):
         """Creates a new script for the given application.
 
@@ -110,474 +52,75 @@ class Script(default.Script):
         - app: the application to create a script for.
         """
 
-        # Set the debug level for all the methods in this script.
+        # So we can take an educated guess at identifying the buddy list.
         #
-        self.debugLevel = debug.LEVEL_FINEST
+        self._buddyListAncestries = [[pyatspi.ROLE_TREE_TABLE,
+                                      pyatspi.ROLE_SCROLL_PANE,
+                                      pyatspi.ROLE_FILLER,
+                                      pyatspi.ROLE_PAGE_TAB,
+                                      pyatspi.ROLE_PAGE_TAB_LIST,
+                                      pyatspi.ROLE_FILLER,
+                                      pyatspi.ROLE_FRAME]]
 
-        # Create two cyclic lists; one that will contain the previous
-        # chat room messages and the other that will contain the names
-        # of the associated chat rooms.
-        #
-        self.allPreviousMessages = RingList(Script.MESSAGE_LIST_LENGTH)
-        self.previousChatRoomNames = RingList(Script.MESSAGE_LIST_LENGTH)
-
-        # Create a dictionary that will be used to contain chat room
-        # specific message histories. The key will be the chat room
-        # name and the value will be a cyclic list of previous messages
-        # for that chat room.
-        #
-        self.chatRoomMessages = {}
-
-        # Initially populate the cyclic lists with empty strings.
-        #
-        i = 0
-        while i < self.allPreviousMessages.maxsize():
-            self.allPreviousMessages.append("")
-            i += 1
-
-        i = 0
-        while i < self.previousChatRoomNames.maxsize():
-            self.previousChatRoomNames.append("")
-            i += 1
-
-        # Keep track of the various text areas for chatting.
-        # The key is the tab and the value is the text area where
-        # the chat occurs.
-        #
-        self.chatAreas = {}
-
-        # Button to handle preferences setting saying whether we want to 
-        # prefix the chat room name for our messages.
-        #
-        self.speakNameCheckButton = None
+        default.Script.__init__(self, app)
 
-        # Keep track of the last status message to see if it's changed.
-        #
-        self.lastStatus = None
+    def getChat(self):
+        """Returns the 'chat' class for this script."""
 
-        # To make pylint happy.
-        #
-        self.focusedChannelRadioButton = None
-        self.allChannelsRadioButton = None
-        self.allMessagesRadioButton = None
-        self.buddyTypingCheckButton = None
-        self.chatRoomHistoriesCheckButton = None
+        return Chat(self, self._buddyListAncestries)
 
-        default.Script.__init__(self, app)
+    def getSpeechGenerator(self):
+        """Returns the speech generator for this script. """
 
-    def getListeners(self):
-        """Add in an AT-SPI event listener "object:children-changed:"
-        events, for this script.
-        """
+        return SpeechGenerator(self)
 
-        listeners = default.Script.getListeners(self)
-        listeners["object:children-changed:"] = self.onChildrenChanged
+    def getUtilities(self):
+        """Returns the utilites for this script."""
 
-        return listeners
+        return Utilities(self)
 
     def setupInputEventHandlers(self):
         """Defines InputEventHandler fields for this script that can be
-        called by the key and braille bindings. In this particular case,
-        we just want to be able to add a handler to toggle whether we
-        prefix chat room messages with the name of the chat room.
+        called by the key and braille bindings. Here we need to add the
+        handlers for chat functionality.
         """
 
-        debug.println(self.debugLevel, "pidgin.setupInputEventHandlers.")
-
         default.Script.setupInputEventHandlers(self)
-        self.inputEventHandlers["togglePrefixHandler"] = \
-            input_event.InputEventHandler(
-                Script.togglePrefix,
-                _("Toggle whether we prefix chat room messages with " \
-                  "the name of the chat room."))
-
-        self.inputEventHandlers["toggleBuddyTypingHandler"] = \
-            input_event.InputEventHandler(
-                Script.toggleBuddyTyping,
-                _("Toggle whether we announce when our buddies are typing."))
-
-        self.inputEventHandlers["toggleMessageHistoriesHandler"] = \
-            input_event.InputEventHandler(
-                Script.toggleMessageHistories,
-                _("Toggle whether we provide chat room specific message " \
-                  "histories."))
-
-        # Add the chat room message history event handler.
-        #
-        self.inputEventHandlers["reviewMessage"] = \
-            input_event.InputEventHandler(
-                Script.readPreviousMessage,
-                _("Speak and braille a previous chat room message."))
+        self.inputEventHandlers.update(self.chat.inputEventHandlers)
 
     def getKeyBindings(self):
-        """Defines the key bindings for this script. Setup the default
-        key bindings, then add one in for toggling whether we prefix
-        chat room messages with the name of the chat room.
+        """Defines the key bindings for this script. Here we need to add
+        the keybindings associated with chat functionality.
 
         Returns an instance of keybindings.KeyBindings.
         """
 
-        debug.println(self.debugLevel, "pidgin.getKeyBindings.")
-
         keyBindings = default.Script.getKeyBindings(self)
-        keyBindings.add(
-            keybindings.KeyBinding(
-                "h",
-                settings.defaultModifierMask,
-                settings.ORCA_MODIFIER_MASK,
-                self.inputEventHandlers["togglePrefixHandler"]))
-
-        keyBindings.add(
-            keybindings.KeyBinding(
-                "",
-                settings.defaultModifierMask,
-                settings.NO_MODIFIER_MASK,
-                self.inputEventHandlers["toggleBuddyTypingHandler"]))
-
-        keyBindings.add(
-            keybindings.KeyBinding(
-                "",
-                settings.defaultModifierMask,
-                settings.NO_MODIFIER_MASK,
-                self.inputEventHandlers["toggleMessageHistoriesHandler"]))
-
-        # keybindings to provide chat room message history.
-        #
-        messageKeys = [ "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9" ]
-        for messageKey in messageKeys:
-            keyBindings.add(
-                keybindings.KeyBinding(
-                    messageKey,
-                    settings.defaultModifierMask,
-                    settings.ORCA_MODIFIER_MASK,
-                    self.inputEventHandlers["reviewMessage"]))
 
-        return keyBindings
+        bindings = self.chat.keyBindings
+        for keyBinding in bindings.keyBindings:
+            keyBindings.add(keyBinding)
 
-    def getSpeechGenerator(self):
-        """Returns the speech generator for this script.
-        """
-
-        return SpeechGenerator(self)
-
-    def getUtilities(self):
-        """Returns the utilites for this script."""
-
-        return Utilities(self)
+        return keyBindings
 
     def getAppPreferencesGUI(self):
         """Return a GtkVBox contain the application unique configuration
-        GUI items for the current application.
+        GUI items for the current application. The chat-related options
+        get created by the chat module.
         """
 
-        vbox = gtk.VBox(False, 0)
-        vbox.set_border_width(12)
-        gtk.Widget.show(vbox)
-
-        # Translators: If this checkbox is checked, then Orca will speak
-        # the name of the chat room.
-        #
-        label = _("_Speak Chat Room name")
-        self.speakNameCheckButton = gtk.CheckButton(label)
-        gtk.Widget.show(self.speakNameCheckButton)
-        gtk.Box.pack_start(vbox, self.speakNameCheckButton, False, False, 0)
-        gtk.ToggleButton.set_active(self.speakNameCheckButton,
-                                    script_settings.prefixChatMessage)
-
-        # Translators: If this checkbox is checked, then Orca will tell
-        # you when one of your buddies is typing a message.
-        #
-        label = _("Announce when your _buddies are typing")
-        self.buddyTypingCheckButton = gtk.CheckButton(label)
-        gtk.Widget.show(self.buddyTypingCheckButton)
-        gtk.Box.pack_start(vbox, self.buddyTypingCheckButton, False, False, 0)
-        gtk.ToggleButton.set_active(self.buddyTypingCheckButton,
-                                    script_settings.announceBuddyTyping)
-
-        # Translators: If this checkbox is checked, then Orca will provide
-        # the user with chat room specific message histories rather than just
-        # a single history which contains the latest messages from all the
-        # chat rooms that they are currently in.
-        #
-        label = _("Provide chat room specific _message histories")
-        self.chatRoomHistoriesCheckButton = gtk.CheckButton(label)
-        gtk.Widget.show(self.chatRoomHistoriesCheckButton)
-        gtk.Box.pack_start(vbox, self.chatRoomHistoriesCheckButton,
-                           False, False, 0)
-        gtk.ToggleButton.set_active(self.chatRoomHistoriesCheckButton,
-                                    script_settings.chatRoomHistories)
-
-        # "Speak Messages" frame.
-        #
-        messagesFrame = gtk.Frame()
-        gtk.Widget.show(messagesFrame)
-        gtk.Box.pack_start(vbox, messagesFrame, False, False, 5)
-
-        messagesAlignment = gtk.Alignment(0.5, 0.5, 1, 1)
-        gtk.Widget.show(messagesAlignment)
-        gtk.Container.add(messagesFrame, messagesAlignment)
-        gtk.Alignment.set_padding(messagesAlignment, 0, 0, 12, 0)
-
-        messagesVBox = gtk.VBox(False, 0)
-        gtk.Widget.show(messagesVBox)
-        gtk.Container.add(messagesAlignment, messagesVBox)
-
-        # Translators: Orca will speak all new chat messages as they appear
-        # irrespective of whether the pidgin application currently has focus.
-        # This is the default behaviour.
-        #
-        self.allMessagesRadioButton = gtk.RadioButton(None, _("All cha_nnels"))
-        gtk.Widget.show(self.allMessagesRadioButton)
-        gtk.Box.pack_start(messagesVBox, self.allMessagesRadioButton,
-                           False, False, 0)
-        gtk.ToggleButton.set_active(self.allMessagesRadioButton,
-            (script_settings.speakMessages == \
-                     script_settings.SPEAK_ALL_MESSAGES))
-
-
-        # Translators: Orca will speak only new chat messages for the channel
-        # that currently has focus, irrespective of whether the pidgin
-        # application has focus.
-        #
-        self.focusedChannelRadioButton = gtk.RadioButton( \
-                             self.allMessagesRadioButton, \
-                             _("A channel only if its _window is active"))
-        gtk.Widget.show(self.focusedChannelRadioButton)
-        gtk.Box.pack_start(messagesVBox, self.focusedChannelRadioButton,
-                           False, False, 0)
-        gtk.ToggleButton.set_active(self.focusedChannelRadioButton,
-            (script_settings.speakMessages == \
-                     script_settings.SPEAK_CHANNEL_WITH_FOCUS))
-
-        # Translators: Orca will speak new chat messages for all channels 
-        # only when the pidgin application has focus.
-        #
-        self.allChannelsRadioButton = gtk.RadioButton( \
-                        self.allMessagesRadioButton,
-                       _("All channels when an_y Pidgin window is active"))
-        gtk.Widget.show(self.allChannelsRadioButton)
-        gtk.Box.pack_start(messagesVBox, self.allChannelsRadioButton,
-                           False, False, 0)
-        gtk.ToggleButton.set_active(self.allChannelsRadioButton,
-            (script_settings.speakMessages == \
-                     script_settings.SPEAK_ALL_CHANNELS_WHEN_FOCUSED))
-
-        # Translators: this is the title of a panel holding options for
-        # how messages in the pidgin chat rooms should be spoken.
-        #
-        messagesLabel = gtk.Label("<b>%s</b>" % _("Speak messages from"))
-        gtk.Widget.show(messagesLabel)
-        gtk.Frame.set_label_widget(messagesFrame, messagesLabel)
-        messagesFrame.set_shadow_type(gtk.SHADOW_NONE)
-        gtk.Label.set_use_markup(messagesLabel, True)
-
-        return vbox
+        return self.chat.getAppPreferencesGUI()
 
     def setAppPreferences(self, prefs):
         """Write out the application specific preferences lines and set the
-        new values.
+        new values. The chat-related options get written out by the chat
+        module.
 
         Arguments:
         - prefs: file handle for application preferences.
         """
 
-        prefix = "orca.scripts.apps.pidgin.script_settings"
-        script_settings.prefixChatMessage = \
-                self.speakNameCheckButton.get_active()
-        prefs.writelines("\n")
-        prefs.writelines("%s.prefixChatMessage = %s\n" % \
-                         (prefix, script_settings.prefixChatMessage))
-
-        script_settings.announceBuddyTyping = \
-                self.buddyTypingCheckButton.get_active()
-        prefs.writelines("%s.announceBuddyTyping = %s\n" % \
-                         (prefix, script_settings.announceBuddyTyping))
-
-        script_settings.chatRoomHistories = \
-                self.chatRoomHistoriesCheckButton.get_active()
-        prefs.writelines("%s.chatRoomHistories = %s\n" % \
-                         (prefix, script_settings.chatRoomHistories))
-
-        if self.allMessagesRadioButton.get_active():
-            script_settings.speakMessages = \
-                    script_settings.SPEAK_ALL_MESSAGES
-            option = ("%s.SPEAK_ALL_MESSAGES" % prefix)
-        elif self.focusedChannelRadioButton.get_active():
-            script_settings.speakMessages = \
-                    script_settings.SPEAK_CHANNEL_WITH_FOCUS
-            option = ("%s.SPEAK_CHANNEL_WITH_FOCUS" % prefix)
-        elif self.allChannelsRadioButton.get_active():
-            script_settings.speakMessages = \
-                    script_settings.SPEAK_ALL_CHANNELS_WHEN_FOCUSED
-            option = ("%s.SPEAK_ALL_CHANNELS_WHEN_FOCUSED" % prefix)
-        prefs.writelines("\n")
-        prefs.writelines("%s.speakMessages = %s\n" % (prefix, option))
-
-    def getAppState(self):
-        """Returns an object that can be passed to setAppState.  This
-        object will be use by setAppState to restore any state information
-        that was being maintained by the script."""
-        return [default.Script.getAppState(self),
-                self.allPreviousMessages,
-                self.previousChatRoomNames,
-                self.chatRoomMessages,
-                self.chatAreas]
-
-    def setAppState(self, appState):
-        """Sets the application state using the given appState object.
-
-        Arguments:
-        - appState: an object obtained from getAppState
-        """
-        try:
-            [defaultAppState,
-             self.allPreviousMessages,
-             self.previousChatRoomNames,
-             self.chatRoomMessages,
-             self.chatAreas] = appState
-            default.Script.setAppState(self, defaultAppState)
-        except:
-            debug.printException(debug.LEVEL_WARNING)
-
-    def togglePrefix(self, inputEvent):
-        """ Toggle whether we prefix chat room messages with the name of
-        the chat room.
-
-        Arguments:
-        - inputEvent: if not None, the input event that caused this action.
-        """
-
-        debug.println(self.debugLevel, "pidgin.togglePrefix.")
-
-        line = _("speak chat room name.")
-        script_settings.prefixChatMessage = \
-                not script_settings.prefixChatMessage
-        if not script_settings.prefixChatMessage:
-            line = _("Do not speak chat room name.")
-
-        self.presentMessage(line)
-
-        return True
-
-    def toggleBuddyTyping(self, inputEvent):
-        """ Toggle whether we announce when our buddies are typing a message.
-
-        Arguments:
-        - inputEvent: if not None, the input event that caused this action.
-        """
-
-        debug.println(self.debugLevel, "pidgin.toggleBuddyTyping.")
-
-        line = _("announce when your buddies are typing.")
-        script_settings.announceBuddyTyping = \
-                not script_settings.announceBuddyTyping
-        if not script_settings.announceBuddyTyping:
-            line = _("Do not announce when your buddies are typing.")
-
-        self.presentMessage(line)
-
-        return True
-
-    def toggleMessageHistories(self, inputEvent):
-        """ Toggle whether we provide chat room specific message histories.
-
-        Arguments:
-        - inputEvent: if not None, the input event that caused this action.
-        """
-
-        debug.println(self.debugLevel, "pidgin.toggleMessageHistories.")
-
-        line = _("Provide chat room specific message histories.")
-        script_settings.chatRoomHistories = \
-                not script_settings.chatRoomHistories
-        if not script_settings.chatRoomHistories:
-            line = _("Do not provide chat room specific message histories.")
-
-        self.presentMessage(line)
-
-        return True
-
-    def utterMessage(self, chatRoomName, message, hasFocus=True):
-        """ Speak/braille a chat room message.
-
-        Arguments:
-        - chatRoomName: name of the chat room this message came from.
-        - message: the chat room message.
-        """
-
-        # Only speak/braille the new message if it matches how the user 
-        # wants chat messages spoken.
-        #
-        if script_settings.speakMessages == \
-               script_settings.SPEAK_ALL_CHANNELS_WHEN_FOCUSED \
-           and orca_state.activeScript != self:
-            return
-        elif script_settings.speakMessages == \
-               script_settings.SPEAK_CHANNEL_WITH_FOCUS \
-           and not hasFocus:
-            return
-
-        text = ""
-        if script_settings.prefixChatMessage:
-            if chatRoomName and chatRoomName != "":
-                text += _("Message from chat room %s") % chatRoomName + " "
-        if message and message != "":
-            text += message
-
-        if len(text.strip()):
-            speech.speak(text)
-        self.displayBrailleMessage(text)
-
-    def readPreviousMessage(self, inputEvent):
-        """ Speak/braille a previous chat room message. Up to nine
-        previous messages are kept.
-
-        Arguments:
-        - inputEvent: if not None, the input event that caused this action.
-        """
-
-        debug.println(self.debugLevel, "pidgin.readPreviousMessage.")
-
-        i = int(inputEvent.event_string[1:])
-        messageNo = Script.MESSAGE_LIST_LENGTH - i
-
-        if script_settings.chatRoomHistories:
-            chatRoomTab = self.getChatRoomTab(orca_state.locusOfFocus)
-            chatRoomName = self.utilities.displayedText(chatRoomTab)
-            if not chatRoomName in self.chatRoomMessages:
-                return
-            messages = self.chatRoomMessages[chatRoomName].get()
-        else:
-            chatRoomNames = self.previousChatRoomNames.get()
-            chatRoomName = chatRoomNames[messageNo]
-            messages = self.allPreviousMessages.get()
-
-        message = messages[messageNo]
-        self.utterMessage(chatRoomName, message)
-
-    def getChatRoomTab(self, obj):
-        """Walk up the hierarchy until we've found the page tab for this
-        chat room, and return that object.
-
-        Arguments:
-        - obj: the accessible component to start from.
-
-        Returns the page tab component for the chat room.
-        """
-
-        if obj:
-            while True:
-                if obj:
-                    if obj.getRole() == pyatspi.ROLE_APPLICATION:
-                        break
-                    elif obj.getRole() == pyatspi.ROLE_PAGE_TAB:
-                        return obj
-                    else:
-                        obj = obj.parent
-                else:
-                    break
-
-        return None
+        self.chat.setAppPreferences(prefs)
 
     def onChildrenChanged(self, event):
         """Called whenever a child object changes in some way.
@@ -590,8 +133,8 @@ class Script(default.Script):
         # has, then announce its name. See bug #469098 for more details.
         #
         if event.type.startswith("object:children-changed:add"):
-            rolesList = [pyatspi.ROLE_PAGE_TAB_LIST, \
-                         pyatspi.ROLE_FILLER, \
+            rolesList = [pyatspi.ROLE_PAGE_TAB_LIST,
+                         pyatspi.ROLE_FILLER,
                          pyatspi.ROLE_FRAME]
             if self.utilities.hasMatchingHierarchy(event.source, rolesList):
                 # As it's possible to get this component hierarchy in other
@@ -612,28 +155,17 @@ class Script(default.Script):
                         line = _("New chat tab %s") % child.name
                         speech.speak(line)
 
-    def isBuddyListEvent(self, event):
-        """If pidgin gets a status changed message for one of the users
-        buddies then just ignore it. See bug #525644 for more details.
+    def onNameChanged(self, event):
+        """Called whenever a property on an object changes.
 
         Arguments:
         - event: the Event
-
-        Return an indication of whether this is a buddy list event.
         """
 
-        isBuddyListEvent = False
-        rolesList = [pyatspi.ROLE_TABLE_CELL, \
-                     pyatspi.ROLE_TABLE_CELL, \
-                     pyatspi.ROLE_TREE_TABLE, \
-                     pyatspi.ROLE_SCROLL_PANE, \
-                     pyatspi.ROLE_FILLER, \
-                     pyatspi.ROLE_PAGE_TAB, \
-                     pyatspi.ROLE_PAGE_TAB_LIST]
-        if self.utilities.hasMatchingHierarchy(event.source, rolesList):
-            isBuddyListEvent = True
-
-        return isBuddyListEvent
+        if self.chat.isInBuddyList(event.source):
+            return
+        else:
+            default.Script.onNameChanged(self, event)
 
     def onTextDeleted(self, event):
         """Called whenever text is deleted from an object.
@@ -642,22 +174,18 @@ class Script(default.Script):
         - event: the Event
         """
 
-        if self.isBuddyListEvent(event):
+        if self.chat.isInBuddyList(event.source):
             return
         else:
             default.Script.onTextDeleted(self, event)
 
-    def onNameChanged(self, event):
-        """Called whenever a property on an object changes.
-
-        Arguments:
-        - event: the Event
-        """
+    def onTextInserted(self, event):
+        """Called whenever text is added to an object."""
 
-        if self.isBuddyListEvent(event):
+        if self.chat.presentInsertedText(event):
             return
-        else:
-            default.Script.onNameChanged(self, event)
+
+        default.Script.onTextInserted(self, event)
 
     def onValueChanged(self, event):
         """Called whenever an object's value changes.  Currently, the
@@ -667,181 +195,23 @@ class Script(default.Script):
         - event: the Event
         """
 
-        if self.isBuddyListEvent(event):
+        if self.chat.isInBuddyList(event.source):
             return
         else:
             default.Script.onValueChanged(self, event)
 
-    def onTextInserted(self, event):
-        """Called whenever text is inserted into one of Gaim's text
-        objects.  If the object is an instant message or chat, speak
-        the text. If we're not watching anything, do the default
-        behavior.
-
-        Arguments:
-        - event: the text inserted Event
-        """
-
-        if self.isBuddyListEvent(event):
-            return
-
-        # Handle non-chat text areas (e.g., adding a new account)
-        # the default way.
-        #
-        state = event.source.getState()
-        if state.contains(pyatspi.STATE_EDITABLE) \
-           and state.contains(pyatspi.STATE_SINGLE_LINE):
-            default.Script.onTextInserted(self, event)
-            return
-
-        chatRoomTab = self.getChatRoomTab(event.source)
-        if not chatRoomTab:
-            default.Script.onTextInserted(self, event)
-            return
-
-        # [[[TODO: HACK - it looks as though the GAIM chat area may
-        # not start emitting text inserted events until we tickle it
-        # by looking at it in the hierarchy.  The simple workaround of
-        # entering flat review and exiting does this.  So, we tickle
-        # the hierarchy here.  We probably should be trying somewhere
-        # else since we may miss the first message in a chat area.  In
-        # addition, this is a source of a very small memory leak since
-        # we do not free up the entries when the tab goes away.  One
-        # would have to engage in hundreds of chats with hundreds of
-        # different people from the same instance of pidgin for that
-        # memory leak to have an issue here.  One thing we could do if
-        # that is deemed a severe enough problem is to check for
-        # children-changed events and clean up in that.]]]
-        #
-        chatArea = None
-        if not chatRoomTab in self.chatAreas:
-            # Different message types (AIM, IRC ...) have a different
-            # component hierarchy for their chat rooms. By testing
-            # with AIM and IRC, we've found that the messages area for
-            # those two type of chats has an index that is the penultimate
-            # text field. Hopefully this is true for other types of chat
-            # as well, but is currently untested.
-            #
-            allTextFields = self.utilities.descendantsWithRole(
-                chatRoomTab, pyatspi.ROLE_TEXT, False)
-
-            # Pidgin 2.7 includes a Find text object, hidden by default.
-            # We can identify (and eliminate) it by STATE_SINGLE_LINE.
-            #
-            allTextFields = \
-                filter(lambda o: 
-                       not o.getState().contains(pyatspi.STATE_SINGLE_LINE),
-                       allTextFields)
-
-            index = len(allTextFields) - 2
-            if index >= 0:
-                self.chatAreas[chatRoomTab] = allTextFields[index]
-                chatArea = self.chatAreas[chatRoomTab]
-        else:
-            chatArea = self.chatAreas[chatRoomTab]
+    def onWindowActivated(self, event):
+        """Called whenever a toplevel window is activated."""
 
-        # Create a new cyclic message list for this chat room name
-        # (if one doesn't already exist) and populate it with empty
-        # strings.
+        # Hack to "tickle" the accessible hierarchy. Otherwise, the
+        # events we need to present text added to the chatroom are
+        # missing.
         #
-        chatRoomName = self.utilities.displayedText(chatRoomTab)
-        if not chatRoomName in self.chatRoomMessages:
-            self.chatRoomMessages[chatRoomName] = \
-                                 RingList(Script.MESSAGE_LIST_LENGTH)
-            i = 0
-            while i < self.chatRoomMessages[chatRoomName].maxsize():
-                self.chatRoomMessages[chatRoomName].append("")
-                i += 1
-
-        lastKey, mods = self.utilities.lastKeyAndModifiers()
-        if event.source and (event.source == chatArea):
-            # We always automatically go back to focus tracking mode when
-            # someone sends us a message.
-            #
-            if self.flatReviewContext:
-                self.toggleFlatReviewMode()
-
-            message = self.utilities.substring(event.source,
-                                               event.detail1,
-                                               event.detail1 + event.detail2)
-            if message and message[0] == "\n":
-                message = message[1:]
-
-            chatRoomName = self.utilities.displayedText(chatRoomTab)
-
-            # If the user doesn't want announcements for when their buddies
-            # are typing (or have stopped typing), and this is such a message,
-            # then just return. The only reliable way to identify such text
-            # is by the scale.  This attribute seems to have been removed by
-            # pidgin, so we'll also check the weight.  We also want to store
-            # the last message because msn seems to be sending a constant
-            # stream of "is typing" updates.
-            #
-            attr, start, end = \
-                self.utilities.textAttributes(event.source, event.detail1)
-            if float(attr.get('scale', '1')) < 1 \
-               or int(attr.get('weight', '400')) < 400:
-                if not script_settings.announceBuddyTyping or \
-                       self.lastStatus == message:
-                    return
-                self.lastStatus = message
-            else:
-                self.lastStatus = None
-
-            # If the new message came from the room with focus, we don't
-            # want to speak its name even if script_settings.prefixChatMessage
-            # is enabled.
-            #
-            state = event.source.getState()
-            hasFocus = state.contains(pyatspi.STATE_SHOWING)
-            if hasFocus:
-                chatRoomName = ""
-            self.utterMessage(chatRoomName, message, hasFocus)
-
-            # Add the latest message to the list of saved ones. For each
-            # one added, the oldest one automatically gets dropped off.
-            # We don't want to do this for the status messages however.
-            #
-            if not self.lastStatus:
-                chatRoomName = self.utilities.displayedText(chatRoomTab)
-
-                self.allPreviousMessages.append(message)
-                self.previousChatRoomNames.append(chatRoomName)
-
-                self.chatRoomMessages[chatRoomName].append(message)
-
-        elif lastKey == "Tab" and event.any_data and event.any_data != "\t":
-            # This is autocompleted text (the name of a user in an IRC
-            # chatroom).  The default script isn't announcing it because
-            # it's not selected.
-            #
-            text = event.any_data
-            if text.decode("UTF-8").isupper():
-                speech.speak(text, self.voices[settings.UPPERCASE_VOICE])
-            else:
-                speech.speak(text)
-
-        else:
-            # Pass the event onto the parent class to be handled in the
-            # default way.
-            #
-            default.Script.onTextInserted(self, event)
-
-    def isInBuddyList(self, obj):
-        """Determines whether or not this object is in the buddy list.
-
-        Arguments:
-        -obj: the Accessible object
-        """
+        allPageTabs = self.utilities.descendantsWithRole(
+            event.source, pyatspi.ROLE_PAGE_TAB)
 
-        rolesList = [pyatspi.ROLE_TABLE_CELL,
-                     pyatspi.ROLE_TREE_TABLE,
-                     pyatspi.ROLE_SCROLL_PANE,
-                     pyatspi.ROLE_FILLER,
-                     pyatspi.ROLE_PAGE_TAB]
+        default.Script.onWindowActivated(self, event)
 
-        return self.utilities.hasMatchingHierarchy(obj, rolesList)
-        
     def visualAppearanceChanged(self, event, obj):
         """Called when the visual appearance of an object changes.
         Overridden here because we get object:state-changed:expanded
@@ -852,7 +222,7 @@ class Script(default.Script):
         - obj: the Accessible whose visual appearance changed.
         """
 
-        if self.isInBuddyList(obj) \
+        if self.chat.isInBuddyList(obj) \
            and event.type.startswith("object:state-changed:expanded"):
 
             # The event is associated with the invisible cell. Set it
diff --git a/src/orca/scripts/apps/pidgin/script_utilities.py b/src/orca/scripts/apps/pidgin/script_utilities.py
index 044d094..7383662 100644
--- a/src/orca/scripts/apps/pidgin/script_utilities.py
+++ b/src/orca/scripts/apps/pidgin/script_utilities.py
@@ -68,7 +68,7 @@ class Utilities(script_utilities.Utilities):
         Returns: a list of all the child nodes
         """
 
-        if not self._script.isInBuddyList(obj):
+        if not self._script.chat.isInBuddyList(obj):
             return script_utilities.Utilities.childNodes(self, obj)
 
         try:
@@ -124,7 +124,7 @@ class Utilities(script_utilities.Utilities):
         if not obj:
             return -1
 
-        if not self._script.isInBuddyList(obj):
+        if not self._script.chat.isInBuddyList(obj):
             return script_utilities.Utilities.nodeLevel(self, obj)
 
         try:
diff --git a/src/orca/scripts/apps/pidgin/speech_generator.py b/src/orca/scripts/apps/pidgin/speech_generator.py
index 6d526dc..2e88848 100644
--- a/src/orca/scripts/apps/pidgin/speech_generator.py
+++ b/src/orca/scripts/apps/pidgin/speech_generator.py
@@ -43,7 +43,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
     def _generateExpandableState(self, obj, **args):
         result = []
-        if self._script.isInBuddyList(obj):
+        if self._script.chat.isInBuddyList(obj):
             # The Pidgin buddy list consists of two columns. The
             # column which is set as the expander column and which
             # also contains the node relationship is hidden.  Hidden
@@ -68,7 +68,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
     def _generateNumberOfChildren(self, obj, **args):
         result = []
-        if self._script.isInBuddyList(obj):
+        if self._script.chat.isInBuddyList(obj):
             # The Pidgin buddy list consists of two columns. The
             # column which is set as the expander column and which
             # also contains the node relationship is hidden.  Hidden



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]