#!/usr/bin/env python
""" The PyKDE application sampler

This module defines the top-level widgets for displaying the sampler
application.


"""
import inspect
import os
import sys

from qt import SIGNAL, SLOT, PYSIGNAL, Qt
from qt import QVBoxLayout, QLabel, QPixmap, QSplitter, QFrame, QDialog
from qt import QSizePolicy, QHBoxLayout, QSpacerItem, QPushButton

from kdecore import i18n, KAboutData, KApplication, KCmdLineArgs, KGlobal
from kdecore import KGlobalSettings, KWin, KWinModule, KURL, KIcon

from kdeui import KComboBox, KListView, KListViewItem, KTabWidget, KTextEdit
from kdeui import KMainWindow, KPushButton, KSplashScreen, KStdAction
from kdeui import KKeyDialog, KEditToolbar

from kio import KTrader
from kparts import createReadOnlyPart, createReadWritePart
from khtml import KHTMLPart

import about
import lib


try:
    __file__
except (NameError, ):
    __file__ = sys.argv[0]


sigDoubleClicked = SIGNAL('doubleClicked(QListViewItem *)')
sigViewItemSelected = SIGNAL('selectionChanged(QListViewItem *)')
sigSampleSelected = PYSIGNAL('sample selected')

blank = KURL('about:blank')


def appConfig(group=None):
    """ appConfig(group=None) -> returns the application KConfig

    """
    config = KGlobal.instance().config()
    if group is not None:
        config.setGroup(group)
    return config


def getIcon(name, group=KIcon.NoGroup, size=KIcon.SizeSmall):
    """ returns a kde icon by name

    """
    return KGlobal.instance().iconLoader().loadIcon(name, group, size)


def getIconSet(name, group=KIcon.NoGroup, size=KIcon.SizeSmall):
    """ returns a kde icon set by name

    """
    return KGlobal.instance().iconLoader().loadIconSet(name, group, size)


def buildPart(parent, query, constraint, writable=False):
    """ builds the first available offered part on the parent

    """
    offers = KTrader.self().query(query, constraint)
    for ptr in offers:
        if writable:
            builder = createReadWritePart
        else:
            builder = createReadOnlyPart
        part = builder(ptr.library(), parent, ptr.name())
        if part:
            break
    return part



class CommonFrame(QFrame):
    """ provides a modicum of reuse

    """
    def __init__(self, parent):
        QFrame.__init__(self, parent)
        layout = QVBoxLayout(self)
        layout.setAutoAdd(True)
        layout.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)


class SamplerFrame(CommonFrame):
    """ frame type that swaps out old widgets for new when told to do so

    """
    def __init__(self, parent):
        CommonFrame.__init__(self, parent)
        self.widget = None

    def setWidget(self, widget):
        self.layout().deleteAllItems()
        previous = self.widget
        if previous:
            previous.close()
            delattr(self, 'widget')
        self.widget = widget

    def showSample(self, item, module):
        try:
            frameType = module.builder()
        except (AttributeError, ):
            print 'No sample callable defined in %s' % (module.name(), )
        else:
            frame = frameType(self)
            self.setWidget(frame)
            frame.show()


class SourceFrame(CommonFrame):
    """ frame with part for displaying python source

    """
    def __init__(self, parent):
        CommonFrame.__init__(self, parent)
        query = ''
        self.part = buildPart(self, 'application/x-python', query, False)

    def showModuleSource(self, item, module):
        if not self.part:
            print 'No part available for displaying python source.'
            return
        try:
            modulefile = inspect.getabsfile(module.module)
        except:
            return
        self.part.openURL(blank)
        if os.path.splitext(modulefile)[-1] == '.py':
            self.part.openURL(KURL('file://%s' % modulefile))


class WebFrame(CommonFrame):
    """ frame with part for viewing web pages

    """
    docBase = 'http://www.riverbankcomputing.com/Docs/PyKDE3/classref/'    

    def __init__(self, parent):
        CommonFrame.__init__(self, parent)
        self.part = part = buildPart(self, 'text/html', "Type == 'Service'")
        #part.connect(part, SIGNAL('khtmlMousePressEvent(a)'), self.onURL)

    def onURL(self, a):
        print '****', a
        
    def showDocs(self, item, module):
        try:
            mod, cls = module.module.docParts
        except (AttributeError, ):
            url = blank
        else:
            url = KURL(self.docUrl(mod, cls))
        self.part.openURL(url)


    def docUrl(self, module, klass):
        """ docUrl(name) -> return a doc url given a name from the kde libs
    
        """
        return '%s/%s/%s.html' % (self.docBase, module, klass, )


