#!/usr/bin/env python
"""
The PyKDE documentation and example browser
"""
#
#     Copyright 2007-2008 Jim Bublitz <jbublitz@nwinternet.com>
#

# This software 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 2 of
# the License, or (at your option) any later version.

# This software 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 this library; see the file COPYING.
# If not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import os, os.path
import sys
from subprocess import Popen

from PyQt4.QtCore import SIGNAL, SLOT, Qt,  QString,  QEvent,  QObject, QVariant

from PyQt4.QtGui import QLabel, QPixmap, QSplitter, QDialog, QPushButton
from PyQt4.QtGui import QTreeWidget, QTreeWidgetItem, QTabWidget, QFont, QDialog
from PyQt4.QtGui import QSizePolicy,  QAbstractItemView, QImage,  QCursor

from PyKDE4.kdecore import KAboutData, KCmdLineArgs, KGlobal, KUrl, KConfig
from PyKDE4.kdecore import KConfigGroup, ki18n, i18n

from PyKDE4.kdeui import KApplication, KTabWidget, KTextEdit
from PyKDE4.kdeui import KXmlGuiWindow, KPushButton, KSplashScreen, KStandardAction
from PyKDE4.kdeui import KToolBar, KHistoryComboBox, KAction, KIcon
from PyKDE4.kdeui import KVBox, KHBox, KTreeWidgetSearchLine, KTreeWidgetSearchLineWidget
from PyKDE4.kdeui import KMessageBox, KArrowButton,  KListWidget, KHelpMenu

from PyKDE4.kio import KRun, KFileDialog, KDirSelectDialog, KIO
from PyKDE4.kparts import KParts
from PyKDE4.khtml import KHTMLPart

from PyKDE4.pykdedocscfg import ConfigDialog, BookmarkEditor, AddBookmarkFolder

import PyKDE4.indexof as indexof

from PyKDE4.setprogramlogo import setprogramlogo

icons = {"kdecore": "kde",  "kdeui": "video-display", "khtml": "internet-web-browser", \
             "kio": "networkmanager",  "kparts": "system-software-update",  "kutils": "configure",  "solid": "hwinfo"}

#set up paths - mostly for first time run
basePath     = os.path.join ("/", "usr", "share", "doc", "python-kde4-doc")
sys.path.append (basePath)
examplePath  = None
relImagePath = os.path.join ("doc", "html", "images")
defaultPath  = None
userPath     = "/home/jim/Documents"

homeKDEPath  = os.path.expanduser (os.path.join ("~", ".kde4", "share", "apps", "pykdedocs"))
if not os.path.exists (homeKDEPath):
    os.mkdir (homeKDEPath)

if not os.path.exists (os.path.join (homeKDEPath, "userindex.txt")):
    print os.path.join (basePath, "userindex.txt"), homeKDEPath
    os.system ("cp -f %s %s" % (os.path.join (basePath, "userindex.txt"), homeKDEPath))
         

paths = {}

delim = "^"

# simplify signal names in connect()
sigViewItemClicked  = SIGNAL ('itemClicked (QTreeWidgetItem *, int)')
sigSampleSelected   = SIGNAL ('sampleSelected (PyQt_PyObject)')
sigTutorialSelected = SIGNAL ('tutorialSelected (PyQt_PyObject)')
sigLinkClicked      = SIGNAL ('openUrlRequest (const KUrl&, const KParts::OpenUrlArguments&, const KParts::BrowserArguments&)')
sigReturnPressed    = SIGNAL ("returnPressed ()")
sigStarted          = SIGNAL ("started (KIO::Job *)")
sigCompleted        = SIGNAL ("completed ()")
sigInfoMsg          = SIGNAL ("infoMessage (const QString&)")
sigStatTxt          = SIGNAL ("setStatusBarText (const QString&)")
sigWinCap           = SIGNAL ("setWindowCaption (const QString&)")
sigNewTab           = SIGNAL ("currentChanged (int)")
sigLocationBar      = SIGNAL ("setLocationBarUrl (const QString)")
sigActivated        = SIGNAL ("activated ()")
sigClicked          = SIGNAL ("clicked ()")
sigTextChanged      = SIGNAL ("textChanged (const QString&)")
sigCloseRequest     = SIGNAL ("closeRequest (QWidget *)")
sigSelectionInfo    = SIGNAL ("selectionInfo (const QString&)")
sigContextMenu      = SIGNAL ("contextMenu (QWidget*, const QPoint&)")
sigItemClicked      = SIGNAL ("itemClicked ( QListWidgetItem*)")


blank  = KUrl ('about:blank')

class CommonFrame(KVBox):
    """
    provides a modicum of reuse
    """
    def __init__(self, parent):
        KVBox.__init__(self, parent)
        self.layout ().setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        
    def enableButtons (self, mainWin):
        mainWin.forwardToolbarAction.setEnabled (False)
        mainWin.backToolbarAction.setEnabled (False)
        mainWin.historyToolbarAction.setEnabled (False)
        mainWin.reloadToolbarAction.setEnabled (False)
        mainWin.stopToolbarAction.setEnabled (False)
        mainWin.homeToolbarAction.setEnabled (False)
        mainWin.copyAction.setEnabled (False)
        mainWin.findAction.setEnabled (False)
        mainWin.findNextAction.setEnabled (False)
        mainWin.addBookmarkAction.setEnabled (False)
        mainWin.printAction.setEnabled (False)
        mainWin.fileSaveAction.setEnabled (False)
        if hasattr (mainWin,  "combo"):
            mainWin.combo.setEditable (False)
            mainWin.combo.setEditText ("")

