# GNU Enterprise Forms - QT3 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: form.py 10017 2009-10-28 14:59:53Z reinhard $
"""
Implementation of the UI layer for the <form> and <dialog> tag
"""

import qt
import os

from gnue.common.apps import GConfig
from gnue.forms.uidrivers.qt3 import dialogs, QTApp
from gnue.forms.uidrivers.qt3.widgets._base import UIHelper

# =============================================================================
# The form
# =============================================================================

class UIForm(UIHelper):
    """
    Implementation of the <form> and <dialog> tag
    """

    _TAB_STYLE = {'left': qt.QTabWidget.Top,
                  'top' : qt.QTabWidget.Top,
                  'right': qt.QTabWidget.Bottom,
                  'bottom': qt.QTabWidget.Bottom}

    _MBOX_KIND = {'Info'    : {'style': qt.QMessageBox.information,
                               'title': u_("Information"),
                               'btns' : [qt.QMessageBox.Ok]},
                  'Warning' : {'style': qt.QMessageBox.warning,
                               'title': u_("Warning"),
                               'btns' : [qt.QMessageBox.Ok]},
                  'Question': {'style': qt.QMessageBox.question,
                               'title': u_("Question"),
                               'btns' : [qt.QMessageBox.Yes,
                                         qt.QMessageBox.No]},
                  'Error'   : {'style': qt.QMessageBox.critical,
                               'title': u_("Error"),
                               'btns' : [qt.QMessageBox.Ok]}}

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

    def __init__(self, event):

        UIHelper.__init__(self, event)

        self.pages = []
        self.main_window = None

        self._form = None
        self.__status_bar = None
        self.__status_fields = []
        self.sizing_enabled = False

        self._inits.extend([self.__set_initial_page])

    # -------------------------------------------------------------------------
    # Create a new window object
    # -------------------------------------------------------------------------

    def _create_widget_(self, event, spacer):
        
        if self._form.style != 'dialog':
            self.main_window = self._uiDriver._parentContainer
            if not self.main_window:
                self.main_window = MainWindow(self)
        else:
            self.main_window = Dialog(self)

        self.main_window.setCaption(self._form.title)

        self.main_widget = qt.QWidget(self.main_window)
        if self._form.style != 'dialog':
            self.main_window.setCentralWidget(self.main_widget)

        base_sizer = qt.QVBoxLayout(self.main_widget)
        base_sizer.setMargin(6)
        base_sizer.setResizeMode(qt.QLayout.Minimum)

        if self._form.style != 'dialog':
            if not self._form._features['GUI:STATUSBAR:SUPPRESS']:
                self.__status_bar = self.main_window.statusBar()
                self.__setup_status_bar()

        if self._form._layout.tabbed != 'none':
            self._container = qt.QTabWidget(self.main_widget)
            self._container.setTabPosition( \
                    self._TAB_STYLE[self._form._layout.tabbed])
            base_sizer.addWidget(self._container)
            self._container.connect(self._container,
                qt.SIGNAL('currentChanged(QWidget*)'), self.__on_page_changed)
        else:
            self._container = self.main_widget

        return self.main_window


    # -------------------------------------------------------------------------
    # Show the form/dialog
    # -------------------------------------------------------------------------

    def _ui_show_(self, modal):

        self._uiDriver.hide_splash()
        self.sizing_enabled = True

        if modal and isinstance(self.main_window, qt.QDialog):
            self.main_window.setModal(True)
            self.main_window.exec_loop()
        else:
            self.main_window.show()


    # -------------------------------------------------------------------------
    # Show a given page
    # -------------------------------------------------------------------------

    def show_page(self, page_index):
        """
        Show a given <page> in the current container

        @param page_index: the zero-based index of the page to show
        """

        if isinstance(self._container, qt.QTabWidget):
            self._container.blockSignals(True)
            try:
                self._container.setCurrentPage(page_index)
            finally:
                self._container.blockSignals(False)
        else:
            self.main_window.setUpdatesEnabled(False)
            try:
                for (index, page) in enumerate(self.pages):
                    if index == page_index:
                        page.show()
                    else:
                        page.hide()
            finally:
                self.main_window.setUpdatesEnabled(True)
                self.main_window.repaint()

    # -------------------------------------------------------------------------
    # Populate the status bar
    # -------------------------------------------------------------------------

    def __setup_status_bar(self):

        self.__status_bar.setSizeGripEnabled(True)

        for i in [-1, 50, 50, 75, 75]:
            widget = qt.QLabel(self.__status_bar)
            self.__status_fields.append(widget)
            if i > -1:
                widget.setMinimumWidth(75)
                stretch = 0
            else:
                stretch = 1
            self.__status_bar.addWidget(widget, stretch, True)

    # -------------------------------------------------------------------------
    # Set the initial page during phased init
    # -------------------------------------------------------------------------

    def __set_initial_page (self):
        self.show_page(0)

    # -------------------------------------------------------------------------
    # Event-handler
    # -------------------------------------------------------------------------

    def __on_page_changed(self, widget):
        index = self._container.indexOf(widget)
        self._container.blockSignals(True)
        try:
            self._form._event_page_changed(index)
        finally:
            self._container.blockSignals(False)



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

    def _ui_begin_wait_(self):
        """
        Display the hourglass cursor on all windows of the application.
        """
        self.main_window.setCursor(qt.QCursor(qt.Qt.WaitCursor))

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

    def _ui_end_wait_(self):
        """
        Display the normal mouse cursor on all windows of the application.
        """
        self.main_window.setCursor(qt.QCursor(qt.Qt.ArrowCursor))

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

    def _ui_beep_(self):
        """
        Ring the system bell
        """
        QTApp.getQtApp().beep()

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

    def _ui_update_status_(self, tip, record_status, insert_status,
            record_number, record_count, page_number, page_count):
        """
        Update the apropriate section of the status bar with the given
        information.

        @param tip: message to be shown in the first section
        @param record_status: message for the second section
        @param insert_status: message for the third section
        @param record_number: number of the current record
        @param record_count: number of available records.  Together with
            record_number this will be set into the 4th section
        @param page_number: number of the current page
        @param page_count: number of available pages.  Together with the
            page_number this will be set into the 5th section
        """ 

        if not self.__status_bar:
            return

        if tip is not None:
            self.__status_fields[0].setText(tip)

        if record_status is not None:
            self.__status_fields[1].setText(record_status)

        if insert_status is not None:
            self.__status_fields[2].setText(insert_status)

        if record_number is not None and record_count is not None:
            if record_number == 0 or record_count == 0:
                self.__status_fields[3].setText("")
            else:
                self.__status_fields[3].setText(
                        "%s/%s" % (record_number, record_count))

        if page_number is not None and page_count is not None:
            if page_number == 0 or page_count == 0:
                self.__status_fields[4].setText("")
            else:
                self.__status_fields[4].setText(
                        "%s/%s" % (page_number, page_count))

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

    def _ui_goto_page_(self, page):
        """
        Change to container to show the requested page

        @param page: UIPage instance to show
        """

        self.show_page(page.page_index)

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

    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.
        """

        boxClass = self._MBOX_KIND[kind]['style']
        buttons = self._MBOX_KIND[kind]['btns'][:]
        if cancel:
            buttons.append(qt.QMessageBox.Cancel)

        while len(buttons) < 3:
            buttons.append(qt.QMessageBox.NoButton)

        if title is None:
            title = self._MBOX_KIND[kind]['title']

        result = boxClass(None, title, message, *buttons)
        if result in [qt.QMessageBox.Cancel, qt.QMessageBox.NoButton]:
            return None
        elif result in [qt.QMessageBox.Ok, qt.QMessageBox.Yes]:
            return True
        else:
            return False

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

    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.
        """

        wst = ";;".join(["%s (%s)" % (desc, filt) for (desc, filt) in wildcard])
        dlg = qt.QFileDialog(default_dir, wst, self.main_window, "File Dialog",
                True)
        dlg.setCaption(title)

        if multiple:
            dmode = qt.QFileDialog.ExistingFiles
        else:
            dmode = qt.QFileDialog.ExistingFile

        if mode.lower().startswith('save'):
            dmode = qt.QFileDialog.AnyFile

        dlg.setMode(dmode)

        while True:
            mres = dlg.exec_loop()
            
            # User cancelled the dialog
            if not mres:
                result = None
                break

            if dmode == qt.QFileDialog.AnyFile:
                fname = unicode(dlg.selectedFile())
                if overwrite_prompt and os.path.exists(fname):
                    answ = self._ui_show_message_( \
                        u_("The file '%(file)s' already exists. " \
                           "Overwrite it?") % {'file': fname},
                        'Question', u_("File already exists"), True)
                    if answ:
                        result = [fname]
                        break

            elif dmode == qt.QFileDialog.ExistingFile:
                result = [unicode(dlg.selectedFile())]
                break
            else:
                result = [unicode(i) for i in dlg.selectedFiles()]
                if result:
                    break

        return result

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

    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: Has no effect in QT3, since the "Create new directory"
            Button is allways available here.

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

        result = qt.QFileDialog.getExistingDirectory(default_dir,
                self.main_window, "Directory Dialog", title, True)
        return unicode(result) or None


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

    def _ui_show_about_(self, name, version, author, description):
        """
        Show the about box describing the current application
        """
        idir = GConfig.getInstalledBase('forms_images', 'common_images')
        icon = os.path.join(idir, 'gnue-icon.png')

        dlg = dialogs.AboutBox(self.main_window, name, version, author,
                description, icon)
        dlg.exec_loop()

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

    def _ui_close_(self):
        """
        Close the window (acutally only hide it)
        """
        self.sizing_enabled = False
        self.main_window.hide()

