eric6/WebBrowser/QtHelp/QtHelpDocumentationDialog.py

changeset 7231
0dcb92a9687d
parent 7229
53054eb5b15a
child 7235
4f23ea4af43c
--- a/eric6/WebBrowser/QtHelp/QtHelpDocumentationDialog.py	Tue Sep 10 19:36:17 2019 +0200
+++ b/eric6/WebBrowser/QtHelp/QtHelpDocumentationDialog.py	Fri Sep 13 18:54:34 2019 +0200
@@ -7,9 +7,11 @@
 Module implementing a dialog to manage the QtHelp documentation database.
 """
 
+import sqlite3
 
 from PyQt5.QtCore import pyqtSlot, Qt, QItemSelectionModel
-from PyQt5.QtWidgets import QDialog
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QListWidgetItem, \
+    QInputDialog, QLineEdit
 from PyQt5.QtHelp import QHelpEngineCore
 
 from E5Gui import E5MessageBox, E5FileDialog
@@ -32,18 +34,51 @@
         super(QtHelpDocumentationDialog, self).__init__(parent)
         self.setupUi(self)
         
-        self.removeButton.setEnabled(False)
-        
         self.__engine = engine
         self.__mw = parent
         
+        self.__initDocumentsTab()
+        self.__initFiltersTab()
+        
+        self.tabWidget.setCurrentIndex(0)
+    
+    @pyqtSlot(int)
+    def on_tabWidget_currentChanged(self, index):
+        """
+        Private slot handling a change of the current tab.
+        
+        @param index index of the current tab
+        @type int
+        """
+        if index != 1 and \
+           (self.__hasChangedFilters() or self.__removedAttributes):
+            yes = E5MessageBox.yesNo(
+                self,
+                self.tr("Unsaved Filter Changes"),
+                self.tr("""The page contains unsaved changes. Shall they be"""
+                        """ saved?"""),
+                yesDefault=True)
+            if yes:
+                self.on_applyFilterChangesButton_clicked()
+    
+    ##################################################################
+    ## Documentations Tab
+    ##################################################################
+    
+    def __initDocumentsTab(self):
+        """
+        Private method to initialize the documents tab.
+        """
+        self.documentsList.clear()
+        self.removeDocumentsButton.setEnabled(False)
+        
         docs = self.__engine.registeredDocumentations()
         self.documentsList.addItems(docs)
-        
+
         self.__registeredDocs = []
         self.__unregisteredDocs = []
         self.__tabsToClose = []
-        
+
         try:
             self.__pluginHelpDocuments = \
                 e5App().getObject("PluginManager").getPluginQtHelpFiles()
@@ -60,11 +95,11 @@
         """
         Private slot handling a change of the documents selection.
         """
-        self.removeButton.setEnabled(
+        self.removeDocumentsButton.setEnabled(
             len(self.documentsList.selectedItems()) != 0)
     
     @pyqtSlot()
-    def on_addButton_clicked(self):
+    def on_addDocumentsButton_clicked(self):
         """
         Private slot to add QtHelp documents to the help database.
         """
@@ -144,9 +179,11 @@
             self.__registeredDocs.append(ns)
             if ns in self.__unregisteredDocs:
                 self.__unregisteredDocs.remove(ns)
+        
+        self.__initFiltersTab()
 
     @pyqtSlot()
-    def on_removeButton_clicked(self):
+    def on_removeDocumentsButton_clicked(self):
         """
         Private slot to remove a document from the help database.
         """
@@ -189,11 +226,13 @@
             self.documentsList.setCurrentRow(
                 0, QItemSelectionModel.ClearAndSelect)
     
-    def hasChanges(self):
+    def hasDocumentationChanges(self):
         """
-        Public slot to test the dialog for changes.
+        Public slot to test the dialog for changes of configured QtHelp
+        documents.
         
         @return flag indicating presence of changes
+        @rtype bool
         """
         return len(self.__registeredDocs) > 0 or \
             len(self.__unregisteredDocs) > 0
@@ -202,6 +241,293 @@
         """
         Public method to get the list of tabs to close.
         
