# -*- coding: utf-8 -*-

"""
CharTables dialog

Provides various kinds of good-to-have-at-hand tables for charsets, colors
and entities.


Brief application overview:

The dialog is wrapped around a QTableView. QAbstractTableModels
are dynamically inserted at runtime. Each model is packed in a ModelFactory
wich can hold multiple models in multiple categories. All ModelFactories
are then handed out to the the controler of the dialog for further processing.


The dialog comes equipped to handle the Eric4 special functionality
of inserting the contents of a selected cell directly into at the current position
of the current editor, but may aswell be used as standalone dialog.


Note that chars may or may not get displayed properly. Also some
ranges in some encodings are left out for performance reasons.

"""

import sys, os
from PyQt4 import QtCore, QtGui

from Ui_chartables import Ui_CT_DialogCharTables

# import model factories

from chartables_model_options import Options
from chartables_controler import Controler
from chartables_models_charsets import CHARSET_MODEL_FACTORIES
from chartables_models_colors import COLOR_MODEL_FACTORIES
from chartables_models_entities import ENTITY_MODEL_FACTORIES
import chartables_lang

from chartables_lib.areatip import AreaTip
from chartables_lib.combobox import ComboBoxWithEditTipWrap

# TODO:
#
# in no particular order
#
# - anyone for more tables?
#
# - currently clipboard and drag offers only 'text/plain' format
# - some visual feedback would be nice to indicate an item was copied/inserted
#
# - tooltips for truncated items are not 100% accurate. Pretty hard to find out
#   when exactly Qt truncates strings in combos.
# - toltip for edit filed in combobox flaps out a tad to fast. Higher delay
#    would be nice
#
# - maybe an option to make the dialog stay topmost?
# - maybe a gridlines on/off option?
#
#
#***********************************************************************
# consts
#***********************************************************************
ICO_APP = 'chartables_small.png'

CFG_ORG_NAME = 'jUrner'
CFG_APP_NAME = 'CTDialogCharTables'
CFG_KEY_STATE = 'State'
CFG_KEY_GEOMETRY = 'Geometry'
CFG_KEY_FLAGS = 'Flags'
CFG_KEY_LOCALE = 'Locale'


ID_TAB = 'tabWidget'
ID_CHARSETTYPECOMBO = 'cmbCharsetType'
ID_CHARSETCOMBO = 'cmbCharset'
ID_COLORTYPECOMBO = 'cmbColorType'
ID_COLORCOMBO = 'cmbColor'
ID_ENTITYTYPECOMBO = 'cmbEntityType'
ID_ENTITYCOMBO = 'cmbEntity'
ID_TABLE = 'tableView'

ID_BUTTON_BOX = 'buttonBox'
ID_BTCOPY = 'btCopy'
ID_BTINSERT = 'btInsert'     # special button for Eric4



MODEL_FACTORIES = (
    (CHARSET_MODEL_FACTORIES, ID_CHARSETTYPECOMBO, ID_CHARSETCOMBO),
    (COLOR_MODEL_FACTORIES, ID_COLORTYPECOMBO, ID_COLORCOMBO),
    (ENTITY_MODEL_FACTORIES, ID_ENTITYTYPECOMBO, ID_ENTITYCOMBO),
    )
    

CMB_MIN_CONTENTS_LEN = 5