class MainFrame (KVBox):
    def __init__(self, parent, helpText, subdir, name):
        KVBox.__init__(self, parent)
        self.subdir = subdir
        self.name   = name
        self.help   = QLabel (i18n (helpText), self)
        self.layout ().setAlignment (self.help, Qt.AlignHCenter)
        
        hBox = KHBox (self)
        self.button = KPushButton(i18n("Run %s" % name), hBox)
        self.button.setMaximumSize (250, 30)
        
        self.connect(self.button, sigClicked, self.popen)

    def popen (self):
        cursor (wait)
        Popen (os.path.join (basePath, 'examples/%sExamples/%s.py' % (self.subdir, self.name)))
        restoreCursor ()

class SamplerFrame(CommonFrame):
    """
    frame type that swaps out old widgets for new when told to do so
    """
    def __init__(self, parent, mainWin):
        CommonFrame.__init__(self, parent)
        self.widget      = None
        self.moduleCache = {}
        self.mainWin     = mainWin

    def setWidget(self, widget):
        if self.widget:
            self.widget.hide ()
        self.layout ().removeWidget (self.widget)
        previous = self.widget
        if previous:
            previous.close ()
            delattr (self, 'widget')
        self.widget = widget
        self.widget.hide ()
        self.layout ().setAlignment (self.widget, Qt.AlignTop)

    def getModule (self, subdir, name):
        # reloading allows edits between views
        if subdir:
            moduleName = "examples.%sExamples.%s" % (subdir, name)
        else:
            moduleName = "examples.%s" % name
        if moduleName in self.moduleCache:
            module = reload (self.moduleCache [moduleName])
        else:
            module = __import__ (moduleName)
            if subdir:
                module = getattr (module, "%sExamples" % subdir)
            module = getattr (module, name)
        self.moduleCache [moduleName] = module
        return module
        
    def showTutorial (self,  (itemName,  displayName)):
        self.name = itemName + ".py"
            
        frame     = KHBox (self)
        button    = KPushButton ("Run %s" % displayName, frame)
        self.connect (button, sigClicked, self.tutorialPopen)
        self.setWidget (frame)
        frame.show ()
        mainWindow.sourceFrame.showTutorialSource (self.name)

    def showSample (self, (path,  itemName)):
        if not self.mainWin.samplesEnabled:
            return
            
        parts  = itemName.split (delim, 2)
        if len (parts) < 3:
                return             
        else:
            name   = parts [2].strip ().lower ().replace (".", "_")
            subdir = parts [0]
            
            try:
                module = self.getModule (subdir, name)
    
                if hasattr (module, "redirect"):
                    name   = getattr (module, "redirect")
                    module = self.getModule (subdir, name)

                if hasattr (module, "runner"):
                    frame = MainFrame (self, getattr (module, "helpText"), subdir, name)
                else:
                    frameType = getattr (module, "MainFrame")
                    frame = frameType (self)

            except:
                module = self.getModule ("", "default")
                setattr (module, "imagePath", os.path.join (basePath, relImagePath))
                frameType = getattr (module, 'MainFrame')
                frame = frameType (self)
        
            self.setWidget (frame)
            frame.show ()
            mainWindow.sourceFrame.showModuleSource (subdir, name, module)

    def popen (self):
        cursor (wait)
        Popen (os.path.join (basePath, 'examples/%sExamples/%s.py' % (self.subdir, self.name)))
        restoreCursor ()

    def tutorialPopen (self):
        cursor (wait)
        Popen ([sys.executable, self.name])
        restoreCursor ()
        
class SourceFrame(CommonFrame):
    """
    frame with KTextEdit for displaying Python source
    """
    def __init__(self, parent, mainWin):
        CommonFrame.__init__(self, parent)
        self.textEd = KTextEdit (self)
        f = QFont ("Courier", 10)
        self.textEd.setFont (f)
        
        self.filename = ""

        hbox = KHBox (self)
        self.saveButton = KPushButton ("Save a copy", hbox)
        self.saveButton.setMaximumSize (100, 30)
        self.connect (self.saveButton, sigClicked, self.slotSaveClicked)
        
    def enableButtons (self, mainWin):
        CommonFrame.enableButtons (self, mainWin)
        mainWin.copyAction.setEnabled (True)
        mainWin.fileSaveAction.setEnabled (True)

    def showTutorialSource (self,  name):
        self.filename = name
        try:
            m = open (self.filename, "r")
            self.text = m.read ()
            m.close ()
            self.textEd.setText (self.text)
        except:
            pass

    def showModuleSource(self, subdir, name, module = None):
        name = name.replace (".", "_")

        self.filename = os.path.join (examplePath, "%sExamples/%s.py" % (subdir, name))
        if not os.path.exists (self.filename):
            self.filename = defaultPath
            
        m = open (self.filename, "r")
        self.text = m.read ()
        m.close ()
        self.textEd.setText (self.text)

    def slotSaveClicked (self):
        saveFile = str (KFileDialog.getSaveFileName (KUrl (userPath), "*.py", self, "Save File As"))
        if saveFile:
            m = open (saveFile, "w")
            m.write (self.text)
            m.close ()
            
    def save (self):
        self.slotSaveClicked ()