-        @return list of tab ids to be closed (list of integers)
+        @return list of tab ids to be closed
+        @rtype list of int
         """
         return self.__tabsToClose
+    
+    ##################################################################
+    ## Filters Tab
+    ##################################################################
+    
+    def __initFiltersTab(self):
+        """
+        Private method to initialize the filters tab.
+        """
+        self.removeFiltersButton.setEnabled(False)
+        self.removeAttributesButton.setEnabled(False)
+        
+        # save the current and selected filters
+        currentFilter = self.filtersList.currentItem()
+        if currentFilter:
+            currentFilterText = currentFilter.text()
+        else:
+            currentFilterText = ""
+        selectedFiltersText = [
+            itm.text() for itm in self.filtersList.selectedItems()]
+        
+        # save the selected attributes
+        selectedAttributesText = [
+            itm.text(0) for itm in self.attributesList.selectedItems()]
+        
+        self.filtersList.clear()
+        self.attributesList.clear()
+        
+        helpEngineCore = QHelpEngineCore(self.__engine.collectionFile())
+        
+        self.__removedFilters = []
+        self.__filterMap = {}
+        self.__filterMapBackup = {}
+        self.__removedAttributes = []
+        
+        for customFilter in helpEngineCore.customFilters():
+            atts = helpEngineCore.filterAttributes(customFilter)
+            self.__filterMapBackup[customFilter] = atts
+            if customFilter not in self.__filterMap:
+                self.__filterMap[customFilter] = atts
+        
+        self.filtersList.addItems(sorted(self.__filterMap.keys()))
+        for attr in helpEngineCore.filterAttributes():
+            QTreeWidgetItem(self.attributesList, [attr])
+        self.attributesList.sortItems(0, Qt.AscendingOrder)
+        
+        if selectedFiltersText or currentFilterText or selectedAttributesText:
+            # restore the selected filters
+            for txt in selectedFiltersText:
+                items = self.filtersList.findItems(txt, Qt.MatchExactly)
+                for itm in items:
+                    itm.setSelected(True)
+            # restore the current filter
+            if currentFilterText:
+                items = self.filtersList.findItems(currentFilterText,
+                                                   Qt.MatchExactly)
+                if items:
+                    self.filtersList.setCurrentItem(
+                        items[0], QItemSelectionModel.NoUpdate)
+            # restore the selected attributes
+            for txt in selectedAttributesText:
+                items = self.attributesList.findItems(txt, Qt.MatchExactly, 0)
+                for itm in items:
+                    itm.setSelected(True)
+        elif self.__filterMap:
+            self.filtersList.setCurrentRow(0)
+    
+    @pyqtSlot(QListWidgetItem, QListWidgetItem)
+    def on_filtersList_currentItemChanged(self, current, previous):
+        """
+        Private slot to update the attributes depending on the current filter.
+        
+        @param current reference to the current item (QListWidgetitem)
+        @param previous reference to the previous current item
+            (QListWidgetItem)
+        """
+        checkedList = []
+        if current is not None:
+            checkedList = self.__filterMap[current.text()]
+        for index in range(0, self.attributesList.topLevelItemCount()):
+            itm = self.attributesList.topLevelItem(index)
+            if itm.text(0) in checkedList:
+                itm.setCheckState(0, Qt.Checked)
+            else:
+                itm.setCheckState(0, Qt.Unchecked)
+    
+    @pyqtSlot()
+    def on_filtersList_itemSelectionChanged(self):
+        """
+        Private slot handling a change of selected filters.
+        """
+        self.removeFiltersButton.setEnabled(
+            len(self.filtersList.selectedItems()) > 0)
+    
+    @pyqtSlot(QTreeWidgetItem, int)
+    def on_attributesList_itemChanged(self, item, column):
+        """
+        Private slot to handle a change of an attribute.
+        
+        @param item reference to the changed item (QTreeWidgetItem)
+        @param column column containing the change (integer)
+        """
+        if self.filtersList.currentItem() is None:
+            return
+        
+        customFilter = self.filtersList.currentItem().text()
+        if customFilter not in self.__filterMap:
+            return
+        
+        newAtts = []
+        for index in range(0, self.attributesList.topLevelItemCount()):
+            itm = self.attributesList.topLevelItem(index)
+            if itm.checkState(0) == Qt.Checked:
+                newAtts.append(itm.text(0))
+        self.__filterMap[customFilter] = newAtts
+    
+    @pyqtSlot()
+    def on_attributesList_itemSelectionChanged(self):
+        """
+        Private slot handling the selection of attributes.
+        """
+        self.removeAttributesButton.setEnabled(
+            len(self.attributesList.selectedItems()) != 0)
+    
+    @pyqtSlot()
+    def on_addFilterButton_clicked(self):
+        """
+        Private slot to add a new filter.
+        """
+        customFilter, ok = QInputDialog.getText(
+            None,
+            self.tr("Add Filter"),
+            self.tr("Filter name:"),
+            QLineEdit.Normal)
+        if not customFilter:
+            return
+        
+        if customFilter not in self.__filterMap:
+            self.__filterMap[customFilter] = []
+            self.filtersList.addItem(customFilter)
+        
+        itm = self.filtersList.findItems(
+            customFilter, Qt.MatchCaseSensitive)[0]
+        self.filtersList.setCurrentItem(itm)
+    
+    @pyqtSlot()
+    def on_removeFiltersButton_clicked(self):
+        """
+        Private slot to remove the selected filters.
+        """
+        ok = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Filters"),
+            self.tr(
+                """Do you really want to remove the selected filters """
+                """from the database?"""))
+        if not ok:
+            return
+        
+        items = self.filtersList.selectedItems()
+        for item in items:
+            itm = self.filtersList.takeItem(self.filtersList.row(item))
+            if itm is None:
+                continue
+            
+            del self.__filterMap[itm.text()]
+            self.__removedFilters.append(itm.text())
+            del itm
+        
+        if self.filtersList.count():
+            self.filtersList.setCurrentRow(
+                0, QItemSelectionModel.ClearAndSelect)
+    
+    @pyqtSlot()
+    def on_removeAttributesButton_clicked(self):
+        """
+        Private slot to remove the selected filter attributes.
+        """
+        ok = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Attributes"),
+            self.tr(
+                """Do you really want to remove the selected attributes """
+                """from the database?"""))
+        if not ok:
+            return
+        
+        items = self.attributesList.selectedItems()
+        for item in items:
+            itm = self.attributesList.takeTopLevelItem(
+                self.attributesList.indexOfTopLevelItem(item))
+            if itm is None:
+                continue
+            
+            attr = itm.text(0)
+            self.__removedAttributes.append(attr)
+            for customFilter in self.__filterMap:
+                if attr in self.__filterMap[customFilter]:
+                    self.__filterMap[customFilter].remove(attr)
+            
+            del itm
+    
+    @pyqtSlot()
+    def on_unusedAttributesButton_clicked(self):
+        """
+        Private slot to select all unused attributes.
+        """
+        # step 1: determine all used attributes
+        attributes = set()
+        for customFilter in self.__filterMap:
+            attributes |= set(self.__filterMap[customFilter])
+        
+        # step 2: select all unused attribute items
+        self.attributesList.clearSelection()
+        for row in range(self.attributesList.topLevelItemCount()):
+            itm = self.attributesList.topLevelItem(row)
+            if itm.text(0) not in attributes:
+                itm.setSelected(True)
+    
+    def __removeAttributes(self):
+        """
+        Private method to remove attributes from the Qt Help database.
+        """
+        try:
+            self.__db = sqlite3.connect(self.__engine.collectionFile())
+        except sqlite3.DatabaseError:
+            pass        # ignore database errors
+        
+        for attr in self.__removedAttributes:
+            self.__db.execute(
+                "DELETE FROM FilterAttributeTable WHERE Name = '{0}'"
+                .format(attr))
+        self.__db.commit()
+        self.__db.close()
+    
+    @pyqtSlot()
+    def on_applyFilterChangesButton_clicked(self):
+        """
+        Private slot to apply the filter changes.
+        """
+        if self.__hasChangedFilters():
+            for customFilter in self.__removedFilters:
+                self.__engine.removeCustomFilter(customFilter)
+            for customFilter in self.__filterMap:
+                self.__engine.addCustomFilter(
+                    customFilter, self.__filterMap[customFilter])
+        
+        if self.__removedAttributes:
+            self.__removeAttributes()
+
+    def __hasChangedFilters(self):
+        """
+        Private method to determine, if there are filter changes.
+        
+        @return flag indicating the presence of filter changes
+        @rtype bool
+        """
+        filtersChanged = False
+        if len(self.__filterMapBackup) != len(self.__filterMap):
+            filtersChanged = True
+        else:
+            for customFilter in self.__filterMapBackup:
+                if customFilter not in self.__filterMap:
+                    filtersChanged = True
+                else:
+                    oldFilterAtts = self.__filterMapBackup[customFilter]
+                    newFilterAtts = self.__filterMap[customFilter]
+                    if len(oldFilterAtts) != len(newFilterAtts):
+                        filtersChanged = True
+                    else:
+                        for attr in oldFilterAtts:
+                            if attr not in newFilterAtts:
+                                filtersChanged = True
+                                break
+
+                if filtersChanged:
+                    break
+        
+        return filtersChanged
+    
+    @pyqtSlot()
+    def on_resetFilterChangesButton_clicked(self):
+        """
+        Private slot to forget the filter changes and reset the tab.
+        """
+        self.__initFiltersTab()

eric ide

mercurial