##-Descr : a simple wxPython XML Viewer
# Author : Thomas Paviot (thomas.paviot@free.fr)
# Note : This program contains code of the wxPython demos wxSTC1 and 2.

from wxPython.wx import *
from wxPython.stc import *
from xml.dom.minidom import *

if wxPlatform == '__WXMSW__':
    faces = { 'times': 'Times New Roman',
              'mono' : 'Courier New',
              'helv' : 'Arial',
              'other': 'Comic Sans MS',
              'size' : 10,
              'size2': 8,
             }
else:
    faces = { 'times': 'Times',
              'mono' : 'Courier',
              'helv' : 'Helvetica',
              'other': 'new century schoolbook',
              'size' : 13,
              'size2': 11,
             }

class MainWindow(wxMDIParentFrame):
    "Fen�tre de visualisation d'un fichier XML"
    def __init__(self):        
        wxMDIParentFrame.__init__(self,None,-1,"XML Viewer",size=(700,600))
        menuBar=wxMenuBar()
        menu=wxMenu()
        menu.Append(10,"&Open")
        menu.AppendSeparator()
        menu.Append(20,"&Quit")
        menuBar.Append(menu,"&File")
        self.SetMenuBar(menuBar)
        EVT_MENU(self,10,self.OnOpen)
        EVT_MENU(self,20,self.OnQuit)        
    
    def OnOpen(self,event):
        "Open an XML file"
        dlg = wxFileDialog(self,"Open XML file",".","","XML files (*.xml)|*.xml|All files (*.*)|*.*",wxOPEN|wxCHANGE_DIR)
        if dlg.ShowModal()==wxID_OK:
            file=dlg.GetPaths()
            filename=file[0]            
        else:
            return False
        dlg.Destroy()
        self.NewXMLDisplayWindow(filename)
        
    def NewXMLDisplayWindow(self,xml_filename):
        try:
            xml_document=parse(xml_filename)
        except:
            print "Parse error of file %s"%xml_filename
            return False
        # Construct keywords string
        def GetNodeNames(node,keywords=""):            
            if not hasattr(node,"data"):
                keywords=keywords+node.nodeName+" "
            for item in node.childNodes:
                keywords=GetNodeNames(item,keywords)
            return keywords
        xml_document_keywords=GetNodeNames(xml_document)
        xml_string="%s"%xml_document.toprettyxml()
        # window definition
        win=wxMDIChildFrame(self,-1,xml_filename,size=(600,500))
        ed=XMLSTC(win,xml_document_keywords)
        ed.SetMarginType(1,wxSTC_MARGIN_NUMBER)
        ed.Colourise(0,-1)
        ed.SetText(xml_string)
        win.Show(True)

    def OnQuit(self,event):
        self.Close()