class OutputFrame(KTextEdit):
    """ text widget that acts (just enough) like a file

    """
    def __init__(self, parent, filehandle):
        KTextEdit.__init__(self, parent)
        self.filehandle = filehandle
        self.setReadOnly(True)
        self.setFont(KGlobalSettings.fixedFont())


    def write(self, text):
        self.insert(text)


    def clear(self):
        self.setText('')


    def __getattr__(self, name):
        return getattr(self.filehandle, name)


class SamplerListView(KListView):
    """ the main list view of samples

    """
    def __init__(self, parent):
        KListView.__init__(self, parent)
        self.addColumn(i18n('Sample'))
        self.setRootIsDecorated(True)

        modules = lib.listmodules()
        modules.sort(lambda a, b: cmp(a[0], b[0]))

        modmap = dict(modules)
        modules = [(name.split('.'), name, mod) for name, mod in modules]
        roots, cache = {}, {}

        for names, modname, module in modules:
            topname, subnames = names[0], names[1:]
            item = roots.get(topname, None)
            if item is None:
                roots[topname] = item = KListViewItem(self, module.labelText())
                item.module = module
                item.setPixmap(0, getIcon(module.icon()))

            bname = ''
            subitem = item
            for subname in subnames:
                bname = '%s.%s' % (bname, subname, )
                item = cache.get(bname, None)
                if item is None:
                    subitem = cache[bname] = \
                        KListViewItem(subitem, module.labelText())
                    subitem.module = module
                    subitem.setPixmap(0, getIcon(module.icon()))
                subitem = item

        for root in roots.values():
            self.setOpen(root, True)

        
