# Orca
#
# Copyright 2006-2008 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser 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 script for gnome-search-tool"""

__id__        = "$Id$"
__version__   = "$Revision$"
__date__      = "$Date$"
__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
__license__   = "LGPL"

import orca.debug as debug
import orca.messages as messages
import orca.scripts.default as default

from orca.orca_i18n import _

import pyatspi
import time
from gi.repository import GLib
from gi.repository import GObject
GObject.threads_init()


########################################################################
#                                                                      #
# The gnome-search-tool script class.                                  #
#                                                                      #
########################################################################

class Script(default.Script):

    def __init__(self, app):
        """Creates a new script for the given application.

        Arguments:
        - app: the application to create a script for.
        """

        default.Script.__init__(self, app)

        # Set the debug level for all the methods in this script.
        #
        self.debugLevel = debug.LEVEL_FINEST

        # The table of files found.
        #
        self.fileTable = None

        # Set to true if we are doing a search.
        #
        self.searching = False

        # Time value using by the interval timer.
        #
        self.startTime = None

        # Interval in seconds, between utterances of "Searching" when a
        # search is in progress.
        #
        self.searchInterval = 5.0

    def getListeners(self):
        """Sets up the AT-SPI event listeners for this script.
        """
        listeners = default.Script.getListeners(self)

        listeners["object:state-changed:showing"]           = \
            self.onStateChanged

        return listeners

    def _speakSearching(self):
        """If we are still searching, let the user know. Then start another
        timer to go off again and repeat this process.
        """

        if not self.searching:
            return False

        currentTime = time.time()
        if not self.startTime or \
           (currentTime > (self.startTime + self.searchInterval)):
            self.presentMessage(messages.SEARCHING)
            self.startTime = time.time()

        return True

    def onStateChanged(self, event):
        """Called whenever an object's state changes.

        Arguments:
        - event: the Event
        """

        details = debug.getAccessibleDetails(self.debugLevel, event.source)
        debug.printObjectEvent(self.debugLevel, event, details)

        rolesList = [pyatspi.ROLE_PUSH_BUTTON, \
                    pyatspi.ROLE_FILLER, \
                    pyatspi.ROLE_FILLER, \
                    pyatspi.ROLE_FILLER, \
                    pyatspi.ROLE_FRAME, \
                    pyatspi.ROLE_APPLICATION]
        visible = event.source.getState().contains(pyatspi.STATE_VISIBLE)

        # Check to see if we have just had an "object:state-changed:showing"
        # event for the Stop button. If the name is "Stop", and one of its
        # states is VISIBLE, that means we have started a search. As the
        # search progresses, regularly inform the user of this by speaking
        # "Searching" (assuming the search tool has focus).
        #
        # Translators: the "Stop" string must match what gnome-search-tool
        # is using.  We hate keying off stuff like this, but we're forced
        # to do so in this case.
        #
        if self.utilities.hasMatchingHierarchy(event.source, rolesList) and \
           event.source.name == _("Stop") and visible:
            debug.println(self.debugLevel,
                          "gnome-search-tool.onNameChanged - " \
                          + "search started.")

            self.searching = True

            # If we don't already have a handle to the table containing the
            # list of files found, then get it now.
            #
            if not self.fileTable:
                frame = self.utilities.topLevelObject(event.source)
                allTables = self.utilities.descendantsWithRole(
                    frame, pyatspi.ROLE_TABLE)
                self.fileTable = allTables[0]

            GLib.idle_add(self._speakSearching)

        # Check to see if we have just had an "object:state-changed:showing"
        # event for the Find button. If the name is "Find", and one of its
        # states is VISIBLE and we are currently searching, that means we
        # have just stopped a search. Inform the user that the search is
        # complete and tell them how many files were found.
        #
        # Translators: the "Find" string must match what gnome-search-tool
        # is using.  We hate keying off stuff like this, but we're forced
        # to do so in this case.
        #
        if self.utilities.hasMatchingHierarchy(event.source, rolesList) \
           and event.source.name == _("Find") and visible and self.searching:
            debug.println(self.debugLevel,
                          "gnome-search-tool.onNameChanged - " \
                          + "search completed.")

            self.searching = False
            self.presentMessage(messages.SEARCH_COMPLETE)
            sensitive = self.fileTable.getState().contains( \
                                                pyatspi.STATE_SENSITIVE)
            if sensitive:
                try:
                    fileCount = self.fileTable.queryTable().nRows
                except NotImplementedError:
                    fileCount = 0
                noFilesString = messages.filesFound(fileCount)
                self.presentMessage(noFilesString)
            else:
                self.presentMessage(messages.FILES_NOT_FOUND)

        # Pass the event onto the parent class to be handled in the default way.
        #
        default.Script.onStateChanged(self, event)
