#-----------------------------------------------------------------------------
# Name: HelpBook.py
# Purpose:
#
# Author: Riaan Booysen
#
# Created: 2003
# RCS-ID: $Id: HelpBook.plug-in.py,v 1.10 2005/05/18 12:11:50 riaan Exp $
# Copyright: (c) 2003 - 2005
# Licence: GPL
#-----------------------------------------------------------------------------
#Boa:Dialog:HelpBookItemDlg
import wx
def createIndexDlg(parent, keyword, location, anchors):
return HelpBookItemDlg(parent, 'Help Book - Index', keyword, 'Keyword(s)', '',
location, 'Location', anchors)
def createContentsDlg(parent, title, htmlTitle, location, anchors):
return HelpBookItemDlg(parent, 'Help Book - Contents', title, 'Title', htmlTitle,
location, 'Location', anchors)
[wxID_HELPBOOKITEMDLG, wxID_HELPBOOKITEMDLGBTNREADTITLE,
wxID_HELPBOOKITEMDLGBUTTON2, wxID_HELPBOOKITEMDLGBUTTON3,
wxID_HELPBOOKITEMDLGCBBANCHORS, wxID_HELPBOOKITEMDLGSTATICTEXT1,
wxID_HELPBOOKITEMDLGSTATICTEXT2, wxID_HELPBOOKITEMDLGTXTPAGE,
wxID_HELPBOOKITEMDLGTXTTITLE,
] = [wx.NewId() for _init_ctrls in range(9)]
class HelpBookItemDlg(wx.Dialog):
def _init_ctrls(self, prnt):
# generated method, don't edit
wx.Dialog.__init__(self, id=wxID_HELPBOOKITEMDLG,
name='HelpBookItemDlg', parent=prnt, pos=wx.Point(452, 359),
size=wx.Size(446, 136), style=wx.DEFAULT_DIALOG_STYLE,
title=self.caption)
self.SetClientSize(wx.Size(438, 109))
self.staticText1 = wx.StaticText(id=wxID_HELPBOOKITEMDLGSTATICTEXT1,
label=self.nameLabel, name='staticText1', parent=self,
pos=wx.Point(8, 16), size=wx.Size(56, 13), style=0)
self.txtTitle = wx.TextCtrl(id=wxID_HELPBOOKITEMDLGTXTTITLE,
name='txtTitle', parent=self, pos=wx.Point(72, 8),
size=wx.Size(232, 21), style=0, value=self.name)
self.staticText2 = wx.StaticText(id=wxID_HELPBOOKITEMDLGSTATICTEXT2,
label=self.valueLabel, name='staticText2', parent=self,
pos=wx.Point(8, 48), size=wx.Size(64, 13), style=0)
self.txtPage = wx.TextCtrl(id=wxID_HELPBOOKITEMDLGTXTPAGE,
name='txtPage', parent=self, pos=wx.Point(72, 40),
size=wx.Size(232, 21), style=0, value=self.value)
self.cbbAnchors = wx.ComboBox(choices=self.valueAnchors,
id=wxID_HELPBOOKITEMDLGCBBANCHORS, name='cbbAnchors', parent=self,
pos=wx.Point(304, 40), size=wx.Size(125, 21), style=0,
value=self.valueAnchorsValue)
self.btnReadTitle = wx.Button(id=wxID_HELPBOOKITEMDLGBTNREADTITLE,
label='Title from HTML', name='btnReadTitle', parent=self,
pos=wx.Point(304, 8), size=wx.Size(123, 23), style=0)
self.btnReadTitle.Bind(wx.EVT_BUTTON, self.OnBtnreadtitleButton,
id=wxID_HELPBOOKITEMDLGBTNREADTITLE)
self.button2 = wx.Button(id=wx.ID_OK, label='OK', name='button2',
parent=self, pos=wx.Point(272, 72), size=wx.Size(75, 23),
style=0)
self.button3 = wx.Button(id=wx.ID_CANCEL, label='Cancel',
name='button3', parent=self, pos=wx.Point(353, 72),
size=wx.Size(75, 23), style=0)
def __init__(self, parent, caption, name, nameLabel, nameDefault,
value, valueLabel, valueAnchors):
self.caption = 'Help Book'
self.caption = caption
self.name = ''
self.name = name
self.nameLabel = 'Name'
self.nameLabel = nameLabel
self.nameDefault = ''
self.nameDefault = nameDefault
self.value = ''
self.value = self.getFilenameValue(value)
self.valueLabel = 'Value'
self.valueLabel = valueLabel
self.valueAnchors = []
self.valueAnchors = valueAnchors
self.valueAnchorsValue = ''
self.valueAnchorsValue = self.getAnchorValue(value)
self._init_ctrls(parent)
self.titleFromHtml = ''
if not self.nameDefault:
self.btnReadTitle.Disable()
self.button2.SetDefault()
def GetResult(self):
name = self.txtTitle.GetValue()
anch = self.cbbAnchors.GetValue().strip()
value = self.txtPage.GetValue()
if anch:
value = value + '#' + anch
return name, value
def OnBtnreadtitleButton(self, event):
self.txtTitle.SetValue(self.titleFromHtml)
#-------------------------------------------------------------------------------
def getAnchorValue(self, value):
if value.find('#') != -1:
return value.split('#', 1)[1]
else:
return ''
def getFilenameValue(self, value):
if value.find('#') != -1:
return value.split('#')[0]
else:
return value
import os, sys, htmllib, formatter
if __name__ == '__main__':
sys.path.append('..')
class HelpConfigParser:
def __init__(self, lines, defaultName='helpbook'):
self.options = {'Compatibility': '1.1',
'Full-text search': 'Yes',
'Default Window': 'wxHelp',
'Default topic': 'index.html',
'Title': defaultName,
}
self.updateFilenameOptions(defaultName)
self.windows = []
self.files = []
self._section = ''
for line in [l.strip() for l in lines if l.strip()]:
if line in ('[OPTIONS]', '[WINDOWS]', '[FILES]'):
self._section = line
else:
if self._section == '[OPTIONS]':
k, v = line.split('=')
self.options[k] = v
elif self._section == '[WINDOWS]':
k, v = line.split('=')
self.windows = v.split(',')
elif self._section == '[FILES]':
self.files.append(line)
def generateConfigData(self):
res = ['[OPTIONS]']
res.extend(['%s=%s'%(key, val) for key, val in self.options.items()])
res.append('')
res.append('[WINDOWS]')
# I have no idea what the magic numbers mean
res.append('wxHelp=,"%(Contents file)s","%(Index file)s",'
'"%(Default topic)s",,,,,,0x2420,,0x380e,,,,,0,,,'%self.options)
res.append('')
res.append('[FILES]')
res.extend(self.files)
return '\n'.join(res)
def updateFilenameOptions(self, name):
self.options.update({'Contents file': name+'.hhc',
'Compiled file': name+'.chm',
'Index file': name+'.hhk'})
class HelpBookParser(htmllib.HTMLParser):
def __init__(self, formatter, verbose=0):
htmllib.HTMLParser.__init__(self, formatter, verbose)
self.indexes = None
self.index = None
self.workstack = []
self.result = []
def start_ul(self, attrs):
indexes = []
if self.indexes is not None:
self.workstack.append(self.indexes)
if self.indexes:
self.indexes[-1] = self.indexes[-1][:2]+(indexes,)
self.indexes = indexes
self.index = None
def end_ul(self):
if not self.workstack:
self.result = self.indexes
else:
self.indexes = self.workstack.pop()
def do_param(self, attrs):
if self.indexes is not None and self.index:
name, value = attrs[0][1], attrs[1][1]
self.index[name == 'Local'] = value
def start_object(self, attrs):
if self.indexes is not None and self.index is None:
self.index = [None, None, None]
def end_object(self):
if self.indexes is not None:
self.indexes.append(tuple(self.index))
self.index = None
def writeHelpBook(items):
res = ['
']
for name, value, children in items:
res.append('- ')
if children:
res.append(writeHelpBook(children))
res.append('
')
return '\n'.join(res)
def parseHelpFile(data, Parser=HelpBookParser):
w = formatter.NullWriter()
f = formatter.NullFormatter(w)
p = Parser(f)
p.feed(data)
return p
class BreakOnTitle(Exception): pass
class HtmlDocDetailParser(htmllib.HTMLParser):
def __init__(self, formatter, verbose=0, breakOnTitle=False):
htmllib.HTMLParser.__init__(self, formatter, verbose)
self.anchors = []
self.title = ''
self.breakOnTitle = breakOnTitle
self.result = [self.title, self.anchors]
_in_title = False
def start_title(self, attrs):
self._in_title = True
def end_title(self):
self._in_title = False
def start_a(self, attrs):
if attrs and attrs[0] and attrs[0][0].lower() == 'name':
self.anchors.append(attrs[0][1])
def end_a(self):
pass
def handle_data(self, data):
if self._in_title:
self.title = self.result[0] = data.strip()
if self.breakOnTitle:
raise BreakOnTitle, self.title
def do_param(self, attrs):
pass#print 'do_param', attrs
def _testHPP():
from pprint import pprint
#pprint(parseHelpFile(open('../Docs/python/python.hhc').read()))
#i = parseHelpFile(open('../Docs/boa/apphelp/apphelp.hhc').read())
#print i
##writeHelpBook(i)
print parseHelpFile(open('../Docs/boa/apphelp/debugger.html').read(), Parser=HtmlDocDetailParser).result
def _testHCP():
h = HelpConfigParser(open('../Docs/wxpython/wx/wx.hhp').readlines())
print h.options, h.windows, h.files
if __name__ == '__main__':
_testHPP()
#_testHCP()
#-------------------------------------------------------------------------------
import Preferences, Utils, Plugins
import PaletteStore
from Models import Controllers, EditorHelper, EditorModels
from Views import EditorViews, SourceViews, StyledTextCtrls
from Explorers import Explorer, ExplorerNodes
import ProcessProgressDlg
import glob, zipfile
from cStringIO import StringIO
class HelpBookModel(EditorModels.SourceModel):
modelIdentifier = 'HelpBook'
defaultName = 'helpbook'
bitmap = 'HelpBook.png'
ext = '.hhp'
imgIdx = EditorHelper.imgHelpBook
def __init__(self, data, name, editor, saved):
EditorModels.SourceModel.__init__(self, data, name, editor, saved)
contents = os.path.splitext(name)[0]+'.hhc'
try:
transport = Explorer.openEx(contents)
data = transport.load()
except ExplorerNodes.TransportError:
transport = None
data = ''
self.contentsModel = EditorModels.SourceModel(data, contents, editor, saved)
self.contentsModel.transport = transport
indexes = os.path.splitext(name)[0]+'.hhk'
try:
transport = Explorer.openEx(indexes)
data = transport.load()
except ExplorerNodes.TransportError:
transport = None
data = ''
self.indexesModel = EditorModels.SourceModel(data, indexes, editor, saved)
self.indexesModel.transport = transport
self.update()
def update(self):
self.config = HelpConfigParser(StringIO(self.data).readlines())
self.contentsModel.update()
self.contents = parseHelpFile(self.contentsModel.data).result
self.indexesModel.update()
self.indexes = parseHelpFile(self.indexesModel.data).result
def load(self, notify=True):
try: self.contentsModel.load(False)
except ExplorerNodes.TransportError: pass
try: self.indexesModel.load(False)
except ExplorerNodes.TransportError: pass
EditorModels.SourceModel.load(self, notify)
def save(self, overwriteNewer=False):
if self.modified:
gen = '\n'
if self.contentsModel.modified:
self.contentsModel.data = gen + writeHelpBook(self.contents)
self.contentsModel.save(overwriteNewer)
if self.indexesModel.modified:
self.indexesModel.data = gen + writeHelpBook(self.indexes)
self.indexesModel.save(overwriteNewer)
self.data = self.config.generateConfigData()
EditorModels.SourceModel.save(self, overwriteNewer)
def saveAs(self, filename):
newDir, newName = os.path.split(filename)
oldDir, oldName = os.path.split(self.filename)
if self.savedAs and newDir != oldDir:
raise ExplorerNodes.TransportSaveError, \
'Once saved, help books files cannot be moved, only renamed.\n'\
'Please move the entrire directory.'
if newName != oldName:
self.config.updateFilenameOptions(os.path.splitext(newName)[0])
self.modified = True
noExtFilename = os.path.splitext(filename)[0]
self.contentsModel.saveAs(noExtFilename+'.hhc')
self.indexesModel.saveAs(noExtFilename+'.hhk')
EditorModels.SourceModel.saveAs(self, filename)
def setModified(self, section='config'):
if section == 'contents':
self.contentsModel.modified = True
elif section == 'indexes':
self.indexesModel.modified = True
self.modified = True
def makeHTB(self):
pass
# XXX Experimental, should just load and save from zip
class HTBHelpBookModel(HelpBookModel):
modelIdentifier = 'BTBHelpBook'
defaultName = 'htbhelpbook'
ext = '.htb'
def __init__(self, data, name, editor, saved):
HelpBookModel.__init__(self, data, name, editor, saved)
def load(self, notify=True):
pass
def save(self, overwriteNewer=False):
pass
## if self.GetItemCount():
## itemFrom = self.GetTopItem()
## itemTo = self.GetTopItem()+1 + self.GetCountPerPage()
## itemTo = min(itemTo, self.GetItemCount()-1)
## self.RefreshItems(itemFrom, itemTo)
class HelpBookFilesView(EditorViews.VirtualListCtrlView):
viewName = 'Files'
addBmp = 'Images/Shared/NewItem.png'
delBmp = 'Images/Shared/DeleteItem.png'
def __init__(self, parent, model, provideActions=True):
if provideActions:
actions = (('Add file', self.OnAddFile, self.addBmp, ''),
('Add files', self.OnAddFiles, '-', ''),
('Remove file', self.OnRemoveFile, self.delBmp, ''),
('Open file', self.OnOpenFile, '-', ''),
('-', None, '-', ''),
('Normalise paths', self.OnNormalisePaths, '-', ''),)
else:
actions = ()
EditorViews.VirtualListCtrlView.__init__(self, parent, model,
wx.LC_REPORT, actions, -1)
self.Bind(wx.EVT_LEFT_DOWN, self.OnFilesLeftDown)
#self.sortOnColumns = [0, 1, 2]
self.InsertColumn(0, 'No')
self.InsertColumn(1, 'Name')
self.InsertColumn(2, 'Path')
self.SetColumnWidth(0, 30)
self.SetColumnWidth(1, 150)
self.SetColumnWidth(2, 300)
if Preferences.hbShowDocumentTitles:
self.InsertColumn(3, 'Title')
self.SetColumnWidth(3, 300)
self.active = True
self.resetCache()
#self.Bind(wx.EVT_LIST_CACHE_HINT, self.OnCacheHint, id=self.GetId())
def refreshCtrl(self):
self.resetCache()
self.SetItemCount(len(self.model.config.files))
def OnGetItemText(self, item, col):
f = self.model.config.files[item]
if col == 0:
return `item`
elif col == 1:
return os.path.basename(f)
elif col == 2:
return f
elif Preferences.hbShowDocumentTitles and col == 3:
if not self.cached[item]:
title = ''
try:
if os.path.splitext(f)[1].lower() not in ('.htm', '.html'):
return ''
docsDir = os.path.dirname(self.model.filename)
try: data = Explorer.openEx(os.path.join(docsDir, f)).load()
except ExplorerNodes.TransportError: return ''
fmtr = formatter.NullFormatter(formatter.NullWriter())
try: HtmlDocDetailParser(fmtr, breakOnTitle=True).feed(data)
except BreakOnTitle, title: return str(title)
except: return ''
else: return ''
finally:
self.cached[item] = title
else:
return self.cached[item]
def doStartDrag(self):
sel = self.GetSelections()
if sel:
filenames = [self.model.config.files[idx] for idx in sel]
filelist = wx.CustomDataObject(wx.CustomDataFormat('FileList'))
filelist.SetData(`filenames`)
#tdo =wx.TextDataObject(filename)
ds = wx.DropSource(self)
ds.SetData(filelist)
ds.DoDragDrop(wx.Drag_DefaultMove)
def OnFilesLeftDown(self, event):
event.Skip()
wx.CallAfter(self.doStartDrag)
helpBookDirMsg = 'Help documents must be in the same directory as the help '\
'book, or a sub-directory. Not allowed outside the tree.'
def OnAddFile(self, event):
if not self.model.savedAs:
wx.LogError('Please save the help book before adding files\n'+self.helpBookDirMsg)
return
filename = self.model.editor.openFileDlg()
if filename:
helpProjDir = os.path.dirname(self.model.filename)
if not filename.startswith(helpProjDir):
wx.LogError(self.helpBookDirMsg)
return
self.model.config.files.append(filename[len(helpProjDir)+1:])
self.refreshCtrl()
self.updateOtherViews()
self.model.setModified()
self.updateEditor()
def OnRemoveFile(self, event):
sel = self.GetSelections()
if sel:
sel.sort()
sel.reverse()
for idx in sel:
del self.model.config.files[idx]
self.refreshCtrl()
self.updateOtherViews()
self.model.setModified()
self.updateEditor()
def updateOtherViews(self):
views = self.model.views
if views.has_key('Contents'):
views['Contents'].files.refreshCtrl()
if views.has_key('Index'):
views['Index'].files.refreshCtrl()
def OnOpenFile(self, event):
idx = self.selected
if idx != -1:
helpBookDir = os.path.dirname(self.model.filename)
self.model.editor.openOrGotoModule(
helpBookDir+'/'+self.model.config.files[idx])
#def OnCacheHint(self, event):
# print 'From', event.GetCacheFrom()
# print 'To', event.GetCacheTo()
def resetCache(self, ):
self.cached = [False]*len(self.model.config.files)
def OnNormalisePaths(self, event):
helpBookDir = os.path.dirname(self.model.localFilename())
delidxs = []
for idx, filename in zip(range(len(self.model.config.files)),
self.model.config.files):
absfn = os.path.normpath(os.path.join(helpBookDir, filename))
if absfn[:len(helpBookDir)] != helpBookDir:
if wx.MessageBox('Filename "%s" not inside help book directory.'
'\n\nRemove?'%absfn, 'Invalid file',
wx.ICON_QUESTION | wx.YES_NO) == wx.YES:
delidxs.append(idx)
else:
self.model.config.files[idx] = absfn[len(helpBookDir)+1:]
if delidxs:
delidxs.reverse()
for idx in delidxs:
del self.model.config.files[idx]
self.refreshCtrl()
self.updateOtherViews()
self.model.setModified()
self.updateEditor()
def OnAddFiles(self, event):
helpBookDir = os.path.dirname(self.model.localFilename())
dlg = wx.DirDialog(self)
try:
dlg.SetPath(helpBookDir)
if dlg.ShowModal() != wx.ID_OK:
return
dir = dlg.GetPath()
finally:
dlg.Destroy()
if not dir.startswith(helpBookDir):
wx.LogError(self.helpBookDirMsg)
return
dlg = wx.TextEntryDialog(self, 'Enter wildcard', 'Add Files', '*.*')
try:
if dlg.ShowModal() != wx.ID_OK:
return
globStr = dlg.GetValue()
finally:
dlg.Destroy()
files = glob.glob(os.path.join(dir, globStr))
for filename in files:
self.model.config.files.append(filename[len(helpBookDir)+1:])
self.refreshCtrl()
self.updateOtherViews()
self.model.setModified()
self.updateEditor()
class FileListDropTarget(wx.PyDropTarget):
def __init__(self):
wx.PyDropTarget.__init__(self)
self.fmt = wx.CustomDataFormat('FileList')
self.data = wx.CustomDataObject(self.fmt)
self.SetDataObject(self.data)
def OnDrop(self, x, y):
return True
def OnData(self, x, y, d):
if self.GetData():
filelist = eval(self.data.GetData())
self.OnDropFiles(x, y, filelist)
return d
class HelpBookIndexDropTarget(FileListDropTarget):
def __init__(self, indexList):
FileListDropTarget.__init__(self)
self.list = indexList
def OnDropFiles(self, x, y, files):
docsDir = os.path.dirname(self.list.model.filename)
for filename in files:
data = Explorer.openEx(os.path.join(docsDir, filename)).load()
prs = parseHelpFile(data, HtmlDocDetailParser)
dlg = createIndexDlg(None, '', filename, prs.anchors)
try:
if dlg.ShowModal() != wx.ID_OK:
return
keyword, location = dlg.GetResult()
finally:
dlg.Destroy()
#item, flags = self.list.HitTest( (x, y) )
self.list.model.indexes.append((keyword, location, None))
self.list.model.setModified('indexes')
self.list.updateEditor()
self.list.refreshCtrl()
class HelpBookIndexView(wx.SplitterWindow, EditorViews.EditorView):
viewName = 'Index'
addBmp = 'Images/Shared/NewItem.png'
delBmp = 'Images/Shared/DeleteItem.png'
def __init__(self, parent, model):
wx.SplitterWindow.__init__(self, parent, -1,
style=wx.CLIP_CHILDREN | wx.NO_3D | wx.SP_3DSASH)
self.indexes = HelpBookIndexListView(self, model, self)
self.files = HelpBookFilesView(self, model, False)
self.SplitVertically(self.indexes, self.files, 350)
EditorViews.EditorView.__init__(self, model, (
('Add index', self.indexes.OnAddIndex, self.addBmp, ''),
('Edit index', self.indexes.OnEditIndex, '-', ''),
('Delete index', self.indexes.OnDeleteIndex, self.delBmp, ''),
), 0)
self.active = True
def refreshCtrl(self):
self.indexes.refreshCtrl()
self.files.refreshCtrl()
class HelpBookIndexListView(EditorViews.VirtualListCtrlView):
def __init__(self, parent, model, parentView=None):
EditorViews.VirtualListCtrlView.__init__(self, parent, model, wx.LC_REPORT,
(), -1)
self.parentView = parentView
self.SetDropTarget(HelpBookIndexDropTarget(self))
#self.sortOnColumns = [0, 1]
self.InsertColumn(0, 'Keyword')
self.InsertColumn(1, 'Location')
self.SetColumnWidth(0, 100)
self.SetColumnWidth(1, 400)
self.active = True
def refreshCtrl(self):
self.SetItemCount(len(self.model.indexes))
def OnGetItemText(self, item, col):
return self.model.indexes[item][col]
def OnRightClick(self, event):
if self.parentView:
menu = self.parentView.generateMenu()
event.GetEventObject().PopupMenuXY(menu, event.GetX(), event.GetY())
menu.Destroy()
def OnAddIndex(self, event):
dlg = createIndexDlg(None, '', '', [])
try:
if dlg.ShowModal() != wx.ID_OK:
return
keyword, location = dlg.GetResult()
finally:
dlg.Destroy()
self.model.indexes.append((keyword, location, None))
self.model.setModified('indexes')
self.updateEditor()
self.refreshCtrl()
def OnEditIndex(self, event):
if self.selected != -1:
keyword, location, c = self.model.indexes[self.selected]
docsDir = os.path.dirname(self.model.filename)
try:
data = Explorer.openEx(
os.path.join(docsDir, location.split('#')[0])).load()
except ExplorerNodes.TransportLoadError:
data = ''
prs = parseHelpFile(data, HtmlDocDetailParser)
dlg = createIndexDlg(None, keyword, location, prs.anchors)
try:
if dlg.ShowModal() != wx.ID_OK:
return
res = dlg.GetResult()
finally:
dlg.Destroy()
if res != (keyword, location):
keyword, location = res
self.model.indexes[self.selected] = (keyword, location, None)
self.model.setModified('indexes')
self.updateEditor()
self.refreshCtrl()
def OnDeleteIndex(self, event):
if self.selected != -1:
del self.model.indexes[self.selected]
self.model.setModified('indexes')
self.updateEditor()
self.refreshCtrl()
class HelpBookContentsDropTarget(FileListDropTarget):
def __init__(self, contentsTree):
FileListDropTarget.__init__(self)
self.tree = contentsTree
def OnDropFiles(self, x, y, files):
item, flags = self.tree.HitTest( (x, y) )
if not item.Ok(): return
name = self.tree.GetItemText(item)
value, items, children = self.tree.GetPyData(item)
docsDir = os.path.dirname(self.tree.model.filename)
for filename in files:
title, page = doContentsDlg(None, filename, docsDir)
if (title, page) == (None, None):
continue
entry = (title, page, None)
if children is None:
idx = items.index( (name, value, children) )
items[idx] = (name, value, [entry])
children = [entry]
else:
children.append(entry)
self.tree.model.setModified('contents')
self.tree.updateEditor()
self.tree.SetItemHasChildren(item, True)
self.tree.SetItemData(item, wx.TreeItemData((value, items, children)))
#if self.tree.GetItemImage(item) != 0:
# self.tree.SetItemImage(item, 1, 1) # XXX Doesn't work
self.tree.SelectItem(item)
self.tree.Collapse(item)
self.tree.Expand(item)
#self.tree.refreshCtrl()
def doContentsDlg(title, text, docsDir):
#text, anchor = (text.split('#')+[''])[:2]
data = Explorer.openEx(os.path.join(docsDir, text.split('#')[0])).load()
prs = parseHelpFile(data, HtmlDocDetailParser)
if title is None:
title = prs.title
dlg = createContentsDlg(None, title, prs.title, text, prs.anchors)
try:
if dlg.ShowModal() != wx.ID_OK:
return None, None
title, location = dlg.GetResult()
finally:
dlg.Destroy()
return title, location
class HelpBookContentsView(wx.SplitterWindow, EditorViews.EditorView):
viewName = 'Contents'
addBmp = 'Images/Shared/NewItem.png'
delBmp = 'Images/Shared/DeleteItem.png'
def __init__(self, parent, model):
wx.SplitterWindow.__init__(self, parent, -1,
style=wx.CLIP_CHILDREN | wx.NO_3D | wx.SP_3DSASH)
self.contents = HelpBookContentsTreeView(self, model, self)
self.files = HelpBookFilesView(self, model, False)
self.SplitVertically(self.contents, self.files, 350)
actions = (('Edit entry', self.contents.OnEditEntry, '-', ''),
('Delete entry', self.contents.OnDeleteEntry, self.delBmp, ''),
('Import...', self.OnImportContents, '-', ''), )
EditorViews.EditorView.__init__(self, model, actions, -1)
self.active = True
def refreshCtrl(self):
self.contents.refreshCtrl()
self.files.refreshCtrl()
def OnImportContents(self, event):
pass
class HelpBookContentsTreeView(wx.TreeCtrl, EditorViews.EditorView):
viewName = 'Contents'
def __init__(self, parent, model, parentView=None):
wx.TreeCtrl.__init__(self, parent, -1,
style=wx.TR_HAS_BUTTONS | wx.SUNKEN_BORDER | wx.TR_DEFAULT_STYLE)
EditorViews.EditorView.__init__(self, model, (), -1)
self.parentView = parentView
#(('Edit entry', self.OnEditEntry, '-', ''),)
self.SetDropTarget(HelpBookContentsDropTarget(self))
self.helpImgLst = wx.ImageList(16, 16)
for artId in (wx.ART_HELP_BOOK, wx.ART_HELP_FOLDER, wx.ART_HELP_PAGE):
bmp = wx.ArtProvider_GetBitmap(artId, wx.ART_TOOLBAR, (16, 16))
self.helpImgLst.Add(bmp)
self.AssignImageList(self.helpImgLst)
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnTreeExpand, id=self.GetId())
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeSelChanged, id=self.GetId())
self.active = True
def refreshCtrl(self):
self.DeleteAllItems()
title = self.model.config.options['Title']
rootItem = self.AddRoot(title, 0,
data=wx.TreeItemData((title, None, self.model.contents)))
self.SetItemHasChildren(rootItem, True)
self.Expand(rootItem)
def recurseAddItems(self, items, treeItem):
# XXX no longer used, build entire tree recursively
for name, value, children in items:
item = self.AppendItem(treeItem, name, children and 1 or 2, -1,
wx.TreeItemData((value, items, children)))
if children:
self.recurseAddItems(children, item)
self.SetItemHasChildren(item, True)
def addTreeItems(self, items, treeItem):
self.DeleteChildren(treeItem)
for name, value, children in items:
item = self.AppendItem(treeItem, name, children and 1 or 2, -1,
wx.TreeItemData((value, items, children)))
if children:
self.SetItemHasChildren(item, True)
def OnRightClick(self, event):
if self.parentView:
menu = self.parentView.generateMenu()
event.GetEventObject().PopupMenuXY(menu, event.GetX(), event.GetY())
menu.Destroy()
def OnTreeExpand(self, event):
item = event.GetItem()
self.addTreeItems(self.GetPyData(item)[-1], item)
def OnTreeSelChanged(self, event):
item = event.GetItem()
if item.Ok():
location = self.GetPyData(item)[0]
self.model.editor.setStatus(location)
def OnEditEntry(self, event):
tree = self
item = tree.GetSelection()
name = tree.GetItemText(item)
value, items, children = tree.GetPyData(item)
if item == tree.GetRootItem():
options = self.model.config.options
dlg = HelpBookItemDlg(self.model.editor, 'Help Book - Contents properties',
options['Title'], 'Title', '',
options['Default topic'], 'Default topic', [])
try:
if dlg.ShowModal() != wx.ID_OK:
return
title, defTopic = dlg.GetResult()
finally:
dlg.Destroy()
if options['Title'] != title or options['Default topic'] != defTopic:
self.model.setModified('config')
self.updateEditor()
options['Title'] = title
options['Default topic'] = defTopic
tree.SetItemData(item, wx.TreeItemData((title, defTopic, children)))
tree.SetItemText(item, title)
# XXX mark hhp modified
return
docsDir = os.path.dirname(self.model.filename)
title, page = doContentsDlg(name, value, docsDir)
if (title, page) == (None, None):
return
if (name, value, children) != (title, page, children):
idx = items.index( (name, value, children) )
items[idx] = (title, page, children)
self.model.setModified('contents')
self.updateEditor()
tree.SetItemData(item, wx.TreeItemData((page, items, children)))
tree.SetItemText(item, title)
def OnDeleteEntry(self, event):
item = self.GetSelection()
if item == self.GetRootItem():
wx.LogError('Cannot delete root node.')
return
name = self.GetItemText(item)
value, items, children = self.GetPyData(item)
idx = items.index( (name, value, children) )
if wx.MessageBox('Delete %s'%name, 'Delete node and children',
wx.ICON_WARNING | wx.YES_NO) == wx.YES:
del items[idx]
self.model.modified = True
self.updateEditor()
self.Collapse(item)
self.Delete(item)
def visitDir((files, excludes), dirname, names):
for name in names:
if name not in excludes:
filename = os.path.join(dirname, name)
if os.path.isfile(filename):
files.append(filename)
wxID_HP_X = wx.NewId()
class HelpBookController(Controllers.SourceController):
Model = HelpBookModel
DefaultViews = [HelpBookFilesView, HelpBookContentsView, HelpBookIndexView]
def actions(self, model):
actions = [
('-', None, '', ''),
('Make HTB', self.OnMakeHTB, '', ''),
]
if wx.Platform == '__WXMSW__':
actions.append( ('Make CHM', self.OnMakeCHM, '', '') )
return Controllers.SourceController.actions(self, model) + actions
def OnMakeHTB(self, event):
model = self.getModel()
dlg = wx.SingleChoiceDialog(model.editor, 'Choose source files',
'Make HTB', ['Files list', 'Entire help book directory'])
try:
if dlg.ShowModal() != wx.ID_OK:
return
selected = dlg.GetSelection()
finally:
dlg.Destroy()
modelFile = model.localFilename()
zipfilename = os.path.splitext(modelFile)[0]+'.htb'
docsDir = os.path.dirname(modelFile)
if selected == 0:
files = [os.path.join(docsDir, f) for f in model.config.files]
elif selected == 1:
files = []
os.path.walk(docsDir, visitDir,
(files, [os.path.basename(zipfilename)]))
wx.BeginBusyCursor()
zf = zipfile.ZipFile(zipfilename, 'w', zipfile.ZIP_DEFLATED)
try:
for filename in files:
zf.write(filename, filename[len(docsDir)+1:])
finally:
wx.EndBusyCursor()
zf.close()
wx.LogMessage('Written %s.'%zipfilename)
def OnMakeCHM(self, event):
modelFile = model.localFilename()
dir, name = os.path.split(modelFile)
cmd = 'hhc %s'%name
cwd = os.getcwd()
try:
os.chdir(runDir)
dlg = ProcessProgressDlg.ProcessProgressDlg(self.editor, cmd, 'Make CHM')
try:
if dlg.ShowModal() == wx.OK:
outls = dlg.output
errls = dlg.errors
else:
return
finally:
dlg.Destroy()
finally:
os.chdir(cwd)
#err = ''.join(errls).strip()
#-------------------------------------------------------------------------------
Plugins.registerPreference('HelpBook', 'hbShowDocumentTitles', 'True',
['Should the document title be parsed from HTML and '
'displayed under "Files".'])
Plugins.registerFileType(HelpBookController)