class SamplerMainWindow(KMainWindow):
    """ the main window

    """
    def __init__(self, *args):
        KMainWindow.__init__(self, *args)
        self.hSplitter = hSplit = QSplitter(Qt.Horizontal, self)
        self.samplesList = samplesList = SamplerListView(hSplit)
        self.vSplitter = vSplit = QSplitter(Qt.Vertical, hSplit)
        self.setCentralWidget(hSplit)
        self.setIcon(getIcon('kmail'))

        hSplit.setOpaqueResize(True)
        vSplit.setOpaqueResize(True)

        self.contentTabs = cTabs = KTabWidget(vSplit)
        self.outputTabs = oTabs = KTabWidget(vSplit)

        self.sampleFrame = SamplerFrame(cTabs)
        self.sourceFrame = SourceFrame(cTabs)
        self.webFrame = WebFrame(cTabs)

        cTabs.insertTab(self.sampleFrame, getIconSet('exec'), i18n('Sample'))
        cTabs.insertTab(self.sourceFrame, getIconSet('source'), i18n('Source'))
        cTabs.insertTab(self.webFrame, getIconSet('help'), i18n('Docs'))

        sys.stdout = self.stdoutFrame = OutputFrame(oTabs, sys.stdout)
        sys.stderr = self.stderrFrame = OutputFrame(oTabs, sys.stderr)

        termIcons = getIconSet('terminal')
        oTabs.insertTab(self.stdoutFrame, termIcons, i18n('stdout'))
        oTabs.insertTab(self.stderrFrame, termIcons, i18n('stderr'))

        self.resize(640, 480)
        height, width = self.height(), self.width()
        hSplit.setSizes([width * 0.35, width * 0.65])
        vSplit.setSizes([height * 0.80, height * 0.20])

        self.xmlRcFileName = os.path.abspath(os.path.join(os.path.dirname(__file__), 'sampler.rc'))
        self.setXMLFile(self.xmlRcFileName)
        config = appConfig()
        actions = self.actionCollection()
        actions.readShortcutSettings("", config)
        self.quitAction = KStdAction.quit(self.close, actions)

        self.toggleMenubarAction = \
            KStdAction.showMenubar(self.showMenubar, actions)
        self.toggleToolbarAction = \
            KStdAction.showToolbar(self.showToolbar, actions)
        self.toggleStatusbarAction = \
            KStdAction.showStatusbar(self.showStatusbar, actions)
        self.configureKeysAction = \
            KStdAction.keyBindings(self.showConfigureKeys, actions)
        self.configureToolbarAction = \
            KStdAction.configureToolbars(self.showConfigureToolbars, actions)
        self.configureAppAction = \
            KStdAction.preferences(self.showConfiguration, actions)

        connect = self.connect
        connect(samplesList, sigViewItemSelected, self.sampleSelected)
        connect(self, sigSampleSelected, self.reloadModule)
        connect(self, sigSampleSelected, self.sourceFrame.showModuleSource)
        connect(self, sigSampleSelected, self.sampleFrame.showSample)
        connect(self, sigSampleSelected, self.webFrame.showDocs)

        self.restoreWindowSize(config)
        self.createGUI(self.xmlRcFileName, 0)
        self.sourceFrame.part.openURL(KURL('file://%s' % os.path.abspath(__file__)))


    def showConfiguration(self):
        """ showConfiguration() -> display the config dialog

        """
        return
        ## not yet implemented
        dlg = configdialog.ConfigurationDialog(self)
        for obj in (self.stderrFrame, self.stdoutFrame, self.pythonShell):
            call = getattr(obj, 'configChanged', None)
            if call:
                self.connect(dlg, util.sigConfigChanged, call)
        dlg.show()


    def senderCheckShow(self, widget):
        """ senderCheckShow(widget) -> show or hide widget if sender is checked

        """
        if self.sender().isChecked():
            widget.show()
        else:
            widget.hide()


    def showMenubar(self):
        """ showMenuBar() -> toggle the menu bar

        """
        self.senderCheckShow(self.menuBar())


    def showToolbar(self):
        """ showToolbar() -> toggle the tool bar

        """
        self.senderCheckShow(self.toolBar())


    def showStatusbar(self):
        """ showStatusbar() -> toggle the status bar

        """
        self.senderCheckShow(self.statusBar())


    def showConfigureKeys(self):
        """ showConfigureKeys() -> show the shortcut keys dialog

        """
        ret = KKeyDialog.configure(self.actionCollection(), self)
        print ret
        if ret == QDialog.Accepted:
            actions = self.actionCollection()
            actions.writeShortcutSettings(None, appConfig())


    def showConfigureToolbars(self):
        """ showConfigureToolbars() -> broken

        """
        dlg = KEditToolbar(self.actionCollection(), self.xmlRcFileName)
        self.connect(dlg, SIGNAL('newToolbarConfig()'), self.rebuildGui)
        #connect(self, sigSampleSelected, self.sourceFrame.showModuleSource)

        dlg.exec_loop()


    def rebuildGui(self):
        """ rebuildGui() -> recreate the gui and refresh the palette

        """
        self.createGUI(self.xmlRcFileName, 0)
        for widget in (self.toolBar(), self.menuBar(), ):
            widget.setPalette(self.palette())


    def sampleSelected(self):
        """ sampleSelected() -> emit the current item and its module

        """
        self.stdoutFrame.clear()
        self.stderrFrame.clear()
        item = self.sender().currentItem()
        self.emit(sigSampleSelected, (item, item.module))


    def setSplashPixmap(self, pixmap):
        """ setSplashPixmap(pixmap) -> assimilate the splash screen pixmap

        """
        target = self.sampleFrame
        label = QLabel(target)
        label.setPixmap(pixmap)
        target.setWidget(label)


    def reloadModule(self, item, module):
        print >> sys.__stdout__, 'reload: ', reload(module.module)
        

if __name__ == '__main__':
    aboutdata = about.about()    
    KCmdLineArgs.init(sys.argv, aboutdata)
    app = KApplication()

    splashpix = QPixmap(os.path.join(lib.samplerpath, 'aboutkde.png'))
    splash = KSplashScreen(splashpix)
    splash.resize(splashpix.size())
    splash.show()
    mainWindow = SamplerMainWindow()
    mainWindow.setSplashPixmap(splashpix)
    mainWindow.show()
    splash.finish(mainWindow)
    app.exec_loop()
