# GNU Enterprise Forms - GTK UI Driver - Form widget
#
# Copyright 2001-2009 Free Software Foundation
#
# 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 3, 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.
#
# $Id: widget.py 9956 2009-10-11 18:54:57Z reinhard $

import gtk
import string
import wrappers
import sys
import types

from gnue.common import events
from gnue.forms.uidrivers.gtk2 import dialogs
from gnue.forms.uidrivers.gtk2.widgets._base import UIHelper


_MBOX_KIND = {'info'    : {'type'   : gtk.MESSAGE_INFO,
                           'buttons': gtk.BUTTONS_CLOSE},
              'warning' : {'type'   : gtk.MESSAGE_WARNING,
                           'buttons': gtk.BUTTONS_CLOSE},
              'question': {'type'   : gtk.MESSAGE_QUESTION,
                           'buttons': gtk.BUTTONS_YES_NO},
              'error'   : {'type'   : gtk.MESSAGE_ERROR,
                           'buttons': gtk.BUTTONS_CLOSE}}

_RESPONSE = {gtk.RESPONSE_OK    : True,
             gtk.RESPONSE_CLOSE : True,
             gtk.RESPONSE_YES   : True,
             gtk.RESPONSE_NO    : False,
             gtk.RESPONSE_CANCEL: None }


# =============================================================================
# This class creates a single instance of a form widget
# =============================================================================

