# GNU Enterprise Forms - GTK UI Driver - User Interface
#
# 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: UIdriver.py 9956 2009-10-11 18:54:57Z reinhard $

import sys
import string
import types
import math

from gnue.common.apps import i18n
from gnue.forms.uidrivers._base import Exceptions
from gnue.forms.uidrivers._base.UIdriver import GFUserInterfaceBase

try:
  import pygtk
  pygtk.require ('2.0')

  import gtk
  import gobject
  import pango

  ## check if we really imported gtk 2.0
  if not hasattr (gtk, "keysyms") or gtk.pygtk_version [1] < 4:
    print u_("Import error: You need pygtk 2.4 to use the gtk2 user "
             "interface driver.")
    print u_("You are actually using gtk %s and pygtk %s") \
             % ("%s.%s.%s" % gtk.gtk_version,
                "%s.%s.%s" % gtk.pygtk_version)

    raise ImportError

except AssertionError, ImportError:
  raise Exceptions.DriverNotSupported, \
      _("The GNUe-Forms GTK driver requires PyGTK and GTK 2.x.")

from gnue.common import events
from gnue.common.apps import GConfig


from gnue.forms.GFForm import *

from gnue.forms.uidrivers.gtk2.GFApp import *
from gnue.forms.uidrivers.gtk2.SplashScreen import *
from gnue.forms.uidrivers.gtk2.widgets._base  import *
from gnue.forms.uidrivers.gtk2 import dialogs

# =============================================================================
# This class implements a User Interface for GTK2
# =============================================================================

class GFUserInterface (GFUserInterfaceBase):

  _WidgetToGFObj = {}
  _WidgetToUIObj = {}

  # ---------------------------------------------------------------------------
  # Constructor
  # ---------------------------------------------------------------------------
  
  def __init__ (self, *args, **params):

    GFUserInterfaceBase.__init__ (self, *args, **params)
    self.name = "GTK2"
    self._display = gtk.gdk.display_manager_get ().get_default_display ()

    self.app = getApp ()

    if not self._disableSplash:
      self.splash = UISplashScreen ()
      self.splash.Show ()

    self.font       = APPFONT

    sr = (CHAR_WIDTH, CHAR_HEIGHT)
    for widgetModule in self._supportedWidgets.values ():
      if hasattr (widgetModule, 'size_request'):
        need = widgetModule.size_request ()
        sr = (max (sr [0], need [0]), max (sr [1], need [1]))

    (self.textWidth, self.textHeight) = sr

    assert gDebug (6, "Metrics %s x %s" % (self.textWidth, self.textHeight))

    self.widgetWidth  = self.textWidth
    self.widgetHeight = self.textHeight


  #############################################################################
  #
  # Incoming Event Processors
  #
  # Processes the incoming events from other objects
  # From here down should be nothing but eventListeners listed

  #
  # mainLoop
  #
  # The primary loop of the user interface.  Called once the UI is
  # fully activated (multiple loops are simulated by GFApp)
  #
  def mainLoop(self):
    self.app.mainLoop()


  # ---------------------------------------------------------------------------
  # Start an input dialog and return the data record or None if cancelled
  # ---------------------------------------------------------------------------

  def _getInput (self, title, fields, cancel = True):

    dialog = dialogs.InputDialog (title, fields, cancel)
    try:
      dialog.run ()
      return dialog.inputData

    finally:
      dialog.destroy ()


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

  def _ui_show_error_(self, message):

    dialog = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_MODAL,
        type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE,
        message_format=message)
    dialog.set_title("GNU Enterprise")
    dialog.run()
    dialog.destroy()


  # ---------------------------------------------------------------------------
  # Show an exception dialog
  # ---------------------------------------------------------------------------

  def _ui_show_exception_(self, group, name, message, detail):
    dialog = ExceptionDisplay (group, name, message, detail)
    try:
      dialog.run ()
    finally:
      dialog.destroy ()


  # ---------------------------------------------------------------------------
  # Process all pending events, but do not block if there are no events
  # ---------------------------------------------------------------------------

  def processMessages (self):
    while gtk.events_pending ():
      gtk.main_iteration (False)


  # ---------------------------------------------------------------------------
  # Exit the application
  # ---------------------------------------------------------------------------

  def _ui_exit_(self):
    """
    Exit the application.
    """

    assert gEnter(6)

    for child in self._children:
      if not isinstance (child.mainWindow, gtk.Dialog):
        child.mainWindow.destroy ()
      else:
        child.mainWindow.response (gtk.RESPONSE_CLOSE)

    self.app.quit ()

    assert gLeave(6)