class BrowserFrame(CommonFrame):
    """
    base for browsers - frame with KHTMLPart for viewing html pages
    inluding navigation
    """
    def __init__(self, parent, mainWin, name, jsEnabled, javaEnabled):
        CommonFrame.__init__(self, parent)

        self.mainWin   = mainWin
        self.name      = name
        self.htmlpart  = KHTMLPart (self)
        self.view      = self.htmlpart.view ()
        self.extension = self.htmlpart.browserExtension ()

        self.setJava (jsEnabled, javaEnabled)
        
        # creates a KParts.PartManager as side-effect
        self.htmlpart.partManager ()
        
        self.connect (self.extension, sigLinkClicked, self.slotOpenURL)
        self.connect (self.extension, sigLocationBar, self.adjustURLList) # pick up *.html#target clicks
        self.connect (self.extension, sigSelectionInfo, self.slotSelection)
        self.connect (self.htmlpart, sigInfoMsg, self.slotInfoMsg)
        self.connect (self.htmlpart, sigStarted, self.slotStarted)
        self.connect (self.htmlpart, sigCompleted, self.slotCompleted)
        self.connect (self.htmlpart, sigStatTxt, self.slotStatusBarText)
        self.connect (self.htmlpart, sigWinCap, self.slotWinCaption)

        self.currentURL   = ""
        self.currentTitle = ""
        self.urls         = []
        self.index        = -1
        self.historyDict  = {}
        self.historyBox = None
        
        self.selectedText = ""

        self.enableButtons (mainWin)