# =============================================================================
# Window class
# =============================================================================

class MainWindow(qt.QMainWindow):
    """
    A forms main window
    """

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

    def __init__(self, ui_form):

        self.ui_form = ui_form
        self._closing_ = False
        qt.QMainWindow.__init__(self)


    # -------------------------------------------------------------------------
    # Close of a form
    # -------------------------------------------------------------------------

    def close(self, *args):
        """
        Only close the form if @I{_closing_} is set to True, otherwise pass the
        close request back to the bound GFForm instance.
        """

        if self._closing_:
            return qt.QMainWindow.close(self, *args)
        else:
            self.ui_form._form.close()
            return False


# =============================================================================
# A dialog type form
# =============================================================================

class Dialog(qt.QDialog):
    """
    Qt-Widget for dialog style forms
    """

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

    def __init__(self, ui_form):

        self.ui_form = ui_form
        self._closing_ = False
        qt.QDialog.__init__(self)


    # -------------------------------------------------------------------------
    # Close a dialog
    # -------------------------------------------------------------------------

    def close(self, *args):
        """
        Only close the form if @I{_closing_} is set to True, otherwise pass the
        close request back to the bound GFForm instance.
        """

        if self._closing_:
            return qt.QDialog.close(self, *args)
        else:
            self.ui_form._form.close()
            return False


# =============================================================================
# Configuration
# =============================================================================

configuration = {
    'baseClass': UIForm,
    'provides': 'GFForm',
    'container': 1
}