class UIForm (UIHelper):
  
  # ---------------------------------------------------------------------------
  # Constructor
  # ---------------------------------------------------------------------------

  def __init__ (self, event):
    self._uiPageList = []
    self.statusBar   = None

    UIHelper.__init__ (self, event)

  
  # ---------------------------------------------------------------------------
  # GTK-Widget creation
  # ---------------------------------------------------------------------------

  def _create_widget (self, event, spacer):

    gfObject = event.object

    self.menu_sb_space = 0

    formWidth  = int (self._form._layout.Char__width)
    formHeight = int (self._form._layout.Char__height)

    self._visibleSize = (formWidth  * self._uiDriver.widgetWidth,
                         formHeight * self._uiDriver.widgetHeight)
    
    self._formSize = formSize = (formWidth * self._uiDriver.widgetWidth,
               (formHeight + self.menu_sb_space) * self._uiDriver.widgetHeight)

    # The main frame of this form
    if self._form.style != 'dialog':
      self.mainWindow = gtk.Window ()
      self.mainWindow.set_resizable (True)
      self.content_table = gtk.Table (4, 1, False)
      self.mainWindow.add (self.content_table)

      self.containerFrame = gtk.Frame ()
      self.content_table.attach (self.containerFrame,
                 # X direction           Y direction
                 0, 1,                   2, 3,
                 gtk.EXPAND | gtk.FILL,  gtk.EXPAND | gtk.FILL,
                 0,                      0)
      self.containerFrame.show ()

    else:
      self.mainWindow = gtk.Dialog (flags = gtk.DIALOG_NO_SEPARATOR |
          gtk.DIALOG_DESTROY_WITH_PARENT)
      self.mainWindow.set_resizable (True)
      self.mainWindow.action_area.set_size_request (-1, 0)

      self.content_table = gtk.Table (4, 1, False)

      self.containerFrame = gtk.Frame ()
      self.content_table.attach (self.containerFrame,
                 # X direction           Y direction
                 0, 1,                   2, 3,
                 gtk.EXPAND | gtk.FILL,  gtk.EXPAND | gtk.FILL,
                 0,                      0)

      wx = (formWidth  + 1) * self._uiDriver.widgetWidth
      wy = (formHeight + 1) * self._uiDriver.widgetHeight

      self.containerFrame.set_size_request (wx, wy)

      self.mainWindow.vbox.pack_start (self.content_table)
      self.containerFrame.show ()
      self.content_table.show ()

    self.mainWindow.connect ('move_focus', self.__on_move_focus)
    self.mainWindow.connect ('delete_event', self.__on_delete_event)
    self.accelGroup = gtk.AccelGroup ()
    self.mainWindow.add_accel_group (self.accelGroup)

    title = "%s" % self._form.title
    self.mainWindow.set_title (self._makeSafe (title))

    if gfObject._layout.tabbed != 'none':
      self._wrapper = wrappers.TabbedWrapper (self)
    else:
      self._wrapper = wrappers.PlainWrapper (self)

    newWidget = self._wrapper.pane
    self._container = newWidget
     
    # Add Statusbar as requested and/or allowed
    if self._form.style != 'dialog':
      if not self._form._features ['GUI:STATUSBAR:SUPPRESS']:
        self.createStatusBar ()

    self._eventHandler = event.eventHandler
    self._wrapper.finalize ()
    self._visible = False

    return newWidget


  # ---------------------------------------------------------------------------
  # Create the status bar
  # ---------------------------------------------------------------------------

  def createStatusBar (self):
    self.statusBar1 = gtk.Statusbar ()
    self.statusBar2 = gtk.Statusbar ()
    self.statusBar3 = gtk.Statusbar ()
    self.statusBar4 = gtk.Statusbar ()
    self.statusBar5 = gtk.Statusbar ()

    self.statusBar1.set_has_resize_grip (False)
    self.statusBar2.set_has_resize_grip (False)
    self.statusBar3.set_has_resize_grip (False)
    self.statusBar4.set_has_resize_grip (False)
    self.statusBar5.set_has_resize_grip (False)

    status_bar_table = gtk.HBox (False)
    status_bar_table.pack_start (self.statusBar1)
    
    status_bar_table2 = gtk.HBox(False)
    status_bar_table2.pack_start (self.statusBar2)
    status_bar_table2.pack_start (self.statusBar3)
    status_bar_table2.pack_start (self.statusBar4)
    status_bar_table2.pack_start (self.statusBar5)

    status_bar_table.pack_start (status_bar_table2)

    self.content_table.attach(status_bar_table,
                 # X direction           Y direction
                 0,1,                    3, 4,
                 gtk.EXPAND | gtk.FILL,  0,
                 0,                      0)
                                                
  # ---------------------------------------------------------------------------
  # show the current form non-modal
  # ---------------------------------------------------------------------------

  def _ui_show_ (self, modal):

    # show_all moves the focus, so we have to remember where it was before and
    # restore it
    widget = self.mainWindow.get_focus ()

    self.mainWindow.show_all ()

    if modal:
      self.mainWindow.set_modal (True)

    if widget != self.mainWindow.get_focus ():
      widget.grab_focus ()

    self._visible = True
    if self._form.style == 'dialog':
      self.mainWindow.run ()
    

  # ---------------------------------------------------------------------------
  # Event handlers
  # ---------------------------------------------------------------------------

  def __on_move_focus(self, window, direction):

        # This event is generated to move the focus in the form by pressing tab
        # or cursor keys. We handle the focus ourselves, so we don't want the
        # window to do this.
        window.emit_stop_by_name('move_focus')

  # ---------------------------------------------------------------------------

  def __on_delete_event(self, window, event):

        # catch the form close event
        try:
            # this raises an exception if the user may not yet close the
            # window!
            self._form.close()
        except:
            # we must return True so the event isn't processed further, so we
            # display the exception manually here
            sys.excepthook(*sys.exc_info())
        return True


  # ---------------------------------------------------------------------------
  # Set the forms title
  # ---------------------------------------------------------------------------

  def _ui_set_title_(self, title):
      self.mainWindow.set_title(title)


  # ---------------------------------------------------------------------------
  # Makes the requested page visible while hiding the others
  # ---------------------------------------------------------------------------

  def _ui_goto_page_ (self, page):
    self._wrapper.setPage (page.widgets [0])
          
                     
  # ---------------------------------------------------------------------------
  # Show a tip in the status bar. Used by the menu.
  # ---------------------------------------------------------------------------

  def _show_tip (self, tip):
    
    if not hasattr (self, 'statusBar1'):
      return
    
    text = self._makeSafe ("%s" % tip)
    context = self.statusBar1.get_context_id ('tip')
    self.statusBar1.push (context, text)


  # ---------------------------------------------------------------------------
  # User feedback functions
  # ---------------------------------------------------------------------------

  def _ui_begin_wait_(self):
    if self.mainWindow.window is not None:
      self.mainWindow.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
    self._uiDriver.processMessages()

  # ---------------------------------------------------------------------------

  def _ui_end_wait_(self):
    if self.mainWindow.window is not None:
      self.mainWindow.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
    self._uiDriver.processMessages ()

  # ---------------------------------------------------------------------------

  def _ui_beep_(self):
    pass

  # ---------------------------------------------------------------------------

  def _ui_update_status_ (self, tip, record_status, insert_status,
          record_number, record_count, page_number, page_count):
    
    if not hasattr (self, 'statusBar1'):
      return
    
    if tip is not None:
      text = self._makeSafe ("%s" % tip)
      context = self.statusBar1.get_context_id ('tip')
      self.statusBar1.push (context, text)

    if record_status:
      context = self.statusBar2.get_context_id ('statusValue')
      self.statusBar2.push (context, self._makeSafe (record_status))
      
    if insert_status:
      context = self.statusBar3.get_context_id ('insertValue')
      self.statusBar3.push (context, self._makeSafe (insert_status))

    if record_number and record_count:
      context = self.statusBar4.get_context_id ('currentRecord_and_maxRecord')
      text    = string.strip ("%s/%s" % (record_number, record_count))
      self.statusBar4.push (context, self._makeSafe (text))

    if page_number and page_count:
      context = self.statusBar5.get_context_id ('currentPage_and_maxPage')
      text    = string.strip ("%s/%s" % (page_number, page_count))
      self.statusBar5.push (context, self._makeSafe (text))

    self._uiDriver.processMessages ()


  # ---------------------------------------------------------------------------
  # create a modal message box
  # ---------------------------------------------------------------------------

  def _ui_show_message_(self, message, kind, title, cancel):
    """
    This function creates a message box of a given kind and returns True, False
    or None depending on the button pressed.

    @param message: the text of the messagebox
    @param kind: type of the message box. Valid types are 'Info', 'Warning',
        'Question', 'Error'
    @param title: title of the message box
    @param cancel: If True a cancel button will be added to the dialog
    @return: True if the Ok-, Close-, or Yes-button was pressed, False if the
        No-button was pressed or None if the Cancel-button was pressed.
    """
    mbRec  = _MBOX_KIND.get (kind.lower ())
    dialog = gtk.MessageDialog (parent = self.mainWindow,
            flags = gtk.DIALOG_MODAL, type = mbRec ['type'],
            buttons = mbRec ['buttons'], message_format = message)

    if title is not None and len (title):
      dialog.set_title (title)

    cButtons = [gtk.BUTTONS_CANCEL, gtk.BUTTONS_OK_CANCEL]
    if cancel and not mbRec ['buttons'] in cButtons:
      dialog.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)

    res = dialog.run ()
    dialog.destroy ()

    return _RESPONSE [res]


  # ---------------------------------------------------------------------------
  # Create a FileChooser dialog to select filenames
  # ---------------------------------------------------------------------------

  def _ui_select_files_(self, title, default_dir, default_file, wildcard,
            mode, multiple, overwrite_prompt, file_must_exist):
        """
        Bring up a dialog for selecting filenames.

        @param title: Message to show on the dialog
        @param default_dir: the default directory, or the empty string
        @param default_file: the default filename, or the empty string
        @param wildcard: a list of tuples describing the filters used by the
            dialog.  Such a tuple constists of a description and a fileter.
            Example: [('PNG Files', '*.png'), ('JPEG Files', '*.jpg')]
            If no wildcard is given, all files will match (*.*)
        @param mode: Is this dialog an open- or a save-dialog.  If mode is
            'save' it is a save dialog, everything else would be an
            open-dialog.
        @param multiple: for open-dialog only: if True, allows selecting
            multiple files
        @param overwrite_prompt: for save-dialog only: if True, prompt for a
            confirmation if a file will be overwritten
        @param file_must_exist: if True, the user may only select files that
            actually exist

        @returns: a sequence of filenames or None if the dialog has been
            cancelled.
        """

        if mode.lower().startswith('save'):
            action = gtk.FILE_CHOOSER_ACTION_SAVE
            button = gtk.STOCK_SAVE
        else:
            action = gtk.FILE_CHOOSER_ACTION_OPEN
            button = gtk.STOCK_OPEN

        dlg = gtk.FileChooserDialog(title, self.mainWindow, action,
                (button, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL,
                    gtk.RESPONSE_REJECT))
        try:
            result = None
            dlg.set_select_multiple(multiple)
            dlg.set_do_overwrite_confirmation(overwrite_prompt)

            dlg.set_current_folder(default_dir)
            dlg.set_filename(default_file)

            for (descr, pattern) in wildcard:
                flt = gtk.FileFilter()
                flt.set_name(descr)
                flt.add_pattern(pattern)
                dlg.add_filter(flt)

            if dlg.run() == gtk.RESPONSE_ACCEPT:
                result = dlg.get_filenames()

        finally:
            dlg.destroy()

        return result


  # ---------------------------------------------------------------------------
  # Create a FileChooser dialog to select a directory name
  # ---------------------------------------------------------------------------

  def _ui_select_dir_(self, title, default_dir, new_dir):
        """
        Bring up a dialog for selecting a directory path.

        @param title: Message to show on the dialog
        @param default_dir: the default directory, or the empty string
        @param new_dir: In GTK2+ the "Create new directory" button is always
            available and can't be turned off

        @returns: a path or None if the dialog has been cancelled.
        """

        dlg = gtk.FileChooserDialog(title, self.mainWindow,
                gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
                (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL,
                    gtk.RESPONSE_REJECT))
        try:
            result = None

            dlg.set_current_folder(default_dir)

            if dlg.run() == gtk.RESPONSE_ACCEPT:
                result = dlg.get_filenames()

        finally:
            dlg.destroy()

        return result


  # ---------------------------------------------------------------------------
  # Display an about box
  # ---------------------------------------------------------------------------

  def _ui_show_about_(self, name, version, author, description):

    dlg = dialogs.AboutBox (name, version, author, description)
    try:
      dlg.run ()

    finally:
      dlg.destroy ()


  # ---------------------------------------------------------------------------
  # Print form screenshot
  # ---------------------------------------------------------------------------

  def _ui_printout_(self, title, subtitle, user):

    pass


  # ---------------------------------------------------------------------------
  # Close the window (actually only hide it)
  # ---------------------------------------------------------------------------

  def _ui_close_(self):

    self.containerFrame.hide ()
    self.mainWindow.hide ()
    self.mainWindow.set_modal (False)
    self._visible = False
