#
# This file is part of GNU Enterprise.
#
# GNU Enterprise is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2, or (at your option) any later version.
#
# GNU Enterprise 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with program; see the file COPYING. If not,
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright 2002-2003 Free Software Foundation
#
# FILE:
# ScreenContainer.py
#
# DESCRIPTION:
#
# NOTES:
#

#from gnue.common.apps import GDebug
import math
import curses
##try:
## from curses import panel
##except ImportError:
##  raise "The use of curses requires at least Python 2.1"

from constants import *
import sio
from utility import _paintBorder
from GraphicObject import GraphicObject
Frame = None

class ScreenContainer(GraphicObject):
  """
  full screen container -- allows the user to place controls on the
  entire screen
  """

  def __init__(self, SIOInstance, **properties):
    """
    Zero Controls list and Metrics dictionary.  Chainload GraphicObject
    parent class.  Event initialization.
    """

    # Nasty, nasty, nasty, nasty... to avoid a recursive import
    global Frame
    if not Frame:
      from Frame import Frame

    self.Controls = []  # Stack of control objects
    self.Frames = []    # Stack of child frames
    self.FrameStackOrder = []  # The display/stack order of frames
    self.Panels = []
    self.Metrics  = {}  # ditionary of dimensions, keyed by control obj
    self.VISIBLE = 1

    apply(GraphicObject.__init__, (self, SIOInstance), properties)

    self.STARTPOINT = 0
    self.breakUp = 0
    self.SetColor(SIOInstance.colors.C_BACKGROUND)
    self.nextCtl = None
    self.eventX = -1
    self.eventY = -1
    self._CurrentFocus = None
    self._CurrentFrame = self
    self.R1 = 0
    self.C1 = 0
    self._ChildOffsetX = 0
    self._ChildOffsetY = 0


  def SetProperty(self, PropertyName, PropertyValue):
    """
    """
    GraphicObject.SetProperty(self, PropertyName, PropertyValue)
    if self.IsPropertyInherited(PropertyName):
      for i in xrange(0, len(self.Controls)):
        self.Controls[i].SetProperty(PropertyName, PropertyValue)

  def AddPanel(self, panel):
    self.Panels.append(panel)

  def AddFrame(self, frame):
    self.Frames.append(frame)
    self.FrameStackOrder.insert(0,frame)
    frame.InheritProperties(self)

  def DelFrame(self, frame):
    self.Frames.pop(self.Frames.index(frame))
    self.FrameStackOrder.pop(self.FrameStackOrder.index(frame))
    for child in frame.Controls:
      del self.Metrics[child]

  def RaiseFrame(self, frame):
    self._CurrentFocus = None
    self._CurrentFrame = frame
    frame.VISIBLE = 1
    self.FrameStackOrder.pop(self.Frames.index(frame))
    self.FrameStackOrder.insert(0,frame)
    frame.Paint()

  def AddControl(self, control):
    """
    Add a control to the screen container.  Control object is 'setup' and
    appened to the Controls list, ABSOLUTE corners are registered in a
    4 member tuple (Y,X,H,W) in Metrics, keyed by control name.
    """

    control._ABSY = control.Y + self.R1 + self._ChildOffsetY
    control._ABSX = control.X + self.C1 + self._ChildOffsetX

    control.InheritProperties(self)
    self.Controls.append(control)
    self.Metrics[control] = (
        control._ABSY, control._ABSX,
        control._ABSY+ control.H,
        control._ABSX+ control.W)

    return control

  def DelControl(self,control = None):
    """Delete a control from the Control stack, and it's dimension
    properties from the Metrics dict."""
    if  self.Controls.count(control) != 0:
      del self.Metrics[control]
      self.Controls.remove(control)

  def Screen(self):
    """Return SCREEN to caller."""
    return self.SCREEN

  def Paint(self, captureRefresh = 1):
    """
    Paint EVERY Control registered in this ScreenContainer
    instance.  Effectively repainting the entire container iteratively.
    """
    if not self.VISIBLE:
      return

    if captureRefresh:
      self.SCREEN.AutoRefresh = 0

    for Control in self.Controls:
      Control.ExecMethod("SYSPAINT", None, None, None)
    for frame in self.FrameStackOrder:
      frame.Paint(0)

    if captureRefresh:
      self.SCREEN.AutoRefresh = 1
      self.SCREEN.Refresh()


  def FindControlByName(self, ControlName):
    "Return control object from self.Controls based on label 'NAME'."
    for c in self.Controls:
      if c.NAME== ControlName:
        return c
    return None

  def Height(self):
    """Return the actual _count_ size of the height."""
    return self.SCREEN.MAXROW + 1

  def Width(self):
    """Return the actual _count_ size of the width."""
    return self.SCREEN.MAXCOL + 1

  def Run(self):
    self.Paint()

    # myControlStackIndex
    myCsi = self.STARTPOINT
    # Sanity check the Csi
    if myCsi == None:
      myCsi = 0

    GDebug.printMesg(0, 'I am a running fewl; s/I/%s/' % self)
    GDebug.printMesg(0, self._CurrentFrame)
    GDebug.printMesg(0, self.Controls)

    # event loop runs forever
    while 1:
      activate = 0

      # if breakUp is set to TermInfoTrue, return ControlStackIndex
      # less one.
      if self.breakUp :
        self.breakUp = 0
        return myCsi-1

      # Sanity check the Control Stack index (again!), reset index to
      # zero on exception.
      try:
        myCtl = self._CurrentFrame.Controls[myCsi]
      except:
        myCsi = 0
        try:
          myCtl = self._CurrentFrame.Controls[myCsi]
        except:
          print "WARNING: No widgets!!"
          return

      # if nextControl is not at the initial state: ie. in the "loaded"
      # state:
      if self.nextCtl != None:
        # get and set the next desired ControlStackIndex based on
        # the next queued up control
        myCsi = self._CurrentFrame.Controls.index(self.nextCtl)
        # reassign myCtl control to the next desired.
        myCtl = self.nextCtl
        # reset the next Control value to None
        self.nextCtl = None
        # My control is set and ready, mark it to be activated.
        activate = 1

      # if this control has explicitly been set to 'non-active' with
      # Control.DeActivate or Control.SetActiveState then don't
      # initialize it.
      if myCtl.active == 0:
        # assign the next active control stack index to myCsi
        myCsi = self.__GetNextActiveCtlIndex(myCsi)

      # Otherwise, we have an 'active' Control!
      # "only go to the control if it can get focus"
      elif myCtl.CANGETFOCUS:
        self._CurrentFocus = myCtl
        # call system level gotfocus
        myCtl.ExecMethod("SYSGOTFOCUS", None, None, None)
        # If a custom GOTFOCUS method has been assigned,
        # has the user supply a Got Focus routine?
        UserGotFocus = myCtl.GetMethod("GOTFOCUS")

        # call user level gotfocus method
        if UserGotFocus <> None:
          # if it's alright to get focus, do so
          if UserGotFocus(myCtl, None, None):
            # do system level run
            if myCtl.ExecMethod( "SYSRUN", activate,
              self.eventY, self.eventX) :
              return myCsi

        else:
          # otherwise, _just_ do system level run
          if myCtl.ExecMethod("SYSRUN", activate,
            self.eventY, self.eventX) :
            return myCsi

        # When in the world do we get program control here?!
        myCtl.ExecMethod("SYSLOSTFOCUS", None, None, None)
        UserLostFocus = myCtl.GetMethod("LOSTFOCUS")

        if UserLostFocus <> None:
          if UserLostFocus(myCtl, None, None):
            myCsi = self.__GetNextActiveCtlIndex(myCsi)
        else:
          myCsi = self.__GetNextActiveCtlIndex(myCsi)

      # if the Control is 'active', BUT can't get focus:
      else:
        myCsi = self.__GetNextActiveCtlIndex(myCsi)

      activate = 0



  def SetFocus(self, myCtl, activate=1, run=1):

    if self._CurrentFocus:
      if not self._LoseFocus(self._CurrentFocus):
        return None

    self._CurrentFocus = myCtl

    myCtl.ExecMethod("SYSGOTFOCUS", None, None, None)
    # If a custom GOTFOCUS method has been assigned,
    # has the user supply a Got Focus routine?
    UserGotFocus = myCtl.GetMethod("GOTFOCUS")

    if run:
      # call user level gotfocus method
      if UserGotFocus <> None:
        # if it's alright to get focus, do so
        if UserGotFocus(myCtl, None, None):
          # do system level run
          if myCtl.ExecMethod( "SYSRUN", activate,
                   self.eventY, self.eventX) :
            return 0

      else:
        # otherwise, _just_ do system level run
        if myCtl.ExecMethod("SYSRUN", activate,
             self.eventY, self.eventX) :
          return 0


  def _LoseFocus(self, myCtl):

    myCtl.ExecMethod("SYSLOSTFOCUS", None, None, None)
    UserLostFocus = myCtl.GetMethod("LOSTFOCUS")

    if UserLostFocus <> None:
      if UserLostFocus(myCtl, None, None):
        return 1
      else:
        return 0

    # if the Control is 'active', BUT can't get focus:
    else:
      return 1



  def BreakOrder(self, Char):
    """
    BreakOrder checks user event input for mouse events. If mouse
    event, the item clicked on is checked for activity, the click
    coordinates are placed in self.eventX and Y, AND nextCtl is set to
    the clicked on item. Otherwise returns TermInfo False."""

    if Char == curses.KEY_MOUSE:
      event = curses.getmouse()
      if event[4] & curses.BUTTON1_CLICKED:
        item = self.GetControlAt(event[2],event[1])
        if isinstance(item, Frame):
          item = Frame.Controls

        if item != None and item.active :
          self.eventY = event[2]
          self.eventX = event[1]
          self.nextCtl = item
          return 1
    return 0


  def KeystrokeHook(self, Char):
    return Char


#
# private functions
#

  def __GetNextActiveCtlIndex(self, active):
    step = BACKWARDS and -1 or 1
    limit = len(self._CurrentFrame.Controls)
    if active < 0 or active >= limit:
      return None
    i = 0
    while i < limit:
      active += step
      if active < 0:
        active = limit-1
      elif active >= limit:
        active = 0
      Con = self._CurrentFrame.Controls[active]
      if Con.active :
        return active
      ++i
    return None


  def GetControlAt(self, Y, X):
    # Check any child frames first
    for frame in self.FrameStackOrder:
      if frame.VISIBLE and frame.C1 <= X <= frame.C2 and frame.R1 <= Y <= R2:
        return frame.GetControlAt(Y, X) or frame

    # Now check our controls
    for control in self.Controls:
      metric = self.Metrics[control]
      if Y >= metric[0] and Y < metric[2] and X >= metric[1] and X < metric[3] and control.active:
        return control
    return None


  def __PaintBorder(self, caption=None):
    DialogWidth = self.Width()
    DialogHeight = self.Height()
    _paintBorder( self, 0,0, DialogHeight, DialogWidth, caption=caption)

