예제 #1
0
    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)
예제 #2
0
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()
예제 #3
0
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()
예제 #4
0
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)
예제 #5
0
파일: __init__.py 프로젝트: hlamer/kate
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('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
            #
            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()
예제 #6
0
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()
예제 #7
0
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)