#        self.htmlpart.openUrl (blank)
        mainWin.statBar.showMessage ("Ready", 2000)

    def setJava (self, jsEnabled, javaEnabled):
        self.htmlpart.setJScriptEnabled (jsEnabled)
        self.htmlpart.setJavaEnabled (javaEnabled)

    def enableButtons (self, mainWin, exceptStop = False):
        if self != self.mainWin.contentTabs.currentWidget ():
            return
        nUrls = len (self.urls)
        mainWin.forwardToolbarAction.setEnabled (nUrls > 1 and self.index < nUrls - 1)
        mainWin.backToolbarAction.setEnabled (nUrls > 1 and self.index > 0)
        mainWin.historyToolbarAction.setEnabled (nUrls > 1)
        mainWin.reloadToolbarAction.setEnabled (nUrls)
        if not exceptStop:
            mainWin.stopToolbarAction.setEnabled (False)
        mainWin.homeToolbarAction.setEnabled (self.name not in ["Docs", "Tutorials"])
        mainWin.addBookmarkAction.setEnabled (self.name not in ["Docs", "Tutorials"])
        mainWin.findAction.setEnabled (nUrls)
        mainWin.findNextAction.setEnabled (nUrls)
        mainWin.copyAction.setEnabled (nUrls)
        mainWin.fileSaveAction.setEnabled (nUrls)
        mainWin.printAction.setEnabled (nUrls)
        if hasattr (mainWin,  "combo"):
            mainWin.combo.setEditable (True)
            mainWin.combo.setEditText (self.currentURL)

    def adjustURLList (self, s):
        self.currentURL = str (s)
        self.mainWin.combo.setEditText (s)

        if self.index < len (self.urls) - 1:
            self.urls = self.urls [:self.index + 1]
            self.index = len (self.urls) - 1
              
        if not self.urls or (self.index == (len (self.urls) - 1) and self.urls [-1].lower () != self.currentURL.lower ()):
            self.urls.append (self.currentURL)
            self.index += 1

        self.enableButtons (mainWindow, True)
    
    def slotOpenURL (self, url, urlargs = None):
        cursor (busy)
        path = ""
        x = url.url ()
        if url.isLocalFile ():
            path = str (url.toLocalFile ())
            
        # displays faster than figuring out the mimetype
        if path and path.endswith (".html"):
            if not os.path.exists (path):
                KMessageBox.sorry (self,  "File %s does not exist" % path,  "File not found")
                restoreCursor ()
                return False
            
            # manages relative addressing in links - begin-write-end doesn't
            result = self.htmlpart.openUrl (url)

        else:
            self.mainWin.statBar.showMessage ("Looking up %s" % url.url (), 1000)
            self.mainWin.stopToolbarAction.setEnabled (True)
            mimetype = KIO.NetAccess.mimetype (url, None)
            if mimetype != "text/html":
                self.mainWin.statBar.showMessage ("Using external application for %s" % url.url (), 1000)
                result = KRun.runUrl (url, mimetype, None)
                self.mainWin.stopToolbarAction.setEnabled (False)
                restoreCursor ()
            else:
                result = self.htmlpart.openUrl (url)

        if result:
           self.adjustURLList (url.url ())
        
        return result
    
    def slotStarted (self, job):
        self.mainWin.stopToolbarAction.setEnabled (True)
                
    def slotCompleted (self):
        self.mainWin.stopToolbarAction.setEnabled (False)
        restoreCursor ()

    def slotInfoMsg (self, s):
        self.mainWin.statBar.showMessage (s)
    
    def slotStatusBarText (self, s):
        sStr = str (s)
        if sStr.startswith ("<qt>"):
            self.mainWin.statBar.showMessage (sStr [4:])
        else:
            self.mainWin.statBar.showMessage (sStr)

    def slotWinCaption (self, s):
        self.currentTitle = QString (s)
        if self.currentTitle not in self.historyDict and not str (s).startswith ("about"):
            self.historyDict [self.currentTitle] = self.currentURL
    
    def slotSelection (self,  s):
        self.selectedText = str (s)             
        
    def back (self):
        self.index = max (0, self.index - 1)
        self.navigate ()

    def forward (self):
        self.index = min (len (self.urls) - 1, self.index + 1)
        self.navigate ()
        
    def navigate (self):
        s = self.urls [self.index]
        if self.htmlpart.openUrl (KUrl (s)):
            self.mainWin.combo.setEditText (s)
            self.currentURL = s
        self.enableButtons (mainWindow)
    
    def reload (self):
        self.htmlpart.closeUrl ()
        mainWindow.stopToolbarAction.setEnabled (False)
        self.slotOpenURL (KUrl (self.currentURL))

    def stop (self):
        print "***** stop!! *****"
        self.htmlpart.closeUrl ()
        mainWindow.stopToolbarAction.setEnabled (False)

    def home (self):
        if not self.name:
            return
        self.htmlpart.closeUrl ()
        itemPath = paths [self.name][0]
        if not itemPath:
            itemPath = KFileDialog.getOpenFileName (KUrl (), "", self.parent (), "Path for %s" % self.name)

            if itemPath:
                paths [self.name] = (str (itemPath), [])
                self.mainWin.samplesList.writePaths ()
            else:
                return 
     
        if os.path.exists (itemPath):
            itemPath = "file://%s" % itemPath
            
        self.slotOpenURL (KUrl (itemPath))            

    def history (self):
        self.historyBox = KVBox (self.parent ())
        flags = Qt.WindowFlags (Qt.Window) & Qt.WindowFlags (Qt.Tool)
        self.historyBox.setWindowFlags (flags)
        self.historyBox.setAttribute (Qt.WA_DeleteOnClose, True)
        self.historyBox.move (self.mainWin.width () * 0.3, 125)  
        self.historyBox.setMinimumWidth (350)
        self.historyList = KListWidget (self.historyBox)
        
        hList = self.historyDict.keys ()
        hList.sort ()
        self.historyList.addItems (hList)
        
        self.connect (self.historyList, sigItemClicked, self.slotItemClicked)
        
        self.historyBox.show ()
        self.historyBox.raise_ ()
        
    def find (self):
        self.htmlpart.findText ()
    
    def findNext (self):
        self.htmlpart.findTextNext ()
        
    def print_ (self):
        self.view.print_ (False)

    def save (self):
        saveFile = str (KFileDialog.getSaveFileName (KUrl (userPath), "*.html", self, "Save File As"))
        if saveFile:
            m = open (saveFile, "w")
            m.write (str (self.htmlpart.documentSource ()))
            m.close ()

    def slotItemClicked (self, listItem):
        s = listItem.text ()
        self.historyBox.close ()
        self.historyBox = None
                
        self.slotOpenURL (KUrl (self.historyDict [s]))

    def showTutorial (self, (itemName, displayName)):
        return self.slotOpenURL (KUrl ("file://%s.html" % itemName))

    def showDocs (self, (path, itemName)):
        return self.slotOpenURL (KUrl ("file://%s" % path))           
   