class XMLSTC(wxStyledTextCtrl):
    def __init__(self,parent,KEYWORDS):
        wxStyledTextCtrl.__init__(self,parent,-1)
        self.SetLexer(wxSTC_LEX_PYTHON)
        self.SetKeyWords(0,KEYWORDS)
        self.SetProperty("fold", "1")
        self.SetProperty("tab.timmy.whinge.level", "1")
        self.SetMargins(0,0)
        self.SetViewWhiteSpace(False)
       
        self.SetEdgeMode(wxSTC_EDGE_BACKGROUND)
        self.SetEdgeColumn(78)

        # Setup a margin to hold fold markers     
        self.SetMarginType(2, wxSTC_MARGIN_SYMBOL)
        self.SetMarginMask(2, wxSTC_MASK_FOLDERS)
        self.SetMarginSensitive(2,True)
        self.SetMarginWidth(2, 15)
        
        self.MarkerDefine(wxSTC_MARKNUM_FOLDEREND,     wxSTC_MARK_BOXPLUSCONNECTED,  "white", "black")
        self.MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUSCONNECTED, "white", "black")
        self.MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_TCORNER,  "white", "black")
        self.MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL,    wxSTC_MARK_LCORNER,  "white", "black")
        self.MarkerDefine(wxSTC_MARKNUM_FOLDERSUB,     wxSTC_MARK_VLINE,    "white", "black")
        self.MarkerDefine(wxSTC_MARKNUM_FOLDER,        wxSTC_MARK_BOXPLUS,  "white", "black")
        self.MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN,    wxSTC_MARK_BOXMINUS, "white", "black")

     
        EVT_STC_MARGINCLICK(self,-1,self.OnMarginClick)

        self.StyleClearAll()
        # Single quoted string
        self.StyleSetSpec(wxSTC_P_CHARACTER, "fore:#000000,bold,face:%(helv)s,size:%(size)d" % faces)
        # Keyword
        self.StyleSetSpec(wxSTC_P_WORD, "fore:#990000,size:%(size)d" % faces)
        # Operators
        self.StyleSetSpec(wxSTC_P_OPERATOR, "fore:#0000FF,size:%(size)d" % faces)
        # Identifiers
        self.StyleSetSpec(wxSTC_P_IDENTIFIER, "fore:#808080,bold,face:%(helv)s,size:%(size)d" % faces)
     
    def OnMarginClick(self, evt):
        "Folds and unfolds as needed"
        if evt.GetMargin() == 2:
            if evt.GetShift() and evt.GetControl():
                self.FoldAll()
            else:
                lineClicked = self.LineFromPosition(evt.GetPosition())
                if self.GetFoldLevel(lineClicked) & wxSTC_FOLDLEVELHEADERFLAG:
                    if evt.GetShift():
                        self.SetFoldExpanded(lineClicked, True)
                        self.Expand(lineClicked, True, True, 1)
                    elif evt.GetControl():
                        if self.GetFoldExpanded(lineClicked):
                            self.SetFoldExpanded(lineClicked, False)
                            self.Expand(lineClicked, False, True, 0)
                        else:
                            self.SetFoldExpanded(lineClicked,True)
                            self.Expand(lineClicked, True, True, 100)
                    else:
                        self.ToggleFold(lineClicked)

    def FoldAll(self):
        lineCount = self.GetLineCount()
        expanding = True

        # find out if we are folding or unfolding
        for lineNum in range(lineCount):
            if self.GetFoldLevel(lineNum) & wxSTC_FOLDLEVELHEADERFLAG:
                expanding = not self.GetFoldExpanded(lineNum)
                break;

        lineNum = 0
        while lineNum < lineCount:
            level = self.GetFoldLevel(lineNum)
            if level & wxSTC_FOLDLEVELHEADERFLAG and \
               (level & wxSTC_FOLDLEVELNUMBERMASK) == wxSTC_FOLDLEVELBASE:

                if expanding:
                    self.SetFoldExpanded(lineNum, True)
                    lineNum = self.Expand(lineNum, True)
                    lineNum = lineNum - 1
                else:
                    lastChild = self.GetLastChild(lineNum, -1)
                    self.SetFoldExpanded(lineNum, False)
                    if lastChild > lineNum:
                        self.HideLines(lineNum+1, lastChild)

            lineNum = lineNum + 1

    def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
        lastChild = self.GetLastChild(line, level)
        line = line + 1
        while line <= lastChild:
            if force:
                if visLevels > 0:
                    self.ShowLines(line, line)
                else:
                    self.HideLines(line, line)
            else:
                if doExpand:
                    self.ShowLines(line, line)

            if level == -1:
                level = self.GetFoldLevel(line)

            if level & wxSTC_FOLDLEVELHEADERFLAG:
                if force:
                    if visLevels > 1:
                        self.SetFoldExpanded(line, True)
                    else:
                        self.SetFoldExpanded(line, False)
                    line = self.Expand(line, doExpand, force, visLevels-1)

                else:
                    if doExpand and self.GetFoldExpanded(line):
                        line = self.Expand(line, True, force, visLevels-1)
                    else:
                        line = self.Expand(line, False, force, visLevels-1)
            else:
                line = line + 1;

        return line

class MyApp(wxPySimpleApp):
    def OnInit(self):
        window=MainWindow()
        window.Show(true)
        return True
    
def main():    
    XMLViewer=MyApp()
    XMLViewer.MainLoop()

if __name__=="__main__":
    main()

