#!/usr/bin/env python
#****************************************************************************
# treemainwin.py, provides a class for the main window
#
# TreeLine, an information storage program
# Copyright (C) 2005, Douglas W. Bell
#
# This is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License, Version 2. This program is
# distributed in the hope that it will be useful, but WITTHOUT ANY WARRANTY.
#*****************************************************************************
try:
from __main__ import __version__, __author__, helpFilePath, iconPath
except ImportError:
__version__ = __author__ = '??'
helpFilePath = None
iconPath = None
from treedoc import TreeDoc, ReadFileError, PasswordError
from treeitem import TreeItem
from nodeformat import NodeFormat
from treeformats import TreeFormats
from treeview import TreeView
from treerightviews import DataOutView, DataEditView
from treedialogs import ConfigDlg, FieldEntry, TypeSetDlg, FieldSelectDlg, \
EditFieldsDlg, ExportDlg, ConditionDlg, NumberingDlg, \
FindTextEntry, RadioChoiceDlg, SpellCheckDlg, \
PrintHeaderDlg, PasswordEntry, PluginListDlg
from option import Option
from optiondlg import OptionDlg, OptionDlgBool, OptionDlgInt, \
OptionDlgStr, OptionDlgDbl, OptionDlgPush, \
OptionDlgRadio
from recentfiles import RecentFiles
from treestates import TreeStates
from printpreview import PrintPrevDlg
from helpview import HelpView
from icondict import IconDict
from spellcheck import SpellCheck, SpellCheckError
from plugininterface import PluginInterface
from optiondefaults import OptionDefaults
import globalref
from qt import Qt, PYSIGNAL, SIGNAL, SLOT, qApp, qVersion, QAction, \
QActionGroup, QApplication, QColor, QColorDialog, QColorGroup, \
QDialog, QFileDialog, QFont, QFontDialog, QFontMetrics, \
QIconSet, QInputDialog, QLineEdit, QMainWindow, QMessageBox, \
QPaintDeviceMetrics, QPainter, QPalette, QPopupMenu, \
QPrinter, QRegion, QSimpleRichText, QSplitter, QString, \
QStringList, QTab, QTabBar, QTimer, QToolBar, QUriDrag, \
QVBoxLayout, QWidget, QWidgetStack
if qVersion()[0] >= '3':
from textedit3 import TitleListView
from qt import QKeySequence
else:
from textedit2 import TitleListView
from qt import QAccel
import sys, os.path, copy, re, codecs
class TreeMainWin(QMainWindow):
"""Main window, menus, toolbar, and status"""
copyFormat = NodeFormat('_DUMMY__ROOT_')
tlPlainFileFilter = u'%s (*.trl *.xml)' % _('TreeLine Files - Plain')
tlCompFileFilter = u'%s (*.trl *.trl.gz)' % _('TreeLine Files - Compressed')
tlEncryptFileFilter = u'%s (*.trl)' % _('TreeLine Files - Encrypted')
tlGenFileFilter = u'%s (*.trl *.xml *.trl.gz)' % _('TreeLine Files')
allFileFilter = u'%s (*)' % _('All Files')
textFileFilter = u'%s (*.txt)' % _('Text Files')
treepadFileFilter = u'%s (*.hjt)' % _('Treepad Files')
xbelFileFilter = u'%s (*.xml)' % _('XBEL Bookmarks')
mozFileFilter = u'%s (*.html *.htm)' % _('Mozilla Bookmarks')
htmlFileFilter = u'%s (*.html *.htm)' % _('Html Files')
xsltFileFilter = u'%s (*.xsl)' % _('XSLT Files')
tableFileFilter = u'%s (*.tbl *.txt)' % _('Table Files')
xmlFileFilter = u'%s (*.xml)' % _('XML Files')
def __init__(self, parent=None, name=None):
QMainWindow.__init__(self, parent, name)
self.setAcceptDrops(True)
globalref.options = Option('treeline', 21)
self.optionDefaults = OptionDefaults(lambda text: \
unicode(qApp.translate('QAccel', text)))
globalref.options.loadAll(self.optionDefaults.defaultOutput())
self.translateKeys()
globalref.treeIcons = IconDict()
modPath = os.path.abspath(sys.path[0])
iconPathList = [iconPath, os.path.join(modPath, 'icons/'), \
os.path.join(modPath, '../icons/')]
globalref.treeIcons.loadIcons(['treeline'], iconPathList)
self.toolIcons = IconDict()
self.setIcon(globalref.treeIcons.getIcon('treeline'))
self.resize(globalref.options.intData('WindowXSize', 10, 10000), \
globalref.options.intData('WindowYSize', 10, 10000))
if globalref.options.boolData('SaveWindowGeom'):
self.move(globalref.options.intData('WindowXPos', 0, 10000), \
globalref.options.intData('WindowYPos', 0, 10000))
self.updateColors()
self.autoSaveTimer = QTimer(self)
self.connect(self.autoSaveTimer, SIGNAL('timeout()'), self.autoSave)
self.showItemChildren = globalref.options.boolData('StartShowChildren')
split = QSplitter(self)
self.setCentralWidget(split)
self.treeView = TreeView(self, split)
rightView = QWidget(split)
rightLayout = QVBoxLayout(rightView)
self.rightStack = QWidgetStack(rightView)
rightLayout.addWidget(self.rightStack)
rightTabs = QTabBar(rightView)
rightTabs.setShape(QTabBar.RoundedBelow)
rightTabs.setFocusPolicy(QWidget.NoFocus)
dataOutId = rightTabs.addTab(QTab(_('&Data Output')))
dataEditId = rightTabs.addTab(QTab(_('Data &Editor')))
titleListId = rightTabs.addTab(QTab(_('Title &List')))
rightLayout.addWidget(rightTabs)
self.tabIds = [dataOutId, dataEditId, titleListId]
self.dataOutSplit = QSplitter(Qt.Vertical, self.rightStack)
self.rightStack.addWidget(self.dataOutSplit, dataOutId)
self.dataEditSplit = QSplitter(Qt.Vertical, self.rightStack)
self.rightStack.addWidget(self.dataEditSplit, dataEditId)
self.titleListSplit = QSplitter(Qt.Vertical, self.rightStack)
self.rightStack.addWidget(self.titleListSplit, titleListId)
parentOutView = DataOutView(False, self.dataOutSplit)
childOutView = DataOutView(True, self.dataOutSplit)
childList = [childOutView]
parentDataView = DataEditView(False, self.dataEditSplit)
childDataView = DataEditView(True, self.dataEditSplit)
childList.append(childDataView)
parentTitleView = TitleListView(False, self.titleListSplit)
childTitleView = TitleListView(True, self.titleListSplit)
childList.append(childTitleView)
if not self.showItemChildren:
for child in childList:
child.hide()
treeFont = self.getFontFromOptions('Tree')
if treeFont:
self.treeView.setFont(treeFont)
outFont = self.getFontFromOptions('Output')
if outFont:
parentOutView.setFont(outFont)
childOutView.setFont(outFont)
editFont = self.getFontFromOptions('Editor')
if editFont:
parentDataView.setFont(editFont)
childDataView.setFont(editFont)
parentTitleView.setFont(editFont)
childTitleView.setFont(editFont)
outSplitPercent = globalref.options.intData('OutputSplitPercent', 1, 99)
self.dataOutSplit.setSizes([outSplitPercent, 100 - outSplitPercent])
editSplitPercent = globalref.options.intData('EditorSplitPercent', \
1, 99)
self.dataEditSplit.setSizes([editSplitPercent, 100 - editSplitPercent])
titleSplitPercent = globalref.options.intData('TitleSplitPercent', \
1, 99)
self.titleListSplit.setSizes([titleSplitPercent, \
100 - titleSplitPercent])
self.connect(rightTabs, SIGNAL('selected(int)'), \
self.viewTabSelect)
self.rightStack.raiseWidget(dataOutId)
mainSplitPercent = globalref.options.intData('TreeSplitPercent', 1, 99)
split.setSizes([mainSplitPercent, 100 - mainSplitPercent])
self.doc = None
self.setTypeDlg = None
self.FindDlg = None
self.helpView = None
self.blockViewChg = False
self.fileImported = False
self.printer = QPrinter()
try:
pageSize = getattr(QPrinter, globalref.options.\
strData('PrintPageSize', False).capitalize())
self.printer.setPageSize(pageSize)
except AttributeError:
self.printer.setPageSize(QPrinter.Letter)
self.printFont = None # set at printing
self.printList = []
self.pageLevelBreaks = []
self.linesPerPage = 0
globalref.updateViewAll = self.updateViews
globalref.updateViewMenuStat = self.updateCmdAvail
globalref.setStatusBar = self.statusBar().message
self.setupMenus()
self.restoreToolbars()
self.statusBar().message(_('Ready'), 2000)
self.fileNew()
self.connect(self.treeView, SIGNAL('selectionChanged()'), \
self.updateRightView)
self.connect(QApplication.clipboard(), SIGNAL('dataChanged()'), \
self.setPasteAvail)
self.connect(self.rightStack, SIGNAL('aboutToShow(QWidget*)'), \
self.updateRightView)
self.connect(self.rightStack, SIGNAL('aboutToShow(int)'), \
rightTabs, SLOT('setCurrentTab(int)'))
self.setPasteAvail()
self.setupPlugins()
def lateInit(self):
"""Set right view active tab, must be after show to get proper size"""
if globalref.options.boolData('SaveWindowGeom'):
viewNum = globalref.options.intData('ActiveRightView', 0, 2)
self.viewTabSelect(self.tabIds[viewNum])
def translateKeys(self):
"""Copy tranlated shortcut key options to English dictionary keys"""
for englishName, transName, unusedKey in \
self.optionDefaults.keyBindList:
for optionDict in globalref.options.dictList:
key = optionDict.get(transName)
if key != None:
optionDict[englishName] = key
def getFontFromOptions(self, optionPrefix):
"""Return font if set in options or None"""
fontName = globalref.options.strData('%sFont' % optionPrefix, True)
if fontName:
try:
fontSize = int(globalref.options.strData('%sFontSize' % \
optionPrefix, True))
except ValueError:
fontSize = 10
font = QFont(fontName, fontSize)
font.setBold(globalref.options.boolData('%sFontBold' % \
optionPrefix))
font.setItalic(globalref.options.boolData('%sFontItalic' % \
optionPrefix))
font.setUnderline(globalref.options.boolData('%sFontUnderline' % \
optionPrefix))
font.setStrikeOut(globalref.options.boolData('%sFontStrikeOut' % \
optionPrefix))
return font
return None
def saveFontToOptions(self, font, optionPrefix):
"""Store font in option settings"""
globalref.options.changeData('%sFont' % optionPrefix, \
unicode(font.family()), True)
globalref.options.changeData('%sFontSize' % optionPrefix, \
unicode(font.pointSize()), True)
globalref.options.changeData('%sFontBold' % optionPrefix, \
font.bold() and 'yes' or 'no', True)
globalref.options.changeData('%sFontItalic' % optionPrefix, \
font.italic() and 'yes' or 'no', True)
globalref.options.changeData('%sFontUnderline' % optionPrefix, \
font.underline() and 'yes' or 'no', True)
globalref.options.changeData('%sFontStrikeOut' % optionPrefix, \
font.strikeOut() and 'yes' or 'no', True)
def updateViews(self):
"""Update tree and child views"""
QApplication.setOverrideCursor(Qt.waitCursor)
self.treeView.updateTree()
QApplication.restoreOverrideCursor()
self.updateRightView()
def updateRightView(self, split=None):
"""Update given right-hand view or the active one"""
QApplication.setOverrideCursor(Qt.waitCursor)
if split:
self.viewToAct[split].setOn(True)
else:
split = self.rightStack.visibleWidget()
for view in split.children():
if hasattr(view, 'updateView') and (self.showItemChildren or \
not view.showChildren):
view.updateView()
if self.setTypeDlg and self.setTypeDlg.isVisible():
self.setTypeDlg.updateDlg()
self.updateCmdAvail()
QApplication.restoreOverrideCursor()
def updateCmdAvail(self):
"""Update the enabled status of menus"""
item = self.doc.selection.currentItem
if not self.doc or not item:
return
self.fileSaveAct.setEnabled(self.doc.modified)
self.editInsertActGrp.setEnabled(item.parent != None)
selParents = [node.parent for node in self.doc.selection]
numChildren = [len(node.childList) for node in self.doc.selection]
self.editUndoAct.setEnabled(len(self.doc.undoStore.undoList))
self.editRedoAct.setEnabled(len(self.doc.redoStore.undoList))
self.editDeleteAct.setEnabled(len(self.doc.selection) and \
self.doc.root not in \
self.doc.selection)
self.editPrevSibActGrp.setEnabled(len(self.doc.selection) and \
None not in \
[node.prevSibling() for node in \
self.doc.selection])
self.editUnindentAct.setEnabled(len(self.doc.selection) and \
None not in selParents and \
None not in [node.parent for node in \
selParents])
self.editMoveDownAct.setEnabled(len(self.doc.selection) and \
None not in \
[node.nextSibling() for node in \
self.doc.selection])
self.dataSelExistActGrp.setEnabled(len(self.doc.selection))
self.dataSelParentActGrp.setEnabled(len(self.doc.selection) and \
len(filter(None, numChildren)))
allAncestors = []
for node in self.doc.selection:
allAncestors.extend(node.ancestorList())
self.dataUnrelParentActGrp.setEnabled(len(self.doc.selection) and \
0 not in numChildren and \
False not in [node not in \
allAncestors \
for node in \
self.doc.selection])
self.dataSingleSelActGrp.setEnabled(len(self.doc.selection) == 1 \
and numChildren[0])
self.toolsRemXsltAct.setEnabled(len(self.doc.xlstLink))
self.statusBar().clear()
if globalref.pluginInterface:
globalref.pluginInterface.execCallback(globalref.pluginInterface.\
viewUpdateCallbacks)
def setPasteAvail(self):
"""Check to see if text is available to paste"""
try:
text = unicode(QApplication.clipboard().text())
except UnicodeError:
text = ''
self.editPasteActGrp.setEnabled(len(text))
def disableAllControls(self):
"""Disable all menu and toolbar commands during tree renaming"""
for group in self.actGrpList:
group.setEnabled(False)
def enableAllControls(self):
"""Enable controls after tree renaming"""
for group in self.actGrpList:
group.setEnabled(True)
self.updateCmdAvail()
def updateColors(self):
"""Adjust the colors to the current option settings"""
pal = QApplication.palette()
background = QColor(globalref.options.intData('BackgroundR', 0, 255), \
globalref.options.intData('BackgroundG', 0, 255), \
globalref.options.intData('BackgroundB', 0, 255))
foreground = QColor(globalref.options.intData('ForegroundR', 0, 255), \
globalref.options.intData('ForegroundG', 0, 255), \
globalref.options.intData('ForegroundB', 0, 255))
pal.setColor(QColorGroup.Base, background)
pal.setColor(QPalette.Active, QColorGroup.Text, foreground)
pal.setColor(QPalette.Inactive, QColorGroup.Text, foreground)
QApplication.setPalette(pal, True)
pal.setColor(QColorGroup.Text, QColor(0, 0, 0))
self.statusBar().setPalette(pal)
def focusWidgetWithAttr(self, attr):
"""Return the focused widget or it's ancestor
that has the given attr or None"""
widget = qApp.focusWidget()
while widget and not hasattr(widget, attr):
widget = widget.parent()
return widget
def setupPlugins(self):
"""Load plugin modules"""
globalref.pluginInterface = PluginInterface(self)
pluginPaths = [os.path.join(os.path.abspath(sys.path[0]), 'plugins')]
userPluginPath = globalref.options.strData('UserPluginDir', True)
if userPluginPath:
pluginPaths.append(userPluginPath)
self.pluginList = []
for pluginPath in pluginPaths:
if os.access(pluginPath, os.R_OK):
sys.path.insert(1, pluginPath)
self.pluginList.extend([name[:-3] for name in \
os.listdir(pluginPath) if \
name.endswith('.py')])
self.pluginInstances = [] # saves returned ref - avoid garbage collect
self.pluginDescript = []
errorList = []
for name in self.pluginList[:]:
try:
module = __import__(name)
if not hasattr(module, 'main'):
raise ImportError
self.pluginInstances.append(module.main(globalref.\
pluginInterface))
descript = module.__doc__
if descript:
descript = [line for line in descript.split('\n') \
if line.strip()][0].strip()
if not descript:
descript = name
self.pluginDescript.append(descript)
except ImportError:
errorList.append(name)
self.pluginList.remove(name)
if errorList:
QMessageBox.warning(self, 'TreeLine', \
_('Could not load plugin module %s') % \
', '.join(errorList))
def setMainCaption(self):
"""Set main window caption using doc filename path"""
caption = ''
if self.doc.fileName:
caption = u'%s [%s] ' % (os.path.basename(self.doc.fileName), \
os.path.dirname(self.doc.fileName))
caption += u'- TreeLine'
if sys.platform == 'win32':
caption += u' (PyQt)'
self.setCaption(caption)
def openFile(self, fileRef, importOnFail=True, addToRecent=True):
"""Open given file, fail quietly if not importOnFail,
return False if file should be removed from recent list,
True otherwise,
fileRef is either file path or file object"""
if hasattr(fileRef, 'read'):
fileName = unicode(fileRef.name, TreeDoc.localEncoding)
else:
fileName = fileRef
autoSaveFile = self.autoSaveFilePath(fileName)
if autoSaveFile and globalref.options.intData('AutoSaveMinutes', \
0, 999):
ans = QMessageBox.information(self, 'TreeLine', \
_('Backup file "%s" exists.\n'\
'A previous session may have '\
'crashed.') % autoSaveFile, \
_('&Restore Backup'), \
_('&Delete Backup'), \
_('&Cancel File Open'), 0, 2)
if ans == 0:
if not self.restoreAutoSaveFile(fileName):
QMessageBox.warning(self, 'TreeLine', \
_('Error - could not restore backup'))
return True
elif ans == 1:
self.delAutoSaveFile(fileName)
else:
return True
QApplication.setOverrideCursor(Qt.waitCursor)
try:
self.doc.readFile(fileRef)
self.fileImported = False
except PasswordError:
QApplication.restoreOverrideCursor()
dlg = PasswordEntry(False, self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
self.doc.storedFileRef.close()
return True
self.doc.setPassword(fileName, dlg.password)
result = self.openFile(self.doc.storedFileRef, importOnFail)
if not dlg.saveIt:
self.doc.clearPassword(fileName)
return result
except (IOError, UnicodeError):
QApplication.restoreOverrideCursor()
QMessageBox.warning(self, 'TreeLine', \
_('Error - could not read file "%s"') % fileName)
return False
except ReadFileError, e:
QApplication.restoreOverrideCursor()
if not importOnFail:
self.doc.storedFileRef.close()
return True
# assume file is not a TreeLine file
dlg = RadioChoiceDlg(_('Import Text'), \
_('Choose Text Import Method'), \
[(_('Tab &indented text, one node per line'), \
self.doc.readTabbed), \
(_('Text &table with header row, one node per line'), \
self.doc.readTable), \
(_('Plain text, one &node per line (CR delimitted)'), \
self.doc.readLines), \
(_('Plain text ¶graphs (blank line delimitted)'), \
self.doc.readPara), \
(_('Treepad &file (text nodes only)'), \
self.doc.readTreepad), \
(_('&XML bookmarks (XBEL format)'), \
self.doc.readXbel), \
(_('&HTML bookmarks (Mozilla format)'), \
self.doc.readMozilla), \
(_('&Generic XML (Non-TreeLine file)'), \
self.doc.readXml)], self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
self.doc.storedFileRef.close()
return True
try:
QApplication.setOverrideCursor(Qt.waitCursor)
apply(dlg.getResult(), (self.doc.storedFileRef,))
except ReadFileError, e:
QApplication.restoreOverrideCursor()
QMessageBox.warning(self, 'TreeLine', _('Error - %s') % e)
return False
self.fileImported = True
self.setMainCaption()
self.treeView.setFocus()
self.doc.root.open = True
QApplication.restoreOverrideCursor()
if addToRecent:
self.recentFiles.addEntry(fileName)
if addToRecent and globalref.options.boolData('PersistTreeState'):
self.treeStates.loadCurrent(self.treeView)
else:
self.updateViews()
self.updateCmdAvail()
self.loadTypeSubMenu()
self.resetAutoSave()
if globalref.pluginInterface:
globalref.pluginInterface.execCallback(globalref.pluginInterface.\
fileOpenCallbacks)
return True
def recentOpen(self, filePath):
"""Open from recentFiles signal"""
if filePath and self.savePrompt():
if not self.openFile(filePath):
self.recentFiles.removeEntry(filePath)
def autoOpen(self):
"""Open last used file"""
if globalref.options.boolData('AutoFileOpen'):
path = self.recentFiles.recentFileName(0)
if path and not self.openFile(path, False):
self.recentFiles.removeEntry(path)
def getFileName(self, caption, defaultExt, filterList, currentFilter=0):
"""Return user specified file name for save as & export"""
dir, name = os.path.split(self.doc.fileName)
dlg = QFileDialog(dir, ';;'.join(filterList), self, '', True)
dlg.setMode(QFileDialog.AnyFile)
if name:
dlg.setSelection(os.path.splitext(name)[0] + defaultExt)
if qVersion()[0] >= '3':
dlg.setSelectedFilter(currentFilter)
elif currentFilter: # can't setSelected, so change order
filterList.insert(0, filterList.pop(currentFilter))
dlg.setFilters(';;'.join(filterList))
dlg.setCaption(caption)
dlg.setIcon(globalref.treeIcons.getIcon('treeline'))
result = dlg.exec_loop()
fileName = unicode(dlg.selectedFile())
if result == QDialog.Accepted and fileName:
selectedFilter = unicode(dlg.selectedFilter())
if '.' not in fileName and \
selectedFilter != TreeMainWin.allFileFilter:
fileName += defaultExt
if selectedFilter == TreeMainWin.tlPlainFileFilter:
self.doc.compressFile = False
self.doc.encryptFile = False
elif selectedFilter == TreeMainWin.tlCompFileFilter:
self.doc.compressFile = True
self.doc.encryptFile = False
elif selectedFilter == TreeMainWin.tlEncryptFileFilter:
self.doc.encryptFile = True
if os.access(fileName.encode(TreeDoc.localEncoding), os.F_OK):
text = _('File "%s" exists.\nReplace existing file?') % fileName
ans = QMessageBox.information(self, 'TreeLine', text, \
_('&Yes'), _('&No'), \
_('&Cancel'), 0, 2)
if ans != 0:
if ans == 1:
return self.getFileName(caption, defaultExt, \
filterList, currentFilter)
return ''
return fileName
return ''
def resetAutoSave(self):
"""Restart auto save timer if the option is enabled"""
self.autoSaveTimer.stop()
minutes = globalref.options.intData('AutoSaveMinutes', 0, 999)
if minutes:
self.autoSaveTimer.start(60000 * minutes)
def autoSave(self):
"""Perform auto save if the option is enabled (called from timer)"""
if self.doc.modified and self.doc.fileName and not self.fileImported:
unsetPassword = False
if self.doc.encryptFile and \
not self.doc.hasPassword(self.doc.fileName):
dlg = PasswordEntry(True, self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.setPassword(self.doc.fileName, dlg.password)
unsetPassword = not dlg.saveIt
try:
self.doc.writeFile(self.doc.fileName + '~', False)
except IOError:
pass
if unsetPassword:
self.doc.clearPassword(self.doc.fileName)
def autoSaveFilePath(self, baseName=''):
"""Return the path to a backup file if it exists"""
filePath = baseName and baseName + '~' or self.doc.fileName + '~'
if len(filePath) > 1 and \
os.access(filePath.encode(TreeDoc.localEncoding), os.R_OK):
return filePath
return ''
def delAutoSaveFile(self, baseName=''):
"""Remove the backup auto save file if it exists"""
filePath = self.autoSaveFilePath(baseName)
if filePath:
try:
os.remove(filePath)
except OSError:
print 'Could not remove backup file %s' % \
filePath.encode(TreeDoc.localEncoding)
def restoreAutoSaveFile(self, baseName):
"""Move baseName~ to baseName by overwriting, return True on success"""
try:
os.remove(baseName)
except OSError:
print 'Could not remove file %s' % \
baseName.encode(TreeDoc.localEncoding)
return False
try:
os.rename(baseName + '~', baseName)
except OSError:
print 'Could not rename file %s' % \
baseName.encode(TreeDoc.localEncoding) + '~'
return False
return True
def saveFile(self, fileRef):
"""Save file to fileName, return True on success,
fileRef is either file path or file object"""
if hasattr(fileRef, 'read'):
fileName = unicode(fileRef.name, TreeDoc.localEncoding)
else:
fileName = fileRef
unsetPassword = False
if self.doc.encryptFile and not self.doc.hasPassword(fileName):
dlg = PasswordEntry(True, self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return False
self.doc.setPassword(fileName, dlg.password)
unsetPassword = not dlg.saveIt
try:
self.doc.writeFile(fileRef)
except IOError:
QMessageBox.warning(self, 'TreeLine', \
_('Error - Could not write to %s') % fileName)
return False
if unsetPassword:
self.doc.clearPassword(fileName)
self.updateCmdAvail()
self.delAutoSaveFile()
self.resetAutoSave()
if globalref.pluginInterface:
globalref.pluginInterface.execCallback(globalref.pluginInterface.\
fileSaveCallbacks)
return True
def fileNew(self):
"""New file command"""
if self.savePrompt():
self.doc = TreeDoc()
self.setMainCaption()
self.treeView.setFocus()
self.updateViews()
self.updateCmdAvail()
self.loadTypeSubMenu()
self.resetAutoSave()
if globalref.pluginInterface:
globalref.pluginInterface.execCallback(globalref.\
pluginInterface.\
fileNewCallbacks)
def fileOpen(self):
"""Open a file"""
if self.savePrompt():
dfltPath = self.recentFiles.firstPath()
if not dfltPath:
dfltPath = os.path.split(self.doc.fileName)[0]
if not dfltPath:
dfltPath = os.environ.get('HOME', '')
if not dfltPath:
dfltPath = '..'
filters = [TreeMainWin.tlGenFileFilter, \
TreeMainWin.textFileFilter, \
TreeMainWin.treepadFileFilter, \
TreeMainWin.xbelFileFilter, \
TreeMainWin.mozFileFilter, \
TreeMainWin.allFileFilter]
fileName = unicode(QFileDialog.getOpenFileName(dfltPath, \
';;'.join(filters), \
self))
if fileName:
self.openFile(fileName)
def fileOpenSample(self):
"""Open a sample template file"""
if self.savePrompt():
path = self.findHelpPath()
if not path:
self.statusBar().message(_('Sample directory not found'))
return
fileName = unicode(QFileDialog.getOpenFileName(path, \
TreeMainWin.tlGenFileFilter, self, \
None, _('Open Sample Template File')))
if fileName:
self.openFile(fileName)
def fileSave(self):
"""Save current file"""
if self.doc.fileName and not self.fileImported:
self.saveFile(self.doc.fileName)
else:
self.fileSaveAs()
def fileSaveAs(self):
"""Save file with a new name"""
oldFileName = self.doc.fileName
filterList = [TreeMainWin.tlPlainFileFilter, \
TreeMainWin.tlCompFileFilter, \
TreeMainWin.tlEncryptFileFilter, \
TreeMainWin.allFileFilter]
currentFilter = self.doc.encryptFile and 2 or \
(self.doc.compressFile and 1 or 0)
fileName = self.getFileName(_('Save As'), '.trl', filterList, \
currentFilter)
if fileName and self.saveFile(fileName):
self.setMainCaption()
self.recentFiles.addEntry(fileName)
self.fileImported = False
self.delAutoSaveFile(oldFileName)
def fileExport(self):
"""Export the file as html, a table or text"""
item = self.doc.selection.currentItem
dlg = ExportDlg(item.title(), self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
indent = globalref.options.intData('IndentOffset', 0, \
TreeView.maxOffset)
try:
if dlg.exportType == ExportDlg.htmlType:
fileName = self.getFileName(_('Export Html'), '.html', \
[TreeMainWin.htmlFileFilter, \
TreeMainWin.allFileFilter])
if not fileName:
return
if dlg.numColumns() == 1:
self.doc.exportHtml(fileName, item, dlg.isRootIncluded(), \
dlg.openOnly(), indent, \
dlg.headerIncluded())
else:
outGroup = item.outputItemList(dlg.isRootIncluded(), \
dlg.openOnly(), True)
# bug in height() - '
' doesn't work
sep = self.doc.lineBreaks and u'
' or u''
for outItem in outGroup:
text = QSimpleRichText(sep.join(outItem.textLines), \
self.font())
# no
on last line - would add newline height
text.setWidth(5000)
outItem.height = text.height()
self.doc.exportHtmlColumns(fileName, outGroup, \
dlg.numColumns(), indent, \
dlg.headerIncluded())
elif dlg.exportType == ExportDlg.dirType:
dirName = QFileDialog.getExistingDirectory('', self, '', \
_('Export to Directory'), \
True)
fileName = unicode(dirName)
if fileName:
self.doc.exportDir(fileName, item, dlg.headerIncluded())
elif dlg.exportType == ExportDlg.xsltType:
dlgText = _('A link to a stylesheet can be added to the '\
'XSL file\nEnter a CSS filename (blank for none)')
link, ok = QInputDialog.getText('TreeLine', dlgText, \
QLineEdit.Normal, \
self.doc.xslCssLink, self)
if ok:
fileName = self.getFileName(_('Export XSLT'), '.xsl', \
[TreeMainWin.xsltFileFilter, \
TreeMainWin.allFileFilter])
if fileName:
if self.doc.xslCssLink != unicode(link):
self.doc.xslCssLink = unicode(link)
self.doc.modified = True
self.doc.exportXslt(fileName, dlg.isRootIncluded(), \
indent)
self.fileSaveAct.setEnabled(self.doc.modified)
elif dlg.exportType == ExportDlg.trlType:
filterList = [TreeMainWin.tlPlainFileFilter, \
TreeMainWin.tlCompFileFilter, \
TreeMainWin.allFileFilter]
origCompress = self.doc.compressFile
fileName = self.getFileName(_('Export Subtree'), '.trl', \
filterList, self.doc.compressFile)
if fileName:
self.doc.exportTrlSubtree(fileName, item)
self.doc.compressFile = origCompress
elif dlg.exportType == ExportDlg.tableType:
fileName = self.getFileName(_('Export Table'), '.tbl', \
[TreeMainWin.tableFileFilter, \
TreeMainWin.allFileFilter])
if fileName:
self.doc.exportTable(fileName, item)
elif dlg.exportType == ExportDlg.textType:
fileName = self.getFileName(_('Export Titles'), '.txt', \
[TreeMainWin.textFileFilter, \
TreeMainWin.allFileFilter])
if fileName:
self.doc.exportTabbedTitles(fileName, item, \
dlg.isRootIncluded(), \
dlg.openOnly())
elif dlg.exportType == ExportDlg.xbelType:
fileName = self.getFileName(_('Export XBEL Bookmarks'), \
'.xml', \
[TreeMainWin.xbelFileFilter, \
TreeMainWin.allFileFilter])
if fileName:
self.doc.exportXbel(fileName, item)
elif dlg.exportType == ExportDlg.mozType:
fileName = self.getFileName(_('Export Html Bookmarks'), \
'.html', \
[TreeMainWin.mozFileFilter, \
TreeMainWin.allFileFilter])
if fileName:
self.doc.exportHtmlBookmarks(fileName, item)
else: # generic XML type
fileName = self.getFileName(_('Export Generic XML'), '.xml', \
[TreeMainWin.xmlFileFilter, \
TreeMainWin.allFileFilter])
if fileName:
self.doc.exportGenericXml(fileName, item)
except IOError:
QMessageBox.warning(self, 'TreeLine', \
_('Error - Could not write to %s') % fileName)
def printPage(self, pageNum, painter):
"""Send pageNum to painter"""
self.doc.fileInfoItem.data[u'Page_Number'] = repr(pageNum)
painter.setFont(self.printFont)
addLines = globalref.options.boolData('PrintLines')
indent = globalref.options.intData('PrintOffset', 0, \
self.treeView.maxOffset)
xLineDelta = indent // 2
yLineDelta = 0
if self.doc.spaceBetween:
yLineDelta = QFontMetrics(self.printFont).ascent()
linePos = QFontMetrics(self.printFont).ascent() // 2 + 1
numCols = globalref.options.intData('PrintNumCols', 1, 9)
colSpacing = 72 * globalref.options.numData('PrintColSpace', 0, \
OptionDefaults.maxPrintMargin)
colWidth = (painter.window().width() - colSpacing * (numCols - 1)) \
// numCols
if qVersion()[0] >= '3': # qt2 to qt3 text draw API change
drawRegion = painter.window()
else:
drawRegion = QRegion(painter.window())
header = self.doc.fileInfoItem.nodeFormat.getHeaderFooter(True)
initY = 0
if header:
text = QSimpleRichText(header, self.printFont)
text.setWidth(painter.window().width())
text.draw(painter, 0, 0, drawRegion, QColorGroup())
initY = 2 * QFontMetrics(self.printFont).lineSpacing()
for colNum in range(numCols):
colPos = colNum * (colSpacing + colWidth)
yPos = initY
if (pageNum - 1) * numCols + colNum >= len(self.printList):
self.doc.fileInfoItem.data[u'Page_Number'] = ''
return
lineStarts = {}
for item in self.printList[(pageNum - 1) * numCols + colNum]:
text = QSimpleRichText(''.join(item.textLines), \
self.printFont)
text.setWidth(colWidth - indent * item.level)
xPos = colPos + indent * item.level
text.draw(painter, xPos, yPos, drawRegion, QColorGroup())
if addLines and item.level:
painter.drawLine(xPos - xLineDelta, yPos + linePos, \
xPos - 2, yPos + linePos)
if item.firstSibling:
lineStarts[item.level] = yPos
if item.lastSibling:
painter.drawLine(xPos - xLineDelta, \
lineStarts.get(item.level, initY), \
xPos - xLineDelta, yPos + linePos)
yPos += item.height
if addLines and item.hasChildren and yLineDelta:
painter.drawLine(xPos + indent - xLineDelta, \
yPos - yLineDelta, \
xPos + indent - xLineDelta, yPos)
if addLines:
for level in self.pageLevelBreaks[(pageNum - 1) * numCols \
+ colNum]:
xPos = colPos + indent * level - xLineDelta
painter.drawLine(xPos, lineStarts.get(level, initY), \
xPos, yPos)
footer = self.doc.fileInfoItem.nodeFormat.getHeaderFooter(False)
if footer:
text = QSimpleRichText(footer, self.printFont)
text.setWidth(painter.window().width())
text.draw(painter, 0, painter.window().height() - 2 * \
QFontMetrics(self.printFont).lineSpacing(), \
drawRegion, QColorGroup())
self.doc.fileInfoItem.data[u'Page_Number'] = ''
def filePrint(self):
"""Print file starting from selected item"""
self.printFont = self.getFontFromOptions('Print')
if not self.printFont or \
globalref.options.boolData('PrintUseOutputFont'):
outputViews = [view for view in self.dataOutSplit.children() if \
hasattr(view, 'updateView')]
self.printFont = outputViews[0].font()
useFile = self.printer.outputToFile()
self.printer.setOutputFileName(os.path.splitext(self.doc.fileName)[0] +\
'.ps')
self.printer.setOutputToFile(useFile)
self.printer.setFullPage(True)
self.printer.setMinMax(1, 99)
self.printer.setFromTo(1, 99)
if not self.printer.setup(self):
return
paintMetrics = QPaintDeviceMetrics(self.printer)
dpi = min(paintMetrics.logicalDpiX(), paintMetrics.logicalDpiY())
scale = dpi / 100.0
xMargin = int(max(self.printer.margins().width() / scale, \
72 * globalref.options.numData('HorizMargin', \
OptionDefaults.minPrintMargin, \
OptionDefaults.maxPrintMargin)))
headerHt = self.doc.fileInfoItem.nodeFormat.getHeaderFooter(True) and \
2 * QFontMetrics(self.printFont).lineSpacing() or 0
footerHt = self.doc.fileInfoItem.nodeFormat.getHeaderFooter(False) and \
2 * QFontMetrics(self.printFont).lineSpacing() or 0
yMargin = int(max(self.printer.margins().height() / scale + \
headerHt + footerHt, \
72 * globalref.options.numData('VertMargin', \
OptionDefaults.minPrintMargin, \
OptionDefaults.maxPrintMargin)))
pageSize = (int(paintMetrics.width() / scale), \
int(paintMetrics.height() / scale))
pageFill = (pageSize[0] - 2 * xMargin, \
pageSize[1] - 2 * yMargin)
# pageFill is size of tree content, pageAvail is content + header/footer
pageAvail = (pageFill[0], pageFill[1] + headerHt + footerHt)
yOffset = yMargin - headerHt
numCols = globalref.options.intData('PrintNumCols', 1, 9)
colSpacing = int(72 * globalref.options.numData('PrintColSpace', 0, \
OptionDefaults.maxPrintMargin))
colWidth = (pageFill[0] - colSpacing * (numCols - 1)) // numCols
item = self.doc.selection.currentItem
includeRoot = globalref.options.boolData('PrintRoot')
openOnly = globalref.options.boolData('PrintOpenOnly')
indent = globalref.options.intData('PrintOffset', 0, \
self.treeView.maxOffset)
outGroup = item.outputItemList(includeRoot, openOnly)
# work-arounds for height() problems with some tags
brTagFix = (re.compile('(
|
)'), u'
')
hrTagFix = (re.compile('(
|
)'), u'
')
hTagFix = (re.compile('([hH][0-9] *>)'), u'\\1
')
fixList = [brTagFix, hrTagFix, hTagFix]
outGroup.setHeights(self.textHeight, colWidth, indent, fixList)
outGroup.joinPrefixItems()
if globalref.options.boolData('PrintLines'):
self.pageLevelBreaks = []
else:
self.pageLevelBreaks = None
firstChildAdjust = globalref.options.boolData('PrintKeepFirstChild') \
and 0.2 or 0.0
self.printList = outGroup.splitPages(pageFill[1], \
self.pageLevelBreaks, \
firstChildAdjust)
for group in self.printList:
group.addPrefix()
if self.doc.lineBreaks:
group.addBreaks()
minPg = max(self.printer.fromPage(), 1)
maxPg = len(self.printList) // numCols + (len(self.printList) \
% numCols and 1 or 0)
if 1 <= self.printer.toPage() < maxPg:
maxPg = self.printer.toPage()
self.doc.fileInfoItem.data[u'Number_of_Pages'] = repr(maxPg)
if globalref.options.boolData('PrintPrev'):
dlg = PrintPrevDlg(minPg, maxPg, pageSize, pageAvail, \
(xMargin, yOffset), self.printPage, self, \
None, True)
dlg.setIcon(globalref.treeIcons.getIcon('treeline'))
dlg.resize(globalref.options.intData('PrintPrevXSize', 10, 10000), \
globalref.options.intData('PrintPrevYSize', 10, 10000))
if globalref.options.boolData('SaveWindowGeom'):
dlg.move(globalref.options.intData('PrintPrevXPos', 0, 10000), \
globalref.options.intData('PrintPrevYPos', 0, 10000))
result = dlg.exec_loop()
if globalref.options.boolData('SaveWindowGeom'):
globalref.options.changeData('PrintPrevXSize', dlg.width(), \
True)
globalref.options.changeData('PrintPrevYSize', dlg.height(), \
True)
globalref.options.changeData('PrintPrevXPos', dlg.x(), True)
globalref.options.changeData('PrintPrevYPos', dlg.y(), True)
globalref.options.writeChanges()
if result != QDialog.Accepted:
self.doc.fileInfoItem.data['Number_of_Pages'] = ''
return
self.printer.setFullPage(False) # fix MS margin problem?
painter = QPainter()
if not painter.begin(self.printer):
self.doc.fileInfoItem.data[u'Number_of_Pages'] = ''
return
if self.printer.pageOrder() == QPrinter.FirstPageFirst:
seq = range(minPg, maxPg + 1)
else:
seq = range(maxPg, minPg - 1, -1)
printMargin = ((pageSize[0] * scale - paintMetrics.width()) // 2, \
(pageSize[1] * scale - paintMetrics.height()) // 2)
painter.setWindow(0, 0, pageAvail[0], pageAvail[1])
painter.setViewport(xMargin * scale - printMargin[0], \
yOffset * scale - printMargin[1], \
pageAvail[0] * scale, pageAvail[1] * scale)
self.printPage(seq.pop(0), painter)
for num in seq:
self.printer.newPage()
self.printPage(num, painter)
painter.end()
self.doc.fileInfoItem.data[u'Number_of_Pages'] = ''
def textHeight(self, text, width):
"""Calculates height of rich text in printer font"""
obj = QSimpleRichText(text, self.printFont)
obj.setWidth(width)
return obj.height()
def filePrintOpt(self):
"""Set margins and page size for printing"""
self.headerDlg = None
self.printFont = None
origPageSize = globalref.options.strData('PrintPageSize', False)
units = globalref.options.strData('PrintUnits', False)
unitOptions = ['PrintColSpace', 'HorizMargin', 'VertMargin']
unitText = _('inches')
if units == 'centimeter':
unitText = _('centimeters')
for optionName in unitOptions:
value = globalref.options.numData(optionName) * 2.54
globalref.options.changeData(optionName, `value`, False)
dlg = OptionDlg(globalref.options, self, None, True)
caption = _('Print Options')
if sys.platform == 'win32':
caption += ' (PyQt)'
dlg.setCaption(caption)
dlg.setIcon(globalref.treeIcons.getIcon('treeline'))
dlg.startGroupBox(_('Features'))
OptionDlgBool(dlg, 'PrintPrev', _('Preview before printing'))
OptionDlgBool(dlg, 'PrintRoot', _('Include root node'))
OptionDlgBool(dlg, 'PrintLines', _('Draw lines to children'))
OptionDlgBool(dlg, 'PrintOpenOnly', _('Print only open node children'))
OptionDlgBool(dlg, 'PrintKeepFirstChild', \
_('Keep first child with parent'))
dlg.startGroupBox(_('Printer Font'))
outputFontOpt = OptionDlgBool(dlg, 'PrintUseOutputFont', \
_('Use Data Output font'))
fontButtonOpt = OptionDlgPush(dlg, _('Set Printer Font'), \
self.setPrintFont)
fontButtonOpt.control.setDisabled(outputFontOpt.control.isChecked())
self.connect(outputFontOpt.control, SIGNAL('toggled(bool)'), \
fontButtonOpt.control, SLOT('setDisabled(bool)'))
dlg.startGroupBox(_('Header/Footer'))
headerButtonOpt = OptionDlgPush(dlg, _('Set Header and Footer'), \
self.setHeader)
dlg.endGroupBox()
dlg.startNewColumn()
OptionDlgRadio(dlg, 'PrintPageSize', _('Default Page Size'), \
[('letter', _('Letter')), ('a4', _('A4'))])
dlg.startGroupBox(_('Columns'), 10)
OptionDlgInt(dlg, 'PrintNumCols', _('Number of columns'), 1, \
OptionDefaults.maxNumCol)
OptionDlgDbl(dlg, 'PrintColSpace', \
_('Space between columns (%s)') % unitText, \
0, OptionDefaults.maxPrintMargin)
dlg.startGroupBox(_('Offsets'), 10)
OptionDlgInt(dlg, 'PrintOffset', _('Child indent offset (points)'), \
0, TreeView.maxOffset)
OptionDlgDbl(dlg, 'HorizMargin', \
_('Horizontal page margins (%s)') % unitText, \
OptionDefaults.minPrintMargin, \
OptionDefaults.maxPrintMargin)
OptionDlgDbl(dlg, 'VertMargin', \
_('Vertical page margins (%s)') % unitText, \
OptionDefaults.minPrintMargin, \
OptionDefaults.maxPrintMargin)
if dlg.exec_loop() == QDialog.Accepted:
if self.printFont:
self.saveFontToOptions(self.printFont, 'Print')
pageSize = globalref.options.strData('PrintPageSize', False)
if pageSize != origPageSize:
self.printer.setPageSize(getattr(QPrinter, \
pageSize.capitalize()))
if self.headerDlg:
self.doc.undoStore.addFormatUndo(self.doc.treeFormats, \
self.doc.fileInfoItem.\
nodeFormat, {}, {})
self.doc.treeFormats.removeQuiet(self.doc.fileInfoItem.\
nodeFormat)
self.doc.fileInfoItem.nodeFormat = self.headerDlg.fileInfoFormat
self.doc.treeFormats.append(self.doc.fileInfoItem.nodeFormat)
self.doc.treeFormats.updateAllLineFields()
self.doc.modified = True
self.updateCmdAvail()
if units == 'centimeter':
for optionName in unitOptions:
value = globalref.options.numData(optionName) / 2.54
globalref.options.changeData(optionName, `value`, False)
globalref.options.writeChanges()
self.headerDlg = None
def setPrintFont(self):
"""Show dialog for setting custom printer font"""
printFont = self.getFontFromOptions('Print')
if not printFont:
outputViews = [view for view in self.dataOutSplit.children() if \
hasattr(view, 'updateView')]
printFont = outputViews[0].font()
font, ok = QFontDialog.getFont(printFont, self)
if ok and font != printFont:
self.printFont = font
def setHeader(self):
"""Show dialog for setting header and footer"""
if not self.headerDlg:
self.headerDlg = PrintHeaderDlg(self, None, True)
if not self.headerDlg.exec_loop() == QDialog.Accepted:
self.headerDlg = None
def editUndo(self):
"""Undo the previous action"""
self.doc.undoStore.undo(self.doc.redoStore)
if self.setTypeDlg and self.setTypeDlg.isVisible():
self.setTypeDlg.loadList()
self.loadTypeSubMenu()
def editRedo(self):
"""Redo the previous undo"""
self.doc.redoStore.undo(self.doc.undoStore)
if self.setTypeDlg and self.setTypeDlg.isVisible():
self.setTypeDlg.loadList()
self.loadTypeSubMenu()
def editCut(self):
"""Cut the branch or text to the clipboard"""
widget = self.focusWidgetWithAttr('copyAvail')
if self.treeView.hasFocus() or not widget or not widget.copyAvail():
self.editCopyTree()
self.editDelete()
else:
widget.cut()
def editCopyTree(self):
"""Copy the tree branch to the clipboard"""
if not self.doc.selection:
item = self.doc.selection.currentItem
elif len(self.doc.selection) > 1:
self.doc.treeFormats.addIfMissing(TreeMainWin.copyFormat)
item = TreeItem(None, TreeMainWin.copyFormat)
for node in self.doc.selection:
item.childList.append(copy.copy(node))
item.childList[-1].parent = item
else:
item = self.doc.selection[0]
clip = QApplication.clipboard()
if qVersion()[0] >= '3' and clip.supportsSelection():
clip.setSelectionMode(True)
textList = []
if len(self.doc.selection) > 1:
for node in self.doc.selection:
textList.extend(node.exportToText())
else:
textList = item.exportToText()
clip.setText(u'\n'.join(textList))
clip.setSelectionMode(False)
clip.setText(u'\n'.join(item.branchXml([TreeMainWin.copyFormat])))
self.doc.treeFormats.removeQuiet(TreeMainWin.copyFormat)
def editCopy(self):
"""Copy the branch or text to the clipboard"""
split = self.rightStack.visibleWidget()
if split == self.dataOutSplit: # check for select in dataOut (no focus)
views = [view for view in split.children() if \
hasattr(view, 'copyAvail') and view.copyAvail()]
if views:
views[0].copy()
return
widget = self.focusWidgetWithAttr('copyAvail')
if self.treeView.hasFocus() or not widget or not widget.copyAvail():
self.editCopyTree()
else:
widget.copy()
def editCopyText(self):
"""Copy node title text to the clipboard"""
if self.doc.selection:
titles = [item.title() for item in self.doc.selection]
else:
titles = [self.doc.selection.currentItem.title()]
clip = QApplication.clipboard()
if qVersion()[0] >= '3' and clip.supportsSelection():
clip.setSelectionMode(True)
clip.setText(u'\n'.join(titles))
clip.setSelectionMode(False)
clip.setText(u'\n'.join(titles))
def editPaste(self):
"""Paste items or text from the clipboard"""
try:
text = unicode(QApplication.clipboard().text())
except UnicodeError:
return
item = self.doc.readXmlString(text, self.treeView.hasFocus())
if self.treeView.hasFocus():
if item:
if item.data:
itemList = [item]
else: # blank item is dummy root of multi-select
itemList = item.childList
parent = self.doc.selection.currentItem
self.doc.undoStore.addChildListUndo(parent)
for node in itemList:
parent.addTree(node)
parent.open = True
self.doc.treeFormats.removeQuiet(TreeMainWin.copyFormat)
self.doc.selection.replace(itemList)
self.updateViews()
else:
print 'Error reading XML string'
else:
if item:
text = item.title()
widget = self.focusWidgetWithAttr('pasteText')
if widget:
widget.pasteText(text)
def editPasteText(self):
"""Paste text from the clipboard"""
try:
text = unicode(QApplication.clipboard().text())
except UnicodeError:
return
item = self.doc.readXmlString(text)
if item and item.data:
text = item.title()
elif item and item.childList:
text = item.childList[0].title()
else:
text = unicode(QApplication.clipboard().text()).\
split(u'\n', 1)[0].strip()
if self.treeView.hasFocus():
item = self.doc.selection.currentItem
self.doc.undoStore.addDataUndo(item)
item.setTitle(text)
self.doc.modified = True
self.doc.selection.replace([item])
self.treeView.updateTreeItem(item, True)
self.updateCmdAvail()
else:
widget = self.focusWidgetWithAttr('pasteText')
if widget:
widget.pasteText(text)
def editInBefore(self):
"""Insert new sibling before selection"""
sibling = self.doc.selection.currentItem
self.doc.undoStore.addChildListUndo(sibling.parent)
newItem = sibling.insertSibling()
if newItem:
if globalref.options.boolData('RenameNewNodes'):
self.doc.selection.replace([newItem])
self.updateViews()
self.treeView.editRename()
else:
self.updateViews()
def editInAfter(self):
"""Insert new sibling after selection"""
sibling = self.doc.selection.currentItem
self.doc.undoStore.addChildListUndo(sibling.parent)
newItem = sibling.insertSibling(inAfter=True)
if newItem:
if globalref.options.boolData('RenameNewNodes'):
self.doc.selection.replace([newItem])
self.updateViews()
self.treeView.editRename()
else:
self.updateViews()
def editAddChild(self):
"""Add a new child to the selected parent"""
parent = self.doc.selection.currentItem
self.doc.undoStore.addChildListUndo(parent)
newItem = parent.addChild()
if newItem:
parent.open = True
if globalref.options.boolData('RenameNewNodes'):
self.doc.selection.replace([newItem])
self.updateViews()
self.treeView.editRename()
else:
self.updateViews()
def editDelete(self):
"""Delete the selected items"""
nextSel = filter(None, [item.parent for item in \
self.doc.selection])
nextSel.extend(filter(None, [item.prevSibling() for item in \
self.doc.selection]))
nextSel.extend(filter(None, [item.nextSibling() for item in \
self.doc.selection]))
self.doc.undoStore.addParentListUndo(self.doc.selection)
for item in self.doc.selection:
item.delete()
while nextSel and nextSel[-1] in self.doc.selection:
del nextSel[-1]
if not nextSel:
nextSel = [self.doc.root]
self.doc.selection.replace([nextSel[-1]])
self.doc.selection.currentItem = nextSel[-1] # Reqd if only root remains
self.updateViews()
def editIndent(self):
"""Indent the selected items"""
self.doc.selection.sortList()
parentList = [item.parent for item in self.doc.selection]
siblingList = [item.prevSibling() for item in self.doc.selection]
self.doc.undoStore.addChildListUndo(parentList + siblingList)
for item in self.doc.selection:
if item.indent():
item.parent.open = True
self.updateViews()
def editUnindent(self):
"""Unindent the selected item"""
self.doc.selection.sortList()
parentList = [item.parent for item in self.doc.selection]
gpList = [item.parent for item in parentList]
self.doc.undoStore.addChildListUndo(parentList + gpList)
self.doc.selection.reverse()
for item in self.doc.selection:
item.unindent()
self.doc.selection.reverse()
self.updateViews()
def editMoveUp(self):
"""Move the selected item up"""
self.doc.selection.sortList()
self.doc.undoStore.addParentListUndo(self.doc.selection, True)
for item in self.doc.selection:
item.move(-1)
self.updateViews()
def editMoveDown(self):
"""Move the selected item down"""
self.doc.selection.sortList()
self.doc.selection.reverse()
self.doc.undoStore.addParentListUndo(self.doc.selection, True)
for item in self.doc.selection:
item.move(1)
self.doc.selection.reverse()
self.updateViews()
def viewToggleToolbar(self,index):
"""Toggle display of toolbar with index == itemId"""
if self.toolbars[index].isVisible():
self.toolbars[index].hide()
self.viewToolbarsMenu.setItemChecked(index, False)
else:
self.toolbars[index].show()
self.viewToolbarsMenu.setItemChecked(index, True)
def viewSelect(self, action):
"""Show right view given by action"""
if not self.blockViewChg:
# avoids multiple raise/updates
self.blockViewChg = True
self.rightStack.raiseWidget(self.actToView[action])
self.blockViewChg = False
def viewTabSelect(self, viewNum):
"""Show right view given by tab number"""
if not self.blockViewChg:
# avoids multiple raise/updates
self.blockViewChg = True
self.rightStack.raiseWidget(viewNum)
self.blockViewChg = False
def viewChildren(self, action):
"""Set to view item with or without children"""
self.showItemChildren = self.childActToBool[action]
for split in [self.dataOutSplit, self.dataEditSplit, \
self.titleListSplit]:
for view in split.children():
if hasattr(view, 'showChildren') and view.showChildren:
if self.showItemChildren:
view.show()
else:
view.hide()
split.refresh()
self.updateRightView()
def viewFocusTree(self):
"""Change focus to tree view"""
self.treeView.setFocus()
def viewDataPageUp(self):
"""Page up the right-hand view"""
split = self.rightStack.visibleWidget()
for view in split.children():
if hasattr(view, 'showChildren') and view.showChildren:
view.scrollPage(-1)
def viewDataPageDown(self):
"""Page down the right-hand view"""
split = self.rightStack.visibleWidget()
for view in split.children():
if hasattr(view, 'showChildren') and view.showChildren:
view.scrollPage(1)
def dataTypeChange(self, id):
"""Change type based on submenu selection"""
names = self.doc.treeFormats.nameList(True)
names.sort()
newFormat = self.doc.treeFormats.findFormat(names[id])
itemList = self.doc.selection
if not itemList:
itemList = [self.doc.selection.currentItem]
self.doc.undoStore.addTypeUndo(itemList)
for item in itemList:
item.changeType(newFormat)
self.doc.modified = True
self.updateViews()
def dataTypeMenuCheck(self):
"""Set check box in type menu to the current type"""
itemList = self.doc.selection
if not itemList:
itemList = [self.doc.selection.currentItem]
currentTypes = []
for item in itemList:
if item.nodeFormat.name not in currentTypes:
currentTypes.append(item.nodeFormat.name)
names = self.doc.treeFormats.nameList(True)
names.sort()
for index, name in enumerate(names):
id = self.typeSubMenu.idAt(index)
if id != -1:
self.typeSubMenu.setItemChecked(id, name in currentTypes)
def dataSet(self, show):
"""Show dialog for setting item data types"""
if show:
if not self.setTypeDlg:
self.setTypeDlg = TypeSetDlg(self)
self.connect(self.setTypeDlg, PYSIGNAL('viewClosed'), \
self.dataSetAct.setOn)
else:
self.setTypeDlg.loadList()
self.setTypeDlg.setCurrentSel()
self.setTypeDlg.updateDlg()
self.setTypeDlg.show()
else:
self.setTypeDlg.hide()
def dataConfig(self):
"""Show dialog for modifying data types"""
currentType = self.doc.selection.currentItem.nodeFormat.name
dlg = ConfigDlg(currentType, self, None, True)
if dlg.exec_loop() == QDialog.Accepted:
self.doc.undoStore.addFormatUndo(self.doc.treeFormats, \
self.doc.fileInfoItem.nodeFormat, \
dlg.fieldRenameDict, \
dlg.typeRenameDict)
self.doc.fileInfoItem.nodeFormat = dlg.fileInfoFormat
self.doc.treeFormats.update(dlg.treeFormats, dlg.typeRenameDict)
self.doc.treeFormats.updateDerivedTypes()
if dlg.fieldRenameDict:
self.doc.treeFormats.renameFields(dlg.fieldRenameDict)
self.doc.treeFormats.updateAllLineFields()
self.doc.treeFormats.updateAutoChoices()
self.doc.modified = True
self.updateViews()
if self.setTypeDlg and self.setTypeDlg.isVisible():
self.setTypeDlg.loadList()
self.loadTypeSubMenu()
def dataCopyTypes(self):
"""Copy the configuration from another TreeLine file"""
dfltDir, name = os.path.split(self.doc.fileName)
fileName = unicode(QFileDialog.getOpenFileName(dfltDir, \
TreeMainWin.tlGenFileFilter, self, None, \
_('Open Configuration File')))
password = ''
while fileName:
QApplication.setOverrideCursor(Qt.waitCursor)
try:
self.doc.treeFormats.configCopy(fileName, password)
self.loadTypeSubMenu()
QApplication.restoreOverrideCursor()
return
except PasswordError:
QApplication.restoreOverrideCursor()
dlg = PasswordEntry(False, self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
password = dlg.password
except (IOError, UnicodeError, ReadFileError):
QApplication.restoreOverrideCursor()
QMessageBox.warning(self, 'TreeLine', \
_('Error - could not read file "%s"') \
% fileName)
return
def dataSortChild(self):
"""Sort children of selected item with chosen keys"""
if not self.doc.selection:
return
children = []
for item in self.doc.selection:
children.extend(item.childList)
fieldList = self.doc.selection[0].commonFields(children)
if fieldList: # has common fields
dlg = FieldSelectDlg(fieldList, _('Sort Keys'), \
_('Select sort keys in order'), True, \
self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.sortFields = dlg.getSelList()
else: # no common fields
if QMessageBox.information(self, 'TreeLine', \
_('No common fields found\nSort by title?'), \
_('Yes'), _('No'), QString.null, 0, 1) == 1:
return
self.doc.sortFields = [('', 1)] # sort by title
self.doc.undoStore.addChildListUndo(self.doc.selection)
for item in self.doc.selection:
item.sortChildren()
self.updateViews()
def dataSortType(self):
"""Sort descendants of a type with keys"""
if not self.doc.selection:
return
typeList = []
for item in self.doc.selection:
for type in item.descendTypes():
if type.name not in typeList:
typeList.append(type.name)
if len(typeList) == 1:
selType = typeList[0]
else:
selType, ok = QInputDialog.getItem(_('Type to Sort'), \
_('Select descendant node type to sort'), \
QStringList.split(u' ', u' '.join(typeList)), \
0, 0, self)
if not ok:
return
selType = unicode(selType)
format = self.doc.treeFormats.findFormat(selType)
fieldList = format.fieldNames()
dlg = FieldSelectDlg(fieldList, _('Sort Keys'), \
_('Select sort keys in order'), True, self, \
None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.sortFields = dlg.getSelList()
undoList = []
for item in self.doc.selection:
undoList.extend([parent for parent in item.descendantGen() \
if parent.childList])
self.doc.undoStore.addChildListUndo(undoList)
for item in self.doc.selection:
item.sortType(format)
self.updateViews()
def dataSortBranch(self):
"""Sort all children of selected item by title only"""
dlg = RadioChoiceDlg(_('Branch Sort'), _('Choose Sort Direction'), \
[(_('&Ascending'), True), \
(_('&Descending'), False)], \
self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.sortFields = [('', dlg.getResult())]
undoList = []
for item in self.doc.selection:
undoList.extend([parent for parent in item.descendantGen() \
if parent.childList])
self.doc.undoStore.addChildListUndo(undoList)
for item in self.doc.selection:
item.sortBranch()
self.updateViews()
def dataFilter(self):
"""Create a new file with filtered contents"""
if not self.doc.selection:
return
typeList = []
for item in self.doc.selection:
for type in item.descendTypes():
if type.name not in typeList:
typeList.append(type.name)
caption = _('Filter Data')
if len(typeList) > 1:
typeList = QStringList.split(' ', ' '.join(typeList))
if sys.platform == 'win32':
caption += ' (PyQt)'
type, ok = QInputDialog.getItem(caption, _('Select data type'), \
typeList, 0, 0, self)
if not ok:
return
type = unicode(type)
else:
type = typeList[0]
format = self.doc.treeFormats.findFormat(type)
dlg = ConditionDlg(caption, format, self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
cond = dlg.conditional()
cond.setupFields(format)
if self.savePrompt():
self.doc.modified = False
for item in self.doc.selection:
item.filterDescendants(format, cond.evaluate)
if self.doc.modified:
dir, name = os.path.split(self.doc.fileName)
self.doc.fileName = os.path.splitext(self.doc.fileName)[0] \
+ _('_filter.trl', 'filter file suffix')
self.setMainCaption()
self.updateViews()
else:
self.statusBar().message(_('No nodes were filtered'), 4000)
def dataEditField(self):
"""Edit a child field in all selected nodes"""
if not self.doc.selection:
return
fieldList = self.doc.selection[0].commonFields(self.doc.selection)
if fieldList: # has common fields
dlg = EditFieldsDlg(fieldList, self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.undoStore.addDataUndo(self.doc.selection)
for item in self.doc.selection:
item.editFields(dlg.resultDict)
if globalref.pluginInterface:
fields = [item.nodeFormat.findField(name) for name in \
dlg.resultDict.keys()]
globalref.pluginInterface.execCallback(globalref.\
pluginInterface.\
dataChangeCallbacks, \
item, fields)
self.updateViews()
else:
QMessageBox.warning(self, 'TreeLine', \
_('No common fields to set'))
def dataNumbering(self):
"""Add numbering to a data field"""
item = self.doc.selection[0]
dlg = NumberingDlg(item.branchFields(), item.maxDescendLevel(), \
self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.undoStore.addBranchUndo(item)
if dlg.currentStyle == NumberingDlg.outlineType:
item.addNumbering(dlg.getField(), dlg.currentFormat, \
dlg.includeRoot(), 0, 0, dlg.startNumber())
elif dlg.currentStyle == NumberingDlg.sectionType:
item.addNumbering(dlg.getField(), dlg.currentFormat, \
dlg.includeRoot(), True, False, dlg.startNumber())
else: # singleType
item.addNumbering(dlg.getField(), dlg.currentFormat, \
False, False, True, dlg.startNumber())
self.updateViews()
def dataAddCat(self):
"""Add child's category items as a new child level"""
if not self.doc.selection:
return
children = []
for item in self.doc.selection:
children.extend(item.childList)
fieldList = self.doc.selection[0].commonFields(children)
if fieldList: # has common fields
dlg = FieldSelectDlg(fieldList, _('Category Fields'), \
_('Select fields for new level'), False, \
self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.undoStore.addBranchUndo(self.doc.selection)
for item in self.doc.selection:
item.addChildCat(dlg.getSelList())
self.updateViews()
else:
QMessageBox.warning(self, 'TreeLine', \
_('Cannot expand without common fields'))
def dataFlatCat(self):
"""Collapse data by merging fields"""
self.doc.undoStore.addBranchUndo(self.doc.selection)
for item in self.doc.selection:
item.flatChildCat()
self.updateViews()
def dataArrangeRef(self):
"""Arrange data using parent references"""
item = self.doc.selection[0]
fieldList = item.childList[0].nodeFormat.fieldNames()
refField, ok = QInputDialog.getItem(_('Reference Field'), \
_('Select field with parent references'), \
QStringList.split(u' ', u' '.join(fieldList)), \
0, 0, self)
if not ok:
return
self.doc.undoStore.addBranchUndo(item)
item.arrangeByRef(unicode(refField))
self.updateViews()
def dataFlatRef(self):
"""Collapse data after adding references to parents"""
item = self.doc.selection[0]
dlg = FieldEntry(_('Flatten by Reference'), \
_('Enter new field name for parent references:'), '', \
[], self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
self.doc.undoStore.addBranchUndo(item)
item.flatByRef(dlg.text)
self.updateViews()
def dataUpdate(self):
"""Update with new fields from reference file"""
dfltDir, name = os.path.split(self.doc.fileName)
fileName = unicode(QFileDialog.getOpenFileName(dfltDir, \
TreeMainWin.tlGenFileFilter, self, None, \
_('Open Reference File')))
password = ''
while fileName:
origDocRef = globalref.docRef
refDoc = TreeDoc()
if password:
refDoc.setPassword(fileName, password)
try:
refDoc.readFile(fileName)
except PasswordError:
globalref.docRef = origDocRef
dlg = PasswordEntry(False, self, None, True)
if dlg.exec_loop() != QDialog.Accepted:
return
password = dlg.password
except (IOError, UnicodeError, ReadFileError):
globalref.docRef = origDocRef
QMessageBox.warning(self, 'TreeLine', \
_('Error - could not read file "%s"') % \
fileName)
return
globalref.docRef = origDocRef
self.doc.undoStore.addBranchUndo(self.doc.root)
total = [0, 0, 0]
for item in self.doc.selection:
result = item.updateByRef(refDoc.root)
total = map(lambda x,y: x + y, total, result)
self.updateViews()
result = _('%(entries)d new entries in %(fields)d new fields in '\
'%(types)d changed types') % \
{'entries':total[0], 'fields':total[1], 'types':total[2]}
self.statusBar().message(result, 6000)
return
def toolsExpand(self):
"""Expand all children of selected item"""
for item in self.doc.selection:
item.openBranch(True)
self.updateViews()
def toolsCollapse(self):
"""Collapse all children of selected item"""
for item in self.doc.selection:
item.openBranch(False)
self.updateViews()
def toolsFind(self, show):
"""Find item matching text string"""
if show:
if not self.FindDlg:
self.FindDlg = FindTextEntry(self)
self.connect(self.FindDlg, PYSIGNAL('viewClosed'), \
self.toolsFindAct.setOn)
self.FindDlg.entry.selectAll()
self.FindDlg.show()
else:
self.FindDlg.hide()
def toolsSpellCheck(self):
"""Spell check the tree's text data strting in the selected branch"""
spellPath = globalref.options.strData('SpellCheckPath', True)
try:
spCheck = SpellCheck(spellPath, self.doc.spellChkLang)
except SpellCheckError:
if sys.platform == 'win32':
ans = QMessageBox.warning(self, \
_('TreeLine Spell Check Error'), \
_('Could not find either aspell.exe or ispell.exe\n'\
'Manually locate?'), _('&Browse'), _('&Cancel'), \
QString.null, 0, 1)
if ans != 0:
return
path = unicode(QFileDialog.getOpenFileName(QString.null, \
_('Program (*.exe)'), self, '', \
_('Locate aspell.exe or ipsell.exe')))
if path:
path = path[:-4]
if ' ' in path:
path = '"%s"' % path
globalref.options.changeData('SpellCheckPath', \
path.encode(TreeDoc.localEncoding), \
True)
globalref.options.writeChanges()
self.toolsSpellCheck()
return
else:
QMessageBox.warning(self, 'TreeLine', \
_('TreeLine Spell Check Error\n'\
'Make sure aspell or ispell is installed'))
return
dlg = SpellCheckDlg(spCheck, self, None, True)
startItem = self.doc.selection.currentItem
for item in startItem.descendantGen():
for field in item.nodeFormat.fieldNames():
text = item.data.get(field, '')
if text:
textLines = text.split('\n')
for i, line in enumerate(textLines):
tmpIgnoreList = []
results = spCheck.checkLine(line)
while results:
globalref.docRef.selection.changeSearchOpen([item])
dlg.setWord(line, results)
if not dlg.newLine and \
dlg.exec_loop() != QDialog.Accepted:
spCheck.close()
return
if dlg.ignoreWord:
tmpIgnoreList.append(dlg.ignoreWord)
if dlg.newLine:
self.doc.undoStore.addDataUndo(item, True)
line = dlg.newLine
textLines[i] = line
item.data[field] = '\n'.join(textLines)
self.doc.modified = True
self.updateViews()
results = spCheck.checkLine(line, tmpIgnoreList)
spCheck.close()
if startItem.parent:
ans = QMessageBox.information(self, _('TreeLine Spell Check'), \
_('Finished checking the branch\n'\
'Continue from the root branch?'), \
_('&Yes'), _('&No'), QString.null, \
0, 1)
if ans == 0:
globalref.docRef.selection.changeSearchOpen([self.doc.root])
self.toolsSpellCheck()
else:
QMessageBox.information(self, _('TreeLine Spell Check'), \
_('Finished checking the branch'))
globalref.docRef.selection.changeSearchOpen([startItem])
def toolsRemXslt(self):
"""Delete reference to XSLT export"""
if self.doc.xlstLink:
self.doc.undoStore.addParamUndo([(self.doc, 'xlstLink')])
self.doc.xlstLink = ''
self.doc.modified = True
self.updateCmdAvail()
def toolsGenOpt(self):
"""Set user preferences for all files"""
self.treeFont = self.treeView.font()
outputViews = [view for view in self.dataOutSplit.children() if \
hasattr(view, 'updateView')]
self.outputFont = outputViews[0].font()
editViews = self.dataEditSplit.children() + \
self.titleListSplit.children()
editViews = [view for view in editViews if hasattr(view, 'updateView')]
self.editFont = editViews[0].font()
oldAutoSave = globalref.options.intData('AutoSaveMinutes', 0, 999)
dlg = OptionDlg(globalref.options, self, None, True)
dlg.setIcon(globalref.treeIcons.getIcon('treeline'))
caption = _('General Options')
if sys.platform == 'win32':
caption += ' (PyQt)'
dlg.setCaption(caption)
dlg.startGroupBox(_('Startup Condition'))
OptionDlgBool(dlg, 'AutoFileOpen', \
_('Automatically open last file used'))
OptionDlgBool(dlg, 'StartShowChildren', \
_('Show children in right-hand view'))
OptionDlgBool(dlg, 'PersistTreeState', \
_('Restore view states of recent files'))
OptionDlgBool(dlg, 'SaveWindowGeom', \
_('Restore window geometry from last exit'))
dlg.startGroupBox(_('Features Available'))
OptionDlgBool(dlg, 'ClickRename', _('Click item to rename'))
OptionDlgBool(dlg, 'DragTree', _('Tree drag && drop available'))
OptionDlgBool(dlg, 'InsertOnEnter', _('Insert node with enter'))
OptionDlgBool(dlg, 'RenameNewNodes', _('Rename new nodes when created'))
OptionDlgBool(dlg, 'OpenSearchNodes', \
_('Automatically open search nodes'))
OptionDlgBool(dlg, 'ShowTreeIcons', _('Show icons in the tree view'))
OptionDlgBool(dlg, 'EnableExecLinks', _('Enable executable links'))
dlg.startGroupBox(_('New Objects'))
OptionDlgBool(dlg, 'CompressNewFiles', \
_('Set new files to compressed by default'))
OptionDlgBool(dlg, 'EncryptNewFiles', \
_('Set new files to encrypted by default'))
OptionDlgBool(dlg, 'HtmlNewFields', \
_('New fields default to HTML content'))
dlg.endGroupBox()
OptionDlgRadio(dlg, 'PrintUnits', _('Printing Units'), \
[('inch', _('Inches')), \
('centimeter', _('Centimeters'))])
dlg.startNewColumn()
dlg.startGroupBox(_('Undo Memory'))
OptionDlgInt(dlg, 'UndoLevels', '%s ' % _('Number of undo levels'), \
0, 99)
dlg.startGroupBox(_('Auto Save'))
OptionDlgInt(dlg, 'AutoSaveMinutes', \
'%s \n(%s)' % \
(_('Minutes between saves'), _('Set to 0 to disable')), \
0, 999)
dlg.startGroupBox(_('Recent Files'))
OptionDlgInt(dlg, 'RecentFiles', \
'%s \n%s' % \
(_('Number of recent files'), _('in the File menu')), \
0, 99)
dlg.startGroupBox(_('Data Editor Formats'), 12)
OptionDlgStr(dlg, 'EditDateFormat', _('Dates'))
OptionDlgStr(dlg, 'EditTimeFormat', _('Times'))
dlg.startGroupBox(_('Appearance'), 10)
OptionDlgInt(dlg, 'IndentOffset', _('Child indent offset (points)'), \
0, TreeView.maxOffset)
OptionDlgInt(dlg, 'MaxEditLines', _('Default max data editor lines'), \
1, OptionDefaults.maxNumLines)
OptionDlgPush(dlg, _('Set Tree Font'), self.setTreeFont)
OptionDlgPush(dlg, _('Set Data Output Font'), self.setOutputFont)
OptionDlgPush(dlg, _('Set Editor Font'), self.setEditFont)
dlg.startNewColumn() # add space if necessary
if dlg.exec_loop() == QDialog.Accepted:
if self.treeFont != self.treeView.font():
self.treeView.setFont(self.treeFont)
self.saveFontToOptions(self.treeFont, 'Tree')
if self.outputFont != outputViews[0].font():
for view in outputViews:
view.setFont(self.outputFont)
self.saveFontToOptions(self.outputFont, 'Output')
if self.editFont != editViews[0].font():
for view in editViews:
view.setFont(self.editFont)
self.saveFontToOptions(self.editFont, 'Editor')
if not globalref.options.boolData('PersistTreeState'):
self.treeStates.clear()
globalref.options.writeChanges()
if oldAutoSave != globalref.options.intData('AutoSaveMinutes', \
0, 999):
self.resetAutoSave()
self.doc.undoStore.levels = globalref.options.\
intData('UndoLevels', 0, 99)
self.doc.redoStore.levels = globalref.options.\
intData('UndoLevels', 0, 99)
self.recentFiles.changeNumEntries(globalref.options.\
intData('RecentFiles', 0, 99))
self.treeView.setTreeStepSize(globalref.options.\
intData('IndentOffset', \
0, TreeView.maxOffset))
self.updateViews()
def setTreeFont(self):
"""Show dialog for setting custom tree font"""
font, ok = QFontDialog.getFont(self.treeFont, self)
if ok:
self.treeFont = font
def setOutputFont(self):
"""Show dialog for setting custom output font"""
font, ok = QFontDialog.getFont(self.outputFont, self)
if ok:
self.outputFont = font
def setEditFont(self):
"""Show dialog for setting custom editor font"""
font, ok = QFontDialog.getFont(self.editFont, self)
if ok:
self.editFont = font
def toolsFileOpt(self):
"""Set file preferences"""
globalref.options.addData('SpaceBetween', \
self.doc.spaceBetween and 'yes' or 'no', False)
globalref.options.addData('LineBreaks', \
self.doc.lineBreaks and 'yes' or 'no', False)
globalref.options.addData('FormHtml', \
self.doc.formHtml and 'yes' or 'no', False)
globalref.options.addData('CompressFile', \
self.doc.compressFile and 'yes' or 'no', False)
globalref.options.addData('EncryptFile', \
self.doc.encryptFile and 'yes' or 'no', False)
globalref.options.addData('ChildFieldSep', self.doc.childFieldSep, \
False)
globalref.options.addData('SpellChkLang', self.doc.spellChkLang, False)
dlg = OptionDlg(globalref.options, self, None, True)
dlg.setIcon(globalref.treeIcons.getIcon('treeline'))
caption = _('File Options')
if sys.platform == 'win32':
caption += ' (PyQt)'
dlg.setCaption(caption)
dlg.startGroupBox(_('Output Formating'))
OptionDlgBool(dlg, 'SpaceBetween', _('Add blank lines between nodes'), \
False)
OptionDlgBool(dlg, 'LineBreaks', _('Add line breaks after each line'), \
False)
OptionDlgBool(dlg, 'FormHtml', \
_('Allow HTML rich text in formats'), False)
dlg.startGroupBox(_('File Storage'))
OptionDlgBool(dlg, 'CompressFile', _('Use file compression'), False)
OptionDlgBool(dlg, 'EncryptFile', _('Use file encryption'), False)
dlg.startGroupBox(_('Embedded Child Fields'))
OptionDlgStr(dlg, 'ChildFieldSep', _('Separator String'), False)
dlg.startGroupBox(_('Spell Check Language'))
OptionDlgStr(dlg, 'SpellChkLang', \
'%s\n%s' % (_('2-letter code (blank'), \
_('for system default)')), \
False)
if dlg.exec_loop() == QDialog.Accepted:
space = globalref.options.boolData('SpaceBetween')
breaks = globalref.options.boolData('LineBreaks')
html = globalref.options.boolData('FormHtml')
compress = globalref.options.boolData('CompressFile')
encrypt = globalref.options.boolData('EncryptFile')
childFieldSep = globalref.options.strData('ChildFieldSep', True)
spellChkLang = globalref.options.strData('SpellChkLang', True)
if space != self.doc.spaceBetween or \
breaks != self.doc.lineBreaks or \
html != self.doc.formHtml or \
compress != self.doc.compressFile or \
encrypt != self.doc.encryptFile or \
childFieldSep != self.doc.childFieldSep or \
spellChkLang != self.doc.spellChkLang:
self.doc.undoStore.addParamUndo([(self.doc, 'spaceBetween'), \
(self.doc, 'lineBreaks'), \
(self.doc, 'formHtml'), \
(self.doc, 'compressFile'), \
(self.doc, 'encryptFile'), \
(self.doc, 'childFieldSep')])
self.doc.spaceBetween = space
self.doc.lineBreaks = breaks
self.doc.formHtml = html
self.doc.compressFile = compress
self.doc.encryptFile = encrypt
self.doc.childFieldSep = childFieldSep
self.doc.spellChkLang = spellChkLang
self.doc.modified = True
self.updateViews()
self.updateCmdAvail()
def toolsBkColor(self):
"""Set view background color"""
background = QColor(globalref.options.intData('BackgroundR', 0, 255), \
globalref.options.intData('BackgroundG', 0, 255), \
globalref.options.intData('BackgroundB', 0, 255))
newColor = QColorDialog.getColor(background, self)
if newColor.isValid() and newColor != background:
globalref.options.changeData('BackgroundR', `newColor.red()`, True)
globalref.options.changeData('BackgroundG', `newColor.green()`, \
True)
globalref.options.changeData('BackgroundB', `newColor.blue()`, True)
globalref.options.writeChanges()
self.updateColors()
def toolsTxtColor(self):
"""Set view text color"""
foreground = QColor(globalref.options.intData('ForegroundR', 0, 255), \
globalref.options.intData('ForegroundG', 0, 255), \
globalref.options.intData('ForegroundB', 0, 255))
newColor = QColorDialog.getColor(foreground, self)
if newColor.isValid() and newColor != foreground:
globalref.options.changeData('ForegroundR', `newColor.red()`, True)
globalref.options.changeData('ForegroundG', `newColor.green()`, \
True)
globalref.options.changeData('ForegroundB', `newColor.blue()`, True)
globalref.options.writeChanges()
self.updateColors()
def helpContents(self):
"""View the Using section of the ReadMe file"""
self.helpReadMe()
self.helpView.scrollToAnchor('using')
def findHelpPath(self):
"""Return the path of the help files or ''"""
modPath = os.path.abspath(sys.path[0])
pathList = [helpFilePath, os.path.join(modPath, '../doc/'), \
modPath, 'doc/']
for path in filter(None, pathList):
try:
f = codecs.open(os.path.join(path, 'README.html'), \
'r', 'utf-8')
f.close()
return path
except IOError:
pass
return ''
def helpReadMe(self):
"""View the ReadMe file"""
if not self.helpView:
fileList = ['README.html']
if globalref.lang and globalref.lang != 'C':
fileList[0:0] = ['README_%s.html' % globalref.lang, \
'README_%s.html' % globalref.lang[:2]]
path = self.findHelpPath()
lineList = []
for fileName in fileList:
try:
f = codecs.open(os.path.join(path, fileName), 'r', 'utf-8')
lineList = f.readlines()
f.close()
break
except IOError:
pass
if not lineList:
self.statusBar().message(_('Read Me file not found'), 4000)
return
self.helpView = HelpView('\n'.join(lineList), path, \
_('TreeLine README File'), self.toolIcons)
self.helpView.setIcon(globalref.treeIcons.getIcon('treeline'))
self.helpView.show()
self.helpView.textView.home()
def helpAbout(self):
"""About this program"""
QMessageBox.about(self, 'TreeLine', \
_('TreeLine, Version %(ver)s\n by %(author)s') % \
{'ver':__version__, 'author':__author__})
def helpPlugin(self):
"""Show loaded plugin modules"""
dlg = PluginListDlg(self.pluginDescript, self, None, True)
dlg.exec_loop()
def savePrompt(self):
"""Ask for save if doc modified, return false on cancel"""
if self.doc:
if self.doc.modified:
text = self.fileImported and _('Save changes?') or \
_('Save changes to "%s"?') % self.doc.fileName
ans = QMessageBox.information(self, 'TreeLine', text, \
_('&Yes'), _('&No'), \
_('&Cancel'), 0, 2)
if ans == 0:
self.fileSave()
elif ans == 2:
return False
else:
self.delAutoSaveFile()
if globalref.options.boolData('PersistTreeState'):
self.treeStates.saveCurrent(self.treeView)
return True
def closeEvent(self, event):
"""Ask for save if doc modified"""
if self.savePrompt():
if globalref.options.boolData('SaveWindowGeom'):
docks = ''
for toolbar in self.toolbars:
params = self.getLocation(toolbar)
docks = docks + ',[' + str(int(toolbar.isVisible())) \
+ ',' + str(params[1]) + ',' + str(params[2]) \
+ ',' + str(int(params[3])) + ',' + str(params[4]) \
+ ']'
globalref.options.changeData('ToolbarData', docks[1:], True)
globalref.options.changeData('WindowXSize', self.width(), True)
globalref.options.changeData('WindowYSize', self.height(), True)
globalref.options.changeData('WindowXPos', self.x(), True)
globalref.options.changeData('WindowYPos', self.y(), True)
treeWidth = self.treeView.width()
rightWidth = self.rightStack.width()
treePercent = int(round(treeWidth * 100.0 / \
(treeWidth + rightWidth)))
globalref.options.changeData('TreeSplitPercent', treePercent, \
True)
mainHeight, childHeight = self.dataOutSplit.sizes()
outPercent = int(round(mainHeight * 100.0 / \
(mainHeight + childHeight)))
globalref.options.changeData('OutputSplitPercent', \
outPercent, True)
mainHeight, childHeight = self.dataEditSplit.sizes()
editPercent = int(round(mainHeight * 100.0 / \
(mainHeight + childHeight)))
globalref.options.changeData('EditorSplitPercent', \
editPercent, True)
mainHeight, childHeight = self.titleListSplit.sizes()
titlePercent = int(round(mainHeight * 100.0 / \
(mainHeight + childHeight)))
globalref.options.changeData('TitleSplitPercent', \
titlePercent, True)
tabId = self.rightStack.id(self.rightStack.visibleWidget())
tabNum = self.tabIds.index(tabId)
globalref.options.changeData('ActiveRightView', tabNum, True)
globalref.options.writeChanges()
event.accept()
else:
event.ignore()
def dragEnterEvent(self, event):
"""Accept drags of files to main window"""
event.accept(QUriDrag.canDecode(event))
def dropEvent(self, event):
"""Drop a file onto window"""
fileList = QStringList()
if QUriDrag.decodeLocalFiles(event, fileList):
if self.savePrompt() and fileList:
self.openFile(unicode(fileList[0]))
def loadTypeSubMenu(self):
"""Update type select submenu with current type names"""
names = self.doc.treeFormats.nameList(True)
names.sort()
self.typeSubMenu.clear()
id = 0
usedShortcuts = []
for name in names:
shortcutPos = 0
try:
while name[shortcutPos] in usedShortcuts:
shortcutPos += 1
usedShortcuts.append(name[shortcutPos])
text = u'%s&%s' % (name[:shortcutPos], name[shortcutPos:])
except IndexError:
text = name
self.typeSubMenu.insertItem(text, id)
id += 1
def accelKey(self, cmdName):
"""Return shortcut key from options, or zero"""
keyList = globalref.options.strData(cmdName, True).split()
if not keyList:
return 0
if qVersion()[0] >= '3':
return QKeySequence('+'.join(keyList))
return QAccel.stringToKey('+'.join([self.optionDefaults.\
revKeyTranslations.get(key, key) \
for key in keyList]))
def accelKeyText(self, cmdName):
"""Return shortcut key text from options, or ''"""
key = self.accelKey(cmdName)
if key:
if qVersion()[0] >= '3':
return unicode(QString(key))
else:
return unicode(QAccel.keyToString(key))
return ''
def restoreToolbars(self):
"""Place toolbars using stored param data"""
data = globalref.options.strData('ToolbarData', False)
params = []
for item in data.split('],['):
params.append([int(x) for x in item.strip('[]').split(',')])
for i, toolbar, param in zip(range(len(self.toolbars)), \
self.toolbars, params):
dock = int(param[1])
if hasattr(Qt, 'Dock'):
dock = Qt.Dock(dock) # necessary for sip >= 4.2
if qVersion()[0] >= '3':
self.moveDockWindow(toolbar, dock, int(param[3]), \
int(param[2]), int(param[4]))
else:
self.moveToolBar(toolbar, dock, int(param[3]), \
int(param[2]), int(param[4]))
self.viewToolbarsMenu.setItemChecked(i, param[0])
if param[0] == 0:
toolbar.hide()
def setupMenus(self):
"""Add menu and toolbar items"""
fileMenu = QPopupMenu(self)
self.menuBar().insertItem(_('&File'), fileMenu)
self.toolbars = [QToolBar(self), QToolBar(self)]
self.parentPopup = QPopupMenu(self.treeView)
self.childPopup = QPopupMenu(self.treeView)
self.fullActGrp = QActionGroup(self)
fileNewAct = QAction(_('New File'), \
QIconSet(self.toolIcons.getIcon('filenew')), \
_('&New'), self.accelKey('FileNew'), \
self.fullActGrp)
fileNewAct.setStatusTip(_('Start a new file'))
fileNewAct.addTo(fileMenu)
fileNewAct.addTo(self.toolbars[0])
self.connect(fileNewAct, SIGNAL('activated()'), self.fileNew)
fileOpenAct = QAction(_('Open File'), \
QIconSet(self.toolIcons.getIcon('fileopen')), \
_('&Open...'), self.accelKey('FileOpen'), \
self.fullActGrp)
fileOpenAct.setStatusTip(_('Open a file from disk'))
fileOpenAct.addTo(fileMenu)
fileOpenAct.addTo(self.toolbars[0])
self.connect(fileOpenAct, SIGNAL('activated()'), self.fileOpen)
fileOpenSampleAct = QAction(_('Open Sample File'), \
_('Open Sa&mple...'), \
self.accelKey('FileOpenSample'), \
self.fullActGrp)
fileOpenSampleAct.setStatusTip(_('Open a sample template file'))
fileOpenSampleAct.addTo(fileMenu)
self.connect(fileOpenSampleAct, SIGNAL('activated()'), \
self.fileOpenSample)
fileMenu.insertSeparator()
self.fileSaveAct = QAction(_('Save File'), \
QIconSet(self.toolIcons.getIcon('filesave')), \
_('&Save'), self.accelKey('FileSave'), \
self.fullActGrp)
self.fileSaveAct.setStatusTip(_('Save changes to the current file'))
self.fileSaveAct.addTo(fileMenu)
self.fileSaveAct.addTo(self.toolbars[0])
self.connect(self.fileSaveAct, SIGNAL('activated()'), self.fileSave)
fileSaveAsAct = QAction(_('Save As'), \
QIconSet(self.toolIcons.getIcon('filesaveas')), \
_('Save &As...'), self.accelKey('FileSaveAs'), \
self.fullActGrp)
fileSaveAsAct.setStatusTip(_('Save the file with a new name'))
fileSaveAsAct.addTo(fileMenu)
self.connect(fileSaveAsAct, SIGNAL('activated()'), self.fileSaveAs)
fileExportAct = QAction(_('Export'), _('&Export...'), \
self.accelKey('FileExport'), self.fullActGrp)
fileExportAct.setStatusTip(_('Export the file as html, as a table '\
'or as text'))
fileExportAct.addTo(fileMenu)
self.connect(fileExportAct, SIGNAL('activated()'), self.fileExport)
fileMenu.insertSeparator()
self.toolbars[0].addSeparator()
filePrintAct = QAction(_('Print File'), \
QIconSet(self.toolIcons.getIcon('fileprint')), \
_('&Print...'), self.accelKey('FilePrint'), \
self.fullActGrp)
filePrintAct.setStatusTip(_('Print starting at the selected node'))
filePrintAct.addTo(fileMenu)
filePrintAct.addTo(self.toolbars[0])
self.connect(filePrintAct, SIGNAL('activated()'), self.filePrint)
filePrintOptAct = QAction(_('Print Options'), _('P&rint Options...'), \
self.accelKey('FilePrintOpt'), \
self.fullActGrp)
filePrintOptAct.setStatusTip(_('Set margins and page size for '\
'printing'))
filePrintOptAct.addTo(fileMenu)
self.connect(filePrintOptAct, SIGNAL('activated()'), self.filePrintOpt)
fileMenu.insertSeparator()
numRecent = globalref.options.intData('RecentFiles', 0, 99)
# start with default numEntries
self.recentFiles = RecentFiles(globalref.options, 4, 30, self)
self.treeStates = TreeStates(self.recentFiles.fileList())
self.connect(self.recentFiles, PYSIGNAL('listChanged'), \
self.treeStates.fileListChanged)
self.connect(self.recentFiles, PYSIGNAL('askOpen'), self.recentOpen)
self.recentFiles.initMenu(fileMenu, 100)
# adjust numEntries and add option keys
self.recentFiles.changeNumEntries(numRecent)
fileMenu.insertSeparator()
self.toolbars[0].addSeparator()
fileQuitAct = QAction(_('Quit'), \
QIconSet(self.toolIcons.getIcon('filequit')), \
_('&Quit'), self.accelKey('FileQuit'), \
self.fullActGrp)
fileQuitAct.setStatusTip(_('Exit the application'))
fileQuitAct.addTo(fileMenu)
self.connect(fileQuitAct, SIGNAL('activated()'), self.close)
editMenu = QPopupMenu(self)
self.menuBar().insertItem(_('&Edit'), editMenu)
self.editUndoAct = QAction(_('Undo'), \
QIconSet(self.toolIcons.getIcon('editundo')), \
_('&Undo'), self.accelKey('EditUndo'), \
self.fullActGrp)
self.editUndoAct.setStatusTip(_('Undo the previous action'))
self.editUndoAct.addTo(editMenu)
self.connect(self.editUndoAct, SIGNAL('activated()'), self.editUndo)
self.editRedoAct = QAction(_('Redo'), \
QIconSet(self.toolIcons.getIcon('editredo')), \
_('&Redo'), self.accelKey('EditRedo'), \
self.fullActGrp)
self.editRedoAct.setStatusTip(_('Redo the previous undo'))
self.editRedoAct.addTo(editMenu)
self.connect(self.editRedoAct, SIGNAL('activated()'), self.editRedo)
editMenu.insertSeparator()
editCutAct = QAction(_('Cut'), \
QIconSet(self.toolIcons.getIcon('editcut')), \
_('Cu&t'), self.accelKey('EditCut'), \
self.fullActGrp)
editCutAct.setStatusTip(_('Cut the branch or text to the clipboard'))
editCutAct.addTo(editMenu)
editCutAct.addTo(self.toolbars[0])
editCutAct.addTo(self.parentPopup)
editCutAct.addTo(self.childPopup)
self.connect(editCutAct, SIGNAL('activated()'), self.editCut)
editCopyAct = QAction(_('Copy'), \
QIconSet(self.toolIcons.getIcon('editcopy')), \
_('&Copy'), self.accelKey('EditCopy'), \
self.fullActGrp)
editCopyAct.setStatusTip(_('Copy the branch or text to the clipboard'))
editCopyAct.addTo(editMenu)
editCopyAct.addTo(self.toolbars[0])
editCopyAct.addTo(self.parentPopup)
editCopyAct.addTo(self.childPopup)
self.connect(editCopyAct, SIGNAL('activated()'), self.editCopy)
editCopyTextAct = QAction(_('Copy Title Text'), _('Cop&y Title Text'), \
self.accelKey('EditCopyText'), \
self.fullActGrp)
editCopyTextAct.setStatusTip(_('Copy node title text to the clipboard'))
editCopyTextAct.addTo(editMenu)
self.connect(editCopyTextAct, SIGNAL('activated()'), self.editCopyText)
self.editPasteActGrp = QActionGroup(self)
editPasteAct = QAction(_('Paste'), \
QIconSet(self.toolIcons.getIcon('editpaste')), \
_('&Paste'), self.accelKey('EditPaste'), \
self.editPasteActGrp)
editPasteAct.setStatusTip(_('Paste nodes or text from the clipboard'))
editPasteAct.addTo(editMenu)
editPasteAct.addTo(self.toolbars[0])
editPasteAct.addTo(self.parentPopup)
editPasteAct.addTo(self.childPopup)
self.connect(editPasteAct, SIGNAL('activated()'), self.editPaste)
editPasteTextAct = QAction(_('PasteText'), _('Pa&ste Node Text'), \
self.accelKey('EditPasteText'), \
self.editPasteActGrp)
editPasteTextAct.setStatusTip(_('Paste text from the clipboard'))
editPasteTextAct.addTo(editMenu)
self.connect(editPasteTextAct, SIGNAL('activated()'), \
self.editPasteText)
editRenameAct = QAction(_('Rename'), _('Re&name'), \
self.accelKey('EditRename'), self.fullActGrp)
editRenameAct.setStatusTip(_('Rename the current tree entry'))
editRenameAct.addTo(editMenu)
editRenameAct.addTo(self.parentPopup)
editRenameAct.addTo(self.childPopup)
self.connect(editRenameAct, SIGNAL('activated()'), \
self.treeView.editRename)
editMenu.insertSeparator()
self.parentPopup.insertSeparator()
self.childPopup.insertSeparator()
self.editInsertActGrp = QActionGroup(self)
editInBeforeAct = QAction(_('Insert Sibling Before'), \
QIconSet(self.toolIcons.getIcon('editinsertbefore')), \
_('Insert Sibling &Before'), \
self.accelKey('EditInBefore'), \
self.editInsertActGrp)
editInBeforeAct.setStatusTip(_('Insert new sibling before selection'))
editInBeforeAct.addTo(editMenu)
self.connect(editInBeforeAct, SIGNAL('activated()'), self.editInBefore)
editInAfterAct = QAction(_('Insert Sibling After'), \
QIconSet(self.toolIcons.getIcon('editinsertafter')), \
_('Insert Sibling &After'), \
self.accelKey('EditInAfter'), \
self.editInsertActGrp)
editInAfterAct.setStatusTip(_('Insert new sibling after selection'))
editInAfterAct.addTo(editMenu)
editInAfterAct.addTo(self.toolbars[1])
editInAfterAct.addTo(self.parentPopup)
editInAfterAct.addTo(self.childPopup)
self.connect(editInAfterAct, SIGNAL('activated()'), self.editInAfter)
editAddChildAct = QAction(_('Add Child'), \
QIconSet(self.toolIcons.getIcon('editadd')), \
_('Add C&hild'), \
self.accelKey('EditAddChild'), \
self.fullActGrp)
editAddChildAct.setStatusTip(_('Add a new child to the selected '\
'parent'))
editAddChildAct.addTo(editMenu)
editAddChildAct.addTo(self.toolbars[1])
editAddChildAct.addTo(self.parentPopup)
editAddChildAct.addTo(self.childPopup)
self.connect(editAddChildAct, SIGNAL('activated()'), self.editAddChild)
editMenu.insertSeparator()
self.toolbars[1].addSeparator()
self.parentPopup.insertSeparator()
self.childPopup.insertSeparator()
self.editDeleteAct = QAction(_('Delete Nodes'), \
QIconSet(self.toolIcons.getIcon('editdel')), \
_('&Delete Node'), \
self.accelKey('EditDelete'), \
self.fullActGrp)
self.editDeleteAct.setStatusTip(_('Delete the selected nodes'))
self.editDeleteAct.addTo(editMenu)
self.editDeleteAct.addTo(self.toolbars[1])
self.editDeleteAct.addTo(self.parentPopup)
self.editDeleteAct.addTo(self.childPopup)
self.connect(self.editDeleteAct, SIGNAL('activated()'), self.editDelete)
self.editPrevSibActGrp = QActionGroup(self)
editIndentAct = QAction(_('Indent Nodes'), \
QIconSet(self.toolIcons.getIcon('editindent')), \
_('&Indent Node'), self.accelKey('EditIndent'), \
self.editPrevSibActGrp)
editIndentAct.setStatusTip(_('Indent the selected nodes'))
editIndentAct.addTo(editMenu)
editIndentAct.addTo(self.toolbars[1])
editIndentAct.addTo(self.parentPopup)
editIndentAct.addTo(self.childPopup)
self.connect(editIndentAct, SIGNAL('activated()'), self.editIndent)
self.editUnindentAct = QAction(_('Unindent Nodes'), \
QIconSet(self.toolIcons.getIcon('editunindent')), \
_('Unind&ent Node'), \
self.accelKey('EditUnindent'), \
self.fullActGrp)
self.editUnindentAct.setStatusTip(_('Unindent the selected nodes'))
self.editUnindentAct.addTo(editMenu)
self.editUnindentAct.addTo(self.toolbars[1])
self.editUnindentAct.addTo(self.parentPopup)
self.editUnindentAct.addTo(self.childPopup)
self.connect(self.editUnindentAct, SIGNAL('activated()'), \
self.editUnindent)
editMenu.insertSeparator()
self.toolbars[1].addSeparator()
self.parentPopup.insertSeparator()
self.childPopup.insertSeparator()
editMoveUpAct = QAction(_('Move Up'), \
QIconSet(self.toolIcons.getIcon('editmoveup')), \
_('&Move Up'), self.accelKey('EditMoveUp'), \
self.editPrevSibActGrp)
editMoveUpAct.setStatusTip(_('Move the selected nodes up'))
editMoveUpAct.addTo(editMenu)
editMoveUpAct.addTo(self.toolbars[1])
editMoveUpAct.addTo(self.parentPopup)
editMoveUpAct.addTo(self.childPopup)
self.connect(editMoveUpAct, SIGNAL('activated()'), self.editMoveUp)
self.editMoveDownAct = QAction(_('Move Down'), \
QIconSet(self.toolIcons.getIcon('editmovedown')), \
_('M&ove Down'), \
self.accelKey('EditMoveDown'), \
self.fullActGrp)
self.editMoveDownAct.setStatusTip(_('Move the selected nodes down'))
self.editMoveDownAct.addTo(editMenu)
self.editMoveDownAct.addTo(self.toolbars[1])
self.editMoveDownAct.addTo(self.parentPopup)
self.editMoveDownAct.addTo(self.childPopup)
self.connect(self.editMoveDownAct, SIGNAL('activated()'), \
self.editMoveDown)
self.toolbars[1].addSeparator()
self.parentPopup.insertSeparator()
self.childPopup.insertSeparator()
viewMenu = QPopupMenu(self)
self.menuBar().insertItem(_('&View'), viewMenu)
self.viewToolbarsMenu = QPopupMenu(viewMenu)
self.viewToolbarsMenu.setCheckable(True)
self.viewToolbarsMenu.insertItem(_('&Standard'), 0)
self.viewToolbarsMenu.insertItem(_('&Nodes'), 1)
viewMenu.insertItem(_('&Toolbars'), self.viewToolbarsMenu)
self.connect(self.viewToolbarsMenu, SIGNAL('activated(int)'), \
self.viewToggleToolbar)
viewMenu.insertSeparator()
viewRightViewGrp = QActionGroup(self)
viewOutAct = QAction(_('Show Data Output'), _('Show &Data Output'), \
self.accelKey('ViewDataOutput'), \
viewRightViewGrp, '', True)
viewOutAct.setStatusTip(_('Show data output in right view'))
viewOutAct.addTo(viewMenu)
viewEditAct = QAction(_('Show Data Editor'), _('Show Data &Editor'), \
self.accelKey('ViewDataEdit'), \
viewRightViewGrp, '', True)
viewEditAct.setStatusTip(_('Show data editor in right view'))
viewEditAct.addTo(viewMenu)
viewTitleAct = QAction(_('Show Title List'), _('Show Title &List'), \
self.accelKey('ViewTitleList'), \
viewRightViewGrp, '', True)
viewTitleAct.setStatusTip(_('Show title list in right view'))
viewTitleAct.addTo(viewMenu)
self.connect(viewRightViewGrp, SIGNAL('selected(QAction*)'), \
self.viewSelect)
self.viewToAct = {self.dataOutSplit: viewOutAct, \
self.dataEditSplit: viewEditAct, \
self.titleListSplit: viewTitleAct}
self.actToView = {viewOutAct: self.dataOutSplit, \
viewEditAct: self.dataEditSplit, \
viewTitleAct: self.titleListSplit}
viewFocusTreeAct = QAction('', '', self.accelKey('ViewGoTree'), \
self.fullActGrp)
self.connect(viewFocusTreeAct, SIGNAL('activated()'), \
self.viewFocusTree)
viewMenu.insertSeparator()
self.toolbars[0].addSeparator()
viewChildrenGrp = QActionGroup(self)
viewSelectAct = QAction(_('Show Selected Node'), \
QIconSet(self.toolIcons.getIcon('viewselect')), \
_('Show Selected &Node'), \
0, viewChildrenGrp, '', True)
viewSelectAct.setStatusTip(_('Show selected node without children '\
'in the right view'))
viewSelectAct.addTo(viewMenu)
viewSelectAct.addTo(self.toolbars[0])
viewChildrenAct = QAction(_('Show Selected Node with Children'), \
QIconSet(self.toolIcons.getIcon('viewchild')), \
_('Show Selected Node &with Children'), \
0, viewChildrenGrp, '', True)
viewChildrenAct.setStatusTip(_('Show selected node with it\'s '\
'children in the right view'))
viewChildrenAct.addTo(viewMenu)
viewChildrenAct.addTo(self.toolbars[0])
self.childActToBool = {viewSelectAct: False, viewChildrenAct: True}
viewSelectAct.setOn(not self.showItemChildren)
viewChildrenAct.setOn(self.showItemChildren)
self.connect(viewChildrenGrp, SIGNAL('selected(QAction*)'), \
self.viewChildren)
viewDataPageUpAct = QAction(_('Data View Page Up'), \
_('Data View Page &Up'), \
self.accelKey('ViewDataPageUp'), \
self.fullActGrp)
self.connect(viewDataPageUpAct, SIGNAL('activated()'), \
self.viewDataPageUp)
viewDataPageDownAct = QAction(_('Data View Page Down'), \
_('Data View &Page Down'), \
self.accelKey('ViewDataPageDown'), \
self.fullActGrp)
self.connect(viewDataPageDownAct, SIGNAL('activated()'), \
self.viewDataPageDown)
self.toolbars[0].addSeparator()
dataMenu = QPopupMenu(self)
self.menuBar().insertItem(_('&Data'), dataMenu)
self.dataSelExistActGrp = QActionGroup(self)
self.typeSubMenu = QPopupMenu(self)
self.typeSubMenu.setCheckable(True)
dataMenu.insertItem(_('Set &Item Type (%s)') \
% self.accelKeyText('DataSetItemType'), \
self.typeSubMenu)
self.parentPopup.insertItem(_('Set &Item Type (%s)') \
% self.accelKeyText('DataSetItemType'), \
self.typeSubMenu)
self.childPopup.insertItem(_('Set &Item Type (%s)') \
% self.accelKeyText('DataSetItemType'), \
self.typeSubMenu)
typeSubMenuAct = QAction('', '', self.accelKey('DataSetItemType'), \
self.fullActGrp)
self.connect(typeSubMenuAct, SIGNAL('activated()'), \
self.treeView.showTypeMenu)
self.connect(self.typeSubMenu, SIGNAL('activated(int)'), \
self.dataTypeChange)
self.connect(self.typeSubMenu, SIGNAL('aboutToShow()'), \
self.dataTypeMenuCheck)
self.dataSetAct = QAction(_('Set Descendant Types'), \
QIconSet(self.toolIcons.getIcon('dataset')), \
_('&Set Descendant Types...'), \
self.accelKey('DataSetDescendType'), \
self, '', True)
self.dataSetAct.setStatusTip(_('Set data type of selections and '\
'children'))
self.dataSetAct.addTo(dataMenu)
self.dataSetAct.addTo(self.toolbars[1])
self.connect(self.dataSetAct, SIGNAL('toggled(bool)'), self.dataSet)
dataConfigAct = QAction(_('Configure Data Types'), \
QIconSet(self.toolIcons.getIcon('dataconf')), \
_('&Configure Data Types...'), \
self.accelKey('DataConfigType'), \
self.fullActGrp)
dataConfigAct.setStatusTip(_('Modify data fields & output lines'))
dataConfigAct.addTo(dataMenu)
dataConfigAct.addTo(self.toolbars[1])
self.connect(dataConfigAct, SIGNAL('activated()'), self.dataConfig)
dataCopyAct = QAction(_('Copy Types from File'), \
_('C&opy Types from File...'), \
self.accelKey('DataCopyTypes'), self.fullActGrp)
dataCopyAct.setStatusTip(_('Copy the configuration from another '\
'TreeLine file'))
dataCopyAct.addTo(dataMenu)
self.connect(dataCopyAct, SIGNAL('activated()'), self.dataCopyTypes)
dataMenu.insertSeparator()
self.parentPopup.insertSeparator()
self.toolbars[1].addSeparator()
self.dataSelParentActGrp = QActionGroup(self)
self.dataUnrelParentActGrp = QActionGroup(self)
self.dataSingleSelActGrp = QActionGroup(self)
dataSortChildAct = QAction(_('Sort Node Children'), \
QIconSet(self.toolIcons.getIcon('datasort')), \
_('Sort &Node Children...'), \
self.accelKey('DataSortChildren'), \
self.dataSelParentActGrp)
dataSortChildAct.setStatusTip(_('Sort the children with chosen keys'))
dataSortChildAct.addTo(dataMenu)
dataSortChildAct.addTo(self.toolbars[1])
dataSortChildAct.addTo(self.parentPopup)
dataSortChildAct.addTo(self.childPopup)
self.connect(dataSortChildAct, SIGNAL('activated()'), \
self.dataSortChild)
dataSortTypeAct = QAction(_('Sort Type in Branch'), \
_('Sort &Type in Branch...'), \
self.accelKey('DataSortType'), \
self.dataUnrelParentActGrp)
dataSortTypeAct.setStatusTip(_('Sort descendants of a type with keys'))
dataSortTypeAct.addTo(dataMenu)
self.connect(dataSortTypeAct, SIGNAL('activated()'), self.dataSortType)
dataSortBranchAct = QAction(_('Sort Branch by Title'), \
_('Sort &Branch by Title...'), \
self.accelKey('DataSortTitle'), \
self.dataSelParentActGrp)
dataSortBranchAct.setStatusTip(_('Sort all descendants by title only'))
dataSortBranchAct.addTo(dataMenu)
self.connect(dataSortBranchAct, SIGNAL('activated()'), \
self.dataSortBranch)
dataMenu.insertSeparator()
dataFilterAct = QAction(_('Filter Data'), \
QIconSet(self.toolIcons.getIcon('datafilter')), \
_('Filter &Data...'), \
self.accelKey('DataFilter'), \
self.dataUnrelParentActGrp)
dataFilterAct.setStatusTip(_('Create a new file with filtered '\
'contents'))
dataFilterAct.addTo(dataMenu)
self.connect(dataFilterAct, SIGNAL('activated()'), self.dataFilter)
dataEditFieldAct = QAction(_('Change Selected Data'), \
_('C&hange Selected Data...'), \
self.accelKey('DataChange'), \
self.dataSelExistActGrp)
dataEditFieldAct.setStatusTip(_('Edit data values for all selected '\
'nodes'))
dataEditFieldAct.addTo(dataMenu)
self.connect(dataEditFieldAct, SIGNAL('activated()'), \
self.dataEditField)
dataNumberingAct = QAction(_('Numbering'), _('N&umbering...'), \
self.accelKey('DataNumber'), \
self.dataSingleSelActGrp)
dataNumberingAct.setStatusTip(_('Add numbering to a given data field'))
dataNumberingAct.addTo(dataMenu)
self.connect(dataNumberingAct, SIGNAL('activated()'), \
self.dataNumbering)
dataMenu.insertSeparator()
dataAddCatAct = QAction(_('Add Category Level'), \
_('&Add Category Level...'), \
self.accelKey('DataCategoryAdd'), \
self.dataUnrelParentActGrp)
dataAddCatAct.setStatusTip(_('Insert category nodes above children'))
dataAddCatAct.addTo(dataMenu)
self.connect(dataAddCatAct, SIGNAL('activated()'), self.dataAddCat)
dataFlatCatAct = QAction(_('Flatten by Category'), \
_('&Flatten by Category'), \
self.accelKey('DataCategoryFlat'), \
self.dataUnrelParentActGrp)
dataFlatCatAct.setStatusTip(_('Collapse data by merging fields'))
dataFlatCatAct.addTo(dataMenu)
self.connect(dataFlatCatAct, SIGNAL('activated()'), self.dataFlatCat)
dataMenu.insertSeparator()
dataArrangeRefAct = QAction(_('Arrange by Reference'), \
_('Arrange by &Reference...'), \
self.accelKey('DataRefArrange'), \
self.dataSingleSelActGrp)
dataArrangeRefAct.setStatusTip(_('Arrange data using parent '\
'references'))
dataArrangeRefAct.addTo(dataMenu)
self.connect(dataArrangeRefAct, SIGNAL('activated()'), \
self.dataArrangeRef)
dataFlatRefAct = QAction(_('Flatten by Reference'), \
_('F&latten by Reference...'), \
self.accelKey('DataRefFlat'), \
self.dataSingleSelActGrp)
dataFlatRefAct.setStatusTip(_('Collapse data after adding references'))
dataFlatRefAct.addTo(dataMenu)
self.connect(dataFlatRefAct, SIGNAL('activated()'), self.dataFlatRef)
dataUpdateAct = QAction(_('Update by Reference'), \
_('&Update by Reference...'), \
self.accelKey('DataRefUpdate'), \
self.dataSelParentActGrp)
dataUpdateAct.setStatusTip(_('Update with new fields from '\
'reference file'))
dataUpdateAct.addTo(dataMenu)
self.connect(dataUpdateAct, SIGNAL('activated()'), self.dataUpdate)
toolsMenu = QPopupMenu(self)
self.menuBar().insertItem(_('&Tools'), toolsMenu)
toolsExpandAct = QAction(_('Expand Full Branch'), \
QIconSet(self.toolIcons.getIcon('toolsexpand')), \
_('&Expand Full Branch'), \
self.accelKey('ToolsExpand'), \
self.dataSelParentActGrp)
toolsExpandAct.setStatusTip(_('Expand all children of selected node'))
toolsExpandAct.addTo(toolsMenu)
toolsExpandAct.addTo(self.parentPopup)
self.connect(toolsExpandAct, SIGNAL('activated()'), self.toolsExpand)
toolsCollapseAct = QAction(_('Collapse Full Branch'), \
QIconSet(self.toolIcons.getIcon('toolscollapse')), \
_('&Collapse Full Branch'), \
self.accelKey('ToolsCollapse'), \
self.dataSelParentActGrp)
toolsCollapseAct.setStatusTip(_('Collapse all children of the '\
'selected node'))
toolsCollapseAct.addTo(toolsMenu)
toolsCollapseAct.addTo(self.parentPopup)
self.connect(toolsCollapseAct, SIGNAL('activated()'), \
self.toolsCollapse)
toolsMenu.insertSeparator()
self.toolsFindAct = QAction(_('Find'), \
QIconSet(self.toolIcons.getIcon('toolsfind')), \
_('&Find...'), self.accelKey('ToolsFind'), \
self, '', True)
self.toolsFindAct.setStatusTip(_('Find node matching text string'))
self.toolsFindAct.addTo(toolsMenu)
self.connect(self.toolsFindAct, SIGNAL('toggled(bool)'), self.toolsFind)
toolsSpellCheckAct = QAction(_('Spell Check'), \
QIconSet(self.toolIcons.getIcon('toolsspell')), \
_('&Spell Check'), \
self.accelKey('ToolsSpellCheck'), \
self.fullActGrp)
toolsSpellCheckAct.setStatusTip(_('Spell check the tree\'s text data'))
toolsSpellCheckAct.addTo(toolsMenu)
self.connect(toolsSpellCheckAct, SIGNAL('activated()'), \
self.toolsSpellCheck)
self.toolsRemXsltAct = QAction(_('Remove XSLT Ref'), \
_('&Remove XSLT Ref'), \
self.accelKey('ToolsRemXLST'), \
self.fullActGrp)
self.toolsRemXsltAct.setStatusTip(_('Delete reference to XSLT export'))
self.toolsRemXsltAct.addTo(toolsMenu)
self.connect(self.toolsRemXsltAct, SIGNAL('activated()'), \
self.toolsRemXslt)
toolsMenu.insertSeparator()
toolsGenOptAct = QAction(_('General Options'), \
QIconSet(self.toolIcons.getIcon('toolsoptions')), \
_('&General Options...'), \
self.accelKey('ToolsGenOptions'), \
self.fullActGrp)
toolsGenOptAct.setStatusTip(_('Set user preferences for all files'))
toolsGenOptAct.addTo(toolsMenu)
self.connect(toolsGenOptAct, SIGNAL('activated()'), self.toolsGenOpt)
toolsFileOptAct = QAction(_('File Options'), _('File &Options...'), \
self.accelKey('ToolsFileOptions'), \
self.fullActGrp)
toolsFileOptAct.setStatusTip(_('Set preferences for this file'))
toolsFileOptAct.addTo(toolsMenu)
self.connect(toolsFileOptAct, SIGNAL('activated()'), self.toolsFileOpt)
toolsBkColorAct = QAction(_('Background Color'), \
QIconSet(self.toolIcons.getIcon('toolsbackcolor')), \
_('&Background Color...'), \
self.accelKey('ToolsBackColor'), self.fullActGrp)
toolsBkColorAct.setStatusTip(_('Set view background color'))
toolsBkColorAct.addTo(toolsMenu)
self.connect(toolsBkColorAct, SIGNAL('activated()'), self.toolsBkColor)
toolsTxtColorAct = QAction(_('Text Color'), \
QIconSet(self.toolIcons.getIcon('toolstextcolor')), \
_('&Text Color...'), \
self.accelKey('ToolsTextColor'), self.fullActGrp)
toolsTxtColorAct.setStatusTip(_('Set view text color'))
toolsTxtColorAct.addTo(toolsMenu)
self.connect(toolsTxtColorAct, SIGNAL('activated()'), \
self.toolsTxtColor)
helpMenu = QPopupMenu(self)
self.menuBar().insertItem(_('&Help'), helpMenu)
helpContentsAct = QAction(_('Help Contents'), \
QIconSet(self.toolIcons.getIcon('helpcontents')), \
_('&Help Contents'), self.accelKey('HelpContents'), \
self.fullActGrp)
helpContentsAct.setStatusTip(_('View information about using TreeLine'))
helpContentsAct.addTo(helpMenu)
helpContentsAct.addTo(self.toolbars[0])
self.connect(helpContentsAct, SIGNAL('activated()'), self.helpContents)
helpReadMeAct = QAction(_('View Full ReadMe'), \
_('&View Full ReadMe'), \
self.accelKey('HelpFullReadMe'), \
self.fullActGrp)
helpReadMeAct.setStatusTip(_('View the entire ReadMe file'))
helpReadMeAct.addTo(helpMenu)
self.connect(helpReadMeAct, SIGNAL('activated()'), self.helpReadMe)
helpAboutAct = QAction(_('About TreeLine'), \
QIconSet(globalref.treeIcons.getIcon('treeline')), \
_('&About TreeLine'), \
self.accelKey('HelpAbout'), self.fullActGrp)
helpAboutAct.setStatusTip(_('About this program'))
helpAboutAct.addTo(helpMenu)
self.connect(helpAboutAct, SIGNAL('activated()'), self.helpAbout)
helpPluginAct = QAction(_('About Plugins'), _('About &Plugins'), \
self.accelKey('HelpPlugin'), self.fullActGrp)
helpPluginAct.setStatusTip(_('Show loaded plugin modules'))
helpPluginAct.addTo(helpMenu)
self.connect(helpPluginAct, SIGNAL('activated()'), self.helpPlugin)
# group list to disable all controls during rename
# would not be req'd in qt3 where full could be parent of others
self.actGrpList = [self.fullActGrp, self.editPasteActGrp, \
self.editInsertActGrp, self.editPrevSibActGrp, \
viewRightViewGrp, viewChildrenGrp, \
self.toolsFindAct, self.dataSetAct, \
self.dataSelExistActGrp, self.dataSelParentActGrp, \
self.dataUnrelParentActGrp, self.dataSingleSelActGrp]
self.pulldownMenuList = [fileMenu, editMenu, viewMenu, dataMenu, \
toolsMenu, helpMenu]