class SamplerListView (KVBox):
    """
    the main list view of PyKDE modules, namespaces and classes
    """
    def __init__(self, parent):
        KVBox.__init__ (self, parent)
        self.tree = QTreeWidget (self)

        self.hBox = KHBox (self)
        self.layout ().insertWidget (0, self.hBox)
        
        self.search    = KTreeWidgetSearchLineWidget (self.hBox, self.tree)
        self.closeBtn  = QPushButton (KIcon ("view-list-tree"), "", self.hBox)
        self.closeBtn.setToolTip ("Collapse Tree")
        
        self.searchLine = self.search.searchLine ()

        self.connect (self.searchLine, sigReturnPressed, self.tree.expandAll)
        self.connect (self.searchLine, sigTextChanged, self.slotTextChanged)
        self.connect (self.closeBtn, sigClicked, self.tree.collapseAll)

        self.expandLevel = int (General.readEntry ("ExpandLevel", "2"))        
        
        self.tree.setHeaderLabel ("Modules")

        self.topItem = item = QTreeWidgetItem (self.tree, ["Documentation"])
        item.setIcon (0, KIcon ("help-about"))
        for title in docTitles :
            subitem = QTreeWidgetItem (item, [title])
            
        item = QTreeWidgetItem (self.tree, ["Tutorials"])
        item.setIcon (0, KIcon ("applications-education"))
        for title in titleList:
            titleItem = QTreeWidgetItem (item, [title])
            for topic in titles [title][1]:
                topicItem = QTreeWidgetItem (titleItem, [topic])          

        m = open (os.path.join (basePath, "index.txt"), "r")

        for line in m:
            parts = line.split (":")
            element = parts [0]
            name    = parts[1].strip ()
            
            if element == "m":
                item1 = QTreeWidgetItem (self.tree, [name])
                files = os.listdir (os.path.join (examplePath, "%sExamples" % name))
                item1.setIcon (0, KIcon (icons [name]))
                
            elif element == "n":
                item2 = QTreeWidgetItem (item1, [name])
                
            elif element == "c":
                item  = QTreeWidgetItem (item2, [name])
                if "%s.py" % (name.lower ().replace (".", "_")) in files:
                    item.setIcon (0, KIcon ("flag-blue"))

        m.close ()
        self.loadBookmarks ()
        
    def addBookmarkFolder (self, name, url):
        global paths, userTitleList
        QTreeWidgetItem (self.tree, [name])
        paths [name] = (url, [])
        userTitleList.append (name)
        
        self.writePaths (False)
        
    def loadBookmarks (self, reload = False):
        global paths, userTitleList, bookmarkFile
        if reload:
            n = self.tree.topLevelItemCount ()
            for i in range (n -1, 0, -1):
                if str (self.tree.topLevelItem (i).text (0)) in paths:
                    self.tree.takeTopLevelItem (i)
            
            userIndexPath = os.path.join (homeKDEPath)
            paths, userTitleList, bookmarkFile = indexof.user (userIndexPath)

        for title in userTitleList:
            item = QTreeWidgetItem (self.tree, [title])
            for bookmark in paths [title][1]:
                item1 = QTreeWidgetItem (item, [bookmark])

    def slotTextChanged (self, s):
        sStr = str (s)
        if len (sStr) >= self.expandLevel:
            self.tree.expandAll ()
            
    def add (self, group, name):
        n = self.tree.topLevelItemCount ()
        for i in range (n -1, 0, -1):
            top = self.tree.topLevelItem (i)            
            s = str (top.text (0))
            if s == group:
                item = QTreeWidgetItem (top, [name])
                self.tree.expandItem (top)
                self.tree.scrollToItem (top, QAbstractItemView.EnsureVisible)
                break
        else:
            KMessageBox.sorry (self, "Item %s does not exist" % group, "Item not found")
                
    def writePaths (self, needSync = True):
        indexof.writeUser (homeKDEPath, paths, userTitleList, bookmarkFile)

        General.writeEntry ("BasePath", basePath)
        General.writeEntry ("UserPath", userPath)

        if needSync:
            General.sync ()

