def __init__(self, parent): QWidget.__init__(self, parent) p = parent.addPage(self, i18n("Titles and Headers")) l = QHBoxLayout(self) # The html view with the score layout example t = QTextBrowser(self) t.setOpenLinks(False) t.setOpenExternalLinks(False) # ensure that the full HTML example page is displayed t.setContentsMargins(2, 2, 2, 2) t.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) t.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) t.setMinimumSize(QSize(350, 350)) #t.document().documentLayout().documentSizeChanged.connect( #lambda size: t.setMinimumSize(size.toSize() + QSize(4, 4))) headers = ly.headers(i18n) msg = i18n("Click to enter a value.") t.setHtml(titles_html.format( copyrightmsg = i18n("bottom of first page"), taglinemsg = i18n("bottom of last page"), **dict((k, "<a title='{0}' href='{1}'>{2}</a>".format(msg, k, v)) for k, v in headers))) l.addWidget(t) t.anchorClicked.connect(lambda qurl: self.findChild(KLineEdit, qurl.toString()).setFocus()) g = QGridLayout() g.setVerticalSpacing(1) g.setColumnMinimumWidth(1, 200) l.addLayout(g) for row, (name, title) in enumerate(headers): l = QLabel(title + ":", self) e = KLineEdit(self) e.setObjectName(name) l.setBuddy(e) g.addWidget(l, row, 0) g.addWidget(e, row, 1) # set completion items parent.complete(e)
class PluginsManagerWidget(QDialog): """Plugin Manager widget""" def __init__(self, parent): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(translations.TR_PLUGIN_MANAGER) self.resize(700, 600) vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) self._txt_data = QTextBrowser() self._txt_data.setOpenLinks(False) vbox.addWidget(QLabel(translations.TR_PROJECT_DESCRIPTION)) vbox.addWidget(self._txt_data) # Footer hbox = QHBoxLayout() btn_close = QPushButton(translations.TR_CLOSE) btnReload = QPushButton(translations.TR_RELOAD) hbox.addWidget(btn_close) hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox.addWidget(btnReload) vbox.addLayout(hbox) self.overlay = ui_tools.Overlay(self) self.overlay.hide() self._oficial_available = [] self._community_available = [] self._locals = [] self._updates = [] self._loading = True self._requirements = {} self.connect(btnReload, SIGNAL("clicked()"), self._reload_plugins) self.thread = ThreadLoadPlugins(self) self.connect(self.thread, SIGNAL("finished()"), self._load_plugins_data) self.connect(self.thread, SIGNAL("plugin_downloaded(PyQt_PyObject)"), self._after_download_plugin) self.connect(self.thread, SIGNAL("plugin_manually_installed(PyQt_PyObject)"), self._after_manual_install_plugin) self.connect(self.thread, SIGNAL("plugin_uninstalled(PyQt_PyObject)"), self._after_uninstall_plugin) self.connect(self._txt_data, SIGNAL("anchorClicked(const QUrl&)"), self._open_link) self.connect(btn_close, SIGNAL('clicked()'), self.close) self.overlay.show() self._reload_plugins() def show_plugin_info(self, data): """Takes data argument, format for HTML and display it""" plugin_description = data[2].replace('\n', '<br>') html = HTML_STYLE.format(name=data[0], version=data[1], description=plugin_description, author=data[3], link=data[4]) self._txt_data.setHtml(html) def _open_link(self, url): """Takes an url argument and open the link on web browser""" link = url.toString() if link.startswith('/plugins/'): link = 'http://ninja-ide.org' + link webbrowser.open(link) def _reload_plugins(self): """Reload all plugins""" self.overlay.show() self._loading = True self.thread.runnable = self.thread.collect_data_thread self.thread.start() def _after_manual_install_plugin(self, plugin): """After installing, take plugin and add it to installedWidget items""" data = {} data['name'] = plugin[0] data['version'] = plugin[1] data['description'] = '' data['authors'] = '' data['home'] = '' self._installedWidget.add_table_items([data]) def _after_download_plugin(self, plugin): """After installing, take plugin and add it to installedWidget items""" oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._installedWidget.add_table_items([oficial_plugin]) self._availableOficialWidget.remove_item(plugin[0]) elif community_plugin: self._installedWidget.add_table_items([community_plugin]) self._availableCommunityWidget.remove_item(plugin[0]) def _after_uninstall_plugin(self, plugin): """After uninstall plugin,make available plugin corresponding to type""" oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._availableOficialWidget.add_table_items([oficial_plugin]) self._installedWidget.remove_item(plugin[0]) elif community_plugin: self._availableCommunityWidget.add_table_items([community_plugin]) self._installedWidget.remove_item(plugin[0]) def _load_plugins_data(self): """Load all the plugins data""" if self._loading: self._tabs.clear() self._updatesWidget = UpdatesWidget(self, copy(self._updates)) self._availableOficialWidget = AvailableWidget( self, copy(self._oficial_available)) self._availableCommunityWidget = AvailableWidget( self, copy(self._community_available)) self._installedWidget = InstalledWidget(self, copy(self._locals)) self._tabs.addTab(self._availableOficialWidget, translations.TR_OFFICIAL_AVAILABLE) self._tabs.addTab(self._availableCommunityWidget, translations.TR_COMMUNITY_AVAILABLE) self._tabs.addTab(self._updatesWidget, translations.TR_UPDATE) self._tabs.addTab(self._installedWidget, translations.TR_INSTALLED) self._manualWidget = ManualInstallWidget(self) self._tabs.addTab(self._manualWidget, translations.TR_MANUAL_INSTALL) self._loading = False self.overlay.hide() self.thread.wait() def download_plugins(self, plugs): """ Install """ self.overlay.show() self.thread.plug = plugs # set the function to run in the thread self.thread.runnable = self.thread.download_plugins_thread self.thread.start() def install_plugins_manually(self, plug): """Install plugin from local zip.""" self.overlay.show() self.thread.plug = plug # set the function to run in the thread self.thread.runnable = self.thread.manual_install_plugins_thread self.thread.start() def mark_as_available(self, plugs): """ Uninstall """ self.overlay.show() self.thread.plug = plugs # set the function to run in the thread self.thread.runnable = self.thread.uninstall_plugins_thread self.thread.start() def update_plugin(self, plugs): """ Update """ self.overlay.show() self.thread.plug = plugs # set the function to run in the thread self.thread.runnable = self.thread.update_plugin_thread self.thread.start() def reset_installed_plugins(self): """Reset all the installed plugins""" local_plugins = plugin_manager.local_plugins() plugins = _format_for_table(local_plugins) self._installedWidget.reset_table(plugins) def resizeEvent(self, event): """Handle Resize events""" self.overlay.resize(event.size()) event.accept()
class PluginsManagerWidget(QDialog): def __init__(self, parent): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(self.tr("Plugins Manager")) self.resize(700, 600) vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) self._txt_data = QTextBrowser() self._txt_data.setOpenLinks(False) vbox.addWidget(QLabel(self.tr("Description:"))) vbox.addWidget(self._txt_data) # Footer hbox = QHBoxLayout() btn_close = QPushButton(self.tr('Close')) btnReload = QPushButton(self.tr("Reload")) hbox.addWidget(btn_close) hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox.addWidget(btnReload) vbox.addLayout(hbox) self.overlay = ui_tools.Overlay(self) self.overlay.hide() self._oficial_available = [] self._community_available = [] self._locals = [] self._updates = [] self._loading = True self._requirements = {} self.connect(btnReload, SIGNAL("clicked()"), self._reload_plugins) self.thread = ThreadLoadPlugins(self) self.connect(self.thread, SIGNAL("finished()"), self._load_plugins_data) self.connect(self.thread, SIGNAL("plugin_downloaded(PyQt_PyObject)"), self._after_download_plugin) self.connect(self.thread, SIGNAL("plugin_uninstalled(PyQt_PyObject)"), self._after_uninstall_plugin) self.connect(self._txt_data, SIGNAL("anchorClicked(const QUrl&)"), self._open_link) self.connect(btn_close, SIGNAL('clicked()'), self.close) self.overlay.show() self._reload_plugins() def show_plugin_info(self, data): plugin_description = data[2].replace('\n', '<br>') html = HTML_STYLE.format(name=data[0], version=data[1], description=plugin_description, author=data[3], link=data[4]) self._txt_data.setHtml(html) def _open_link(self, url): link = url.toString() if link.startswith('/plugins/'): link = 'http://ninja-ide.org' + link webbrowser.open(link) def _reload_plugins(self): self.overlay.show() self._loading = True self.thread.runnable = self.thread.collect_data_thread self.thread.start() def _after_download_plugin(self, plugin): oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._installedWidget.add_table_items([oficial_plugin]) self._availableOficialWidget.remove_item(plugin[0]) elif community_plugin: self._installedWidget.add_table_items([community_plugin]) self._availableCommunityWidget.remove_item(plugin[0]) def _after_uninstall_plugin(self, plugin): #make available the plugin corresponding to the type oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._availableOficialWidget.add_table_items([oficial_plugin]) self._installedWidget.remove_item(plugin[0]) elif community_plugin: self._availableCommunityWidget.add_table_items([community_plugin]) self._installedWidget.remove_item(plugin[0]) def _load_plugins_data(self): if self._loading: self._tabs.clear() self._updatesWidget = UpdatesWidget(self, copy(self._updates)) self._availableOficialWidget = AvailableWidget(self, copy(self._oficial_available)) self._availableCommunityWidget = AvailableWidget(self, copy(self._community_available)) self._installedWidget = InstalledWidget(self, copy(self._locals)) self._tabs.addTab(self._availableOficialWidget, self.tr("Official Available")) self._tabs.addTab(self._availableCommunityWidget, self.tr("Community Available")) self._tabs.addTab(self._updatesWidget, self.tr("Updates")) self._tabs.addTab(self._installedWidget, self.tr("Installed")) self._loading = False self.overlay.hide() def download_plugins(self, plugs): """ Install """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.download_plugins_thread self.thread.start() def mark_as_available(self, plugs): """ Uninstall """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.uninstall_plugins_thread self.thread.start() def update_plugin(self, plugs): """ Update """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.update_plugin_thread self.thread.start() def reset_installed_plugins(self): local_plugins = plugin_manager.local_plugins() plugins = _format_for_table(local_plugins) self._installedWidget.reset_table(plugins) def resizeEvent(self, event): self.overlay.resize(event.size()) event.accept()
class AdvancedObjectWidget(QWidget): def __init__(self, index, currentTemplate="classic.html", parent=None, entryTemplate = None): QWidget.__init__(self, parent) w = 24 h = 24 self.entryModel = None # Standard pixmaps used by the widget self.reloadPixmap = pixmapFromTheme( "view-refresh", ":/icons/32/view-refresh", w, h) self.savePixmap = pixmapFromTheme( "document-save", ":/icons/32/document-save",w, h) self.addPixmap = pixmapFromTheme( "list-add", ":/icons/32/list-add", w, h) self.deleteSmallPixmap = pixmapFromTheme( "list-remove", ":/icons/32/list-remove", w, h) self.treeIndex = index self.setLayout(QVBoxLayout(self)) self.layout().setSpacing(0) self.layout().setContentsMargins(0, 0, 0, 0) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # create the widget containing the data self.textBrowser = QTextBrowser() self.textBrowser.setOpenLinks(False) self.textBrowser.setWordWrapMode(QTextOption.WrapAnywhere) self.layout().addWidget(self.textBrowser) self.textBrowser.anchorClicked.connect(self.anchorClicked) self.currentDocument = '' self.addingToComboBox = False # create the combobox containing the different views self.comboBox = QComboBox() self.currentTemplate = currentTemplate self.errorTemplate = "error.html" self.usedTemplates = [] # FIXED: Need a more robust way for locating the path used in # the TemplateFactory for locating the template view # files # >>> with base.util.Paths.getLumaRoot this should work. # Probably needs some validation testing on platforms # other than Linux # Another issue occured when running Luma after a installation # from a source distribution. Because the site-packages is only # intended for pure python modules, the html templates is not # installed, resulting in an Exception when trying to view an # entry in the Browser plugin. The setup.py script is modified # such that the needed html templates is copied into a folder # in the path returned by `base.util.Paths.getConfigPrefix`. s = QtCore.QSettings() configPrefix = s.value('application/config_prefix').toString() templatesPath = os.path.join(unicode(configPrefix).encode('utf-8'), 'browser-templates') # If we run Luma from a development environment the isntalled # templatesPath do most likely not exist. We therefore use the # directory in the repository if not os.path.isdir(templatesPath): templatesPath = unicode( os.path.join( getLumaRoot(), 'plugins', 'browser', 'templates') ) self.templateFactory = TemplateFactory(templatesPath) self.htmlParser = HtmlParser(self.textBrowser) self.str_RELOAD= QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Reload") self.str_SAVE = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Save entry") self.str_SAVE_CONTINUE = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Do you want to save the entry before continuing?") self.str_SAVE_FAILED = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Saving failed, continue anyway?") self.str_DELETE = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Delete object") self.str_DELETE_CONFIRM = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Do you really want to delete the object?") self.str_ADD = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Add attribute") self.str_SWITCH_VIEWS = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Switch between views") self.str_REASON = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Reason:") self.str_EXPORT_BINARY = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Export binary attribute to file") self.str_EXPORT_BINARY_EXCEPT = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Could not export binary data to file.") self.str_SELECT_ANOTHER_FILE = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Please select another filename.") self.str_NO_TEMPLATE = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "No templates available") self.str_MISSING_ENTRY = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "Did'nt receive a ldap-object, it might have been deleted") self.str_ENTRY_INVALID = QtCore.QCoreApplication.translate("AdvancedObjectWidget", "The ldap object is not valid\nClick Yes to view the object anyway\nNo to view the errors \nIgnore to view the object and ignore this message in later attempts.") self.buildToolBar() ############################################################################### @staticmethod def smartObjectCopy(smartObject): return SmartDataObject(copy.deepcopy([smartObject.dn, smartObject.data]), copy.deepcopy(smartObject.serverMeta)) ############################################################################### def getSmartObject(self): return self.entryModel.getSmartObject() ############################################################################### def initModel(self, smartObject, create=False, entryTemplate = None): """ sets up the model, and connects it to this object """ if not create: # use a copy of the smartObject smartObject = AdvancedObjectWidget.smartObjectCopy(smartObject) self.baseDN = smartObject.getDN() self.entryModel = EntryModel(smartObject, self, entryTemplate) self.htmlParser.setModel(self.entryModel) self.entryModel.modelChangedSignal.connect(self.modelChanged) success, exceptionMsg, exceptionObject = self.entryModel.initModel(create) if not success: errorMsg = "%s<br><br>%s: %s" % (exceptionMsg, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) ############################################################################### def loadTemplates(self): """ Loads all templates that matches with the current objectclasses """ self.usedTemplates = [] objectClasses = self.getSmartObject().getObjectClasses() newIndex = -1 i = 0 for objectClass, fileName in self.templateFactory.getTemplateList(): if objectClass == '' or objectClass in objectClasses: if fileName == self.currentTemplate: newIndex = i self.usedTemplates.append(fileName) i += 1 if newIndex == -1: newIndex = 0 self.currentTemplate = self.usedTemplates[newIndex] #TODO do this properly, signals ignored self.addingToComboBox = True self.comboBox.clear() self.comboBox.addItems(self.usedTemplates) self.comboBox.setCurrentIndex(newIndex) self.addingToComboBox = False ############################################################################### @pyqtSlot(bool) def modelChanged(self, reload): if reload: item = None if self.treeIndex and self.treeIndex.isValid(): row = self.treeIndex.row() column = self.treeIndex.column() # QPersistenIndex doesn't have internalPointer() # so we aquire a QModelIndex which does item = self.treeIndex.sibling(row,column).internalPointer() if not(self.entryModel.VALID): if item == None or not(item.serverParent.ignoreItemErrors): result = QMessageBox.question(self, self.trUtf8(""), self.str_ENTRY_INVALID, QMessageBox.Yes | QMessageBox.No | QMessageBox.Ignore, QMessageBox.No) if result == QMessageBox.No: self.currentTemplate = self.errorTemplate elif result == QMessageBox.Ignore: if not(item == None): item.serverParent.ignoreItemErrors = True self.displayValues() ############################################################################### def displayValues(self): # Something went wrong. We have no data object. # This might happen if we want to refresh an item and # it might be deleted already. if None == self.entryModel.getSmartObject(): QMessageBox.critical(self, self.str_MISSING_ENTRY) self.enableToolButtons(False) return self.loadTemplates() if self.currentTemplate == None: selt.textBrowser.setHtml(self.str_NO_TEMPLATE) return htmlTemplate = self.templateFactory.getTemplateFile(self.currentTemplate) self.currentDocument = self.htmlParser.parseHtml(htmlTemplate) self.textBrowser.setHtml(self.currentDocument) self.enableToolButtons(True) ############################################################################### def enableToolButtons(self, enable): if None == self.entryModel: self.saveButton.setEnabled(False) self.deleteObjectButton.setEnabled(False) self.reloadButton.setEnabled(True) self.addAttributeButton.setEnabled(False) return if self.entryModel.EDITED and not self.entryModel.CREATE: self.saveButton.setEnabled(enable) else: self.saveButton.setEnabled(False) if self.entryModel.ISLEAF: self.deleteObjectButton.setEnabled(enable) else: self.deleteObjectButton.setEnabled(False) if self.entryModel.CREATE: self.reloadButton.setEnabled(False) else: self.reloadButton.setEnabled(enable) self.addAttributeButton.setEnabled(enable) ############################################################################### def buildToolBar(self): self.toolBar = QToolBar() self.toolBar.layout().setContentsMargins(0, 0, 0, 0) # Reload button self.reloadButton = QToolButton(self.toolBar) self.reloadButton.setIcon(QIcon(self.reloadPixmap)) self.reloadButton.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.reloadButton.setAutoRaise(True) self.reloadButton.setBackgroundRole(self.backgroundRole()) self.reloadButton.setToolTip(self.str_RELOAD) self.connect(self.reloadButton, SIGNAL("clicked()"), self.refreshView) self.toolBar.addWidget(self.reloadButton) # Save button self.saveButton = QToolButton(self.toolBar) self.saveButton.setIcon(QIcon(self.savePixmap)) self.saveButton.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.saveButton.setAutoRaise(True) self.saveButton.setBackgroundRole(self.backgroundRole()) self.saveButton.setToolTip(self.str_SAVE) self.connect(self.saveButton, SIGNAL("clicked()"), self.saveObject) self.toolBar.addWidget(self.saveButton) self.toolBar.addSeparator() # Add attribute button self.addAttributeButton = QToolButton(self.toolBar) self.addAttributeButton.setIcon(QIcon(self.addPixmap)) self.addAttributeButton.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.addAttributeButton.setAutoRaise(True) self.addAttributeButton.setBackgroundRole(self.backgroundRole()) self.addAttributeButton.setToolTip(self.str_ADD) self.connect(self.addAttributeButton, SIGNAL("clicked()"), self.addAttribute) self.toolBar.addWidget(self.addAttributeButton) # Delete button self.deleteObjectButton = QToolButton(self.toolBar) self.deleteObjectButton.setIcon(QIcon(self.deleteSmallPixmap)) self.deleteObjectButton.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.deleteObjectButton.setAutoRaise(True) self.deleteObjectButton.setBackgroundRole(self.backgroundRole()) self.deleteObjectButton.setToolTip(self.str_DELETE) self.connect(self.deleteObjectButton, SIGNAL("clicked()"), self.deleteObject) self.toolBar.addWidget(self.deleteObjectButton) self.comboBox.setToolTip(self.str_SWITCH_VIEWS) self.connect(self.comboBox, SIGNAL("currentIndexChanged(int)"), self.changeView) self.toolBar.addWidget(self.comboBox) self.enableToolButtons(False) self.layout().insertWidget(0, self.toolBar) ############################################################################### @pyqtSlot("int") def changeView(self, index): """ change between different views """ if index == -1 or self.addingToComboBox: return self.currentTemplate = self.usedTemplates[index] self.displayValues() ############################################################################### def aboutToChange(self): """ Asks the user whether changes should be saved returns True if changes were saved, or discarded """ if not self.entryModel.EDITED: return True result = QMessageBox.warning(self, self.str_SAVE, self.str_SAVE_CONTINUE, QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Cancel) # TODO add exception message if result == QMessageBox.Save: if not self.saveObject(): # Saving failed result = QMessageBox.question(None, self.trUtf8(""), self.str_SAVE_FAILED, QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) return not(result == QMessageBox.Cancel) ############################################################################### # TODO: add logging for each error @pyqtSlot() def refreshView(self): """ Refreshes the LDAP data from server and displays values. """ if self.aboutToChange(): success, exceptionMsg, exceptionObject = self.entryModel.reloadModel() if not success: errorMsg = "%s<br><br>%s: %s" % (exceptionMsg, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) else: self.displayValues() ############################################################################### # TODO: add logging for each error @pyqtSlot() def saveObject(self): success, exceptionMsg, exceptionObject = self.entryModel.saveModel() if not success: # Saving failed errorMsg = "%s<br><br>%s: %s" % (exceptionMsg, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) return False else: # update the smartObject in the tree if self.entryModel.CREATE: pass elif self.treeIndex and self.treeIndex.isValid(): row = self.treeIndex.row() column = self.treeIndex.column() # QPersistenIndex doesn't have internalPointer() # so we aquire a QModelIndex which does index = self.treeIndex.sibling(row,column) index.internalPointer().itemData = self.getSmartObject() return True ############################################################################### @pyqtSlot() def addAttribute(self): """ Add attributes to the current object. """ dialog = AddAttributeWizard(self) dialog.setData(self.smartObjectCopy(self.entryModel.getSmartObject())) dialog.exec_() if dialog.result() == QDialog.Rejected: return attribute = str(dialog.attributeBox.currentText()) showAll = dialog.enableAllBox.isChecked() if dialog.binaryBox.isChecked(): attributeSet = set([attribute + ";binary"]) else: attributeSet = set([attribute]) if showAll and not(attribute.lower() in dialog.possibleAttributes): objectClass = str(dialog.classBox.currentItem().text()) self.entryModel.addObjectClass(objectClass) serverSchema = ObjectClassAttributeInfo(self.entryModel.smartObject.getServerMeta()) mustAttributes = serverSchema.getAllMusts([objectClass]) mustAttributes = mustAttributes.difference(set(self.entryModel.smartObject.getAttributeList())) attributeSet = mustAttributes.union(set([attribute])) for x in attributeSet: self.entryModel.addAttributeValue(x, None) self.displayValues() ############################################################################### # TODO: add logging for each error, remove tab and node from parent @pyqtSlot() def deleteObject(self): buttonClicked = QMessageBox.critical(self, self.str_DELETE, self.str_DELETE_CONFIRM, QMessageBox.Yes, QMessageBox.No) if not (buttonClicked == QMessageBox.Yes): return # If we have an index, use it tell the item to delete itself # so that the view is updated if self.treeIndex and self.treeIndex.isValid(): row = self.treeIndex.row() column = self.treeIndex.column() # QPersistenIndex doesn't have internalPointer() # so we aquire a QModelIndex which does index = self.treeIndex.sibling(row,column) success, message = index.model().deleteItem(index) if success: self.enableToolButtons(False) self.deleteLater() else: errorMsg = "%s" % (message) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) # if not, we just delete it ourselves since there's not view on the object else: success, message, exceptionObject = self.entryModel.deleteObject() if success: self.enableToolButtons(False) self.deleteLater() else: errorMsg = "%s<br><br>%s: %s" % (message, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) ############################################################################### @pyqtSlot("QUrl") def anchorClicked(self, url): """ Called when an anchor (<a href=".." /> is clicked """ nameString = unicode(url.toString()) tmpList = nameString.split("__") if tmpList[0] in self.entryModel.getSmartObject().getObjectClasses(): self.entryModel.deleteObjectClass(tmpList[0]) else: if not len(tmpList) == 3: return attributeName, index, operation = tmpList[0], int(tmpList[1]), tmpList[2] if operation == "edit": self.editAttribute(attributeName, index) elif operation == "delete": self.deleteAttribute(attributeName, index) elif operation == "export": self.exportAttribute(attributeName, index) ############################################################################### def editAttribute(self, attributeName, index): smartObject = self.entryModel.getSmartObject() oldDN = smartObject.getDN() addAttribute = False if attributeName == 'RDN': # TODO correct this, used on creation? oldValue = oldDN smartObject.setDN(self.baseDN) else: if smartObject.hasAttribute(attributeName): addValue = False oldValue = smartObject.getAttributeValue(attributeName, index) if oldValue == None: oldValue = '' else: addValue = True oldValue = '' dialog = getEditorWidget(self, smartObject, attributeName, index) dialog.exec_() if dialog.result() == QDialog.Accepted: # TODO check attribute types newValue = dialog.getValue() if not (newValue == None): if attributeName == 'RDN': self.entryModel.setDN(newValue) if dialog.addAttributeBox.isChecked(): addAttribute = unicode(dialog.attributeBox.currentText()) addValue = unicode(dialog.valueEdit.text()) self.entryModel.addAttributeValue(addAttribute, [addValue]) else: if addValue: self.entryModel.addAttributeValue(attributeName, [newValue]) else: self.entryModel.editAttribute(attributeName, index, newValue) else: if attributeName == 'RDN': smartObject.setDN(oldDN.decode('utf-8')) ############################################################################### def deleteAttribute(self, attributeName, index): self.entryModel.deleteAttribute(attributeName, index) ############################################################################### def exportAttribute(self, attributeName, index): """ Show the dialog for exporting binary attribute data. """ value = self.getSmartObject().getAttributeValue(attributeName, index) fileName = unicode(QFileDialog.getSaveFileName(\ self, self.str_EXPORT_BINARY, QString(""), "All files (*)", None)) if unicode(fileName) == "": return try: fileHandler = open(fileName, "w") fileHandler.write(value) fileHandler.close() SAVED = True except IOError, e: msg = "%s %s:\n\n%s\n\n%s" % (self.str_EXPORT_BINARY_EXCEPT, self.str_REASON, str(e), self.str_SELECT_ANOTHER_FILE) result = QMessageBox.warning(\ self, self.str_EXPORT_BINARY, msg, QMessageBox.Cancel | QMessageBox.Ok, QMessageBox.Cancel)
class CMakeToolView(QObject): '''CMake tool view class TODO Remember last dir/position/is-advanced? TODO Make the cache view editable and run `cmake` to reconfigure ''' cmakeCache = [] def __init__(self): super(CMakeToolView, self).__init__(None) self.toolView = kate.mainInterfaceWindow().createToolView( 'cmake_utils' , kate.Kate.MainWindow.Bottom , KIcon('cmake').pixmap(32, 32) , i18nc('@title:tab', 'CMake') ) self.toolView.installEventFilter(self) # By default, the toolview has box layout, which is not easy to delete. # For now, just add an extra widget. tabs = QTabWidget(self.toolView) # Make a page to view cmake cache self.cacheViewPage = uic.loadUi( os.path.join(os.path.dirname(__file__), settings.CMAKE_TOOLVIEW_CACHEVIEW_UI) ) self.cacheViewPage.buildDir.setText(kate.sessionConfiguration[settings.PROJECT_DIR]) # TODO It seems not only KTextEditor's SIP files are damn out of date... # KUrlRequester actually *HAS* setPlaceholderText() method... but damn SIP # files for KIO are damn out of date either! A NEW BUG NEEDS TO BE ADDED! # (but I have fraking doubts that it will be closed next few damn years) # #self.buildDir.setPlaceholderText(i18nc('@info', 'Project build directory')) self.cacheViewPage.buildDir.lineEdit().setPlaceholderText( i18nc('@info/plain', 'Project build directory') ) self.cacheViewPage.buildDir.setMode( KFile.Mode(KFile.Directory | KFile.ExistingOnly | KFile.LocalOnly) ) self.cacheViewPage.cacheItems.sortItems(0, Qt.AscendingOrder) self.cacheViewPage.cacheFilter.setTreeWidget(self.cacheViewPage.cacheItems) tabs.addTab(self.cacheViewPage, i18nc('@title:tab', 'CMake Cache Viewer')) # Make a page w/ cmake help splitter = QSplitter(Qt.Horizontal, tabs) self.vewHelpPage = uic.loadUi( os.path.join(os.path.dirname(__file__), settings.CMAKE_TOOLVIEW_HELP_UI) ) self.vewHelpPage.helpFilter.setTreeWidget(self.vewHelpPage.helpTargets) self.updateHelpIndex() # Prepare Help view self.helpPage = QTextBrowser(splitter) self.helpPage.setReadOnly(True) self.helpPage.setOpenExternalLinks(False) self.helpPage.setOpenLinks(False) splitter.addWidget(self.vewHelpPage) splitter.addWidget(self.helpPage) splitter.setStretchFactor(0, 10) splitter.setStretchFactor(1, 20) tabs.addTab(splitter, i18nc('@title:tab', 'CMake Help')) # Make a page w/ some instant settings self.cfgPage = uic.loadUi( os.path.join(os.path.dirname(__file__), settings.CMAKE_TOOLVIEW_SETTINGS_UI) ) self.cfgPage.mode.setChecked(kate.sessionConfiguration[settings.TOOLVIEW_ADVANCED_MODE]) self.cfgPage.htmlize.setChecked(kate.sessionConfiguration[settings.TOOLVIEW_BEAUTIFY]) tabs.addTab(self.cfgPage, i18nc('@title:tab', 'Tool View Settings')) # Connect signals self.cacheViewPage.cacheItems.itemActivated.connect(self.insertIntoCurrentDocument) self.cacheViewPage.buildDir.returnPressed.connect(self.updateCacheView) self.cacheViewPage.buildDir.urlSelected.connect(self.updateCacheView) self.cfgPage.mode.toggled.connect(self.updateCacheView) self.cfgPage.mode.toggled.connect(self.saveSettings) self.cfgPage.htmlize.toggled.connect(self.updateHelpText) self.cfgPage.htmlize.toggled.connect(self.saveSettings) self.vewHelpPage.helpTargets.itemActivated.connect(self.updateHelpText) self.vewHelpPage.helpTargets.itemDoubleClicked.connect(self.insertHelpItemIntoCurrentDocument) self.helpPage.anchorClicked.connect(openDocument) # Refresh the cache view self._updateCacheView(self.cacheViewPage.buildDir.text()) def __del__(self): '''Plugins that use a toolview need to delete it for reloading to work.''' assert(self.toolView is not None) mw = kate.mainInterfaceWindow() if mw: mw.hideToolView(self.toolView) mw.destroyToolView(self.toolView) self.toolView.removeEventFilter(self) self.toolView = None def eventFilter(self, obj, event): """Hide the CMake tool view on ESCAPE key""" if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Escape: kate.mainInterfaceWindow().hideToolView(self.toolView) return True return self.toolView.eventFilter(obj, event) @pyqtSlot() def saveSettings(self): kate.sessionConfiguration[settings.TOOLVIEW_ADVANCED_MODE] = self.cfgPage.mode.isChecked() kate.sessionConfiguration[settings.TOOLVIEW_BEAUTIFY] = self.cfgPage.htmlize.isChecked() @pyqtSlot() def updateCacheView(self): self._updateCacheView(self.cacheViewPage.buildDir.text()) def _updateCacheView(self, build_dir): # Do nothing if build dir is not configured if not build_dir: return self.cacheViewPage.cacheItems.clear() # Remove previously collected cache is_advanced = self.cfgPage.mode.isChecked() try: items = cmake_help_parser.get_cache_content(build_dir, is_advanced) except ValueError as error: kate.ui.popup( i18nc('@title:window', 'Error') , i18nc( '@info:tooltip' , 'Unable to get CMake cache content:<nl/><message>%1</message>' , str(error) ) , 'dialog-error' ) return # Add items to a list for key, value in items.items(): item = QTreeWidgetItem(self.cacheViewPage.cacheItems, [key, value[1], value[0]]) item.setToolTip(0, value[2]) self.cacheViewPage.cacheItems.resizeColumnToContents(0) self.cacheViewPage.cacheItems.resizeColumnToContents(1) self.cacheViewPage.cacheItems.resizeColumnToContents(2) def updateHelpIndex(self): # Add commands group commands = QTreeWidgetItem( self.vewHelpPage.helpTargets , [i18nc('@item::inlistbox/plain', 'Commands')] , cmake_help_parser.help_category.COMMAND ) deprecated = [cmd[0] for cmd in cmake_help_parser.get_cmake_deprecated_commands_list()] for cmd in cmake_help_parser.get_cmake_commands_list(): c = QTreeWidgetItem( commands , [cmd] , cmake_help_parser.help_category.HELP_ITEM ) global _cmake_completion_model schema = KColorScheme(QPalette.Normal, KColorScheme.Selection) if _cmake_completion_model.has_completion_for_command(cmd): c.setForeground(0, schema.foreground(KColorScheme.PositiveText).color()) else: if cmd in deprecated: c.setForeground(0, schema.foreground(KColorScheme.NeutralText).color()) else: c.setForeground(0, schema.foreground(KColorScheme.NegativeText).color()) # Add modules group standard_modules = cmake_help_parser.get_cmake_modules_list() total_modules_count = len(standard_modules) custom_modules = {} for path in kate.sessionConfiguration[settings.AUX_MODULE_DIRS]: modules_list = cmake_help_parser.get_cmake_modules_list(path) filtered_modules_list = [mod for mod in modules_list if mod not in standard_modules] filtered_modules_list.sort() custom_modules[ i18nc('@item:inlistbox', 'Modules from %1 (%2)', path, len(path)) ] = filtered_modules_list total_modules_count += len(filtered_modules_list) custom_modules[ i18nc('@item:inlistbox', 'Standard modules (%1)', len(standard_modules)) ] = standard_modules # modules = QTreeWidgetItem( self.vewHelpPage.helpTargets , [i18nc('@item::inlistbox/plain', 'Modules (%1)', total_modules_count)] , cmake_help_parser.help_category.MODULE ) for from_path, modules_list in custom_modules.items(): ss_item = QTreeWidgetItem( modules , [from_path] , cmake_help_parser.help_category.MODULE ) for mod in modules_list: m = QTreeWidgetItem( ss_item , [mod] , cmake_help_parser.help_category.HELP_ITEM ) # Add policies group policies = QTreeWidgetItem( self.vewHelpPage.helpTargets , [i18nc('@item::inlistbox/plain', 'Policies')] , cmake_help_parser.help_category.POLICY ) for pol in cmake_help_parser.get_cmake_policies_list(): p = QTreeWidgetItem( policies , [pol] , cmake_help_parser.help_category.HELP_ITEM ) # Add properties group properties = QTreeWidgetItem( self.vewHelpPage.helpTargets , [i18nc('@item::inlistbox/plain', 'Properties')] , cmake_help_parser.help_category.PROPERTY ) for subsection, props_list in cmake_help_parser.get_cmake_properties_list().items(): ss_item = QTreeWidgetItem( properties , [subsection] , cmake_help_parser.help_category.PROPERTY ) for prop in props_list: v = QTreeWidgetItem( ss_item , [prop[0]] , cmake_help_parser.help_category.HELP_ITEM ) v.setToolTip(0, prop[1]) # Add variables group variables = QTreeWidgetItem( self.vewHelpPage.helpTargets , [i18nc('@item::inlistbox/plain', 'Variables')] , cmake_help_parser.help_category.VARIABLE ) for subsection, vars_list in cmake_help_parser.get_cmake_vars_list().items(): ss_item = QTreeWidgetItem( variables , [subsection] , cmake_help_parser.help_category.VARIABLE ) for var in vars_list: v = QTreeWidgetItem( ss_item , [var[0]] , cmake_help_parser.help_category.HELP_ITEM ) v.setToolTip(0, var[1]) # self.vewHelpPage.helpTargets.resizeColumnToContents(0) @pyqtSlot() def updateHelpText(self): tgt = self.vewHelpPage.helpTargets.currentItem() if tgt is None or tgt.type() != cmake_help_parser.help_category.HELP_ITEM: return parent = tgt.parent() if parent is None: return category = parent.type() text = cmake_help_parser.get_help_on(category, tgt.text(0)) if not self.cfgPage.htmlize.isChecked(): self.helpPage.setText(text[text.index('\n') + 1:]) return # TODO How *else* we can beautify the text? lines = text.splitlines()[1:] file_link_re = re.compile('Defined in: (.*)') file_link_sb = 'Defined in: <a href="file://\\1">\\1</a>' pre = False para = True for i, line in enumerate(lines): # Remove '&', '<' and '>' from text # TODO Use some HTML encoder instead of this... line = line.replace('&', '&').replace('<', '<').replace('>', '>') # if line.lstrip().startswith('Defined in: '): line = file_link_re.sub(file_link_sb, line) # if i == 0: line = '<h1>{}</h1>'.format(line) elif line.startswith(' ' * cmake_help_parser.CMAKE_HELP_VARBATIM_TEXT_PADDING_SIZE): if not pre: line = '<pre>' + line pre = True elif len(line.strip()) == 0: if pre: line = line + '</pre>' pre = False elif para: line = line + '</p>' para = False else: line = '<p>' + line para = True lines[i] = line self.helpPage.setText('\n'.join(lines)) @pyqtSlot(QTreeWidgetItem, int) def insertIntoCurrentDocument(self, item, column): if item is not None and column == 0: view = kate.activeView() document = kate.activeDocument() document.startEditing() document.insertText(view.cursorPosition(), item.text(0)) document.endEditing() @pyqtSlot(QTreeWidgetItem, int) def insertHelpItemIntoCurrentDocument(self,item, column): if item is not None and item.type() == cmake_help_parser.help_category.HELP_ITEM and column == 0: view = kate.activeView() document = kate.activeDocument() document.startEditing() document.insertText(view.cursorPosition(), item.text(0)) document.endEditing()
class PluginsManagerWidget(QDialog): """Plugin Manager widget""" def __init__(self, parent): QDialog.__init__(self, parent, Qt.Dialog) self.setWindowTitle(translations.TR_PLUGIN_MANAGER) self.resize(700, 600) vbox = QVBoxLayout(self) self._tabs = QTabWidget() vbox.addWidget(self._tabs) self._txt_data = QTextBrowser() self._txt_data.setOpenLinks(False) vbox.addWidget(QLabel(translations.TR_PROJECT_DESCRIPTION)) vbox.addWidget(self._txt_data) # Footer hbox = QHBoxLayout() btn_close = QPushButton(translations.TR_CLOSE) btnReload = QPushButton(translations.TR_RELOAD) hbox.addWidget(btn_close) hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) hbox.addWidget(btnReload) vbox.addLayout(hbox) self.overlay = ui_tools.Overlay(self) self.overlay.hide() self._oficial_available = [] self._community_available = [] self._locals = [] self._updates = [] self._loading = True self._requirements = {} self.connect(btnReload, SIGNAL("clicked()"), self._reload_plugins) self.thread = ThreadLoadPlugins(self) self.connect(self.thread, SIGNAL("finished()"), self._load_plugins_data) self.connect(self.thread, SIGNAL("plugin_downloaded(PyQt_PyObject)"), self._after_download_plugin) self.connect(self.thread, SIGNAL("plugin_manually_installed(PyQt_PyObject)"), self._after_manual_install_plugin) self.connect(self.thread, SIGNAL("plugin_uninstalled(PyQt_PyObject)"), self._after_uninstall_plugin) self.connect(self._txt_data, SIGNAL("anchorClicked(const QUrl&)"), self._open_link) self.connect(btn_close, SIGNAL('clicked()'), self.close) self.overlay.show() self._reload_plugins() def show_plugin_info(self, data): """Takes data argument, format for HTML and display it""" plugin_description = data[2].replace('\n', '<br>') html = HTML_STYLE.format(name=data[0], version=data[1], description=plugin_description, author=data[3], link=data[4]) self._txt_data.setHtml(html) def _open_link(self, url): """Takes an url argument and open the link on web browser""" link = url.toString() if link.startswith('/plugins/'): link = 'http://ninja-ide.org' + link webbrowser.open(link) def _reload_plugins(self): """Reload all plugins""" self.overlay.show() self._loading = True self.thread.runnable = self.thread.collect_data_thread self.thread.start() def _after_manual_install_plugin(self, plugin): """After installing, take plugin and add it to installedWidget items""" data = {} data['name'] = plugin[0] data['version'] = plugin[1] data['description'] = '' data['authors'] = '' data['home'] = '' self._installedWidget.add_table_items([data]) def _after_download_plugin(self, plugin): """After installing, take plugin and add it to installedWidget items""" oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._installedWidget.add_table_items([oficial_plugin]) self._availableOficialWidget.remove_item(plugin[0]) elif community_plugin: self._installedWidget.add_table_items([community_plugin]) self._availableCommunityWidget.remove_item(plugin[0]) def _after_uninstall_plugin(self, plugin): """After uninstall plugin,make available plugin corresponding to type""" oficial_plugin = _get_plugin(plugin[0], self._oficial_available) community_plugin = _get_plugin(plugin[0], self._community_available) if oficial_plugin: self._availableOficialWidget.add_table_items([oficial_plugin]) self._installedWidget.remove_item(plugin[0]) elif community_plugin: self._availableCommunityWidget.add_table_items([community_plugin]) self._installedWidget.remove_item(plugin[0]) def _load_plugins_data(self): """Load all the plugins data""" if self._loading: self._tabs.clear() self._updatesWidget = UpdatesWidget(self, copy(self._updates)) self._availableOficialWidget = AvailableWidget(self, copy(self._oficial_available)) self._availableCommunityWidget = AvailableWidget(self, copy(self._community_available)) self._installedWidget = InstalledWidget(self, copy(self._locals)) self._tabs.addTab(self._availableOficialWidget, translations.TR_OFFICIAL_AVAILABLE) self._tabs.addTab(self._availableCommunityWidget, translations.TR_COMMUNITY_AVAILABLE) self._tabs.addTab(self._updatesWidget, translations.TR_UPDATE) self._tabs.addTab(self._installedWidget, translations.TR_INSTALLED) self._manualWidget = ManualInstallWidget(self) self._tabs.addTab(self._manualWidget, translations.TR_MANUAL_INSTALL) self._loading = False self.overlay.hide() self.thread.wait() def download_plugins(self, plugs): """ Install """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.download_plugins_thread self.thread.start() def install_plugins_manually(self, plug): """Install plugin from local zip.""" self.overlay.show() self.thread.plug = plug #set the function to run in the thread self.thread.runnable = self.thread.manual_install_plugins_thread self.thread.start() def mark_as_available(self, plugs): """ Uninstall """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.uninstall_plugins_thread self.thread.start() def update_plugin(self, plugs): """ Update """ self.overlay.show() self.thread.plug = plugs #set the function to run in the thread self.thread.runnable = self.thread.update_plugin_thread self.thread.start() def reset_installed_plugins(self): """Reset all the installed plugins""" local_plugins = plugin_manager.local_plugins() plugins = _format_for_table(local_plugins) self._installedWidget.reset_table(plugins) def resizeEvent(self, event): """Handle Resize events""" self.overlay.resize(event.size()) event.accept()
class AdvancedObjectWidget(QWidget): def __init__(self, index, currentTemplate="classic.html", parent=None, entryTemplate=None): QWidget.__init__(self, parent) w = 24 h = 24 self.entryModel = None # Standard pixmaps used by the widget self.reloadPixmap = pixmapFromTheme("view-refresh", ":/icons/32/view-refresh", w, h) self.savePixmap = pixmapFromTheme("document-save", ":/icons/32/document-save", w, h) self.addPixmap = pixmapFromTheme("list-add", ":/icons/32/list-add", w, h) self.deleteSmallPixmap = pixmapFromTheme("list-remove", ":/icons/32/list-remove", w, h) self.treeIndex = index self.setLayout(QVBoxLayout(self)) self.layout().setSpacing(0) self.layout().setContentsMargins(0, 0, 0, 0) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # create the widget containing the data self.textBrowser = QTextBrowser() self.textBrowser.setOpenLinks(False) self.textBrowser.setWordWrapMode(QTextOption.WrapAnywhere) self.layout().addWidget(self.textBrowser) self.textBrowser.anchorClicked.connect(self.anchorClicked) self.currentDocument = '' self.addingToComboBox = False # create the combobox containing the different views self.comboBox = QComboBox() self.currentTemplate = currentTemplate self.errorTemplate = "error.html" self.usedTemplates = [] # FIXED: Need a more robust way for locating the path used in # the TemplateFactory for locating the template view # files # >>> with base.util.Paths.getLumaRoot this should work. # Probably needs some validation testing on platforms # other than Linux # Another issue occured when running Luma after a installation # from a source distribution. Because the site-packages is only # intended for pure python modules, the html templates is not # installed, resulting in an Exception when trying to view an # entry in the Browser plugin. The setup.py script is modified # such that the needed html templates is copied into a folder # in the path returned by `base.util.Paths.getConfigPrefix`. s = QtCore.QSettings() configPrefix = s.value('application/config_prefix').toString() templatesPath = os.path.join( unicode(configPrefix).encode('utf-8'), 'browser-templates') # If we run Luma from a development environment the isntalled # templatesPath do most likely not exist. We therefore use the # directory in the repository if not os.path.isdir(templatesPath): templatesPath = unicode( os.path.join(getLumaRoot(), 'plugins', 'browser', 'templates')) self.templateFactory = TemplateFactory(templatesPath) self.htmlParser = HtmlParser(self.textBrowser) self.str_RELOAD = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Reload") self.str_SAVE = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Save entry") self.str_SAVE_CONTINUE = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Do you want to save the entry before continuing?") self.str_SAVE_FAILED = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Saving failed, continue anyway?") self.str_DELETE = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Delete object") self.str_DELETE_CONFIRM = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Do you really want to delete the object?") self.str_ADD = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Add attribute") self.str_SWITCH_VIEWS = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Switch between views") self.str_REASON = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Reason:") self.str_EXPORT_BINARY = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Export binary attribute to file") self.str_EXPORT_BINARY_EXCEPT = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Could not export binary data to file.") self.str_SELECT_ANOTHER_FILE = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Please select another filename.") self.str_NO_TEMPLATE = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "No templates available") self.str_MISSING_ENTRY = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "Did'nt receive a ldap-object, it might have been deleted") self.str_ENTRY_INVALID = QtCore.QCoreApplication.translate( "AdvancedObjectWidget", "The ldap object is not valid\nClick Yes to view the object anyway\nNo to view the errors \nIgnore to view the object and ignore this message in later attempts." ) self.buildToolBar() ############################################################################### @staticmethod def smartObjectCopy(smartObject): return SmartDataObject( copy.deepcopy([smartObject.dn, smartObject.data]), copy.deepcopy(smartObject.serverMeta)) ############################################################################### def getSmartObject(self): return self.entryModel.getSmartObject() ############################################################################### def initModel(self, smartObject, create=False, entryTemplate=None): """ sets up the model, and connects it to this object """ if not create: # use a copy of the smartObject smartObject = AdvancedObjectWidget.smartObjectCopy(smartObject) self.baseDN = smartObject.getDN() self.entryModel = EntryModel(smartObject, self, entryTemplate) self.htmlParser.setModel(self.entryModel) self.entryModel.modelChangedSignal.connect(self.modelChanged) success, exceptionMsg, exceptionObject = self.entryModel.initModel( create) if not success: errorMsg = "%s<br><br>%s: %s" % (exceptionMsg, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) ############################################################################### def loadTemplates(self): """ Loads all templates that matches with the current objectclasses """ self.usedTemplates = [] objectClasses = self.getSmartObject().getObjectClasses() newIndex = -1 i = 0 for objectClass, fileName in self.templateFactory.getTemplateList(): if objectClass == '' or objectClass in objectClasses: if fileName == self.currentTemplate: newIndex = i self.usedTemplates.append(fileName) i += 1 if newIndex == -1: newIndex = 0 self.currentTemplate = self.usedTemplates[newIndex] #TODO do this properly, signals ignored self.addingToComboBox = True self.comboBox.clear() self.comboBox.addItems(self.usedTemplates) self.comboBox.setCurrentIndex(newIndex) self.addingToComboBox = False ############################################################################### @pyqtSlot(bool) def modelChanged(self, reload): if reload: item = None if self.treeIndex and self.treeIndex.isValid(): row = self.treeIndex.row() column = self.treeIndex.column() # QPersistenIndex doesn't have internalPointer() # so we aquire a QModelIndex which does item = self.treeIndex.sibling(row, column).internalPointer() if not (self.entryModel.VALID): if item == None or not (item.serverParent.ignoreItemErrors): result = QMessageBox.question( self, self.trUtf8(""), self.str_ENTRY_INVALID, QMessageBox.Yes | QMessageBox.No | QMessageBox.Ignore, QMessageBox.No) if result == QMessageBox.No: self.currentTemplate = self.errorTemplate elif result == QMessageBox.Ignore: if not (item == None): item.serverParent.ignoreItemErrors = True self.displayValues() ############################################################################### def displayValues(self): # Something went wrong. We have no data object. # This might happen if we want to refresh an item and # it might be deleted already. if None == self.entryModel.getSmartObject(): QMessageBox.critical(self, self.str_MISSING_ENTRY) self.enableToolButtons(False) return self.loadTemplates() if self.currentTemplate == None: selt.textBrowser.setHtml(self.str_NO_TEMPLATE) return htmlTemplate = self.templateFactory.getTemplateFile( self.currentTemplate) self.currentDocument = self.htmlParser.parseHtml(htmlTemplate) self.textBrowser.setHtml(self.currentDocument) self.enableToolButtons(True) ############################################################################### def enableToolButtons(self, enable): if None == self.entryModel: self.saveButton.setEnabled(False) self.deleteObjectButton.setEnabled(False) self.reloadButton.setEnabled(True) self.addAttributeButton.setEnabled(False) return if self.entryModel.EDITED and not self.entryModel.CREATE: self.saveButton.setEnabled(enable) else: self.saveButton.setEnabled(False) if self.entryModel.ISLEAF: self.deleteObjectButton.setEnabled(enable) else: self.deleteObjectButton.setEnabled(False) if self.entryModel.CREATE: self.reloadButton.setEnabled(False) else: self.reloadButton.setEnabled(enable) self.addAttributeButton.setEnabled(enable) ############################################################################### def buildToolBar(self): self.toolBar = QToolBar() self.toolBar.layout().setContentsMargins(0, 0, 0, 0) # Reload button self.reloadButton = QToolButton(self.toolBar) self.reloadButton.setIcon(QIcon(self.reloadPixmap)) self.reloadButton.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.reloadButton.setAutoRaise(True) self.reloadButton.setBackgroundRole(self.backgroundRole()) self.reloadButton.setToolTip(self.str_RELOAD) self.connect(self.reloadButton, SIGNAL("clicked()"), self.refreshView) self.toolBar.addWidget(self.reloadButton) # Save button self.saveButton = QToolButton(self.toolBar) self.saveButton.setIcon(QIcon(self.savePixmap)) self.saveButton.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.saveButton.setAutoRaise(True) self.saveButton.setBackgroundRole(self.backgroundRole()) self.saveButton.setToolTip(self.str_SAVE) self.connect(self.saveButton, SIGNAL("clicked()"), self.saveObject) self.toolBar.addWidget(self.saveButton) self.toolBar.addSeparator() # Add attribute button self.addAttributeButton = QToolButton(self.toolBar) self.addAttributeButton.setIcon(QIcon(self.addPixmap)) self.addAttributeButton.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.addAttributeButton.setAutoRaise(True) self.addAttributeButton.setBackgroundRole(self.backgroundRole()) self.addAttributeButton.setToolTip(self.str_ADD) self.connect(self.addAttributeButton, SIGNAL("clicked()"), self.addAttribute) self.toolBar.addWidget(self.addAttributeButton) # Delete button self.deleteObjectButton = QToolButton(self.toolBar) self.deleteObjectButton.setIcon(QIcon(self.deleteSmallPixmap)) self.deleteObjectButton.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.deleteObjectButton.setAutoRaise(True) self.deleteObjectButton.setBackgroundRole(self.backgroundRole()) self.deleteObjectButton.setToolTip(self.str_DELETE) self.connect(self.deleteObjectButton, SIGNAL("clicked()"), self.deleteObject) self.toolBar.addWidget(self.deleteObjectButton) self.comboBox.setToolTip(self.str_SWITCH_VIEWS) self.connect(self.comboBox, SIGNAL("currentIndexChanged(int)"), self.changeView) self.toolBar.addWidget(self.comboBox) self.enableToolButtons(False) self.layout().insertWidget(0, self.toolBar) ############################################################################### @pyqtSlot("int") def changeView(self, index): """ change between different views """ if index == -1 or self.addingToComboBox: return self.currentTemplate = self.usedTemplates[index] self.displayValues() ############################################################################### def aboutToChange(self): """ Asks the user whether changes should be saved returns True if changes were saved, or discarded """ if not self.entryModel.EDITED: return True result = QMessageBox.warning( self, self.str_SAVE, self.str_SAVE_CONTINUE, QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Cancel) # TODO add exception message if result == QMessageBox.Save: if not self.saveObject(): # Saving failed result = QMessageBox.question( None, self.trUtf8(""), self.str_SAVE_FAILED, QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) return not (result == QMessageBox.Cancel) ############################################################################### # TODO: add logging for each error @pyqtSlot() def refreshView(self): """ Refreshes the LDAP data from server and displays values. """ if self.aboutToChange(): success, exceptionMsg, exceptionObject = self.entryModel.reloadModel( ) if not success: errorMsg = "%s<br><br>%s: %s" % (exceptionMsg, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) else: self.displayValues() ############################################################################### # TODO: add logging for each error @pyqtSlot() def saveObject(self): success, exceptionMsg, exceptionObject = self.entryModel.saveModel() if not success: # Saving failed errorMsg = "%s<br><br>%s: %s" % (exceptionMsg, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) return False else: # update the smartObject in the tree if self.entryModel.CREATE: pass elif self.treeIndex and self.treeIndex.isValid(): row = self.treeIndex.row() column = self.treeIndex.column() # QPersistenIndex doesn't have internalPointer() # so we aquire a QModelIndex which does index = self.treeIndex.sibling(row, column) index.internalPointer().itemData = self.getSmartObject() return True ############################################################################### @pyqtSlot() def addAttribute(self): """ Add attributes to the current object. """ dialog = AddAttributeWizard(self) dialog.setData(self.smartObjectCopy(self.entryModel.getSmartObject())) dialog.exec_() if dialog.result() == QDialog.Rejected: return attribute = str(dialog.attributeBox.currentText()) showAll = dialog.enableAllBox.isChecked() if dialog.binaryBox.isChecked(): attributeSet = set([attribute + ";binary"]) else: attributeSet = set([attribute]) if showAll and not (attribute.lower() in dialog.possibleAttributes): objectClass = str(dialog.classBox.currentItem().text()) self.entryModel.addObjectClass(objectClass) serverSchema = ObjectClassAttributeInfo( self.entryModel.smartObject.getServerMeta()) mustAttributes = serverSchema.getAllMusts([objectClass]) mustAttributes = mustAttributes.difference( set(self.entryModel.smartObject.getAttributeList())) attributeSet = mustAttributes.union(set([attribute])) for x in attributeSet: self.entryModel.addAttributeValue(x, None) self.displayValues() ############################################################################### # TODO: add logging for each error, remove tab and node from parent @pyqtSlot() def deleteObject(self): buttonClicked = QMessageBox.critical(self, self.str_DELETE, self.str_DELETE_CONFIRM, QMessageBox.Yes, QMessageBox.No) if not (buttonClicked == QMessageBox.Yes): return # If we have an index, use it tell the item to delete itself # so that the view is updated if self.treeIndex and self.treeIndex.isValid(): row = self.treeIndex.row() column = self.treeIndex.column() # QPersistenIndex doesn't have internalPointer() # so we aquire a QModelIndex which does index = self.treeIndex.sibling(row, column) success, message = index.model().deleteItem(index) if success: self.enableToolButtons(False) self.deleteLater() else: errorMsg = "%s" % (message) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) # if not, we just delete it ourselves since there's not view on the object else: success, message, exceptionObject = self.entryModel.deleteObject() if success: self.enableToolButtons(False) self.deleteLater() else: errorMsg = "%s<br><br>%s: %s" % (message, self.str_REASON, str(exceptionObject)) QMessageBox.critical(self, self.trUtf8(""), self.trUtf8(errorMsg)) ############################################################################### @pyqtSlot("QUrl") def anchorClicked(self, url): """ Called when an anchor (<a href=".." /> is clicked """ nameString = unicode(url.toString()) tmpList = nameString.split("__") if tmpList[0] in self.entryModel.getSmartObject().getObjectClasses(): self.entryModel.deleteObjectClass(tmpList[0]) else: if not len(tmpList) == 3: return attributeName, index, operation = tmpList[0], int( tmpList[1]), tmpList[2] if operation == "edit": self.editAttribute(attributeName, index) elif operation == "delete": self.deleteAttribute(attributeName, index) elif operation == "export": self.exportAttribute(attributeName, index) ############################################################################### def editAttribute(self, attributeName, index): smartObject = self.entryModel.getSmartObject() oldDN = smartObject.getDN() addAttribute = False if attributeName == 'RDN': # TODO correct this, used on creation? oldValue = oldDN smartObject.setDN(self.baseDN) else: if smartObject.hasAttribute(attributeName): addValue = False oldValue = smartObject.getAttributeValue(attributeName, index) if oldValue == None: oldValue = '' else: addValue = True oldValue = '' dialog = getEditorWidget(self, smartObject, attributeName, index) dialog.exec_() if dialog.result() == QDialog.Accepted: # TODO check attribute types newValue = dialog.getValue() if not (newValue == None): if attributeName == 'RDN': self.entryModel.setDN(newValue) if dialog.addAttributeBox.isChecked(): addAttribute = unicode( dialog.attributeBox.currentText()) addValue = unicode(dialog.valueEdit.text()) self.entryModel.addAttributeValue( addAttribute, [addValue]) else: if addValue: self.entryModel.addAttributeValue( attributeName, [newValue]) else: self.entryModel.editAttribute(attributeName, index, newValue) else: if attributeName == 'RDN': smartObject.setDN(oldDN.decode('utf-8')) ############################################################################### def deleteAttribute(self, attributeName, index): self.entryModel.deleteAttribute(attributeName, index) ############################################################################### def exportAttribute(self, attributeName, index): """ Show the dialog for exporting binary attribute data. """ value = self.getSmartObject().getAttributeValue(attributeName, index) fileName = unicode(QFileDialog.getSaveFileName(\ self, self.str_EXPORT_BINARY, QString(""), "All files (*)", None)) if unicode(fileName) == "": return try: fileHandler = open(fileName, "w") fileHandler.write(value) fileHandler.close() SAVED = True except IOError, e: msg = "%s %s:\n\n%s\n\n%s" % (self.str_EXPORT_BINARY_EXCEPT, self.str_REASON, str(e), self.str_SELECT_ANOTHER_FILE) result = QMessageBox.warning(\ self, self.str_EXPORT_BINARY, msg, QMessageBox.Cancel | QMessageBox.Ok, QMessageBox.Cancel)