#***********************************************************************
#
#***********************************************************************
class DialogCharTables(QtGui.QWidget, Ui_CT_DialogCharTables):
    """Main dialog"""


    def __init__(self, *args, **kwargs):
        """
        constructor

        @keyparam flagsEx CTFEX_* runtime flags
        @keyparam localeName if specified adjusts the language of the dialog
            to the specified language
        """
        QtGui.QWidget.__init__(self, *args)

        self.options = Options(self, flagsEx=kwargs.get('flagsEx', Options.CTFEX_NONE))
        localeName = kwargs.get('localeName', None)

        self.setupUi(localeName=localeName)



    def setupUi(self, localeName=None):
        """
        overwritten method to setup the user interface

        @param parent parent of the dialog
        """

        self.controler = None


        # get settings
        settings = self.getSettingsObject()
        settingsState = settings.value(
                                    CFG_KEY_STATE,
                                    QtCore.QVariant('')
                                    ).toString()
        settingsFlags, ok = settings.value(
                                    CFG_KEY_FLAGS,
                                    QtCore.QVariant(Options.CTF_MASK_DEFAULT)
                                    ).toInt()

        self.options.flags = settingsFlags

        self.options.areatips = AreaTip(self)

        # adjust language
        if not self.options.flagsEx & self.options.CTFEX_NOTRANSLATE:
            if not localeName:
                settingsLocale = settings.value(
                                        CFG_KEY_LOCALE,
                                        QtCore.QVariant(self.options.localeName)
                                        ).toString()
                localeName = str(settingsLocale)

            self.options.language = chartables_lang.Language()
            if not self.options.language.hasLocaleName(localeName):
                localeName = self.options.language.getDefaultLocaleName()
            self.options.localeName =localeName
            self.options.language.installTranslator(self, localeName)



        Ui_CT_DialogCharTables.setupUi(self, self)


        # init main table
        table = self.controlById(ID_TABLE)
        table.verticalHeader().setVisible(False)
        table.setDragEnabled(True)
        table.setSelectionMode(table.SingleSelection)
        self.connect(
            table,
            QtCore.SIGNAL('activated(const QModelIndex&)'),
            self._onActivated
            )

        # init factories
        tab = self.controlById(ID_TAB)
        self.controler = Controler(table, tab, options=self.options)
        self.connect(
            self.controler,
            QtCore.SIGNAL('optionsDisplayed(const QWidget&, bool)'),
            self._onOptionsDisplayed
            )
        self.connect(
            self.controler,
            QtCore.SIGNAL('modelInserted(const QAbstractTableModel&)'),
            self._onModelInserted
            )

        for factories, idTypeCmb, idModelCmb in MODEL_FACTORIES:
            cmbType = self.controlById(idTypeCmb)
            cmbModel = self.controlById(idModelCmb)

            # strings may be long here, so truncate if necessary
            # and show tooltips if necessary
            cmbModel.setMinimumContentsLength(CMB_MIN_CONTENTS_LEN)
            cmbModel.setSizeAdjustPolicy(cmbModel.AdjustToMinimumContentsLength)
            ComboBoxWithEditTipWrap(cmbModel)

            self.controler.newFactory(factories, cmbType, cmbModel)


        # init buttons
        buttonBox = self.controlById(ID_BUTTON_BOX)
            
        btCopy = self.controlById(ID_BTCOPY)
        buttonBox.addButton(btCopy, buttonBox.ActionRole)
        btCopy.setEnabled(False)
        self.connect(btCopy, QtCore.SIGNAL('clicked()'), self._onBtCopyClicked)

        # init Eric4 special to handle 'Insert At' button and QSettings
        btInsert = self.controlById(ID_BTINSERT)
        buttonBox.addButton(btInsert, buttonBox.ActionRole)
        btInsert.setEnabled(False)
        btInsert.setVisible(False)
        

        # throw application icon on the caption
        fpath = os.path.join(os.path.split(__file__)[0], ICO_APP)
        if os.path.isfile(fpath):
            ico = QtGui.QIcon(fpath)
            if not ico.isNull():
                self.setWindowIcon(ico)

                
        # restore state
        self.controler.restoreState(str(settingsState))
        v = settings.value(CFG_KEY_GEOMETRY)
        if v.isValid():
            self.restoreGeometry(v.toByteArray())
        



    def closeEvent(self, evt):
        """protected slot called when the dialog is closed"""
        settings = self.getSettingsObject()
        settings.setValue(CFG_KEY_STATE, QtCore.QVariant(self.controler.saveState()))
        settings.setValue(CFG_KEY_FLAGS, QtCore.QVariant(self.options.flags))
        settings.setValue(CFG_KEY_LOCALE, QtCore.QVariant(self.options.localeName))
        settings.setValue(CFG_KEY_GEOMETRY, QtCore.QVariant(self.saveGeometry()))


    def getSettingsObject(self):
        """public method to return the QSettings instance for the dialog"""
        return QtCore.QSettings(
                    CFG_ORG_NAME,
                    CFG_APP_NAME,
                    self
                    )


    def _getCurrentModelCellText(self):
        """protected method to return content (string) of the current index in
        the main table
        """
        index = self.controlById(ID_TABLE).selectionModel().currentIndex()
        if index.isValid():
            v = index.data()
            if v.isValid():
                return v.toString()
        return ''


    def _enableSelectionButtons(self, flag):
        """protected slot to enable/disable buttons acting on the current selection"""
        self.controlById(ID_BTCOPY).setEnabled(flag)


    def _clipCopy(self, text):
        """protected method to copy a chunk of text to the clipboard"""
        if text:
            clipb = QtGui.QApplication.clipboard()
            clipb.setText(text, clipb.Clipboard)
            if clipb.supportsSelection():
                clipb.setText(text, clipb.Selection)


    def _onBtCopyClicked(self):
        """protected slot called when the Copy button is hit"""
        self._clipCopy(self._getCurrentModelCellText())


    def _onActivated(self, index):
        """protected slot to copy selected cell to clipboard (enter or doubleclick)"""
        self._clipCopy(self._getCurrentModelCellText())


    def _onOptionsDisplayed(self, optionsWidget, flag):
        """protected slot called when the options tab is displayed"""
        method = flag and self.connect or self.disconnect
        method(
            optionsWidget,
            QtCore.SIGNAL('languageChanged(QString&)'),
            self.translate
            )


    def _onModelInserted(self, model):
        """protected slot called when the current model changes"""

        # we are intersted here in selection changes
        table = self.controlById(ID_TABLE)
        selectionModel = table.selectionModel()
        table.connect(
                selectionModel,
                QtCore.SIGNAL('selectionChanged(const QItemSelection&, const QItemSelection&)'),
                self._onSelectionChanged
                )
        self._enableSelectionButtons(False)


    def _onSelectionChanged(self, selected, deselected):
        """protected slot called when the selection of the main table changes"""

        has_selection = False
        indexes = selected.indexes()
        if indexes:
            v = indexes[0].data()
            if v.isValid():
                if v.toString():
                    has_selection = True
        self._enableSelectionButtons(has_selection)


    def translate(self, localeName):
        """translates the user interface according to the specified localeName

        @param localeName (string, QString) name of the locale like 'en' or 'de'

        If no translation is found matching the specified localeName a default
        the user interface is translated to a default language
        """
        if not self.options.flagsEx & self.options.CTFEX_NOTRANSLATE:
            localeName = str(localeName)
            self.options.localeName = localeName
            self.options.language.installTranslator(self, localeName)
        Ui_CT_DialogCharTables.retranslateUi(self, self)
        self.controler.retranslate()



    def controlById(self, idControl):
        """
        convenience method to return a contol given its id

        @param idControl actually the name of the control as specified in designer
        """
        return getattr(self, idControl)




#***********************************************************************
#
#***********************************************************************
if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    dlg = DialogCharTables()
    dlg.show()
    res = app.exec_()
    sys.exit(res)