class SamplerMainWindow(KXmlGuiWindow):
    """
    the main window
    """
    def __init__(self, *args):
        KXmlGuiWindow.__init__(self, *args)
        self.computePaths (basePath)
        self.favor = int (General.readEntry ("Favor", "0"))
        self.historyLength = int (General.readEntry ("History Length", "20"))
        self.samplesEnabled = int (General.readEntry ("Samples Enabled", "1"))
        
        self.hSplitter = hSplit = QSplitter(Qt.Horizontal, self)
        self.samplesList = samplesList = SamplerListView(hSplit)
        self.setCentralWidget(hSplit)

        hSplit.setOpaqueResize(True)

        self.contentTabs = cTabs = KTabWidget(hSplit)
        tabPosition = int (General.readEntry ("TabPosition", "3"))
        self.setTabPosition (tabPosition)

        # actions need to be defined before implementing
        # KHTMLParts for browsers - causes KHTMLParts
        # problems otherwise

        self.initActions (self.actionCollection())
        
        connect = self.connect
        connect (self.stopToolbarAction, sigActivated, self.slotStop)
        connect (self.reloadToolbarAction, sigActivated, self.slotReload)           
        connect (self.historyToolbarAction, sigActivated, self.slotHistory)           
        connect (self.newFolderToolbarAction, sigActivated, self.slotNewFolder)           

        self.statBar = self.statusBar ()

        self.jsEnabled   = General.readEntry ("EnableJS", "False") == "True"
        self.javaEnabled = General.readEntry ("EnableJava", "False") == "True"
       
        self.sampleFrame = SamplerFrame(cTabs, self)
        self.sourceFrame = SourceFrame(cTabs, self)
        self.docFrame    = BrowserFrame (cTabs, self, "Docs", self.jsEnabled, self.javaEnabled)
        self.tabs        = {}

        cTabs.insertTab(0, self.sampleFrame, KIcon ('project-open'), 'Sample')
        cTabs.insertTab(1, self.sourceFrame, KIcon ('text-x-python'), 'Source')
        cTabs.insertTab(2, self.docFrame, KIcon ('document-properties'), 'Docs')

        height, width = self.height(), self.width()
        hSplit.setSizes([width * 0.25, width * 0.75])

        connect (samplesList.tree, sigViewItemClicked, self.sampleSelected)
        connect (self, sigSampleSelected, self.sampleFrame.showSample)
        connect (self, sigSampleSelected, self.docFrame.showDocs)
        connect (cTabs, sigNewTab, self.slotNewTab)       
        connect (cTabs, sigContextMenu, self.slotContextMenu)

        self.xmlRcFileName = QString (os.path.join (basePath, 'pykdedocsui.rc'))
        self.setXML (self.xmlRcFileName)
        self.createGUI(self.xmlRcFileName)

        self.restorePosAndSize ()

        mainToolBar = self.toolBar ("mainToolBar")
        mainToolBar.setToolButtonStyle (Qt.ToolButtonIconOnly)
        mainToolBar.setMinimumWidth (325)
        
        browserToolBar = self.toolBar ("browserToolBar")
        
        label = QLabel (" URL:")
        label.setFixedWidth (40)

        self.combo = KHistoryComboBox (browserToolBar)
        self.combo.setEditable (True)
        self.combo.setSizePolicy (QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.combo.setMaximumHeight (browserToolBar.height () - 4)
        self.combo.insertItems (General.readEntry ("Url History", []))

        
        browserToolBar.addWidget (label)
        browserToolBar.addWidget (self.combo)       
        connect (self.combo.lineEdit (), sigReturnPressed, self.slotReturnPressed)
        
        browserToolBar.installEventFilter (self)

        self.combo.setEditText ("")
        self.currentBrowser ().enableButtons (self)
        
    def eventFilter (self, obj, e):
        if e.type() == QEvent.KeyPress\
          and e.key () in  [Qt.Key_Enter, Qt.Key_Return]\
          and self.combo.hasFocus ():
            self.slotReturnPressed ()
        return KXmlGuiWindow.eventFilter (self, obj, e)

    def initActions (self, actions):
        self.quitAction           = KStandardAction.quit(app.quit, actions)       
        self.fileSaveAction       = KStandardAction.save(self.slotFileSave, actions)
        self.configureAppAction   = KStandardAction.preferences(self.showConfiguration, actions)
        self.copyAction           = KStandardAction.copy (self.slotCopy, actions)
        self.backToolbarAction    = KStandardAction.back (self.slotBack, actions)
        self.forwardToolbarAction = KStandardAction.forward (self.slotForward, actions)
        self.homeToolbarAction    = KStandardAction.home (self.slotHome, actions)
        self.findAction           = KStandardAction.find (self.slotFind, actions)
        self.findNextAction       = KStandardAction.findNext (self.slotFindNext, actions)
        self.addBookmarkAction    = KStandardAction.addBookmark (self.slotAddBookmark, actions)
        self.editBookmarkActions  = KStandardAction.editBookmarks (self.slotEditBookmarks, actions)
        self.printAction          = KStandardAction.print_ (self.slotPrint, actions)
        
        self.reloadToolbarAction    = KAction (KIcon ("view-refresh"), "Reload", self)
        self.stopToolbarAction      = KAction (KIcon ("process-stop"), "Stop", self)
        self.historyToolbarAction   = KAction (KIcon ("view-history"), "History", self)
        self.newFolderToolbarAction = KAction (KIcon ("folder-new"), "New Bookmark Folder", self)
        
        actions.addAction ("reload", self.reloadToolbarAction)            
        actions.addAction ("stop", self.stopToolbarAction)
        actions.addAction ("history", self.historyToolbarAction)
        actions.addAction ("newFolder", self.newFolderToolbarAction)
    
        helpMenu = KHelpMenu (self, aboutData, False)
        self.menuBar ().addMenu (helpMenu.menu ())
        self.aboutAction    = helpMenu.action (KHelpMenu.menuAboutApp)
        self.aboutAction.setIcon (KIcon ("help-about"))
        self.aboutKDEAction = helpMenu.action (KHelpMenu.menuAboutKDE)
        actions.addAction ("about", self.aboutAction)
        actions.addAction ("aboutKDE", self.aboutKDEAction)

    def computePaths (self, bPath):
        global basePath, examplePath, defaultPath
        if not bPath:
            basePath = str (General.readEntry ("BasePath", sys.path [0]))
        else:
            basePath = str (bPath)

        examplePath  = os.path.join (basePath, "examples")
        defaultPath  = os.path.join (examplePath, "default.py")

    def slotFileSave (self):
        self.currentBrowser ().save ()        

    def slotCopy (self): 
        if self.contentTabs.currentIndex () > 1:
            app.clipboard ().setText (self.currentBrowser ().selectedText)
    
    def slotContextMenu (self, widget, point):
        index = self.contentTabs.currentIndex ()
        
        if index < 3:
            return
            
        result = KMessageBox.questionYesNo (self, "Close this tab?", "Clost this tab?")
        if result == KMessageBox.Yes:
            del self.tabs [self.contentTabs.widget (index).name]
            self.contentTabs.removeTab (index)
    
    # signals from the browser navigation buttons all connect
    # here so we can forward themto the active browser window
    
    def slotBack (self):
        self.currentBrowser ().back ()
    
    def slotForward (self):
        self.currentBrowser ().forward ()
    
    def slotReload (self):
        self.currentBrowser ().reload ()
    
    def slotStop (self):
        self.currentBrowser ().stop ()

    def slotHome (self):
        self.currentBrowser ().home ()
    
    def slotHistory (self):
        self.currentBrowser ().history ()
    
    def slotReturnPressed (self):
        s = self.combo.lineEdit ().text ()       
        add = self.currentBrowser ().slotOpenURL (KUrl (s))

        if add:
            self.combo.addToHistory (s)            
            
    def slotFind (self):
        self.currentBrowser ().find ()

    def slotFindNext (self):
        self.currentBrowser ().findNext ()

    def slotAddBookmark (self):
        c     = self.currentBrowser ()
        path  = c.currentURL
        name  = str (c.currentTitle)
        group = c.name
        paths [group][1].append (name)
        if name not in bookmarkFile:
            bookmarkFile [name] = path
        
        indexof.writeUser (homeKDEPath, paths, userTitleList, bookmarkFile)
        self.samplesList.add (group, name)
        
    def slotEditBookmarks (self):
        dlg = BookmarkEditor (basePath, self)
        result = dlg.exec_ ()
        if result == QDialog.Accepted:
            self.samplesList.loadBookmarks (True)
            
    def slotPrint (self):
        self.currentBrowser ().print_ ()
        
    def slotNewFolder (self):
        dlg = AddBookmarkFolder (self)
        while True:
            result = dlg.exec_ ()
            if result == QDialog.Accepted:
                name = dlg.name
                if name in paths:
                    KMessageBox.sorry (self, "%s already exists. Names must be unique." %  name, "Duplicate Folder Name")
                elif name:
                    url  = dlg.url
                    self.samplesList.addBookmarkFolder (name, url)
                    break
            else:
                break

    def slotNewTab (self, b):
        self.currentBrowser ().enableButtons (self)

    def setTabPosition (self, pos):
        tabPositions = [QTabWidget.North, QTabWidget.South, QTabWidget.West, QTabWidget.East]
        self.contentTabs.setTabPosition (tabPositions [pos])

    def currentBrowser (self):
        return self.contentTabs.currentWidget ()
        
    def showConfiguration(self):
        """
        showConfiguration() -> display the config dialog
        """
        dlg = ConfigDialog (self, config, paths)
        result = dlg.exec_ ()
        if result == QDialog.Accepted:
            self.jsEnabled   = dlg.jsEnabled
            self.javaEnabled = dlg.javaEnabled

            for i in range (2, self.contentTabs.count ()):
                self.contentTabs.widget (i).setJava (self.jsEnabled, self.javaEnabled)
                
            self.computePaths (dlg.basePath)
            global userPath
            userPath = dlg.userPath
            
            self.favor = dlg.favor
            self.samplesEnabled = dlg.samplesEnabled
            self.historyLength = dlg.historyLength
    
            self.samplesList.writePaths (False)
            General.writeEntry ("ExpandLevel", str (dlg.expandValue))
            General.writeEntry ("EnableJS", str (self.jsEnabled))
            General.writeEntry ("EnableJave", str (self.javaEnabled))
            General.writeEntry ("TabPosition", str (dlg.tabOrient))
            General.writeEntry ("Favor", str (dlg.favor))
            General.writeEntry ("Samples Enabled", str (dlg.samplesEnabled))
            General.writeEntry ("History Length", str (dlg.historyLength))

            self.setTabPosition (dlg.tabOrient)
    
            config.sync ()
    
    def getFullName (self, item, nameSoFar):
        """
        recurse up the tree to get a complete name of the item clicked
        """
        if not item.parent ():
            return nameSoFar
        else:
            return self.getFullName (item.parent (), "%s%s%s" % (str (item.parent ().text (0)), delim, nameSoFar))

    def sampleSelected(self, item, dummy = 0):
        """
        sampleSelected() -> emit the current item and its module
        """
        if item:
            itemName = self.getFullName (item, str (item.text (0)))
            items    = itemName.split (delim, 1)
            n        = len (items)

            if items [0] in paths:
                if items [0] not in self.tabs:
                    newBrowser = BrowserFrame (self.contentTabs, self, items [0], self.jsEnabled, self.javaEnabled)
                    self.tabs [items [0]]  = self.contentTabs.addTab (newBrowser, items [0])
                    if n == 1:
                        newBrowser.home ()

                self.contentTabs.setCurrentIndex (self.tabs [items [0]])
                
                if n == 2:
                    self.currentBrowser ().slotOpenURL (KUrl (bookmarkFile [items [1]]))

            elif items [0] == "Documentation":
                if n == 1:
                    path = os.path.join (basePath, "doc", "html", "index.html")
                    self.contentTabs.setCurrentIndex (2)
                    self.emit (sigSampleSelected, (path, items [0]))
                else:
                    path = os.path.join (basePath, "doc", "html", docs [items [1]])
                    self.contentTabs.setCurrentIndex (2)
                    self.emit (sigSampleSelected, (path, items [1]))
                
            elif items [0] == "Tutorials":
                items  = itemName.split (delim, 2)
                n      = len (items)
                if items [0]  not in self.tabs:
                    newBrowser = BrowserFrame (self.contentTabs, self, "Tutorials", self.jsEnabled, self.javaEnabled)
                    self.tabs [items [0]]  = self.contentTabs.addTab (newBrowser, KIcon ("applications-education"), items [0])
                    self.connect (self, sigTutorialSelected, newBrowser.showTutorial)
                    self.connect (self, sigTutorialSelected, self.sampleFrame.showTutorial)

                self.contentTabs.setCurrentIndex (self.tabs [items [0]])
                
                if n == 1:
                    path = os.path.join (basePath, "tutorials", "index")
                    self.emit (sigTutorialSelected, (path, items [0]))
                    
                elif n == 2:
                    path = os.path.join (basePath, "tutorials", titles [items [1]][0], "index")
                    if os.path.exists (path + ".html"):
                        self.emit (sigTutorialSelected, (path, items [1]))
                        
                else:
                    path = os.path.join (basePath, "tutorials", titles [items [1]][0], topicFile [items [2]])
                    self.emit (sigTutorialSelected, (path, items [2]))                  
            
            else:
                items  = itemName.split (delim, 2)
                n      = len (items)
                module = items [0]
                            
                htmlPath = os.path.join (basePath, "doc", "html")
                if n == 1:
                    path = os.path.join (htmlPath, "%s.html" % module)
                    self.contentTabs.setCurrentIndex (2)

                elif n == 2:
                    namespace = items [1]
                    path = os.path.join (htmlPath, module, "%s.html" % namespace)           
                    self.contentTabs.setCurrentIndex (2)

                else:
                    cls = items [2]
                    path = os.path.join (htmlPath, module, "%s.html" % cls)           
                    self.contentTabs.setCurrentIndex (self.favor)
                
                self.emit (sigSampleSelected, (path, itemName))

    def setSplashPixmap(self, pixmap):
        """
        setSplashPixmap(pixmap) -> assimilate the splash screen pixmap
        """
        target = self.sampleFrame
        label = QLabel(target)
        label.setPixmap(pixmap)
        target.setWidget(label)

    def restorePosAndSize (self):
        w = General.readEntry ("Width", "640")
        h = General.readEntry ("Height", "480")
        x = General.readEntry ("LastX", "0")
        y = General.readEntry ("LastY", "0")

        self.setGeometry (int (x), int (y), int (w), int (h))

    def queryClose (self):
        """
        save config data before exiting
        """
        General.writeEntry ("Width", str (self.width ()))
        General.writeEntry ("Height", str (self.height ()))
        General.writeEntry ("LastX", str (self.x ()))
        General.writeEntry ("LastY", str (self.y ()))
        General.writeEntry ("ExpandLevel", str (self.samplesList.expandLevel))
        General.writeEntry ("Url History", self.combo.historyItems ()[:self.historyLength])

        self.samplesList.writePaths (True)        
        
        return True       
        
if __name__ == '__main__':
    # the KAboutData arguments
    appName     = "pykdedocs"
    catalog     = ""
    programName = ki18n ("pykdedocs")
    version     = "1.0"
    description = ki18n ("PyKDE4 Doc and Example Viewer")
    license     = KAboutData.License_GPL
    copyright   = ki18n ("(c) 2007 Jim Bublitz")
    text        = ki18n ("")
    homePage    = "http://www.riverbankcomputing.com"
    bugEmail    = "jbublitz@nwinternet.com"

    aboutData   = KAboutData (appName, catalog, programName, version, description,
                              license, copyright, text, homePage, bugEmail)
    
    aboutData.addAuthor (ki18n ("Troy Melhase"), ki18n ("original concept"))
    aboutData.addAuthor (ki18n ("Jim Bublitz"), ki18n ("pykdedocs"))
   
    KCmdLineArgs.init(sys.argv, aboutData)
    app = KApplication()
    
    # read the config file here and make config and General global
    config = KConfig ()
    General = KConfigGroup (config, "General")
    basePath = str (General.readEntry ("BasePath", basePath))
    
    logo  = QImage (os.path.join (basePath, relImagePath, 'aboutkde.png'))
    setprogramlogo (aboutData, logo)

    splashpix = QPixmap.fromImage (logo)
    splash = KSplashScreen(splashpix)
    splash.show()

    docs, docTitles = indexof.documentation (basePath)
    titles, titleList, topicFile = indexof.tutorials (basePath)
    paths, userTitleList, bookmarkFile = indexof.user (homeKDEPath)
    
    mainWindow = SamplerMainWindow()
    mainWindow.setSplashPixmap(splashpix)
        
    cursor  = app.setOverrideCursor 
    restoreCursor = app.restoreOverrideCursor
    busy = QCursor (Qt.BusyCursor)
    wait = QCursor (Qt.WaitCursor)

    mainWindow.show ()
    mainWindow.setWindowIcon (KIcon ("help-about"))
    mainWindow.sampleSelected (mainWindow.samplesList.topItem)
    splash.finish(mainWindow)
    app.exec_ ()
