def handle_starttag(self, tag, attrs): """Called by the reader at each open tag""" if tag == 'dt' or tag == 'h1': # start any entry self.text = '' elif tag == 'dl': # start indent self.currentParent = self.currentItem self.currentItem = None elif tag == 'h3': # start folder if not self.currentParent: raise HtmlParseError, 'No valid parent folder' self.currentItem = TreeItem(self.currentParent, self.folderFormat.name) self.currentParent.childList.append(self.currentItem) elif tag == 'a': # start link if not self.currentParent: raise HtmlParseError, 'No valid parent folder' self.currentItem = TreeItem(self.currentParent, self.bookmarkFormat.name) self.currentParent.childList.append(self.currentItem) for name, value in attrs: if name == 'href': self.currentItem.data[TreeFormats.linkFieldName] = value elif tag == 'hr': # separator if not self.currentParent: raise HtmlParseError, 'No valid parent folder' item = TreeItem(self.currentParent, self.separatorFormat.name) self.currentParent.childList.append(item) self.currentItem = None
def readLines(self, fileRef, errors='strict'): """Import plain text, node per line""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readLines(fileRef, 'replace') else: f.close() return f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.addTableFields([TreeFormats.textFieldName]) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: line = line.strip() if line: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) newItem.data[TreeFormats.textFieldName] = line self.root = newRoot self.fileName = filePath
def readPara(self, fileRef, errors='strict'): """Import plain text, blank line delimitted""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) fullText = f.read().replace('\r', '') except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readPara(fileRef, 'replace') else: f.close() return textList = fullText.split('\n\n') f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.iconName = 'doc' defaultFormat.addTableFields([TreeFormats.textFieldName]) defaultFormat.fieldList[0].numLines = globalref.options.\ intData('MaxEditLines', 1, optiondefaults.maxNumLines) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: line = line.strip() if line: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) newItem.data[TreeFormats.textFieldName] = line self.root = newRoot self.fileName = filePath
def readTable(self, fileRef, errors='strict'): """Import table data into a flat tree - raise exception on failure""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTable(fileRef, 'replace') else: f.close() return f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.addTableFields(textList.pop(0).strip().split('\t')) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) lineList = line.strip().split('\t') try: for num in range(len(lineList)): newItem.data[self.treeFormats[TreeFormats.formatDefault]. fieldList[num].name] = lineList[num].strip() except IndexError: print 'Too few headings to read data as a table' raise ReadFileError(_('Too few headings to read data as table')) self.root = newRoot self.fileName = filePath
def exportDirPage(self, dirName, nodeList): """Write tree to nested direct struct with html page for each node""" oldDir = os.getcwd() os.chdir(dirName.encode(sys.getfilesystemencoding())) cssLines = ['#sidebar {', 'width: 16em;', 'float: left;', 'clear: left;', 'border-right: 1px solid black;', 'margin-right: 1em;', '}'] try: f = codecs.open('default.css', 'w', 'utf-8') f.writelines([(line + '\n').encode('utf-8') for line in cssLines]) except (IOError, UnicodeError): print 'Error - could not write file to default.css' raise IOError(_('Error - cannot write file to %s') % 'default.css') f.close() if len(nodeList) > 1: self.treeFormats.addIfMissing(TreeDoc.copyFormat) item = TreeItem(None, TreeDoc.copyFormat.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] linkDict = {} item.createDirPageLinkDict(linkDict, os.getcwd()) item.exportDirPage(linkDict) self.treeFormats.removeQuiet(TreeDoc.copyFormat) os.chdir(oldDir)
def exportTrlSubtree(self, fileRef, nodeList, addBranches=True): """Write subtree TRL file starting form item""" lines = [u'<?xml version="1.0" encoding="utf-8" ?>'] if self.xlstLink: lines.append(u'<?%s?>' % self.xlstLink) if not addBranches: newList = [] for item in nodeList: # replace items with childless items newItem = TreeItem(item.parent, item.formatName) newItem.data = item.data newList.append(newItem) nodeList = newList if len(nodeList) > 1: format = nodeformat.NodeFormat(TreeFormats.rootFormatDefault, {}, TreeFormats.fieldDefault) self.treeFormats.addIfMissing(format) item = TreeItem(None, format.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] lines.extend(item.branchXml([], True)) try: f = self.getWriteFileObj(fileRef, self.compressFile) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' self.treeFormats.removeQuiet(TreeDoc.copyFormat) raise f.close() self.treeFormats.removeQuiet(TreeDoc.copyFormat)
def readTabbed(self, fileRef, errors='strict'): """Import tabbed data into a flat tree - raise exception on failure""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTabbed(fileRef, 'replace') else: f.close() return f.close() bufList = [(text.count('\t', 0, len(text) - len(text.lstrip())), text.strip()) for text in textList if text.strip()] if bufList: buf = bufList.pop(0) if buf[0] == 0: # set default formats ROOT & DEFAULT self.treeFormats = TreeFormats({}, True) newRoot = TreeItem(None, TreeFormats.rootFormatDefault) newRoot.setTitle(buf[1]) if newRoot.loadTabbedChildren(bufList): self.root = newRoot self.fileName = filePath return raise ReadFileError(_('Error in tabbed list'))
def startElement(self, name, attrs): """Called by the reader at the open tag of each element""" if name == u'folder' or name == u'xbel': newItem = TreeItem(self.currentItem, self.folderFormat.name) if self.currentItem: self.currentItem.childList.append(newItem) else: self.rootItem = newItem if attrs.get(u'folded', '') == 'no': newItem.open = True self.currentItem = newItem elif name == u'bookmark': newItem = TreeItem(self.currentItem, self.bookmarkFormat.name) if self.currentItem: self.currentItem.childList.append(newItem) else: raise xml.sax.SAXException, 'No valid parent folder' newItem.data[TreeFormats.linkFieldName] = attrs.get(u'href', '') self.currentItem = newItem elif name == u'title': self.text = '' elif name == u'separator': newItem = TreeItem(self.currentItem, self.separatorFormat.name) if self.currentItem: self.currentItem.childList.append(newItem) else: raise xml.sax.SAXException, 'No valid parent folder' self.currentItem = newItem else: # unsupported tags pass
def __init__(self, headers, data, parent=None) -> None: super(TreeModel, self).__init__(parent) rootData = [] for header in headers: rootData.append(header) self._rootItem = TreeItem(rootData)
def __init__(self, headers, data, tablemodel, parent=None): super(TreeModel, self).__init__(parent) rootData = [header for header in headers] self.rootItem = TreeItem(rootData) self.treeDict = data self.tablemodel = tablemodel self.setupModelData(data, self.rootItem) self.examingParents = False self.examiningChildren = False
def readTreepad(self, fileRef, errors='strict'): """Read Treepad text-node file""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.read().split('<end node> 5P9i0s8y19Z') f.close() except UnicodeError: # error common - broken unicode on windows print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTreepad(fileRef, 'replace') else: f.close() return self.treeFormats = TreeFormats() format = nodeformat.NodeFormat(TreeFormats.formatDefault) titleFieldName = _('Title', 'title field name') format.addNewField(titleFieldName) format.addLine(u'{*%s*}' % titleFieldName) numLines = globalref.options.intData('MaxEditLines', 1, optiondefaults.maxNumLines) format.addNewField(TreeFormats.textFieldName, {'lines': repr(numLines)}) format.addLine(u'{*%s*}' % TreeFormats.textFieldName) self.treeFormats[format.name] = format itemList = [] for text in textList: text = text.strip() if text: try: text = text.split('<node>', 1)[1].lstrip() lines = text.split('\n') title = lines[0] level = int(lines[1]) lines = lines[2:] except (ValueError, IndexError): print 'Error - bad file format in %s' % \ filePath.encode(globalref.localTextEncoding) raise ReadFileError(_('Bad file format in %s') % filePath) item = TreeItem(None, format.name) item.data[titleFieldName] = title item.data[TreeFormats.textFieldName] = '\n'.join(lines) item.level = level itemList.append(item) self.root = itemList[0] parentList = [] for item in itemList: if item.level != 0: parentList = parentList[:item.level] item.parent = parentList[-1] parentList[-1].childList.append(item) parentList.append(item) self.root = itemList[0] self.fileName = filePath
def __set_card_treeview_status(self): card_list = [] cards = pypulse.PULSE.get_cards() for idx in cards: active_profile = cards[idx]['active_profile'] if active_profile: if active_profile['n_sinks'] > 1: io_num = _("%d Outputs") % active_profile['n_sinks'] else: io_num = _("%d Output") % active_profile['n_sinks'] if active_profile['n_sources'] > 1: io_num += " / " + _("%d Inputs") % active_profile['n_sources'] else: io_num += " / " + _("%d Input") % active_profile['n_sources'] if 'device.description' in cards[idx]['proplist']: description = cards[idx]['proplist']['device.description'].strip('\x00') else: description = "" card_info = "%s(%s)[%s]" % (description, io_num, active_profile['description']) else: if 'device.description' in cards[idx]['proplist']: card_info = cards[idx]['proplist']['device.description'] else: card_info = " " card_list.append(TreeItem(self.image_widgets["device"], card_info, cards[idx]['name'], idx)) self.view_widgets["ad_hardware"].add_items(card_list, clear_first=True) if card_list: self.view_widgets["ad_hardware"].set_select_rows([0])
def readOdf(self, fileRef): """Read an Open Document Format (ODF) file""" self.treeFormats = TreeFormats(None, True) rootItem = TreeItem(None, TreeFormats.rootFormatDefault, TreeDoc.rootTitleDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.addNewField(TreeFormats.textFieldName, { u'html': 'n', u'lines': '6' }) defaultFormat.changeOutputLines([ u'<b>{*%s*}</b>' % TreeFormats.fieldDefault, u'{*%s*}' % TreeFormats.textFieldName ]) try: f = self.getReadFileObj(fileRef) filePath = unicode(f.name, sys.getfilesystemencoding()) zip = zipfile.ZipFile(f, 'r') text = zip.read('content.xml') handler = treexmlparse.OdfSaxHandler(rootItem, defaultFormat) xml.sax.parseString(text, handler) except (zipfile.BadZipfile, KeyError): f.close() raise ReadFileError(_('Could not unzip ODF file')) except UnicodeError: f.close() raise ReadFileError(_('Problem with Unicode characters in file')) except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open corrupt ODF file')) f.close() self.root = rootItem self.fileName = filePath
def startElement(self, name, attrs): """Called by the reader at the open tag of each element""" format = self.formats.get(name, None) if not format: format = nodeformat.NodeFormat(name) self.formats[name] = format newItem = TreeItem(self.currentItem, name) if self.currentItem: self.currentItem.childList.append(newItem) elif self.rootItem: raise xml.sax.SAXException, 'Invalid XML file' else: self.rootItem = newItem self.currentItem = newItem for key in attrs.keys(): format.addFieldIfNew(key) newItem.data[key] = attrs[key]
def __init__(self, folderFormat, bookmarkFormat, separatorFormat): HTMLParser.HTMLParser.__init__(self) self.folderFormat = folderFormat self.bookmarkFormat = bookmarkFormat self.separatorFormat = separatorFormat self.rootItem = TreeItem(None, self.folderFormat.name) self.rootItem.data[TreeFormats.fieldDefault] = _('Bookmarks') self.currentItem = self.rootItem self.currentParent = None self.text = ''
def __init__(self, filePath=None, setNewDefaults=False, importType=None): """Open filePath (can also be file ref) if given, setNewDefaults uses user defaults for compression & encryption, importType gives an import method to read the file""" globalref.docRef = self self.root = None self.treeFormats = TreeFormats() self.fileInfoItem = TreeItem(None, nodeformat.FileInfoFormat.name) self.fileInfoFormat = None TreeDoc.copyFormat = nodeformat.NodeFormat('_DUMMY__ROOT_', {}, TreeFormats.fieldDefault) self.undoStore = undo.UndoRedoStore() self.redoStore = undo.UndoRedoStore() self.sortFields = [''] self.fileName = '' self.spaceBetween = True self.lineBreaks = True self.formHtml = True self.childFieldSep = TreeDoc.childFieldSepDflt self.spellChkLang = '' self.xlstLink = '' self.xslCssLink = '' self.tlVersion = __version__ self.fileInfoFormat = nodeformat.FileInfoFormat() if filePath: if importType: getattr(self, importType)(filePath) else: self.readFile(filePath) else: self.treeFormats = TreeFormats({}, True) self.root = TreeItem(None, TreeFormats.rootFormatDefault) self.root.setTitle(TreeDoc.rootTitleDefault) self.modified = False if setNewDefaults or not hasattr(self, 'compressFile'): self.compressFile = globalref.options.boolData('CompressNewFiles') self.encryptFile = globalref.options.boolData('EncryptNewFiles') elif not hasattr(self, 'encryptFile'): self.encryptFile = False self.selection = treeselection.TreeSelection([self.root]) self.fileInfoFormat.translateFields() self.fileInfoFormat.updateFileInfo()
def exportDirPage(self, dirName, nodeList): """Write tree to nested direct struct with html page for each node""" oldDir = os.getcwd() os.chdir(dirName.encode(sys.getfilesystemencoding())) cssLines = [ '#sidebar {', 'width: 16em;', 'float: left;', 'clear: left;', 'border-right: 1px solid black;', 'margin-right: 1em;', '}' ] try: f = codecs.open('default.css', 'w', 'utf-8') f.writelines([(line + '\n').encode('utf-8') for line in cssLines]) except (IOError, UnicodeError): print 'Error - could not write file to default.css' raise IOError(_('Error - cannot write file to %s') % 'default.css') f.close() if len(nodeList) > 1: self.treeFormats.addIfMissing(TreeDoc.copyFormat) item = TreeItem(None, TreeDoc.copyFormat.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] linkDict = {} item.createDirPageLinkDict(linkDict, os.getcwd()) item.exportDirPage(linkDict) self.treeFormats.removeQuiet(TreeDoc.copyFormat) os.chdir(oldDir)
def exportDirTable(self, dirName, nodeList, addHeader=False): """Write tree to nested directory struct with html tables""" oldDir = os.getcwd() os.chdir(dirName.encode(sys.getfilesystemencoding())) if addHeader: header = self.fileInfoFormat.getHeaderFooter(True) footer = self.fileInfoFormat.getHeaderFooter(False) else: header = footer = '' if len(nodeList) > 1: self.treeFormats.addIfMissing(TreeDoc.copyFormat) item = TreeItem(None, TreeDoc.copyFormat.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] linkDict = {} item.createDirTableLinkDict(linkDict, os.getcwd()) item.exportDirTable(linkDict, None, header, footer) self.treeFormats.removeQuiet(TreeDoc.copyFormat) os.chdir(oldDir)
def readTable(self, fileRef, errors='strict'): """Import table data into a flat tree - raise exception on failure""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTable(fileRef, 'replace') else: f.close() return f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.addTableFields(textList.pop(0).strip().split('\t')) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) lineList = line.strip().split('\t') try: for num in range(len(lineList)): newItem.data[self.treeFormats[TreeFormats.formatDefault]. fieldList[num].name] = lineList[num].strip() except IndexError: print 'Too few headings to read data as a table' raise ReadFileError( _('Too few headings to read data as table')) self.root = newRoot self.fileName = filePath
def startElement(self, name, attrs): """Called by the reader at the open tag of each element""" if attrs.get(u'item', ''): # reading TreeItem or bare format format = self.formats.get(name, None) if not format: format = nodeformat.NodeFormat(name, attrs) self.formats[name] = format if attrs.get(u'item', '') == 'y': # reading TreeItem newItem = TreeItem(self.currentItem, name) if self.currentItem: self.currentItem.childList.append(newItem) else: self.rootItem = newItem if attrs.get(u'nospace', '').startswith('y'): self.docRef.spaceBetween = False if attrs.get(u'nobreaks', '').startswith('y'): self.docRef.lineBreaks = False if attrs.get(u'nohtml', '').startswith('y'): self.docRef.formHtml = False if attrs.get(u'childsep', ''): self.docRef.childFieldSep = attrs.get(u'childsep', '') self.docRef.spellChkLang = attrs.get(u'spellchk', '') self.docRef.xslCssLink = attrs.get(u'xslcss', '') self.docRef.tlVersion = attrs.get(u'tlversion', '') self.currentItem = newItem else: # reading bare format self.bareFormat = format else: # reading data self.text = '' self.dataEntry = name if not self.currentItem: raise xml.sax.SAXException, 'No valid item' currentFormat = self.bareFormat if not currentFormat: try: currentFormat = self.formats[self.currentItem.formatName] except KeyError: raise xml.sax.SAXException, 'Invalid node type' if name not in currentFormat.fieldNames( ): # add new field to format try: currentFormat.addNewField(name, attrs) except (NameError, KeyError): raise xml.sax.SAXException, 'Invalid field type'
def __set_input_treeview_status(self): current_source = pypulse.get_fallback_source_index() sources = pypulse.PULSE.get_input_devices() input_list = [] i = 0 selected_row = -1 for idx in sources: if current_source is not None and current_source == idx: selected_row = i if 'device.description' in sources[idx]['proplist']: description = sources[idx]['proplist']['device.description'].strip('\x00') else: description = "" input_list.append(TreeItem(self.image_widgets["device"], description, sources[idx]['name'], idx)) i += 1 self.view_widgets["ad_input"].add_items(input_list, clear_first=True) if not (selected_row < 0): self.view_widgets["ad_input"].set_select_rows([selected_row])
def __init__(self, filePath=None, setNewDefaults=False, importType=None): """Open filePath (can also be file ref) if given, setNewDefaults uses user defaults for compression & encryption, importType gives an import method to read the file""" globalref.docRef = self self.root = None self.treeFormats = TreeFormats() self.fileInfoItem = TreeItem(None, nodeformat.FileInfoFormat.name) self.fileInfoFormat = None TreeDoc.copyFormat = nodeformat.NodeFormat('_DUMMY__ROOT_', {}, TreeFormats.fieldDefault) self.undoStore = undo.UndoRedoStore() self.redoStore = undo.UndoRedoStore() self.sortFields = [''] self.fileName = '' self.spaceBetween = True self.lineBreaks = True self.formHtml = True self.childFieldSep = TreeDoc.childFieldSepDflt self.spellChkLang = '' self.xlstLink = '' self.xslCssLink = '' self.tlVersion = __version__ self.fileInfoFormat = nodeformat.FileInfoFormat() if filePath: if importType: getattr(self, importType)(filePath) else: self.readFile(filePath) else: self.treeFormats = TreeFormats({}, True) self.root = TreeItem(None, TreeFormats.rootFormatDefault) self.root.setTitle(TreeDoc.rootTitleDefault) self.modified = False if setNewDefaults or not hasattr(self, 'compressFile'): self.compressFile = globalref.options.boolData('CompressNewFiles') self.encryptFile = globalref.options.boolData('EncryptNewFiles') self.selection = treeselection.TreeSelection([self.root]) self.fileInfoFormat.translateFields() self.fileInfoFormat.updateFileInfo()
def setupModelData(self, lines, parent): parents = [] indentations = [] parents.append(parent) indentations.append(0) number = 0 while number < len(lines): position = 0 while position < len(lines[number]): if lines[number][position] != ' ': break position = position + 1 lineData = lines[number][position:].strip() if lineData: # Read the column data from the rest of the line. columnStrings = [x for x in lineData.split('\t') if x] columnData = [None] * len(columnStrings) for i, columnString in enumerate(columnStrings): columnData[i] = columnString if position > indentations[-1]: # The last child of the current parent is now the new parent # unless the current parent has no children. if parents[-1].childCount() > 0: parents.append(parents[-1].child(parents[-1].childCount() - 1)) indentations.append(position) else: while position < indentations[-1] and len(parents) > 0: parents.pop(-1) indentations.pop(-1) # Append a new item to the current parent's list of children. parents[-1].appendChild(TreeItem(columnData, parents[-1])) number = number + 1
class TreeModel(QAbstractItemModel): #Roles to filter input and output view Item_Role = Qt.UserRole def __init__(self, headers, data, parent=None) -> None: super(TreeModel, self).__init__(parent) rootData = [] for header in headers: rootData.append(header) self._rootItem = TreeItem(rootData) #self._setupModelData(data.split('\n'), self._rootItem) def rootItem(self) -> TreeItem: """ Return the root item of this model """ return self._rootItem def getItem(self, index) -> TreeItem: """ Return the TreeItem from model using the index """ if index.isValid(): item = index.internalPointer() if item: return item return self._rootItem def data(self, index, role) -> object: """ Return data from model to the view """ if not index.isValid(): return None item = self.getItem(index) if role == Qt.DisplayRole or role == Qt.EditRole: return item.data(index.column()) if role == TreeModel.Item_Role: return item return QVariant() def headerData(self, section, orientation, role) -> object: if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self._rootItem.data(section) def index(self, row, column, parent=QModelIndex()) -> QModelIndex: """ Obtain model indexes corresponding to children of given parent item """ #Only return model indexes for child items if the parent index in invalid (root item) #or if it has a zero column number if parent.isValid() and parent.column() != 0: return QModelIndex() parentItem = self.getItem(parent) if not parentItem: return QModelIndex() #Create a model index to uniquely with the row and column numbers #and a pointer to the item childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) return QModelIndex() def parent(self, index) -> QModelIndex: """ Return the model indexes for parents of item by finding the correspoinding item for a give model index, using its parent() function to obtain its parent item, then creating a model index to represent the parent. Item without parents, including the root item, are handled by returning a null model index. Otherwise, a model index ins created and returned as in the index() function, with a suitable row number, but with a zero column """ if not index.isValid(): return QModelIndex() childItem = self.getItem(index) parentItem = None if childItem: parentItem = childItem.parent() if parentItem == self._rootItem or not parentItem: return QModelIndex() return self.createIndex(parentItem.childNumber(), 0, parentItem) def rowCount(self, parent=QModelIndex()) -> int: """ Return the children that parent has """ parentItem = self.getItem(parent) if parentItem: return parentItem.childCount() else: return 0 def columnCount(self, parent=QModelIndex()) -> int: """ All items are defined to have the same number of columns associated with them """ return self._rootItem.columnCount() def flags(self, index) -> Qt.ItemFlags: """ Return flags ItemIsEditable, ItemIsSelectable and ItemIsEnable to be able to edit and select item """ if not index.isValid(): return Qt.NoItemFlags return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled def setData(self, index, value, role=Qt.EditRole) -> bool: """ Set data from view back to the model """ if role != Qt.EditRole: return False item = self.getItem(index) result = item.setData(index.column(), value) if result: self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole]) return result def setHeaderData(self, section, orientation, value, role=Qt.EditRole) -> bool: if role != Qt.EditRole or orientation != Qt.Horizontal: return False result = self._rootItem.setData(section, value) if result: self.headerDataChanged.emit(orientation, section, section) return result def insertColumns(self, position, columns, parent=QModelIndex()) -> bool: """ Insert columns into the model """ self.beginInsertColumns(parent, position, position + columns - 1) success = self._rootItem.insertColumns(position, columns) self.endInsertColumns() return success def removeColumns(self, position, columns, parent=QModelIndex()) -> bool: """ Remove columns from the model """ self.beginRemoveColumns(parent, position, position + columns - 1) success = self._rootItem.removeColumns(position, columns) self.endRemoveColumns() if self._rootItem.columnCount() == 0: self.removeRows(0, self.rowCount()) return success def insertRows(self, position, rows, parent=QModelIndex()) -> bool: """ Insert rows into model """ parentItem = self.getItem(parent) if not parentItem: return False self.beginInsertRows(parent, position, position + rows - 1) success = parentItem.insertChildren(position, rows, self._rootItem.columnCount()) self.endInsertRows() return success def removeRows(self, position, rows, parent=QModelIndex()) -> bool: """ Remove rows from model """ parentItem = self.getItem(parent) if not parentItem: return False self.beginRemoveRows(parent, position, position + rows - 1) success = parentItem.removeChildren(position, rows) self.endRemoveRows() return success
def __init__(self): TreeItem.__init__(self)
class TreeModel(QAbstractItemModel): checkChanged = Signal(int, str) def __init__(self, headers, data, tablemodel, parent=None): super(TreeModel, self).__init__(parent) rootData = [header for header in headers] self.rootItem = TreeItem(rootData) self.treeDict = data self.tablemodel = tablemodel self.setupModelData(data, self.rootItem) self.examingParents = False self.examiningChildren = False def changeLeafCheck(self, source): curNode = self.rootItem.child(0) keyNames = source.split("/") curRow = 0 for key in keyNames: for i in range(curNode.childCount()): if curNode.child(i).itemData[0] == key: curNode = curNode.child(i) curRow = i break anIndex = self.createIndex(curRow, curNode.columnCount(), curNode) self.setData(anIndex, 0, Qt.CheckStateRole) def columnCount(self, parent=QModelIndex()): return self.rootItem.columnCount() def data(self, index, role): if not index.isValid(): return None item = self.getItem(index) if role == Qt.DisplayRole: return item.data(index.column()) elif role == Qt.CheckStateRole: return item.checked return None def flags(self, index): if not index.isValid(): return 0 return Qt.ItemIsUserCheckable | super(TreeModel, self).flags(index) def getItem(self, index): if index.isValid(): item = index.internalPointer() if item: return item return self.rootItem def headerData(self, section, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self.rootItem.data(section) return None def index(self, row, column, parent=QModelIndex()): if parent.isValid() and parent.column() != 0: return QModelIndex() parentItem = self.getItem(parent) childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QModelIndex() def insertColumns(self, position, columns, parent=QModelIndex()): self.beginInsertColumns(parent, position, position + columns - 1) success = self.rootItem.insertColumns(position, columns) self.endInsertColumns() return success def insertRows(self, position, rows, parent=QModelIndex()): parentItem = self.getItem(parent) self.beginInsertRows(parent, position, position + rows - 1) success = parentItem.insertChildren(position, rows, self.rootItem.columnCount()) self.endInsertRows() return success def parent(self, index): if not index.isValid(): return QModelIndex() childItem = self.getItem(index) parentItem = childItem.parent() if parentItem == self.rootItem: return QModelIndex() return self.createIndex(parentItem.childNumber(), 0, parentItem) def removeColumns(self, position, columns, parent=QModelIndex()): self.beginRemoveColumns(parent, position, position + columns - 1) success = self.rootItem.removeColumns(position, columns) self.endRemoveColumns() if self.rootItem.columnCount() == 0: self.removeRows(0, self.rowCount()) return success def removeRows(self, position, rows, parent=QModelIndex()): parentItem = self.getItem(parent) self.beginRemoveRows(parent, position, position + rows - 1) success = parentItem.removeChildren(position, rows) self.endRemoveRows() return success def rowCount(self, parent=QModelIndex()): parentItem = self.getItem(parent) return parentItem.childCount() def setData(self, index, value, role=Qt.EditRole): if role == Qt.EditRole: item = self.getItem(index) result = item.setData(index.column(), value) if result: self.dataChanged.emit(index, index) return result elif role == Qt.CheckStateRole: item = self.getItem(index) item.checked = value checked = item.checked source="" sourceList= [] while item.parentItem: sourceList.insert(0,item.itemData[0]+"/") item = item.parentItem if self.examingParents == False: self.checkChanged.emit(checked,source.join(sourceList)[:-1]) self.checkChildren(index,value) self.checkParent(index) self.dataChanged.emit(index, index) return True return False def setHeaderData(self, section, orientation, value, role=Qt.EditRole): if role != Qt.EditRole or orientation != Qt.Horizontal: return False result = self.rootItem.setData(section, value) if result: self.headerDataChanged.emit(orientation, section, section) return result def setupModelData(self, data, parent): visited={} queue=[] grandParents = {} for key in data.keys(): visited[(parent.itemData[0])]=[key] queue.append((key,parent,"")) grandParents[key] = (data[key],parent) curDict = data tempSource= "" while queue: poppedItem = queue.pop(0) child = poppedItem[0] parentOfChild = poppedItem[1] childSource = poppedItem[2] parent = parentOfChild parent.insertChildren(parent.childCount(),1,self.rootItem.columnCount()) parent.child(parent.childCount() -1).setData(0,child) if child in grandParents: curDict = grandParents[child][0] tempSource = childSource+child+"/" for curChild in range(grandParents[child][1].childCount()): if child == grandParents[child][1].child(curChild).itemData[0]: parent = grandParents[child][1].child(curChild) visited[(parent.itemData[0])]=[] if isinstance(curDict, dict): for key in curDict.keys(): if key not in visited[(parent.itemData[0])]: visited[(parent.itemData[0])].append(key) queue.append((key,parent,tempSource)) if (isinstance(curDict[key],dict)): grandParents[key]= (curDict[key],parent) else: self.tablemodel.addRow(curDict,tempSource,key) def checkChildren(self,index,value): self.examingChildren = True if not index.isValid() or self.examingParents: self.examingChildren = False return else: childCount = self.rowCount(index) for i in range(childCount): child = index.child(i, 0) if value == 1: return self.setData(child, value, Qt.CheckStateRole) self.checkChildren(child,value) def checkParent(self,index): self.examingParents = True if not index.isValid() or self.examiningChildren: self.examingParents = False return else: parent = index.parent() if parent.data() == None: self.examingParents = False return value = self.checkChildrenStates(parent) self.setData(parent, value, Qt.CheckStateRole) def checkChildrenStates(self, index): childCount = self.rowCount(index) checkedChildrenCount = 0 for i in range(childCount): child = index.child(i, 0) if child.data(Qt.CheckStateRole) == Qt.Checked or child.data(Qt.CheckStateRole) == Qt.PartiallyChecked: checkedChildrenCount += 1 if checkedChildrenCount == 0: return 0 elif checkedChildrenCount == childCount: return 2 else: return 1
class TreeModelU(TreeModel): checkChanged = Signal(int, str) def __init__(self, headers, data, tablemodel, editables, parent=None): super(TreeModel, self).__init__(parent) rootData = [header for header in headers] self.rootItem = TreeItem(rootData) self.treeDict = data self.tablemodel = tablemodel self.editableKeys = editables self.examingParents = False self.examiningChildren = False self.setupModelData(data, self.rootItem) self.checkList() def checkList(self): missingSources = [] displayMessage = False for i in range(len(self.tablemodel.templatesources)): if "Custom Input" not in self.tablemodel.templatesources[i]: if self.tablemodel.templatesources[ i] not in self.tablemodel.newmetadatasources: missingSources.append(self.tablemodel.templatesources[i]) displayMessage = True for i in range(len(self.tablemodel.newmetadataList)): self.tablemodel.addRow(self.tablemodel.newmetadataList[i]) if displayMessage: QMessageBox.warning( None, QApplication.applicationDisplayName(), "Bad stuff happens. " + "The file extracted is missing Source: \n\n" + str(missingSources)) self.tablemodel.newmetadataList = [] self.tablemodel.newmetadatasources = [] def setupModelData(self, data, parent): visited = {} queue = [] grandParents = {} for key in data.keys(): visited[(parent.itemData[0])] = [key] queue.append((key, parent, "")) grandParents[key] = (data[key], parent) curDict = data tempSource = "" while queue: poppedItem = queue.pop(0) child = poppedItem[0] parentOfChild = poppedItem[1] childSource = poppedItem[2] parent = parentOfChild parent.insertChildren(parent.childCount(), 1, self.rootItem.columnCount()) parent.child(parent.childCount() - 1).setData(0, child) if child in grandParents: curDict = grandParents[child][0] tempSource = childSource + child + "/" for curChild in range(grandParents[child][1].childCount()): if child == grandParents[child][1].child( curChild).itemData[0]: parent = grandParents[child][1].child(curChild) visited[(parent.itemData[0])] = [] if isinstance(curDict, dict): for key in curDict.keys(): if key not in visited[(parent.itemData[0])]: visited[(parent.itemData[0])].append(key) queue.append((key, parent, tempSource)) if (isinstance(curDict[key], dict)): grandParents[key] = (curDict[key], parent) else: self.tablemodel.prepRow(curDict, tempSource, key)
class TreeModelR(TreeModel): checkChanged = Signal(int, str) def __init__(self, headers, data, tablemodel, newList, filterModel, parent=None): super(TreeModel, self).__init__(parent) rootData = [header for header in headers] self.rootItem = TreeItem(rootData) self.treeDict = data self.tablemodel = tablemodel self.newList = newList self.filterModel = filterModel self.examingParents = False self.examiningChildren = False self.setupModelData(data, self.rootItem) def setupModelData(self, data, parent): visited = {} queue = [] grandParents = {} for key in data.keys(): visited[(parent.itemData[0])] = [key] queue.append((key, parent, "")) grandParents[key] = (data[key], parent) curDict = data tempSource = "" while queue: poppedItem = queue.pop(0) child = poppedItem[0] parentOfChild = poppedItem[1] childSource = poppedItem[2] parent = parentOfChild parent.insertChildren(parent.childCount(), 1, self.rootItem.columnCount()) parent.child(parent.childCount() - 1).setData(0, child) for i in range(len(self.newList)): if child == self.newList[i]["Key"]: self.tablemodel.beginInsertRows( self.tablemodel.index( len(self.tablemodel.metadataList), 0), i, i) self.tablemodel.metadataList.append(self.newList[i]) self.tablemodel.endInsertRows() parent.child(parent.childCount() - 1).checked = self.newList[i]["Checked"] self.filterModel.checkList(self.newList[i]["Checked"], self.newList[i]["Source"]) if child in grandParents: curDict = grandParents[child][0] tempSource = childSource + child + "/" for curChild in range(grandParents[child][1].childCount()): if child == grandParents[child][1].child( curChild).itemData[0]: parent = grandParents[child][1].child(curChild) visited[(parent.itemData[0])] = [] if isinstance(curDict, dict): for key in curDict.keys(): if key not in visited[(parent.itemData[0])]: visited[(parent.itemData[0])].append(key) queue.append((key, parent, tempSource)) if (isinstance(curDict[key], dict)): grandParents[key] = (curDict[key], parent) else: pass
def addItem(self, level): """Add a child of the current item""" item = TreeItem(self.currentItem, self.defaultFormat.name) self.currentItem.childList.append(item) self.currentItem = item self.currentLevel = level
class TreeModel(QAbstractItemModel): def __init__(self, data, parent=None): super(TreeModel, self).__init__(parent) self.rootItem = TreeItem(['Date', 'Data (KB)', 'Data (MB)'], None) self.setupModel(data) def columnCount(self, parent): if parent.isValid(): return parent.internalPointer().columnCount() return self.rootItem.columnCount() def data(self, index, role): if not index.isValid(): return None if role == Qt.TextAlignmentRole: return Qt.AlignRight if role == Qt.DisplayRole: return index.internalPointer().data(index.column()) return None def headerData(self, section, orientation, role): if role == Qt.TextAlignmentRole: return Qt.AlignRight if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self.rootItem.data(section) def index(self, row, column, parent): if not self.hasIndex(row, column, parent): return QModelIndex() if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QModelIndex() def parent(self, index): if not index.isValid(): return QModelIndex() parentItem = index.internalPointer().parent() if parentItem == self.rootItem: return QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem) def rowCount(self, parent): if parent.column() > 0: return 0 if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() return parentItem.childCount() def setupModel(self, data): for daily in data: items = [daily.day, str(daily.dataUsed), "%.02f" % (daily.dataUsed / 1024.0,)] child = TreeItem(items, self.rootItem) self.rootItem.childItems.append(child) for detail in daily.detail: detail.append('') child.childItems.append(TreeItem(detail, child))
def __init__(self, data, parent=None): super().__init__(parent) self.rootItem = TreeItem([self.tr('title'), self.tr('summary')]) self.setupModelData(data.split('\n'), self.rootItem)
class TreeDoc(object): """Tree document class - stores root and has tree utilities""" passwordDict = {} childFieldSepDflt = ', ' rootTitleDefault = _('Main', 'default root title') folderName = _('FOLDER', 'bookmark format folder name') bookmarkName = _('BOOKMARK', 'bookmark format name') separatorName = _('SEPARATOR', 'bookmark format separator name') bookmarkRootTitle = _('Bookmarks') copyFormat = None def __init__(self, filePath=None, setNewDefaults=False, importType=None): """Open filePath (can also be file ref) if given, setNewDefaults uses user defaults for compression & encryption, importType gives an import method to read the file""" globalref.docRef = self self.root = None self.treeFormats = TreeFormats() self.fileInfoItem = TreeItem(None, nodeformat.FileInfoFormat.name) self.fileInfoFormat = None TreeDoc.copyFormat = nodeformat.NodeFormat('_DUMMY__ROOT_', {}, TreeFormats.fieldDefault) self.undoStore = undo.UndoRedoStore() self.redoStore = undo.UndoRedoStore() self.sortFields = [''] self.fileName = '' self.spaceBetween = True self.lineBreaks = True self.formHtml = True self.childFieldSep = TreeDoc.childFieldSepDflt self.spellChkLang = '' self.xlstLink = '' self.xslCssLink = '' self.tlVersion = __version__ self.fileInfoFormat = nodeformat.FileInfoFormat() if filePath: if importType: getattr(self, importType)(filePath) else: self.readFile(filePath) else: self.treeFormats = TreeFormats({}, True) self.root = TreeItem(None, TreeFormats.rootFormatDefault) self.root.setTitle(TreeDoc.rootTitleDefault) self.modified = False if setNewDefaults or not hasattr(self, 'compressFile'): self.compressFile = globalref.options.boolData('CompressNewFiles') self.encryptFile = globalref.options.boolData('EncryptNewFiles') self.selection = treeselection.TreeSelection([self.root]) self.fileInfoFormat.translateFields() self.fileInfoFormat.updateFileInfo() def hasPassword(self, filePath): """Return True if a password is available for filePath""" key = filePath.encode(sys.getfilesystemencoding()) return TreeDoc.passwordDict.has_key(key) def setPassword(self, filePath, password): """Set encrytion password for the filePath""" key = filePath.encode(sys.getfilesystemencoding()) TreeDoc.passwordDict[key] = password.encode('utf-8') def clearPassword(self, filePath): """Remove password for filePath if present""" key = filePath.encode(sys.getfilesystemencoding()) try: del TreeDoc.passwordDict[key] except KeyError: pass def getReadFileObj(self, fileRef): """Return file object and set self.compressFile to False/True, fileRef is either file path or file object""" if not hasattr(fileRef, 'read'): fileRef = file(fileRef.encode(sys.getfilesystemencoding()), 'rb') # binary mode req'd for encryption if hasattr(fileRef, 'seek'): fileRef.seek(0) prefix = fileRef.read(2) if hasattr(fileRef, 'seek'): fileRef.seek(0) else: oldFileRef = fileRef fileRef = StringIO.StringIO(prefix + oldFileRef.read()) fileRef.name = oldFileRef.name oldFileRef.close() if prefix == '\037\213': name = fileRef.name fileRef = gzip.GzipFile(fileobj=fileRef) fileRef.name = name # may already be a gzip object from before password prompt self.compressFile = isinstance(fileRef, gzip.GzipFile) return fileRef def decryptFile(self, fileObj): """Decrypt file if was encrypted""" name = fileObj.name prefix = fileObj.read(len(encryptPrefix)) self.encryptFile = prefix == encryptPrefix if self.encryptFile: password = TreeDoc.passwordDict.get(fileObj.name, '') if not password: fileObj.close() raise PasswordError, 'Missing password' try: text = p3.p3_decrypt(fileObj.read(), password) except p3.CryptError: fileObj.close() raise PasswordError, 'Incorrect password' fileObj.close() fileObj = StringIO.StringIO(text) fileObj.name = name else: fileObj.seek(0) return fileObj def getEncodedFileObj(self, fileRef, encoding, errors): """Return open file object with specified encoding""" return codecs.getreader(encoding)(self.getReadFileObj(fileRef), errors) def getWriteFileObj(self, fileRef, forceCompress): """Return write file object, compressed or not based on forceCompress, but always compress if has .gz extension, fileRef is either file path or file object""" if not hasattr(fileRef, 'read'): fileRef = file(fileRef.encode(sys.getfilesystemencoding()), 'wb') if fileRef.name.endswith('.gz') or forceCompress: name = fileRef.name fileRef = gzip.GzipFile(fileobj=fileRef) fileRef.name = name return fileRef def readFile(self, fileRef): """Open and read file - raise exception on failure, fileRef is either file path or file object""" filePath = hasattr(fileRef, 'read') and \ unicode(fileRef.name, sys.getfilesystemencoding()) or \ fileRef try: f = self.getReadFileObj(fileRef) f = self.decryptFile(f) handler = treexmlparse.TreeSaxHandler(self) input = xml.sax.InputSource() input.setByteStream(f) input.setEncoding('utf-8') reader = xml.sax.make_parser() reader.setContentHandler(handler) reader.setFeature(xml.sax.handler.feature_external_ges, 0) reader.parse(input) except IOError: print 'Error - could not read file', \ filePath.encode(globalref.localTextEncoding) raise except UnicodeError: print 'Error - bad Unicode in file', \ filePath.encode(globalref.localTextEncoding) f.close() raise except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open as treeline file')) f.close() self.root = handler.rootItem self.fileName = filePath self.treeFormats = TreeFormats(handler.formats) self.fileInfoFormat.replaceListFormat() self.treeFormats.updateAutoChoices() self.treeFormats.updateUniqueID() self.treeFormats.updateDerivedTypes() if not self.tlVersion: # file from before 0.12.80, fix number format for format in self.treeFormats.values(): for field in format.fieldList: if field.typeName == 'Number': field.format = field.format.replace(',', '\,') def readTabbed(self, fileRef, errors='strict'): """Import tabbed data into a flat tree - raise exception on failure""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTabbed(fileRef, 'replace') else: f.close() return f.close() bufList = [(text.count('\t', 0, len(text) - len(text.lstrip())), text.strip()) for text in textList if text.strip()] if bufList: buf = bufList.pop(0) if buf[0] == 0: # set default formats ROOT & DEFAULT self.treeFormats = TreeFormats({}, True) newRoot = TreeItem(None, TreeFormats.rootFormatDefault) newRoot.setTitle(buf[1]) if newRoot.loadTabbedChildren(bufList): self.root = newRoot self.fileName = filePath return raise ReadFileError(_('Error in tabbed list')) def readTable(self, fileRef, errors='strict'): """Import table data into a flat tree - raise exception on failure""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTable(fileRef, 'replace') else: f.close() return f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.addTableFields(textList.pop(0).strip().split('\t')) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) lineList = line.strip().split('\t') try: for num in range(len(lineList)): newItem.data[self.treeFormats[TreeFormats.formatDefault]. fieldList[num].name] = lineList[num].strip() except IndexError: print 'Too few headings to read data as a table' raise ReadFileError(_('Too few headings to read data as table')) self.root = newRoot self.fileName = filePath def readLines(self, fileRef, errors='strict'): """Import plain text, node per line""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readLines(fileRef, 'replace') else: f.close() return f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.addTableFields([TreeFormats.textFieldName]) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: line = line.strip() if line: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) newItem.data[TreeFormats.textFieldName] = line self.root = newRoot self.fileName = filePath def readPara(self, fileRef, errors='strict'): """Import plain text, blank line delimitted""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) fullText = f.read().replace('\r', '') except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readPara(fileRef, 'replace') else: f.close() return textList = fullText.split('\n\n') f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.iconName = 'doc' defaultFormat.addTableFields([TreeFormats.textFieldName]) defaultFormat.fieldList[0].numLines = globalref.options.\ intData('MaxEditLines', 1, optiondefaults.maxNumLines) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: line = line.strip() if line: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) newItem.data[TreeFormats.textFieldName] = line self.root = newRoot self.fileName = filePath def readTreepad(self, fileRef, errors='strict'): """Read Treepad text-node file""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.read().split('<end node> 5P9i0s8y19Z') f.close() except UnicodeError: # error common - broken unicode on windows print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTreepad(fileRef, 'replace') else: f.close() return self.treeFormats = TreeFormats() format = nodeformat.NodeFormat(TreeFormats.formatDefault) titleFieldName = _('Title', 'title field name') format.addNewField(titleFieldName) format.addLine(u'{*%s*}' % titleFieldName) numLines = globalref.options.intData('MaxEditLines', 1, optiondefaults.maxNumLines) format.addNewField(TreeFormats.textFieldName, {'lines': repr(numLines)}) format.addLine(u'{*%s*}' % TreeFormats.textFieldName) self.treeFormats[format.name] = format itemList = [] for text in textList: text = text.strip() if text: try: text = text.split('<node>', 1)[1].lstrip() lines = text.split('\n') title = lines[0] level = int(lines[1]) lines = lines[2:] except (ValueError, IndexError): print 'Error - bad file format in %s' % \ filePath.encode(globalref.localTextEncoding) raise ReadFileError(_('Bad file format in %s') % filePath) item = TreeItem(None, format.name) item.data[titleFieldName] = title item.data[TreeFormats.textFieldName] = '\n'.join(lines) item.level = level itemList.append(item) self.root = itemList[0] parentList = [] for item in itemList: if item.level != 0: parentList = parentList[:item.level] item.parent = parentList[-1] parentList[-1].childList.append(item) parentList.append(item) self.root = itemList[0] self.fileName = filePath def createBookmarkFormat(self): """Return a set of formats for bookmark imports""" treeFormats = TreeFormats() format = nodeformat.NodeFormat(TreeDoc.folderName) format.addNewField(TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.iconName = 'folder_3' treeFormats[format.name] = format format = nodeformat.NodeFormat(TreeDoc.bookmarkName) format.addNewField(TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.addNewField(TreeFormats.linkFieldName, {'type': 'URL'}) format.addLine(u'{*%s*}' % TreeFormats.linkFieldName) format.iconName = 'bookmark' treeFormats[format.name] = format format = nodeformat.NodeFormat(TreeDoc.separatorName) format.addNewField(TreeFormats.fieldDefault) format.addLine(u'------------------') format.addLine(u'<hr>') treeFormats[format.name] = format return treeFormats def readXbel(self, fileRef): """Read XBEL format bookmarks""" formats = self.createBookmarkFormat() try: f = self.getReadFileObj(fileRef) filePath = unicode(f.name, sys.getfilesystemencoding()) handler = treexmlparse.\ XbelSaxHandler(formats[TreeDoc.folderName], formats[TreeDoc.bookmarkName], formats[TreeDoc.separatorName]) input = xml.sax.InputSource() input.setByteStream(f) input.setEncoding('utf-8') reader = xml.sax.make_parser() reader.setContentHandler(handler) reader.setFeature(xml.sax.handler.feature_external_ges, 0) reader.parse(input) except UnicodeError: print 'Error - bad Unicode in file', \ filePath.encode(globalref.localTextEncoding) f.close() raise ReadFileError(_('Problem with Unicode characters in file')) except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open as XBEL file')) f.close() if not handler.rootItem: raise ReadFileError(_('Could not open as XBEL file')) self.root = handler.rootItem if not self.root.data.get(TreeFormats.fieldDefault, ''): self.root.data[TreeFormats.fieldDefault] = \ TreeDoc.bookmarkRootTitle self.fileName = filePath self.treeFormats = formats def readMozilla(self, fileRef, errors='strict'): """Read Mozilla HTML format bookmarks""" formats = self.createBookmarkFormat() try: f = self.getEncodedFileObj(fileRef, 'utf-8', errors) filePath = unicode(f.name, sys.getfilesystemencoding()) fullText = f.read() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readMozilla(fileRef, 'replace') else: f.close() return try: handler = treexmlparse.\ HtmlBookmarkHandler(formats[TreeDoc.folderName], formats[TreeDoc.bookmarkName], formats[TreeDoc.separatorName]) handler.feed(fullText) handler.close() except treexmlparse.HtmlParseError: raise ReadFileError(_('Could not open as HTML bookmark file')) if not handler.rootItem: raise ReadFileError(_('Could not open as HTML bookmark file')) self.root = handler.rootItem if not self.root.data.get(TreeFormats.fieldDefault, ''): self.root.data[TreeFormats.fieldDefault] = \ TreeDoc.bookmarkRootTitle self.fileName = filePath self.treeFormats = formats def readXml(self, fileRef): """Read a generic (non-TreeLine) XML file""" try: f = self.getReadFileObj(fileRef) filePath = unicode(f.name, sys.getfilesystemencoding()) handler = treexmlparse.GenericXmlHandler() input = xml.sax.InputSource() input.setByteStream(f) input.setEncoding('utf-8') reader = xml.sax.make_parser() reader.setContentHandler(handler) reader.setFeature(xml.sax.handler.feature_external_ges, 0) reader.parse(input) except UnicodeError: print 'Error - bad Unicode in file', \ filePath.encode(globalref.localTextEncoding) f.close() raise ReadFileError(_('Problem with Unicode characters in file')) except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open XML file')) f.close() if not handler.rootItem: raise ReadFileError(_('Could not open XML file')) self.root = handler.rootItem self.fileName = filePath self.treeFormats = TreeFormats(handler.formats) for format in self.treeFormats.values(): format.fixImportedFormat(treexmlparse.GenericXmlHandler. textFieldName) def readOdf(self, fileRef): """Read an Open Document Format (ODF) file""" self.treeFormats = TreeFormats(None, True) rootItem = TreeItem(None, TreeFormats.rootFormatDefault, TreeDoc.rootTitleDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.addNewField(TreeFormats.textFieldName, {u'html': 'n', u'lines': '6'}) defaultFormat.changeOutputLines([u'<b>{*%s*}</b>' % TreeFormats.fieldDefault, u'{*%s*}' % TreeFormats.textFieldName]) try: f = self.getReadFileObj(fileRef) filePath = unicode(f.name, sys.getfilesystemencoding()) zip = zipfile.ZipFile(f, 'r') text = zip.read('content.xml') handler = treexmlparse.OdfSaxHandler(rootItem, defaultFormat) xml.sax.parseString(text, handler) except (zipfile.BadZipfile, KeyError): f.close() raise ReadFileError(_('Could not unzip ODF file')) except UnicodeError: f.close() raise ReadFileError(_('Problem with Unicode characters in file')) except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open corrupt ODF file')) f.close() self.root = rootItem self.fileName = filePath def readXmlString(self, string): """Read xml string and return top item or None""" try: handler = treexmlparse.TreeSaxHandler(self) xml.sax.parseString(string.encode('utf-8'), handler) except xml.sax.SAXException: return None return handler.rootItem def readXmlStringAndFormat(self, string): """Read xml string and return tuple of top item and new formats""" try: handler = treexmlparse.TreeSaxHandler(self) xml.sax.parseString(string.encode('utf-8'), handler) except xml.sax.SAXException: return (None, []) formats = [format for format in handler.formats.values() if format.name not in self.treeFormats] try: formats.remove(TreeDoc.copyFormat) except ValueError: pass formatNames = [format.name for format in formats] + \ self.treeFormats.keys() for format in formats: if format.genericType not in formatNames: format.genericType = '' return (handler.rootItem, formats) def writeFile(self, fileRef, updateInfo=True): """Write file - raises IOError on failure""" lines = [u'<?xml version="1.0" encoding="utf-8" ?>'] if self.xlstLink: lines.append(u'<?%s?>' % self.xlstLink) lines.extend(self.root.branchXml([], True)) text = '\n'.join(lines).encode('utf-8') try: f = self.getWriteFileObj(fileRef, self.compressFile) except IOError: print 'Error - could not write file' raise filePath = unicode(f.name, sys.getfilesystemencoding()) if self.encryptFile: key = filePath.encode(sys.getfilesystemencoding()) password = TreeDoc.passwordDict.get(key, '') if not password: if key.endswith('~'): # for auto-save filename password = TreeDoc.passwordDict.get(key[:-1], '') if not password: raise PasswordError, 'Missing password' text = encryptPrefix + p3.p3_encrypt(text, password) try: f.write(text) except IOError: print 'Error - could not write file', \ filePath.encode(globalref.localTextEncoding) raise f.close() if filePath.endswith('.gz'): self.compressFile = True if updateInfo: self.modified = False self.tlVersion = __version__ self.fileName = filePath self.fileInfoFormat.updateFileInfo() def exportHtml(self, fileRef, item, includeRoot, openOnly=False, indent=20, addHeader=False): """Save branch as html to file w/o columns""" outGroup = item.outputItemList(includeRoot, openOnly, True) self.exportHtmlColumns(fileRef, outGroup, 1, indent, addHeader) def exportHtmlColumns(self, fileRef, outGroup, numCol=1, indent=20, addHeader=False): """Save contents of outGroup as html to file in columns""" try: f = self.getWriteFileObj(fileRef, False) except IOError: print 'Error - could not write file' raise filePath = unicode(f.name, sys.getfilesystemencoding()) if self.lineBreaks: outGroup.addBreaks() outGroups = outGroup.splitColumns(numCol) for group in outGroups: group.addPrefix() group.addIndents() htmlTitle = os.path.splitext(os.path.basename(filePath))[0] lines = [u'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 '\ 'Transitional//EN">', u'<html>', u'<head>', u'<meta http-equiv="Content-Type" content="text/html; '\ 'charset=utf-8">', u'<title>%s</title>' % htmlTitle, u'<style type="text/css"><!--', u'div {margin-left: %dpx}'\ % indent, u'td {padding: 10px}', u'tr {vertical-align: top}', u'--></style>', u'</head>', u'<body>'] if addHeader: header = self.fileInfoFormat.getHeaderFooter(True) if header: lines.append(header) lines.extend([u'<table>', u'<tr><td>']) for item in outGroups[0]: lines.extend(item.textLines) for group in outGroups[1:]: lines.append(u'</td><td>') for item in group: lines.extend(item.textLines) lines.extend([u'</td></tr>', u'</table>']) if addHeader: footer = self.fileInfoFormat.getHeaderFooter(False) if footer: lines.append(footer) lines.extend([u'</body>', u'</html>']) try: f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file', \ filePath.encode(globalref.localTextEncoding) raise f.close() def exportDirTable(self, dirName, nodeList, addHeader=False): """Write tree to nested directory struct with html tables""" oldDir = os.getcwd() os.chdir(dirName.encode(sys.getfilesystemencoding())) if addHeader: header = self.fileInfoFormat.getHeaderFooter(True) footer = self.fileInfoFormat.getHeaderFooter(False) else: header = footer = '' if len(nodeList) > 1: self.treeFormats.addIfMissing(TreeDoc.copyFormat) item = TreeItem(None, TreeDoc.copyFormat.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] linkDict = {} item.createDirTableLinkDict(linkDict, os.getcwd()) item.exportDirTable(linkDict, None, header, footer) self.treeFormats.removeQuiet(TreeDoc.copyFormat) os.chdir(oldDir) def exportDirPage(self, dirName, nodeList): """Write tree to nested direct struct with html page for each node""" oldDir = os.getcwd() os.chdir(dirName.encode(sys.getfilesystemencoding())) cssLines = ['#sidebar {', 'width: 16em;', 'float: left;', 'clear: left;', 'border-right: 1px solid black;', 'margin-right: 1em;', '}'] try: f = codecs.open('default.css', 'w', 'utf-8') f.writelines([(line + '\n').encode('utf-8') for line in cssLines]) except (IOError, UnicodeError): print 'Error - could not write file to default.css' raise IOError(_('Error - cannot write file to %s') % 'default.css') f.close() if len(nodeList) > 1: self.treeFormats.addIfMissing(TreeDoc.copyFormat) item = TreeItem(None, TreeDoc.copyFormat.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] linkDict = {} item.createDirPageLinkDict(linkDict, os.getcwd()) item.exportDirPage(linkDict) self.treeFormats.removeQuiet(TreeDoc.copyFormat) os.chdir(oldDir) def exportXslt(self, fileRef, includeRoot, indent=20): """Write XSLT file and add link in treeline file""" try: f = self.getWriteFileObj(fileRef, False) except IOError: print 'Error - could not write file' raise filePath = unicode(f.name, sys.getfilesystemencoding()) title = os.path.splitext(os.path.basename(filePath))[0] lines = [u'<xsl:stylesheet version="1.0" '\ u'xmlns:xsl="http://www.w3.org/1999/XSL/Transform">', u"<xsl:output method='html'/>", u'<xsl:template match="/">', u'<html>', u'<head>'] if self.xslCssLink: lines.append('<link rel="stylesheet" href="%s" type="text/css"/>' % self.xslCssLink) lines.extend([u'<title>%s</title>' % title, u'</head>', u'<body>', u'<xsl:apply-templates/>', u'</body>', u'</html>', u'</xsl:template>']) if not includeRoot: lines.extend([u'', u'<xsl:template match="/%s">' % self.root.formatName, u'<xsl:apply-templates/>', u'</xsl:template>']) for formatName in self.treeFormats: lines.extend(self.treeFormats[formatName]. xsltTemplate(indent, True)) lines.extend([u'', u'<xsl:template match="*" />', u'', u'</xsl:stylesheet>']) try: f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file', \ filePath.encode(globalref.localTextEncoding) raise f.close() # find relative link path trlPath = os.path.abspath(self.fileName).split(os.sep) xslPath = os.path.abspath(filePath).split(os.sep) while trlPath[0] == xslPath[0]: del trlPath[0] del xslPath[0] xslPath = '/'.join(['..'] * (len(trlPath) - len(xslPath)) + xslPath) link = u'xml-stylesheet type="text/xsl" href="%s"' % xslPath if self.xlstLink != link: self.xlstLink = link self.modified = True def exportTrlSubtree(self, fileRef, nodeList, addBranches=True): """Write subtree TRL file starting form item""" lines = [u'<?xml version="1.0" encoding="utf-8" ?>'] if self.xlstLink: lines.append(u'<?%s?>' % self.xlstLink) if not addBranches: newList = [] for item in nodeList: # replace items with childless items newItem = TreeItem(item.parent, item.formatName) newItem.data = item.data newList.append(newItem) nodeList = newList if len(nodeList) > 1: format = nodeformat.NodeFormat(TreeFormats.rootFormatDefault, {}, TreeFormats.fieldDefault) self.treeFormats.addIfMissing(format) item = TreeItem(None, format.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] lines.extend(item.branchXml([], True)) try: f = self.getWriteFileObj(fileRef, self.compressFile) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' self.treeFormats.removeQuiet(TreeDoc.copyFormat) raise f.close() self.treeFormats.removeQuiet(TreeDoc.copyFormat) def exportTable(self, fileRef, nodeList, addBranches=True): """Write data to table for nodes or children of nodes""" if addBranches: newList = [] for item in nodeList: newList.extend(item.childList) nodeList = newList typeList = [] headings = [] tableList = [] for item in nodeList: itemFormat = item.nodeFormat() if itemFormat not in typeList: for field in itemFormat.fieldNames(): if field not in headings: headings.append(field) typeList.append(itemFormat) tableList.append(u'\t'.join([item.data.get(head, '') for head in headings])) tableList.insert(0, u'\t'.join([head for head in headings])) try: text = os.linesep.join(tableList).\ encode(globalref.localTextEncoding, 'strict') except (ValueError, UnicodeError): print 'Warning - bad unicode characters were replaced' text = os.linesep.join(tableList).\ encode(globalref.localTextEncoding, 'replace') try: f = self.getWriteFileObj(fileRef, False) f.write(text) except IOError: print 'Error - could not write file' raise f.close() def exportTabbedTitles(self, fileRef, nodeList, addBranches=True, includeRoot=True, openOnly=False): """Write tabbed titles for descendants of item""" if addBranches: initLevel = not includeRoot and -1 or 0 titleList = [] for item in nodeList: itemList = item.exportToText(initLevel, openOnly) if not includeRoot: del itemList[0] titleList.extend(itemList) else: titleList = [item.title() for item in nodeList] try: text = os.linesep.join(titleList).\ encode(globalref.localTextEncoding, 'strict') except (ValueError, UnicodeError): print 'Warning - bad unicode characters were replaced' text = os.linesep.join(titleList).\ encode(globalref.localTextEncoding, 'replace') try: f = self.getWriteFileObj(fileRef, False) f.write(text) except IOError: print 'Error - could not write file' raise f.close() def exportXbel(self, fileRef, nodeList, addBranches=True): """Export XBEL bookmarks""" if len(nodeList) > 1 or not addBranches: title = TreeDoc.bookmarkRootTitle level = 1 else: title = xml.sax.saxutils.escape(nodeList[0].title(), escDict) level = 0 lines = [u'<!DOCTYPE xbel>', u'<xbel>', u'<title>%s</title>' % title] for item in nodeList: lines.extend(item.exportXbelBookmarks(level, addBranches)) lines.append(u'</xbel>') try: f = self.getWriteFileObj(fileRef, False) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' raise f.close() def exportHtmlBookmarks(self, fileRef, nodeList, addBranches=True): """Export HTML bookmarks""" if len(nodeList) > 1 or not addBranches: title = TreeDoc.bookmarkRootTitle level = 1 else: title = xml.sax.saxutils.escape(nodeList[0].title()) level = 0 lines = [u'<!DOCTYPE NETSCAPE-Bookmark-file-1>', u'<META HTTP-EQUIV="Content-Type" CONTENT="text/html; '\ 'charset=UTF-8">', u'<TITLE>%s</TITLE>' % title, u'<H1>%s</H1>' % title, ''] for item in nodeList: lines.extend(item.exportHtmlBookmarks(level, addBranches)) try: f = self.getWriteFileObj(fileRef, False) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' raise f.close() def exportGenericXml(self, fileRef, nodeList, addBranches=True): """Export generic XML""" lines = [u'<?xml version="1.0" encoding="utf-8" ?>'] level = 0 if len(nodeList) > 1: lines.append(u'<%s>' % TreeFormats.rootFormatDefault) level = 1 for item in nodeList: lines.extend(item.exportGenericXml(treexmlparse.GenericXmlHandler. textFieldName, level)) if len(nodeList) > 1: lines.append(u'</%s>' % TreeFormats.rootFormatDefault) try: f = self.getWriteFileObj(fileRef, False) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' raise f.close() def exportOdf(self, fileRef, nodeList, fontName, fontSize, fontFixed=False, addBranches=True, includeRoot=True, openOnly=False): """Export an ODF format text file""" TreeItem.maxLevel = 0 commonHeader = u'<?xml version="1.0" encoding="utf-8" ?>' commonAttr = u' office:version="1.0" '\ 'xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:'\ 'xsl-fo-compatible:1.0" '\ 'xmlns:office="urn:oasis:names:tc:opendocument:'\ 'xmlns:office:1.0" '\ 'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:'\ 'style:1.0" '\ 'xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:'\ 'svg-compatible:1.0" '\ 'xmlns:text="urn:oasis:names:tc:opendocument:'\ 'xmlns:text:1.0">' pitch = fontFixed and 'fixed' or 'variable' sizeDelta = 2 fontDecl = [u'<office:font-face-decls>', u'<style:font-face style:font-pitch="%s" style:name="%s" '\ 'svg:font-family="%s"/>' % (pitch, fontName, fontName), u'</office:font-face-decls>'] lines = [commonHeader, u'<office:document-content' + commonAttr] lines.extend(fontDecl) lines.extend([u'<office:body>', u'<office:text>']) for item in nodeList: lines.extend(item.exportOdf(1, addBranches, includeRoot, openOnly)) lines.extend([u'</office:text>', u'</office:body>', u'</office:document-content>']) manifest = [commonHeader, u'<manifest:manifest xmlns:manifest="urn:oasis:names:tc:'\ 'opendocument:xmlns:manifest:1.0">', u'<manifest:file-entry manifest:media-type="application/'\ 'vnd.oasis.opendocument.text" manifest:full-path="/"/>', u'<manifest:file-entry manifest:media-type="text/xml" '\ 'manifest:full-path="content.xml"/>', u'<manifest:file-entry manifest:media-type="text/xml" '\ 'manifest:full-path="styles.xml"/>', u'</manifest:manifest>'] styles = [commonHeader, u'<office:document-styles' + commonAttr] styles.extend(fontDecl) styles.extend([u'<office:styles>', u'<style:default-style style:family="paragraph">', u'<style:paragraph-properties '\ 'style:writing-mode="page"/>', u'<style:text-properties fo:font-size="%dpt" '\ 'fo:hyphenate="false" style:font-name="%s"/>' % (fontSize, fontName), u'</style:default-style>', u'<style:style style:name="Standard" '\ 'style:class="text" style:family="paragraph"/>', u'<style:style style:name="Text_20_body" '\ 'style:display-name="Text body" style:class="text" '\ 'style:family="paragraph" '\ 'style:parent-style-name="Standard">', u'<style:paragraph-properties '\ 'fo:margin-bottom="6.0pt"/>', u'</style:style>', u'<style:style style:name="Heading" '\ 'style:class="text" style:family="paragraph" '\ 'style:next-style-name="Text_20_body" '\ 'style:parent-style-name="Standard">', u'<style:paragraph-properties '\ 'fo:keep-with-next="always" fo:margin-bottom="6.0pt" '\ 'fo:margin-top="12.0pt"/>', u'<style:text-properties fo:font-size="%dpt" '\ 'style:font-name="%s"/>' % (fontSize + sizeDelta, fontName), u'</style:style>']) outline = [u'<text:outline-style>'] for level in range(1, TreeItem.maxLevel + 1): size = fontSize if level <= 2: size += 2 * sizeDelta elif level <=4: size += sizeDelta italic = ' ' if level % 2 == 0: italic = 'fo:font-style="italic" ' styles.extend([u'<style:style style:name="Heading_20_%d" '\ 'style:display-name="Heading %d" '\ 'style:class="text" style:family="paragraph" '\ 'style:parent-style-name="Heading" '\ 'style:default-outline-level="%d">' % \ (level, level, level), u'<style:text-properties fo:font-size="%dpt" '\ '%s fo:font-weight="bold"/>' % \ (size, italic), u'</style:style>']) outline.append(u'<text:outline-level-style text:level="%d" '\ 'style:num-format=""/>' % level) styles.extend(outline) styles.extend([u'</text:outline-style>', u'</office:styles>', u'<office:automatic-styles>', u'<style:page-layout style:name="pm1">', u'<style:page-layout-properties '\ 'fo:margin-bottom="0.75in" fo:margin-left="0.75in" '\ 'fo:margin-right="0.75in" fo:margin-top="0.75in" '\ 'fo:page-height="11in" fo:page-width="8.5in" '\ 'style:print-orientation="portrait"/>', u'</style:page-layout>', u'</office:automatic-styles>', u'<office:master-styles>', u'<style:master-page style:name="Standard" '\ 'style:page-layout-name="pm1"/>', u'</office:master-styles>', u'</office:document-styles>']) try: f = zipfile.ZipFile(fileRef, 'w', zipfile.ZIP_DEFLATED) f.writestr('content.xml', u'\n'.join(lines).encode('utf-8') + '\n') f.writestr('META-INF/manifest.xml', u'\n'.join(manifest) + '\n') f.writestr('styles.xml', u'\n'.join(styles) + '\n') except IOError: print 'Error - could not write file' raise f.close()
class TreeDoc(object): """Tree document class - stores root and has tree utilities""" passwordDict = {} childFieldSepDflt = ', ' rootTitleDefault = _('Main', 'default root title') folderName = _('FOLDER', 'bookmark format folder name') bookmarkName = _('BOOKMARK', 'bookmark format name') separatorName = _('SEPARATOR', 'bookmark format separator name') bookmarkRootTitle = _('Bookmarks') copyFormat = None def __init__(self, filePath=None, setNewDefaults=False, importType=None): """Open filePath (can also be file ref) if given, setNewDefaults uses user defaults for compression & encryption, importType gives an import method to read the file""" globalref.docRef = self self.root = None self.treeFormats = TreeFormats() self.fileInfoItem = TreeItem(None, nodeformat.FileInfoFormat.name) self.fileInfoFormat = None TreeDoc.copyFormat = nodeformat.NodeFormat('_DUMMY__ROOT_', {}, TreeFormats.fieldDefault) self.undoStore = undo.UndoRedoStore() self.redoStore = undo.UndoRedoStore() self.sortFields = [''] self.fileName = '' self.spaceBetween = True self.lineBreaks = True self.formHtml = True self.childFieldSep = TreeDoc.childFieldSepDflt self.spellChkLang = '' self.xlstLink = '' self.xslCssLink = '' self.tlVersion = __version__ self.fileInfoFormat = nodeformat.FileInfoFormat() if filePath: if importType: getattr(self, importType)(filePath) else: self.readFile(filePath) else: self.treeFormats = TreeFormats({}, True) self.root = TreeItem(None, TreeFormats.rootFormatDefault) self.root.setTitle(TreeDoc.rootTitleDefault) self.modified = False if setNewDefaults or not hasattr(self, 'compressFile'): self.compressFile = globalref.options.boolData('CompressNewFiles') self.encryptFile = globalref.options.boolData('EncryptNewFiles') elif not hasattr(self, 'encryptFile'): self.encryptFile = False self.selection = treeselection.TreeSelection([self.root]) self.fileInfoFormat.translateFields() self.fileInfoFormat.updateFileInfo() def hasPassword(self, filePath): """Return True if a password is available for filePath""" key = filePath.encode(sys.getfilesystemencoding()) return TreeDoc.passwordDict.has_key(key) def setPassword(self, filePath, password): """Set encrytion password for the filePath""" key = filePath.encode(sys.getfilesystemencoding()) TreeDoc.passwordDict[key] = password.encode('utf-8') def clearPassword(self, filePath): """Remove password for filePath if present""" key = filePath.encode(sys.getfilesystemencoding()) try: del TreeDoc.passwordDict[key] except KeyError: pass def getReadFileObj(self, fileRef): """Return file object and set self.compressFile to False/True, fileRef is either file path or file object""" if not hasattr(fileRef, 'read'): fileRef = file(fileRef.encode(sys.getfilesystemencoding()), 'rb') # binary mode req'd for encryption if hasattr(fileRef, 'seek'): fileRef.seek(0) prefix = fileRef.read(2) if hasattr(fileRef, 'seek'): fileRef.seek(0) else: oldFileRef = fileRef fileRef = StringIO.StringIO(prefix + oldFileRef.read()) fileRef.name = oldFileRef.name oldFileRef.close() if prefix == '\037\213': name = fileRef.name fileRef = gzip.GzipFile(fileobj=fileRef) fileRef.name = name # may already be a gzip object from before password prompt self.compressFile = isinstance(fileRef, gzip.GzipFile) return fileRef def decryptFile(self, fileObj): """Decrypt file if was encrypted""" name = fileObj.name prefix = fileObj.read(len(encryptPrefix)) self.encryptFile = prefix == encryptPrefix if self.encryptFile: password = TreeDoc.passwordDict.get(fileObj.name, '') if not password: fileObj.close() raise PasswordError, 'Missing password' try: text = p3.p3_decrypt(fileObj.read(), password) except p3.CryptError: fileObj.close() raise PasswordError, 'Incorrect password' fileObj.close() fileObj = StringIO.StringIO(text) fileObj.name = name else: fileObj.seek(0) return fileObj def getEncodedFileObj(self, fileRef, encoding, errors): """Return open file object with specified encoding""" return codecs.getreader(encoding)(self.getReadFileObj(fileRef), errors) def getWriteFileObj(self, fileRef, forceCompress): """Return write file object, compressed or not based on forceCompress, but always compress if has .gz extension, fileRef is either file path or file object""" if not hasattr(fileRef, 'read'): fileRef = file(fileRef.encode(sys.getfilesystemencoding()), 'wb') if fileRef.name.endswith('.gz') or forceCompress: name = fileRef.name fileRef = gzip.GzipFile(fileobj=fileRef) fileRef.name = name return fileRef def readFile(self, fileRef): """Open and read file - raise exception on failure, fileRef is either file path or file object""" filePath = hasattr(fileRef, 'read') and \ unicode(fileRef.name, sys.getfilesystemencoding()) or \ fileRef try: f = self.getReadFileObj(fileRef) f = self.decryptFile(f) handler = treexmlparse.TreeSaxHandler(self) input = xml.sax.InputSource() input.setByteStream(f) input.setEncoding('utf-8') reader = xml.sax.make_parser() reader.setContentHandler(handler) reader.setFeature(xml.sax.handler.feature_external_ges, 0) reader.parse(input) except IOError: print 'Error - could not read file', \ filePath.encode(globalref.localTextEncoding) raise except UnicodeError: print 'Error - bad Unicode in file', \ filePath.encode(globalref.localTextEncoding) f.close() raise except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open as treeline file')) f.close() self.root = handler.rootItem self.fileName = filePath self.treeFormats = TreeFormats(handler.formats) self.fileInfoFormat.replaceListFormat() self.treeFormats.updateAutoChoices() self.treeFormats.updateUniqueID() self.treeFormats.updateDerivedTypes() if not self.tlVersion: # file from before 0.12.80, fix number format for format in self.treeFormats.values(): for field in format.fieldList: if field.typeName == 'Number': field.format = field.format.replace(',', '\,') def readTabbed(self, fileRef, errors='strict'): """Import tabbed data into a flat tree - raise exception on failure""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTabbed(fileRef, 'replace') else: f.close() return f.close() bufList = [(text.count('\t', 0, len(text) - len(text.lstrip())), text.strip()) for text in textList if text.strip()] if bufList: buf = bufList.pop(0) if buf[0] == 0: # set default formats ROOT & DEFAULT self.treeFormats = TreeFormats({}, True) newRoot = TreeItem(None, TreeFormats.rootFormatDefault) newRoot.setTitle(buf[1]) if newRoot.loadTabbedChildren(bufList): self.root = newRoot self.fileName = filePath return raise ReadFileError(_('Error in tabbed list')) def readTable(self, fileRef, errors='strict'): """Import table data into a flat tree - raise exception on failure""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTable(fileRef, 'replace') else: f.close() return f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.addTableFields(textList.pop(0).strip().split('\t')) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) lineList = line.strip().split('\t') try: for num in range(len(lineList)): newItem.data[self.treeFormats[TreeFormats.formatDefault]. fieldList[num].name] = lineList[num].strip() except IndexError: print 'Too few headings to read data as a table' raise ReadFileError( _('Too few headings to read data as table')) self.root = newRoot self.fileName = filePath def readLines(self, fileRef, errors='strict'): """Import plain text, node per line""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.readlines() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readLines(fileRef, 'replace') else: f.close() return f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.addTableFields([TreeFormats.textFieldName]) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: line = line.strip() if line: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) newItem.data[TreeFormats.textFieldName] = line self.root = newRoot self.fileName = filePath def readPara(self, fileRef, errors='strict'): """Import plain text, blank line delimitted""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) fullText = f.read().replace('\r', '') except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readPara(fileRef, 'replace') else: f.close() return textList = fullText.split('\n\n') f.close() self.treeFormats = TreeFormats({}, True) # set defaults ROOT & DEFAULT newRoot = TreeItem(None, TreeFormats.rootFormatDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.fieldList = [] defaultFormat.lineList = [] defaultFormat.iconName = 'doc' defaultFormat.addTableFields([TreeFormats.textFieldName]) defaultFormat.fieldList[0].numLines = globalref.options.\ intData('MaxEditLines', 1, optiondefaults.maxNumLines) newRoot.setTitle(TreeDoc.rootTitleDefault) for line in textList: line = line.strip() if line: newItem = TreeItem(newRoot, TreeFormats.formatDefault) newRoot.childList.append(newItem) newItem.data[TreeFormats.textFieldName] = line self.root = newRoot self.fileName = filePath def readTreepad(self, fileRef, errors='strict'): """Read Treepad text-node file""" try: f = self.getEncodedFileObj(fileRef, globalref.localTextEncoding, errors) filePath = unicode(f.name, sys.getfilesystemencoding()) textList = f.read().split('<end node> 5P9i0s8y19Z') f.close() except UnicodeError: # error common - broken unicode on windows print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readTreepad(fileRef, 'replace') else: f.close() return self.treeFormats = TreeFormats() format = nodeformat.NodeFormat(TreeFormats.formatDefault) titleFieldName = _('Title', 'title field name') format.addNewField(titleFieldName) format.addLine(u'{*%s*}' % titleFieldName) numLines = globalref.options.intData('MaxEditLines', 1, optiondefaults.maxNumLines) format.addNewField(TreeFormats.textFieldName, {'lines': repr(numLines)}) format.addLine(u'{*%s*}' % TreeFormats.textFieldName) self.treeFormats[format.name] = format itemList = [] for text in textList: text = text.strip() if text: try: text = text.split('<node>', 1)[1].lstrip() lines = text.split('\n') title = lines[0] level = int(lines[1]) lines = lines[2:] except (ValueError, IndexError): print 'Error - bad file format in %s' % \ filePath.encode(globalref.localTextEncoding) raise ReadFileError(_('Bad file format in %s') % filePath) item = TreeItem(None, format.name) item.data[titleFieldName] = title item.data[TreeFormats.textFieldName] = '\n'.join(lines) item.level = level itemList.append(item) self.root = itemList[0] parentList = [] for item in itemList: if item.level != 0: parentList = parentList[:item.level] item.parent = parentList[-1] parentList[-1].childList.append(item) parentList.append(item) self.root = itemList[0] self.fileName = filePath def createBookmarkFormat(self): """Return a set of formats for bookmark imports""" treeFormats = TreeFormats() format = nodeformat.NodeFormat(TreeDoc.folderName) format.addNewField(TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.iconName = 'folder_3' treeFormats[format.name] = format format = nodeformat.NodeFormat(TreeDoc.bookmarkName) format.addNewField(TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.addLine(u'{*%s*}' % TreeFormats.fieldDefault) format.addNewField(TreeFormats.linkFieldName, {'type': 'URL'}) format.addLine(u'{*%s*}' % TreeFormats.linkFieldName) format.iconName = 'bookmark' treeFormats[format.name] = format format = nodeformat.NodeFormat(TreeDoc.separatorName) format.addNewField(TreeFormats.fieldDefault) format.addLine(u'------------------') format.addLine(u'<hr>') treeFormats[format.name] = format return treeFormats def readXbel(self, fileRef): """Read XBEL format bookmarks""" formats = self.createBookmarkFormat() try: f = self.getReadFileObj(fileRef) filePath = unicode(f.name, sys.getfilesystemencoding()) handler = treexmlparse.\ XbelSaxHandler(formats[TreeDoc.folderName], formats[TreeDoc.bookmarkName], formats[TreeDoc.separatorName]) input = xml.sax.InputSource() input.setByteStream(f) input.setEncoding('utf-8') reader = xml.sax.make_parser() reader.setContentHandler(handler) reader.setFeature(xml.sax.handler.feature_external_ges, 0) reader.parse(input) except UnicodeError: print 'Error - bad Unicode in file', \ filePath.encode(globalref.localTextEncoding) f.close() raise ReadFileError(_('Problem with Unicode characters in file')) except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open as XBEL file')) f.close() if not handler.rootItem: raise ReadFileError(_('Could not open as XBEL file')) self.root = handler.rootItem if not self.root.data.get(TreeFormats.fieldDefault, ''): self.root.data[TreeFormats.fieldDefault] = \ TreeDoc.bookmarkRootTitle self.fileName = filePath self.treeFormats = formats def readMozilla(self, fileRef, errors='strict'): """Read Mozilla HTML format bookmarks""" formats = self.createBookmarkFormat() try: f = self.getEncodedFileObj(fileRef, 'utf-8', errors) filePath = unicode(f.name, sys.getfilesystemencoding()) fullText = f.read() except UnicodeError: print 'Warning - bad unicode characters were replaced' if errors == 'strict': self.readMozilla(fileRef, 'replace') else: f.close() return try: handler = treexmlparse.\ HtmlBookmarkHandler(formats[TreeDoc.folderName], formats[TreeDoc.bookmarkName], formats[TreeDoc.separatorName]) handler.feed(fullText) handler.close() except treexmlparse.HtmlParseError: raise ReadFileError(_('Could not open as HTML bookmark file')) if not handler.rootItem: raise ReadFileError(_('Could not open as HTML bookmark file')) self.root = handler.rootItem if not self.root.data.get(TreeFormats.fieldDefault, ''): self.root.data[TreeFormats.fieldDefault] = \ TreeDoc.bookmarkRootTitle self.fileName = filePath self.treeFormats = formats def readXml(self, fileRef): """Read a generic (non-TreeLine) XML file""" try: f = self.getReadFileObj(fileRef) filePath = unicode(f.name, sys.getfilesystemencoding()) handler = treexmlparse.GenericXmlHandler() input = xml.sax.InputSource() input.setByteStream(f) input.setEncoding('utf-8') reader = xml.sax.make_parser() reader.setContentHandler(handler) reader.setFeature(xml.sax.handler.feature_external_ges, 0) reader.parse(input) except UnicodeError: print 'Error - bad Unicode in file', \ filePath.encode(globalref.localTextEncoding) f.close() raise ReadFileError(_('Problem with Unicode characters in file')) except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open XML file')) f.close() if not handler.rootItem: raise ReadFileError(_('Could not open XML file')) self.root = handler.rootItem self.fileName = filePath self.treeFormats = TreeFormats(handler.formats) for format in self.treeFormats.values(): format.fixImportedFormat( treexmlparse.GenericXmlHandler.textFieldName) def readOdf(self, fileRef): """Read an Open Document Format (ODF) file""" self.treeFormats = TreeFormats(None, True) rootItem = TreeItem(None, TreeFormats.rootFormatDefault, TreeDoc.rootTitleDefault) defaultFormat = self.treeFormats[TreeFormats.formatDefault] defaultFormat.addNewField(TreeFormats.textFieldName, { u'html': 'n', u'lines': '6' }) defaultFormat.changeOutputLines([ u'<b>{*%s*}</b>' % TreeFormats.fieldDefault, u'{*%s*}' % TreeFormats.textFieldName ]) try: f = self.getReadFileObj(fileRef) filePath = unicode(f.name, sys.getfilesystemencoding()) zip = zipfile.ZipFile(f, 'r') text = zip.read('content.xml') handler = treexmlparse.OdfSaxHandler(rootItem, defaultFormat) xml.sax.parseString(text, handler) except (zipfile.BadZipfile, KeyError): f.close() raise ReadFileError(_('Could not unzip ODF file')) except UnicodeError: f.close() raise ReadFileError(_('Problem with Unicode characters in file')) except xml.sax.SAXException: f.close() raise ReadFileError(_('Could not open corrupt ODF file')) f.close() self.root = rootItem self.fileName = filePath def readXmlString(self, string): """Read xml string and return top item or None""" try: handler = treexmlparse.TreeSaxHandler(self) xml.sax.parseString(string.encode('utf-8'), handler) except xml.sax.SAXException: return None return handler.rootItem def readXmlStringAndFormat(self, string): """Read xml string and return tuple of top item and new formats""" try: handler = treexmlparse.TreeSaxHandler(self) xml.sax.parseString(string.encode('utf-8'), handler) except xml.sax.SAXException: return (None, []) formats = [ format for format in handler.formats.values() if format.name not in self.treeFormats ] try: formats.remove(TreeDoc.copyFormat) except ValueError: pass formatNames = [format.name for format in formats] + \ self.treeFormats.keys() for format in formats: if format.genericType not in formatNames: format.genericType = '' return (handler.rootItem, formats) def writeFile(self, fileRef, updateInfo=True): """Write file - raises IOError on failure""" lines = [u'<?xml version="1.0" encoding="utf-8" ?>'] if self.xlstLink: lines.append(u'<?%s?>' % self.xlstLink) lines.extend(self.root.branchXml([], True)) text = '\n'.join(lines).encode('utf-8') try: f = self.getWriteFileObj(fileRef, self.compressFile) except IOError: print 'Error - could not write file' raise filePath = unicode(f.name, sys.getfilesystemencoding()) if self.encryptFile: key = filePath.encode(sys.getfilesystemencoding()) password = TreeDoc.passwordDict.get(key, '') if not password: if key.endswith('~'): # for auto-save filename password = TreeDoc.passwordDict.get(key[:-1], '') if not password: raise PasswordError, 'Missing password' text = encryptPrefix + p3.p3_encrypt(text, password) try: f.write(text) except IOError: print 'Error - could not write file', \ filePath.encode(globalref.localTextEncoding) raise f.close() if filePath.endswith('.gz'): self.compressFile = True if updateInfo: self.modified = False self.tlVersion = __version__ self.fileName = filePath self.fileInfoFormat.updateFileInfo() def exportHtml(self, fileRef, item, includeRoot, openOnly=False, indent=20, addHeader=False): """Save branch as html to file w/o columns""" outGroup = item.outputItemList(includeRoot, openOnly, True) self.exportHtmlColumns(fileRef, outGroup, 1, indent, addHeader) def exportHtmlColumns(self, fileRef, outGroup, numCol=1, indent=20, addHeader=False): """Save contents of outGroup as html to file in columns""" try: f = self.getWriteFileObj(fileRef, False) except IOError: print 'Error - could not write file' raise filePath = unicode(f.name, sys.getfilesystemencoding()) if self.lineBreaks: outGroup.addBreaks() outGroups = outGroup.splitColumns(numCol) for group in outGroups: group.addPrefix() group.addIndents() htmlTitle = os.path.splitext(os.path.basename(filePath))[0] lines = [u'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 '\ 'Transitional//EN">', u'<html>', u'<head>', u'<meta http-equiv="Content-Type" content="text/html; '\ 'charset=utf-8">', u'<title>%s</title>' % htmlTitle, u'<style type="text/css"><!--', u'div {margin-left: %dpx}'\ % indent, u'td {padding: 10px}', u'tr {vertical-align: top}', u'--></style>', u'</head>', u'<body>'] if addHeader: header = self.fileInfoFormat.getHeaderFooter(True) if header: lines.append(header) lines.extend([u'<table>', u'<tr><td>']) for item in outGroups[0]: lines.extend(item.textLines) for group in outGroups[1:]: lines.append(u'</td><td>') for item in group: lines.extend(item.textLines) lines.extend([u'</td></tr>', u'</table>']) if addHeader: footer = self.fileInfoFormat.getHeaderFooter(False) if footer: lines.append(footer) lines.extend([u'</body>', u'</html>']) try: f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file', \ filePath.encode(globalref.localTextEncoding) raise f.close() def exportDirTable(self, dirName, nodeList, addHeader=False): """Write tree to nested directory struct with html tables""" oldDir = os.getcwd() os.chdir(dirName.encode(sys.getfilesystemencoding())) if addHeader: header = self.fileInfoFormat.getHeaderFooter(True) footer = self.fileInfoFormat.getHeaderFooter(False) else: header = footer = '' if len(nodeList) > 1: self.treeFormats.addIfMissing(TreeDoc.copyFormat) item = TreeItem(None, TreeDoc.copyFormat.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] linkDict = {} item.createDirTableLinkDict(linkDict, os.getcwd()) item.exportDirTable(linkDict, None, header, footer) self.treeFormats.removeQuiet(TreeDoc.copyFormat) os.chdir(oldDir) def exportDirPage(self, dirName, nodeList): """Write tree to nested direct struct with html page for each node""" oldDir = os.getcwd() os.chdir(dirName.encode(sys.getfilesystemencoding())) cssLines = [ '#sidebar {', 'width: 16em;', 'float: left;', 'clear: left;', 'border-right: 1px solid black;', 'margin-right: 1em;', '}' ] try: f = codecs.open('default.css', 'w', 'utf-8') f.writelines([(line + '\n').encode('utf-8') for line in cssLines]) except (IOError, UnicodeError): print 'Error - could not write file to default.css' raise IOError(_('Error - cannot write file to %s') % 'default.css') f.close() if len(nodeList) > 1: self.treeFormats.addIfMissing(TreeDoc.copyFormat) item = TreeItem(None, TreeDoc.copyFormat.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] linkDict = {} item.createDirPageLinkDict(linkDict, os.getcwd()) item.exportDirPage(linkDict) self.treeFormats.removeQuiet(TreeDoc.copyFormat) os.chdir(oldDir) def exportXslt(self, fileRef, includeRoot, indent=20): """Write XSLT file and add link in treeline file""" try: f = self.getWriteFileObj(fileRef, False) except IOError: print 'Error - could not write file' raise filePath = unicode(f.name, sys.getfilesystemencoding()) title = os.path.splitext(os.path.basename(filePath))[0] lines = [u'<xsl:stylesheet version="1.0" '\ u'xmlns:xsl="http://www.w3.org/1999/XSL/Transform">', u"<xsl:output method='html'/>", u'<xsl:template match="/">', u'<html>', u'<head>'] if self.xslCssLink: lines.append('<link rel="stylesheet" href="%s" type="text/css"/>' % self.xslCssLink) lines.extend([ u'<title>%s</title>' % title, u'</head>', u'<body>', u'<xsl:apply-templates/>', u'</body>', u'</html>', u'</xsl:template>' ]) if not includeRoot: lines.extend([ u'', u'<xsl:template match="/%s">' % self.root.formatName, u'<xsl:apply-templates/>', u'</xsl:template>' ]) for formatName in self.treeFormats: lines.extend(self.treeFormats[formatName].xsltTemplate( indent, True)) lines.extend( [u'', u'<xsl:template match="*" />', u'', u'</xsl:stylesheet>']) try: f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file', \ filePath.encode(globalref.localTextEncoding) raise f.close() # find relative link path trlPath = os.path.abspath(self.fileName).split(os.sep) xslPath = os.path.abspath(filePath).split(os.sep) while trlPath[0] == xslPath[0]: del trlPath[0] del xslPath[0] xslPath = '/'.join(['..'] * (len(trlPath) - len(xslPath)) + xslPath) link = u'xml-stylesheet type="text/xsl" href="%s"' % xslPath if self.xlstLink != link: self.xlstLink = link self.modified = True def exportTrlSubtree(self, fileRef, nodeList, addBranches=True): """Write subtree TRL file starting form item""" lines = [u'<?xml version="1.0" encoding="utf-8" ?>'] if self.xlstLink: lines.append(u'<?%s?>' % self.xlstLink) if not addBranches: newList = [] for item in nodeList: # replace items with childless items newItem = TreeItem(item.parent, item.formatName) newItem.data = item.data newList.append(newItem) nodeList = newList if len(nodeList) > 1: format = nodeformat.NodeFormat(TreeFormats.rootFormatDefault, {}, TreeFormats.fieldDefault) self.treeFormats.addIfMissing(format) item = TreeItem(None, format.name) item.data[TreeFormats.fieldDefault] = TreeDoc.rootTitleDefault for child in nodeList: item.childList.append(child) child.parent = item else: item = nodeList[0] lines.extend(item.branchXml([], True)) try: f = self.getWriteFileObj(fileRef, self.compressFile) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' self.treeFormats.removeQuiet(TreeDoc.copyFormat) raise f.close() self.treeFormats.removeQuiet(TreeDoc.copyFormat) def exportTable(self, fileRef, nodeList, addBranches=True): """Write data to table for nodes or children of nodes""" if addBranches: newList = [] for item in nodeList: newList.extend(item.childList) nodeList = newList typeList = [] headings = [] tableList = [] for item in nodeList: itemFormat = item.nodeFormat() if itemFormat not in typeList: for field in itemFormat.fieldNames(): if field not in headings: headings.append(field) typeList.append(itemFormat) tableList.append(u'\t'.join( [item.data.get(head, '') for head in headings])) tableList.insert(0, u'\t'.join([head for head in headings])) try: text = os.linesep.join(tableList).\ encode(globalref.localTextEncoding, 'strict') except (ValueError, UnicodeError): print 'Warning - bad unicode characters were replaced' text = os.linesep.join(tableList).\ encode(globalref.localTextEncoding, 'replace') try: f = self.getWriteFileObj(fileRef, False) f.write(text) except IOError: print 'Error - could not write file' raise f.close() def exportTabbedTitles(self, fileRef, nodeList, addBranches=True, includeRoot=True, openOnly=False): """Write tabbed titles for descendants of item""" if addBranches: initLevel = not includeRoot and -1 or 0 titleList = [] for item in nodeList: itemList = item.exportToText(initLevel, openOnly) if not includeRoot: del itemList[0] titleList.extend(itemList) else: titleList = [item.title() for item in nodeList] try: text = os.linesep.join(titleList).\ encode(globalref.localTextEncoding, 'strict') except (ValueError, UnicodeError): print 'Warning - bad unicode characters were replaced' text = os.linesep.join(titleList).\ encode(globalref.localTextEncoding, 'replace') try: f = self.getWriteFileObj(fileRef, False) f.write(text) except IOError: print 'Error - could not write file' raise f.close() def exportXbel(self, fileRef, nodeList, addBranches=True): """Export XBEL bookmarks""" if len(nodeList) > 1 or not addBranches: title = TreeDoc.bookmarkRootTitle level = 1 else: title = xml.sax.saxutils.escape(nodeList[0].title(), escDict) level = 0 lines = [u'<!DOCTYPE xbel>', u'<xbel>', u'<title>%s</title>' % title] for item in nodeList: lines.extend(item.exportXbelBookmarks(level, addBranches)) lines.append(u'</xbel>') try: f = self.getWriteFileObj(fileRef, False) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' raise f.close() def exportHtmlBookmarks(self, fileRef, nodeList, addBranches=True): """Export HTML bookmarks""" if len(nodeList) > 1 or not addBranches: title = TreeDoc.bookmarkRootTitle level = 1 else: title = xml.sax.saxutils.escape(nodeList[0].title()) level = 0 lines = [u'<!DOCTYPE NETSCAPE-Bookmark-file-1>', u'<META HTTP-EQUIV="Content-Type" CONTENT="text/html; '\ 'charset=UTF-8">', u'<TITLE>%s</TITLE>' % title, u'<H1>%s</H1>' % title, ''] for item in nodeList: lines.extend(item.exportHtmlBookmarks(level, addBranches)) try: f = self.getWriteFileObj(fileRef, False) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' raise f.close() def exportGenericXml(self, fileRef, nodeList, addBranches=True): """Export generic XML""" lines = [u'<?xml version="1.0" encoding="utf-8" ?>'] level = 0 if len(nodeList) > 1: lines.append(u'<%s>' % TreeFormats.rootFormatDefault) level = 1 for item in nodeList: lines.extend( item.exportGenericXml( treexmlparse.GenericXmlHandler.textFieldName, level)) if len(nodeList) > 1: lines.append(u'</%s>' % TreeFormats.rootFormatDefault) try: f = self.getWriteFileObj(fileRef, False) f.writelines([(line + '\n').encode('utf-8') for line in lines]) except IOError: print 'Error - could not write file' raise f.close() def exportOdf(self, fileRef, nodeList, fontName, fontSize, fontFixed=False, addBranches=True, includeRoot=True, openOnly=False): """Export an ODF format text file""" TreeItem.maxLevel = 0 commonHeader = u'<?xml version="1.0" encoding="utf-8" ?>' commonAttr = u' office:version="1.0" '\ 'xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:'\ 'xsl-fo-compatible:1.0" '\ 'xmlns:office="urn:oasis:names:tc:opendocument:'\ 'xmlns:office:1.0" '\ 'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:'\ 'style:1.0" '\ 'xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:'\ 'svg-compatible:1.0" '\ 'xmlns:text="urn:oasis:names:tc:opendocument:'\ 'xmlns:text:1.0">' pitch = fontFixed and 'fixed' or 'variable' sizeDelta = 2 fontDecl = [u'<office:font-face-decls>', u'<style:font-face style:font-pitch="%s" style:name="%s" '\ 'svg:font-family="%s"/>' % (pitch, fontName, fontName), u'</office:font-face-decls>'] lines = [commonHeader, u'<office:document-content' + commonAttr] lines.extend(fontDecl) lines.extend([u'<office:body>', u'<office:text>']) for item in nodeList: lines.extend(item.exportOdf(1, addBranches, includeRoot, openOnly)) lines.extend([ u'</office:text>', u'</office:body>', u'</office:document-content>' ]) manifest = [commonHeader, u'<manifest:manifest xmlns:manifest="urn:oasis:names:tc:'\ 'opendocument:xmlns:manifest:1.0">', u'<manifest:file-entry manifest:media-type="application/'\ 'vnd.oasis.opendocument.text" manifest:full-path="/"/>', u'<manifest:file-entry manifest:media-type="text/xml" '\ 'manifest:full-path="content.xml"/>', u'<manifest:file-entry manifest:media-type="text/xml" '\ 'manifest:full-path="styles.xml"/>', u'</manifest:manifest>'] styles = [commonHeader, u'<office:document-styles' + commonAttr] styles.extend(fontDecl) styles.extend([u'<office:styles>', u'<style:default-style style:family="paragraph">', u'<style:paragraph-properties '\ 'style:writing-mode="page"/>', u'<style:text-properties fo:font-size="%dpt" '\ 'fo:hyphenate="false" style:font-name="%s"/>' % (fontSize, fontName), u'</style:default-style>', u'<style:style style:name="Standard" '\ 'style:class="text" style:family="paragraph"/>', u'<style:style style:name="Text_20_body" '\ 'style:display-name="Text body" style:class="text" '\ 'style:family="paragraph" '\ 'style:parent-style-name="Standard">', u'<style:paragraph-properties '\ 'fo:margin-bottom="6.0pt"/>', u'</style:style>', u'<style:style style:name="Heading" '\ 'style:class="text" style:family="paragraph" '\ 'style:next-style-name="Text_20_body" '\ 'style:parent-style-name="Standard">', u'<style:paragraph-properties '\ 'fo:keep-with-next="always" fo:margin-bottom="6.0pt" '\ 'fo:margin-top="12.0pt"/>', u'<style:text-properties fo:font-size="%dpt" '\ 'style:font-name="%s"/>' % (fontSize + sizeDelta, fontName), u'</style:style>']) outline = [u'<text:outline-style>'] for level in range(1, TreeItem.maxLevel + 1): size = fontSize if level <= 2: size += 2 * sizeDelta elif level <= 4: size += sizeDelta italic = ' ' if level % 2 == 0: italic = 'fo:font-style="italic" ' styles.extend([u'<style:style style:name="Heading_20_%d" '\ 'style:display-name="Heading %d" '\ 'style:class="text" style:family="paragraph" '\ 'style:parent-style-name="Heading" '\ 'style:default-outline-level="%d">' % \ (level, level, level), u'<style:text-properties fo:font-size="%dpt" '\ '%s fo:font-weight="bold"/>' % \ (size, italic), u'</style:style>']) outline.append(u'<text:outline-level-style text:level="%d" '\ 'style:num-format=""/>' % level) styles.extend(outline) styles.extend([u'</text:outline-style>', u'</office:styles>', u'<office:automatic-styles>', u'<style:page-layout style:name="pm1">', u'<style:page-layout-properties '\ 'fo:margin-bottom="0.75in" fo:margin-left="0.75in" '\ 'fo:margin-right="0.75in" fo:margin-top="0.75in" '\ 'fo:page-height="11in" fo:page-width="8.5in" '\ 'style:print-orientation="portrait"/>', u'</style:page-layout>', u'</office:automatic-styles>', u'<office:master-styles>', u'<style:master-page style:name="Standard" '\ 'style:page-layout-name="pm1"/>', u'</office:master-styles>', u'</office:document-styles>']) try: f = zipfile.ZipFile(fileRef, 'w', zipfile.ZIP_DEFLATED) f.writestr('content.xml', u'\n'.join(lines).encode('utf-8') + '\n') f.writestr('META-INF/manifest.xml', u'\n'.join(manifest) + '\n') f.writestr('styles.xml', u'\n'.join(styles) + '\n') except IOError: print 'Error - could not write file' raise f.close()
def __init__(self, data, parent=None): super(TreeModel, self).__init__(parent) self.rootItem = TreeItem(['Date', 'Data (KB)', 'Data (MB)'], None) self.setupModel(data)
class TreeModel(QAbstractItemModel): def __init__(self, data, parent=None): super().__init__(parent) self.rootItem = TreeItem([self.tr('title'), self.tr('summary')]) self.setupModelData(data.split('\n'), self.rootItem) def __del__(self): # delete self.rootItem pass def columnCount(self, parent): ''' QModelIndex.internalPointer() -> TreeItem Args: parent (QModelIndex): QModelIndex ''' if parent.isValid(): return parent.internalPointer().columnCount() return self.rootItem.columnCount() def data(self, index, role): if not index: return QVariant() if role != Qt.DisplayRole: return QVariant() item = index.internalPointer() return item.data(index.column()) def flags(self, index): if not index.isValid(): return Qt.NoItemFlags return QAbstractItemModel.flags(self, index) def headerData(self, section, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self.rootItem.data(section) return QVariant() def index(self, row, column, parent=QModelIndex()): if not self.hasIndex(row, column, parent): return QModelIndex() parentItem = None if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) return QModelIndex() def parent(self, index): if not index.isValid(): return QModelIndex() childItem = index.internalPointer() parentItem = childItem.parentItem() if parentItem == self.rootItem: return QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem) def rowCount(self, parent=QModelIndex()): parentItem = None if parent.column() > 0: return 0 if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() return parentItem.childCount() def setupModelData(self, lines, parent): parents = [] indentations = [] parents.append(parent) indentations.append(0) number = 0 while number < len(lines): position = 0 while position < len(lines[number]): if lines[number][position] != ' ': break position = position + 1 lineData = lines[number][position:].strip() if lineData: # Read the column data from the rest of the line. columnStrings = [x for x in lineData.split('\t') if x] columnData = [None] * len(columnStrings) for i, columnString in enumerate(columnStrings): columnData[i] = columnString if position > indentations[-1]: # The last child of the current parent is now the new parent # unless the current parent has no children. if parents[-1].childCount() > 0: parents.append(parents[-1].child(parents[-1].childCount() - 1)) indentations.append(position) else: while position < indentations[-1] and len(parents) > 0: parents.pop(-1) indentations.pop(-1) # Append a new item to the current parent's list of children. parents[-1].appendChild(TreeItem(columnData, parents[-1])) number = number + 1