# -----------------------------------------------------------------------------
# Get font metrics and font description for a given font 
# -----------------------------------------------------------------------------

def _getFontMetrics (fontName = None):
  """
  """
  label = gtk.Label ()

  try:
    if fontName is not None:
      label.modify_font (pango.FontDescription (fontName))

    context  = label.get_pango_context ()
    fontDesc = context.get_font_description ()
    metrics  = context.get_metrics (fontDesc)

    width  = pango.PIXELS (metrics.get_approximate_char_width ())
    height = pango.PIXELS (metrics.get_ascent () + metrics.get_descent ())

    # add 25 percent to the average character width
    width  = int (math.ceil (width * 1.25))

  finally:
    label.destroy ()

  return (width, height, fontDesc)



# =============================================================================
# This class implements a dialog for displaying exceptions
# =============================================================================

class ExceptionDisplay (gtk.Dialog):

  _TITLE = {'system'     : _("GNUe Internal System Error"),
            'admin'      : _("GNUe Unexpected Error"),
            'application': _("GNUe Application Error")}

  _FORMAT = {
     'system': u_("An unexpected internal error has occured:\n%s.\n"
                  "This means you have found a bug in GNU Enterprise. "
                  "Please report it to gnue-dev@gnu.org"),
     'admin': u_("An unexpected error has occured:\n%s.\n"
                 "Please contact your system administrator."),
     'application': u_("An unexpected error has occured:\n%s.\n"
                       "Please contact your system administrator.")}

  # ---------------------------------------------------------------------------
  # Constructor
  # ---------------------------------------------------------------------------

  def __init__ (self, group, name, message, detail):

    gtk.Dialog.__init__ (self, self._TITLE.get (group, _('Error')), None,
                       gtk.DIALOG_MODAL, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))

    self.set_border_width (5)

    button = gtk.Button (u_('>> Detail'))
    button.connect ('clicked', self.__toggleDetail)
    self.action_area.pack_start (button)
    button.show ()

    hbox = gtk.HBox (spacing = 16)

    image = gtk.Image ()
    image.set_from_stock (gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
    image.set_alignment (0, 0)
    hbox.pack_start (image, expand = False)
    image.show ()

    vbox = gtk.VBox (spacing = 16)

    label = gtk.Label (self._FORMAT.get (group, "%s") % message)
    label.set_line_wrap (True)
    label.set_alignment (0, 0)
    vbox.pack_start (label, expand = False)
    label.show ()

    textView = gtk.TextView ()
    textView.set_pixels_above_lines (2)
    textView.set_left_margin (2)
    textView.set_wrap_mode (gtk.WRAP_NONE)
    textView.set_cursor_visible (True)
    # Just set the textview to 'readonly' so using clipboard still works
    textView.set_editable (False)
    textView.modify_font (pango.FontDescription ('monospace'))
    textView.get_buffer ().set_text (detail)

    self.detailView = gtk.ScrolledWindow ()
    self.detailView.add_with_viewport (textView)
    self.detailView.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    (w, h) = self.__getDetailSize (detail)
    self.detailView.set_size_request (w, h)
    textView.show ()
    vbox.pack_start (self.detailView)

    hbox.pack_start (vbox)
    vbox.show ()

    self.vbox.pack_start (hbox, padding = 8)
    hbox.show ()

    self._showsDetail  = False
    self._originalSize = self.get_size ()
    self.set_position (gtk.WIN_POS_CENTER_ALWAYS)


  # ---------------------------------------------------------------------------
  # toggle visibility of the detail display
  # ---------------------------------------------------------------------------

  def __toggleDetail (self, button):
    if self._showsDetail:
      self.detailView.hide_all ()
      button.set_label (u_(">> Details"))
      self.resize (*self._originalSize)
    else:
      self.detailView.show_all ()
      button.set_label (u_("<< Details"))
  
    self._showsDetail = not self._showsDetail


  # ---------------------------------------------------------------------------
  # Try to figure out a width and heigth for the traceback-text
  # ---------------------------------------------------------------------------

  def __getDetailSize (self, detail):

    lines = detail.splitlines ()
    maxH = len (lines) + 3
    maxW = 0

    for line in lines:
      maxW = max (maxW, len (line))

    maxW += 5
    (w, h) = _getFontMetrics ('monospace') [:2]

    return (min (w * maxW, gtk.gdk.screen_width ()),
            min (h * maxH, gtk.gdk.screen_height () - 100))


# =============================================================================
# Get the current default application font and it's metrics
# =============================================================================

(CHAR_WIDTH, CHAR_HEIGHT, APPFONT) = _getFontMetrics ()
