Пример #1
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(AdBlockManager, self).__init__(parent)

        self.__loaded = False
        self.__subscriptionsLoaded = False
        self.__enabled = False
        self.__adBlockDialog = None
        self.__adBlockExceptionsDialog = None
        self.__adBlockNetwork = None
        self.__adBlockPage = None
        self.__subscriptions = []
        self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions")
        self.__saveTimer = AutoSaver(self, self.save)

        self.__defaultSubscriptionUrlString = \
            "abp:subscribe?location=" \
            "https://easylist-downloads.adblockplus.org/easylist.txt&"\
            "title=EasyList"
        self.__customSubscriptionUrlString = \
            bytes(self.__customSubscriptionUrl().toEncoded()).decode()

        self.rulesChanged.connect(self.__saveTimer.changeOccurred)
Пример #2
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(CookieJar, self).__init__(parent)

        self.__loaded = False
        self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo
        self.__saveTimer = AutoSaver(self, self.__save)

        self.__cookiesFile = os.path.join(Utilities.getConfigDir(),
                                          "web_browser", "cookies.ini")

        self.__store = WebBrowserWindow.webProfile().cookieStore()
        try:
            self.__store.setCookieFilter(self.__cookieFilter)
        except AttributeError:
            # pre Qt 5.11
            pass
        self.__store.cookieAdded.connect(self.__cookieAdded)
        self.__store.cookieRemoved.connect(self.__cookieRemoved)

        self.__load()
        self.__store.loadAllCookies()
Пример #3
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(PasswordManager, self).__init__(parent)

        # setup userscript to monitor forms
        script = QWebEngineScript()
        script.setName("_eric_passwordmonitor")
        script.setInjectionPoint(QWebEngineScript.DocumentReady)
        script.setWorldId(WebBrowserPage.SafeJsWorld)
        script.setRunsOnSubFrames(True)
        script.setSourceCode(Scripts.setupFormObserver())
        profile = WebBrowser.WebBrowserWindow.WebBrowserWindow.webProfile()
        profile.scripts().insert(script)

        self.__logins = {}
        self.__loginForms = {}
        self.__never = []
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)
Пример #4
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(DownloadManager, self).__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(Qt.Window)

        self.__saveTimer = AutoSaver(self, self.save)

        self.__model = DownloadModel(self)
        self.__manager = Helpviewer.HelpWindow.HelpWindow\
            .networkAccessManager()

        self.__iconProvider = None
        self.__downloads = []
        self.__downloadDirectory = ""
        self.__loaded = False

        self.setDownloadDirectory(Preferences.getUI("DownloadPath"))

        self.downloadsView.setShowGrid(False)
        self.downloadsView.verticalHeader().hide()
        self.downloadsView.horizontalHeader().hide()
        self.downloadsView.setAlternatingRowColors(True)
        self.downloadsView.horizontalHeader().setStretchLastSection(True)
        self.downloadsView.setModel(self.__model)
        self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.downloadsView.customContextMenuRequested.connect(
            self.__customContextMenuRequested)

        self.__load()
Пример #5
0
 def __init__(self, engine, parent=None):
     """
     Constructor
     
     @param engine reference to the help engine (QHelpEngine)
     @param parent reference to the parent object (QObject)
     """
     super(NetworkManager, self).__init__(parent)
     
     from E5Network.E5NetworkProxyFactory import E5NetworkProxyFactory
     
     self.__proxyFactory = E5NetworkProxyFactory()
     if Preferences.getUI("UseSystemProxy"):
         QNetworkProxyFactory.setUseSystemConfiguration(True)
     else:
         QNetworkProxyFactory.setApplicationProxyFactory(
             self.__proxyFactory)
         QNetworkProxyFactory.setUseSystemConfiguration(False)
     
     self.languagesChanged()
     
     if SSL_AVAILABLE:
         self.__sslErrorHandler = E5SslErrorHandler(self)
         self.sslErrors.connect(self.__sslErrorHandler.sslErrorsReplySlot)
     
     self.__temporarilyIgnoredSslErrors = {}
     self.__permanentlyIgnoredSslErrors = {}
     # dictionaries of permanently and temporarily ignored SSL errors
     
     self.__loaded = False
     self.__saveTimer = AutoSaver(self, self.__save)
     
     self.changed.connect(self.__saveTimer.changeOccurred)
     self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired)
     self.authenticationRequired.connect(
         lambda reply, auth: self.authentication(reply.url(), auth))
     
     from .EricSchemeHandler import EricSchemeHandler
     self.__ericSchemeHandler = EricSchemeHandler()
     WebBrowserWindow.webProfile().installUrlSchemeHandler(
         QByteArray(b"eric"), self.__ericSchemeHandler)
     
     if engine:
         from .QtHelpSchemeHandler import QtHelpSchemeHandler
         self.__qtHelpSchemeHandler = QtHelpSchemeHandler(engine)
         WebBrowserWindow.webProfile().installUrlSchemeHandler(
             QByteArray(b"qthelp"), self.__qtHelpSchemeHandler)
     
     self.__interceptor = NetworkUrlInterceptor(self)
     try:
         WebBrowserWindow.webProfile().setUrlRequestInterceptor(
             self.__interceptor)
     except AttributeError:
         #  Qt < 5.13
         WebBrowserWindow.webProfile().setRequestInterceptor(
             self.__interceptor)
     
     WebBrowserWindow.cookieJar()
Пример #6
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(BookmarksManager, self).__init__(parent)
     
     self.__saveTimer = AutoSaver(self, self.save)
     self.entryAdded.connect(self.__saveTimer.changeOccurred)
     self.entryRemoved.connect(self.__saveTimer.changeOccurred)
     self.entryChanged.connect(self.__saveTimer.changeOccurred)
     
     self.__initialize()
Пример #7
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(UserAgentManager, self).__init__(parent)

        self.__agents = {}
        # dictionary with agent strings indexed by host name
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)
Пример #8
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(CookieJar, self).__init__(parent)

        self.__loaded = False
        self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo
        self.__saveTimer = AutoSaver(self, self.save)

        self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "browser",
                                          "cookies.ini")
Пример #9
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(PasswordManager, self).__init__(parent)
     
     self.__logins = {}
     self.__loginForms = {}
     self.__never = []
     self.__loaded = False
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.changed.connect(self.__saveTimer.changeOccurred)
Пример #10
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(ZoomManager, self).__init__(parent)

        self.__zoomDB = {}

        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)

        self.__loaded = False
Пример #11
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent widget (QWidget)
     """
     super(DownloadManager, self).__init__(parent)
     self.setupUi(self)
     
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.__model = DownloadModel(self)
     self.__manager = Helpviewer.HelpWindow.HelpWindow\
         .networkAccessManager()
     
     self.__iconProvider = None
     self.__downloads = []
     self.__downloadDirectory = ""
     self.__loaded = False
     
     self.setDownloadDirectory(Preferences.getUI("DownloadPath"))
     
     self.downloadsView.setShowGrid(False)
     self.downloadsView.verticalHeader().hide()
     self.downloadsView.horizontalHeader().hide()
     self.downloadsView.setAlternatingRowColors(True)
     self.downloadsView.horizontalHeader().setStretchLastSection(True)
     self.downloadsView.setModel(self.__model)
     self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
     self.downloadsView.customContextMenuRequested.connect(
         self.__customContextMenuRequested)
     
     self.__load()
Пример #12
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(AdBlockManager, self).__init__(parent)
     
     self.__loaded = False
     self.__subscriptionsLoaded = False
     self.__enabled = False
     self.__adBlockDialog = None
     self.__adBlockExceptionsDialog = None
     self.__adBlockNetwork = None
     self.__adBlockPage = None
     self.__subscriptions = []
     self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions")
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.__defaultSubscriptionUrlString = \
         "abp:subscribe?location=" \
         "https://easylist-downloads.adblockplus.org/easylist.txt&"\
         "title=EasyList"
     self.__customSubscriptionUrlString = \
         bytes(self.__customSubscriptionUrl().toEncoded()).decode()
     
     self.rulesChanged.connect(self.__saveTimer.changeOccurred)
Пример #13
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(IrcNetworkManager, self).__init__(parent)

        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.__settings = Preferences.Prefs.settings

        self.__networks = {}
        self.__identities = {}

        self.dataChanged.connect(self.__saveTimer.changeOccurred)
Пример #14
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(WebIconProvider, self).__init__(parent)

        self.__encoding = "iso-8859-1"
        self.__iconsFileName = "web_site_icons.json"
        self.__iconDatabasePath = ""  # saving of icons disabled

        self.__iconsDB = {}
        self.__loaded = False

        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)
Пример #15
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object
        @type QObject
        """
        super(AdBlockManager, self).__init__(parent)

        self.__loaded = False
        self.__subscriptionsLoaded = False
        self.__enabled = False
        self.__adBlockDialog = None
        self.__adBlockExceptionsDialog = None
        self.__adBlockNetwork = None
        self.__adBlockPage = None
        self.__subscriptions = []
        self.__exceptedHosts = Preferences.getWebBrowser("AdBlockExceptions")
        self.__saveTimer = AutoSaver(self, self.save)
        self.__limitedEasyList = Preferences.getWebBrowser(
            "AdBlockUseLimitedEasyList")

        self.__defaultSubscriptionUrlString = (
            "abp:subscribe?location="
            "https://easylist-downloads.adblockplus.org/easylist.txt&"
            "title=EasyList")
        self.__additionalDefaultSubscriptionUrlStrings = (
            "abp:subscribe?location=https://raw.githubusercontent.com/"
            "hoshsadiq/adblock-nocoin-list/master/nocoin.txt&"
            "title=NoCoin", )
        self.__customSubscriptionUrlString = (bytes(
            self.__customSubscriptionUrl().toEncoded()).decode())

        self.__mutex = QMutex()
        self.__matcher = AdBlockMatcher(self)

        self.rulesChanged.connect(self.__saveTimer.changeOccurred)
        self.rulesChanged.connect(self.__rulesChanged)

        self.__interceptor = AdBlockUrlInterceptor(self)

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        WebBrowserWindow.networkManager().installUrlInterceptor(
            self.__interceptor)
Пример #16
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(DownloadManager, self).__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(Qt.Window)

        self.__winTaskbarButton = None

        self.__saveTimer = AutoSaver(self, self.save)

        self.__model = DownloadModel(self)
        self.__manager = WebBrowserWindow.networkManager()

        self.__iconProvider = None
        self.__downloads = []
        self.__downloadDirectory = ""
        self.__loaded = False

        self.__rowHeightMultiplier = 1.1

        self.setDownloadDirectory(Preferences.getUI("DownloadPath"))

        self.downloadsView.setShowGrid(False)
        self.downloadsView.verticalHeader().hide()
        self.downloadsView.horizontalHeader().hide()
        self.downloadsView.setAlternatingRowColors(True)
        self.downloadsView.horizontalHeader().setStretchLastSection(True)
        self.downloadsView.setModel(self.__model)
        self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.downloadsView.customContextMenuRequested.connect(
            self.__customContextMenuRequested)

        self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self)
        self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked)

        self.__load()

        self.__updateTimer = QBasicTimer()
Пример #17
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        if parent is None:
            parent = e5App()
        super(OpenSearchManager, self).__init__(parent)

        self.__replies = []
        self.__engines = {}
        self.__keywords = {}
        self.__current = ""
        self.__loading = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)

        self.load()
Пример #18
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(SpeedDial, self).__init__(parent)
     
     self.__regenerateScript = True
     
     self.__webPages = []
     
     self.__initialScript = ""
     self.__thumbnailsDirectory = ""
     
     self.__thumbnailers = []
     
     self.__initialize()
     
     self.__saveTimer = AutoSaver(self, self.save)
     self.pagesChanged.connect(self.__saveTimer.changeOccurred)
Пример #19
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(HistoryManager, self).__init__(parent)

        self.__saveTimer = AutoSaver(self, self.save)
        self.__daysToExpire = Preferences.getHelp("HistoryLimit")
        self.__history = []
        self.__lastSavedUrl = ""

        self.__expiredTimer = QTimer(self)
        self.__expiredTimer.setSingleShot(True)
        self.__expiredTimer.timeout.connect(self.__checkForExpired)

        self.__frequencyTimer = QTimer(self)
        self.__frequencyTimer.setSingleShot(True)
        self.__frequencyTimer.timeout.connect(self.__refreshFrequencies)

        self.entryAdded.connect(self.__saveTimer.changeOccurred)
        self.entryRemoved.connect(self.__saveTimer.changeOccurred)

        self.__load()

        from .HistoryModel import HistoryModel
        from .HistoryFilterModel import HistoryFilterModel
        from .HistoryTreeModel import HistoryTreeModel

        self.__historyModel = HistoryModel(self, self)
        self.__historyFilterModel = \
            HistoryFilterModel(self.__historyModel, self)
        self.__historyTreeModel = \
            HistoryTreeModel(self.__historyFilterModel, self)

        super(HistoryManager, self).setDefaultInterface(self)
        self.__startFrequencyTimer()
Пример #20
0
 def __init__(self, parent = None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     QNetworkCookieJar.__init__(self, parent)
     
     self.__loaded = False
     self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.__cookiesFile = os.path.join(Utilities.getConfigDir(), 
                                       "browser", "cookies.ini")
Пример #21
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(UserAgentManager, self).__init__(parent)
     
     self.__agents = {}
     # dictionary with agent strings indexed by host name
     self.__loaded = False
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.changed.connect(self.__saveTimer.changeOccurred)
Пример #22
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(BookmarksManager, self).__init__(parent)
     
     self.__saveTimer = AutoSaver(self, self.save)
     self.entryAdded.connect(self.__saveTimer.changeOccurred)
     self.entryRemoved.connect(self.__saveTimer.changeOccurred)
     self.entryChanged.connect(self.__saveTimer.changeOccurred)
     
     self.__initialize()
 def __init__(self, parent = None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     QObject.__init__(self, parent)
     
     self.__logins = {}
     self.__loginForms = {}
     self.__never = []
     self.__loaded = False
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred)
Пример #24
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(ZoomManager, self).__init__(parent)
     
     self.__zoomDB = {}
     
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.changed.connect(self.__saveTimer.changeOccurred)
     
     self.__loaded = False
Пример #25
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(IrcNetworkManager, self).__init__(parent)
     
     self.__loaded = False
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.__settings = Preferences.Prefs.settings
     
     self.__networks = {}
     self.__identities = {}
     
     self.dataChanged.connect(self.__saveTimer.changeOccurred)
 def __init__(self, parent = None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     QObject.__init__(self, parent)
     
     self.__loaded = False
     self.__subscriptionsLoaded = False
     self.__enabled = False
     self.__adBlockDialog = None
     self.__adBlockNetwork = None
     self.__adBlockPage = None
     self.__subscriptions = []
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.connect(self, SIGNAL("rulesChanged()"), self.__saveTimer.changeOccurred)
Пример #27
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     if parent is None:
         parent = e5App()
     super(OpenSearchManager, self).__init__(parent)
     
     self.__replies = []
     self.__engines = {}
     self.__keywords = {}
     self.__current = ""
     self.__loading = False
     self.__saveTimer = AutoSaver(self, self.save)
     
     self.changed.connect(self.__saveTimer.changeOccurred)
     
     self.load()
Пример #28
0
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        if parent is None:
            parent = e4App()
        QObject.__init__(self, parent)

        self.__replies = []
        self.__engines = {}
        self.__keywords = {}
        self.__current = QString()
        self.__loading = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred)

        self.load()
 def __init__(self, parent = None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     QObject.__init__(self, parent)
     
     self.__loaded = False
     self.__saveTimer = AutoSaver(self, self.save)
     self.__bookmarkRootNode = None
     self.__toolbar = None
     self.__menu = None
     self.__bookmarksModel = None
     self.__commands = QUndoStack()
     
     self.connect(self, SIGNAL("entryAdded"), 
                  self.__saveTimer.changeOccurred)
     self.connect(self, SIGNAL("entryRemoved"), 
                  self.__saveTimer.changeOccurred)
     self.connect(self, SIGNAL("entryChanged"), 
                  self.__saveTimer.changeOccurred)
Пример #30
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(HistoryManager, self).__init__(parent)
     
     self.__saveTimer = AutoSaver(self, self.save)
     self.__daysToExpire = Preferences.getHelp("HistoryLimit")
     self.__history = []
     self.__lastSavedUrl = ""
     
     self.__expiredTimer = QTimer(self)
     self.__expiredTimer.setSingleShot(True)
     self.__expiredTimer.timeout.connect(self.__checkForExpired)
     
     self.__frequencyTimer = QTimer(self)
     self.__frequencyTimer.setSingleShot(True)
     self.__frequencyTimer.timeout.connect(self.__refreshFrequencies)
     
     self.entryAdded.connect(self.__saveTimer.changeOccurred)
     self.entryRemoved.connect(self.__saveTimer.changeOccurred)
     
     self.__load()
     
     from .HistoryModel import HistoryModel
     from .HistoryFilterModel import HistoryFilterModel
     from .HistoryTreeModel import HistoryTreeModel
     
     self.__historyModel = HistoryModel(self, self)
     self.__historyFilterModel = \
         HistoryFilterModel(self.__historyModel, self)
     self.__historyTreeModel = \
         HistoryTreeModel(self.__historyFilterModel, self)
     
     super(HistoryManager, self).setDefaultInterface(self)
     self.__startFrequencyTimer()
Пример #31
0
 def __init__(self, parent=None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     super(SpeedDial, self).__init__(parent)
     
     self.__regenerateScript = True
     
     self.__webPages = []
     self.__webFrames = []
     
     self.__initialScript = ""
     self.__thumbnailsDirectory = ""
     
     self.__thumbnailers = []
     
     self.__initialize()
     
     self.pagesChanged.connect(self.__pagesChanged)
     
     self.__saveTimer = AutoSaver(self, self.save)
     self.pagesChanged.connect(self.__saveTimer.changeOccurred)
 def __init__(self, parent = None):
     """
     Constructor
     
     @param parent reference to the parent object (QObject)
     """
     QWebHistoryInterface.__init__(self, parent)
     
     self.__saveTimer = AutoSaver(self, self.save)
     self.__daysToExpire = Preferences.getHelp("HistoryLimit")
     self.__history = []
     self.__lastSavedUrl = QString()
     
     self.__expiredTimer = QTimer()
     self.__expiredTimer.setSingleShot(True)
     self.connect(self.__expiredTimer, SIGNAL("timeout()"), 
                  self.__checkForExpired)
     
     self.__frequencyTimer = QTimer()
     self.__frequencyTimer.setSingleShot(True)
     self.connect(self.__frequencyTimer, SIGNAL("timeout()"), 
                  self.__refreshFrequencies)
     
     self.connect(self, SIGNAL("entryAdded"), 
                  self.__saveTimer.changeOccurred)
     self.connect(self, SIGNAL("entryRemoved"), 
                  self.__saveTimer.changeOccurred)
     
     self.__load()
     
     self.__historyModel = HistoryModel(self, self)
     self.__historyFilterModel = HistoryFilterModel(self.__historyModel, self)
     self.__historyTreeModel = HistoryTreeModel(self.__historyFilterModel, self)
     
     QWebHistoryInterface.setDefaultInterface(self)
     self.__startFrequencyTimer()
Пример #33
0
class UserAgentManager(QObject):
    """
    Class implementing a user agent manager.
    
    @signal changed() emitted to indicate a change
    @signal userAgentSettingsSaved() emitted after the user agent settings
        were saved
    """
    changed = pyqtSignal()
    userAgentSettingsSaved = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(UserAgentManager, self).__init__(parent)

        self.__agents = {}
        # dictionary with agent strings indexed by host name
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)

    def getFileName(self):
        """
        Public method to get the file name of the user agents file.
        
        @return name of the user agents file (string)
        """
        return os.path.join(Utilities.getConfigDir(), "web_browser",
                            "userAgentSettings.xml")

    def save(self):
        """
        Public slot to save the user agent entries to disk.
        """
        if not self.__loaded:
            return

        from .UserAgentWriter import UserAgentWriter
        agentFile = self.getFileName()
        writer = UserAgentWriter()
        if not writer.write(agentFile, self.__agents):
            E5MessageBox.critical(
                None, self.tr("Saving user agent data"),
                self.tr("""<p>User agent data could not be saved to"""
                        """ <b>{0}</b></p>""").format(agentFile))
        else:
            self.userAgentSettingsSaved.emit()

    def __load(self):
        """
        Private method to load the saved user agent settings.
        """
        agentFile = self.getFileName()
        from .UserAgentReader import UserAgentReader
        reader = UserAgentReader()
        self.__agents = reader.read(agentFile)
        if reader.error() != QXmlStreamReader.NoError:
            E5MessageBox.warning(
                None, self.tr("Loading user agent data"),
                self.tr("""Error when loading user agent data on"""
                        """ line {0}, column {1}:\n{2}""").format(
                            reader.lineNumber(), reader.columnNumber(),
                            reader.errorString()))

        self.__loaded = True

    def reload(self):
        """
        Public method to reload the user agent settings.
        """
        if not self.__loaded:
            return

        self.__agents = {}
        self.__load()

    def close(self):
        """
        Public method to close the user agents manager.
        """
        self.__saveTimer.saveIfNeccessary()

    def removeUserAgent(self, host):
        """
        Public method to remove a user agent entry.
        
        @param host host name (string)
        """
        if host in self.__agents:
            del self.__agents[host]
            self.changed.emit()

    def allHostNames(self):
        """
        Public method to get a list of all host names we a user agent setting
        for.
        
        @return sorted list of all host names (list of strings)
        """
        if not self.__loaded:
            self.__load()

        return sorted(self.__agents.keys())

    def hostsCount(self):
        """
        Public method to get the number of available user agent settings.
        
        @return number of user agent settings (integer)
        """
        if not self.__loaded:
            self.__load()

        return len(self.__agents)

    def userAgent(self, host):
        """
        Public method to get the user agent setting for a host.
        
        @param host host name (string)
        @return user agent string (string)
        """
        if not self.__loaded:
            self.__load()

        for agentHost in self.__agents:
            if host.endswith(agentHost):
                return self.__agents[agentHost]

        return ""

    def setUserAgent(self, host, agent):
        """
        Public method to set the user agent string for a host.
        
        @param host host name (string)
        @param agent user agent string (string)
        """
        if host != "" and agent != "":
            self.__agents[host] = agent
            self.changed.emit()

    def userAgentForUrl(self, url):
        """
        Public method to determine the user agent for the given URL.
        
        @param url URL to determine user agent for (QUrl)
        @return user agent string (string)
        """
        if url.isValid():
            host = url.host()
            return self.userAgent(host)

        return ""

    def setUserAgentForUrl(self, url, agent):
        """
        Public method to set the user agent string for an URL.
        
        @param url URL to register user agent setting for (QUrl)
        @param agent new current user agent string (string)
        """
        if url.isValid():
            host = url.host()
            self.setUserAgent(host, agent)
Пример #34
0
class BookmarksManager(QObject):
    """
    Class implementing the bookmarks manager.
    
    @signal entryAdded(BookmarkNode) emitted after a bookmark node has been
        added
    @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a
        bookmark node has been removed
    @signal entryChanged(BookmarkNode) emitted after a bookmark node has been
        changed
    @signal bookmarksSaved() emitted after the bookmarks were saved
    @signal bookmarksReloaded() emitted after the bookmarks were reloaded
    """
    entryAdded = pyqtSignal(BookmarkNode)
    entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode)
    entryChanged = pyqtSignal(BookmarkNode)
    bookmarksSaved = pyqtSignal()
    bookmarksReloaded = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(BookmarksManager, self).__init__(parent)
        
        self.__saveTimer = AutoSaver(self, self.save)
        self.entryAdded.connect(self.__saveTimer.changeOccurred)
        self.entryRemoved.connect(self.__saveTimer.changeOccurred)
        self.entryChanged.connect(self.__saveTimer.changeOccurred)
        
        self.__initialize()
    
    def __initialize(self):
        """
        Private method to initialize some data.
        """
        self.__loaded = False
        self.__bookmarkRootNode = None
        self.__toolbar = None
        self.__menu = None
        self.__bookmarksModel = None
        self.__commands = QUndoStack()
    
    @classmethod
    def getFileName(cls):
        """
        Class method to get the file name of the bookmark file.
        
        @return name of the bookmark file (string)
        """
        return os.path.join(Utilities.getConfigDir(), "browser",
                            "bookmarks.xbel")
    
    def close(self):
        """
        Public method to close the bookmark manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def undoRedoStack(self):
        """
        Public method to get a reference to the undo stack.
        
        @return reference to the undo stack (QUndoStack)
        """
        return self.__commands
    
    def changeExpanded(self):
        """
        Public method to handle a change of the expanded state.
        """
        self.__saveTimer.changeOccurred()
    
    def reload(self):
        """
        Public method used to initiate a reloading of the bookmarks.
        """
        self.__initialize()
        self.load()
        self.bookmarksReloaded.emit()
    
    def load(self):
        """
        Public method to load the bookmarks.
        
        @exception RuntimeError raised to indicate an error loading the
            bookmarks
        """
        if self.__loaded:
            return
        
        self.__loaded = True
        
        bookmarkFile = self.getFileName()
        if not QFile.exists(bookmarkFile):
            from . import DefaultBookmarks_rc       # __IGNORE_WARNING__
            bookmarkFile = QFile(":/DefaultBookmarks.xbel")
            bookmarkFile.open(QIODevice.ReadOnly)
        
        from .XbelReader import XbelReader
        reader = XbelReader()
        self.__bookmarkRootNode = reader.read(bookmarkFile)
        if reader.error() != QXmlStreamReader.NoError:
            E5MessageBox.warning(
                None,
                self.tr("Loading Bookmarks"),
                self.tr(
                    """Error when loading bookmarks on line {0},"""
                    """ column {1}:\n {2}""")
                .format(reader.lineNumber(),
                        reader.columnNumber(),
                        reader.errorString()))
        
        others = []
        for index in range(
                len(self.__bookmarkRootNode.children()) - 1, -1, -1):
            node = self.__bookmarkRootNode.children()[index]
            if node.type() == BookmarkNode.Folder:
                if (node.title == self.tr("Toolbar Bookmarks") or
                    node.title == BOOKMARKBAR) and \
                   self.__toolbar is None:
                    node.title = self.tr(BOOKMARKBAR)
                    self.__toolbar = node
                
                if (node.title == self.tr("Menu") or
                    node.title == BOOKMARKMENU) and \
                   self.__menu is None:
                    node.title = self.tr(BOOKMARKMENU)
                    self.__menu = node
            else:
                others.append(node)
            self.__bookmarkRootNode.remove(node)
        
        if len(self.__bookmarkRootNode.children()) > 0:
            raise RuntimeError("Error loading bookmarks.")
        
        if self.__toolbar is None:
            self.__toolbar = BookmarkNode(BookmarkNode.Folder,
                                          self.__bookmarkRootNode)
            self.__toolbar.title = self.tr(BOOKMARKBAR)
        else:
            self.__bookmarkRootNode.add(self.__toolbar)
        
        if self.__menu is None:
            self.__menu = BookmarkNode(BookmarkNode.Folder,
                                       self.__bookmarkRootNode)
            self.__menu.title = self.tr(BOOKMARKMENU)
        else:
            self.__bookmarkRootNode.add(self.__menu)
        
        for node in others:
            self.__menu.add(node)
        
        self.__convertFromOldBookmarks()
    
    def save(self):
        """
        Public method to save the bookmarks.
        """
        if not self.__loaded:
            return
        
        from .XbelWriter import XbelWriter
        writer = XbelWriter()
        bookmarkFile = self.getFileName()
        
        # save root folder titles in English (i.e. not localized)
        self.__menu.title = BOOKMARKMENU
        self.__toolbar.title = BOOKMARKBAR
        if not writer.write(bookmarkFile, self.__bookmarkRootNode):
            E5MessageBox.warning(
                None,
                self.tr("Saving Bookmarks"),
                self.tr("""Error saving bookmarks to <b>{0}</b>.""")
                .format(bookmarkFile))
        
        # restore localized titles
        self.__menu.title = self.tr(BOOKMARKMENU)
        self.__toolbar.title = self.tr(BOOKMARKBAR)
        
        self.bookmarksSaved.emit()
    
    def addBookmark(self, parent, node, row=-1):
        """
        Public method to add a bookmark.
        
        @param parent reference to the node to add to (BookmarkNode)
        @param node reference to the node to add (BookmarkNode)
        @param row row number (integer)
        """
        if not self.__loaded:
            return
        
        self.setTimestamp(node, BookmarkNode.TsAdded,
                          QDateTime.currentDateTime())
        
        command = InsertBookmarksCommand(self, parent, node, row)
        self.__commands.push(command)
    
    def removeBookmark(self, node):
        """
        Public method to remove a bookmark.
        
        @param node reference to the node to be removed (BookmarkNode)
        """
        if not self.__loaded:
            return
        
        parent = node.parent()
        row = parent.children().index(node)
        command = RemoveBookmarksCommand(self, parent, row)
        self.__commands.push(command)
    
    def setTitle(self, node, newTitle):
        """
        Public method to set the title of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param newTitle title to be set (string)
        """
        if not self.__loaded:
            return
        
        command = ChangeBookmarkCommand(self, node, newTitle, True)
        self.__commands.push(command)
    
    def setUrl(self, node, newUrl):
        """
        Public method to set the URL of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param newUrl URL to be set (string)
        """
        if not self.__loaded:
            return
        
        command = ChangeBookmarkCommand(self, node, newUrl, False)
        self.__commands.push(command)
    
    def setNodeChanged(self, node):
        """
        Public method to signal changes of bookmarks other than title, URL
        or timestamp.
        
        @param node reference to the bookmark (BookmarkNode)
        """
        self.__saveTimer.changeOccurred()
    
    def setTimestamp(self, node, timestampType, timestamp):
        """
        Public method to set the URL of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param timestampType type of the timestamp to set
            (BookmarkNode.TsAdded, BookmarkNode.TsModified,
            BookmarkNode.TsVisited)
        @param timestamp timestamp to set (QDateTime)
        """
        if not self.__loaded:
            return
        
        assert timestampType in [BookmarkNode.TsAdded,
                                 BookmarkNode.TsModified,
                                 BookmarkNode.TsVisited]
        
        if timestampType == BookmarkNode.TsAdded:
            node.added = timestamp
        elif timestampType == BookmarkNode.TsModified:
            node.modified = timestamp
        elif timestampType == BookmarkNode.TsVisited:
            node.visited = timestamp
        self.__saveTimer.changeOccurred()
    
    def bookmarks(self):
        """
        Public method to get a reference to the root bookmark node.
        
        @return reference to the root bookmark node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__bookmarkRootNode
    
    def menu(self):
        """
        Public method to get a reference to the bookmarks menu node.
        
        @return reference to the bookmarks menu node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__menu
    
    def toolbar(self):
        """
        Public method to get a reference to the bookmarks toolbar node.
        
        @return reference to the bookmarks toolbar node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__toolbar
    
    def bookmarksModel(self):
        """
        Public method to get a reference to the bookmarks model.
        
        @return reference to the bookmarks model (BookmarksModel)
        """
        if self.__bookmarksModel is None:
            from .BookmarksModel import BookmarksModel
            self.__bookmarksModel = BookmarksModel(self, self)
        return self.__bookmarksModel
    
    def importBookmarks(self):
        """
        Public method to import bookmarks.
        """
        from .BookmarksImportDialog import BookmarksImportDialog
        dlg = BookmarksImportDialog()
        if dlg.exec_() == QDialog.Accepted:
            importRootNode = dlg.getImportedBookmarks()
            if importRootNode is not None:
                self.addBookmark(self.menu(), importRootNode)
    
    def exportBookmarks(self):
        """
        Public method to export the bookmarks.
        """
        fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
            None,
            self.tr("Export Bookmarks"),
            "eric6_bookmarks.xbel",
            self.tr("XBEL bookmarks (*.xbel);;"
                    "XBEL bookmarks (*.xml);;"
                    "HTML Bookmarks (*.html)"))
        if not fileName:
            return
        
        ext = QFileInfo(fileName).suffix()
        if not ext:
            ex = selectedFilter.split("(*")[1].split(")")[0]
            if ex:
                fileName += ex
        
        ext = QFileInfo(fileName).suffix()
        if ext == "html":
            from .NsHtmlWriter import NsHtmlWriter
            writer = NsHtmlWriter()
        else:
            from .XbelWriter import XbelWriter
            writer = XbelWriter()
        if not writer.write(fileName, self.__bookmarkRootNode):
            E5MessageBox.critical(
                None,
                self.tr("Exporting Bookmarks"),
                self.tr("""Error exporting bookmarks to <b>{0}</b>.""")
                .format(fileName))
    
    def __convertFromOldBookmarks(self):
        """
        Private method to convert the old bookmarks into the new ones.
        """
        bmNames = Preferences.Prefs.settings.value('Bookmarks/Names')
        bmFiles = Preferences.Prefs.settings.value('Bookmarks/Files')
        
        if bmNames is not None and bmFiles is not None:
            if len(bmNames) == len(bmFiles):
                convertedRootNode = BookmarkNode(BookmarkNode.Folder)
                convertedRootNode.title = self.tr("Converted {0}")\
                    .format(QDate.currentDate().toString(
                        Qt.SystemLocaleShortDate))
                for i in range(len(bmNames)):
                    node = BookmarkNode(BookmarkNode.Bookmark,
                                        convertedRootNode)
                    node.title = bmNames[i]
                    url = QUrl(bmFiles[i])
                    if not url.scheme():
                        url.setScheme("file")
                    node.url = url.toString()
                self.addBookmark(self.menu(), convertedRootNode)
                
                Preferences.Prefs.settings.remove('Bookmarks')
    
    def iconChanged(self, url):
        """
        Public slot to update the icon image for an URL.
        
        @param url URL of the icon to update (QUrl or string)
        """
        if isinstance(url, QUrl):
            url = url.toString()
        nodes = self.bookmarksForUrl(url)
        for node in nodes:
            self.bookmarksModel().entryChanged(node)
    
    def bookmarkForUrl(self, url, start=StartRoot):
        """
        Public method to get a bookmark node for a given URL.
        
        @param url URL of the bookmark to search for (QUrl or string)
        @keyparam start indicator for the start of the search
            (StartRoot, StartMenu, StartToolBar)
        @return bookmark node for the given url (BookmarkNode)
        """
        if start == StartMenu:
            startNode = self.__menu
        elif start == StartToolBar:
            startNode = self.__toolbar
        else:
            startNode = self.__bookmarkRootNode
        if startNode is None:
            return None
        
        if isinstance(url, QUrl):
            url = url.toString()
        
        return self.__searchBookmark(url, startNode)
    
    def __searchBookmark(self, url, startNode):
        """
        Private method get a bookmark node for a given URL.
        
        @param url URL of the bookmark to search for (string)
        @param startNode reference to the node to start searching
            (BookmarkNode)
        @return bookmark node for the given url (BookmarkNode)
        """
        bm = None
        for node in startNode.children():
            if node.type() == BookmarkNode.Folder:
                bm = self.__searchBookmark(url, node)
            elif node.type() == BookmarkNode.Bookmark:
                if node.url == url:
                    bm = node
            if bm is not None:
                return bm
        return None
    
    def bookmarksForUrl(self, url, start=StartRoot):
        """
        Public method to get a list of bookmark nodes for a given URL.
        
        @param url URL of the bookmarks to search for (QUrl or string)
        @keyparam start indicator for the start of the search
            (StartRoot, StartMenu, StartToolBar)
        @return list of bookmark nodes for the given url (list of BookmarkNode)
        """
        if start == StartMenu:
            startNode = self.__menu
        elif start == StartToolBar:
            startNode = self.__toolbar
        else:
            startNode = self.__bookmarkRootNode
        if startNode is None:
            return None
        
        if isinstance(url, QUrl):
            url = url.toString()
        
        return self.__searchBookmarks(url, startNode)
    
    def __searchBookmarks(self, url, startNode):
        """
        Private method get a list of bookmark nodes for a given URL.
        
        @param url URL of the bookmarks to search for (string)
        @param startNode reference to the node to start searching
            (BookmarkNode)
        @return list of bookmark nodes for the given url (list of BookmarkNode)
        """
        bm = []
        for node in startNode.children():
            if node.type() == BookmarkNode.Folder:
                bm.extend(self.__searchBookmarks(url, node))
            elif node.type() == BookmarkNode.Bookmark:
                if node.url == url:
                    bm.append(node)
        return bm
Пример #35
0
class HistoryManager(QWebHistoryInterface):
    """
    Class implementing the history manager.
    
    @signal historyCleared() emitted after the history has been cleared
    @signal historyReset() emitted after the history has been reset
    @signal entryAdded(HistoryEntry) emitted after a history entry has been
        added
    @signal entryRemoved(HistoryEntry) emitted after a history entry has been
        removed
    @signal entryUpdated(int) emitted after a history entry has been updated
    @signal historySaved() emitted after the history was saved
    """

    historyCleared = pyqtSignal()
    historyReset = pyqtSignal()
    entryAdded = pyqtSignal(HistoryEntry)
    entryRemoved = pyqtSignal(HistoryEntry)
    entryUpdated = pyqtSignal(int)
    historySaved = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(HistoryManager, self).__init__(parent)

        self.__saveTimer = AutoSaver(self, self.save)
        self.__daysToExpire = Preferences.getHelp("HistoryLimit")
        self.__history = []
        self.__lastSavedUrl = ""

        self.__expiredTimer = QTimer(self)
        self.__expiredTimer.setSingleShot(True)
        self.__expiredTimer.timeout.connect(self.__checkForExpired)

        self.__frequencyTimer = QTimer(self)
        self.__frequencyTimer.setSingleShot(True)
        self.__frequencyTimer.timeout.connect(self.__refreshFrequencies)

        self.entryAdded.connect(self.__saveTimer.changeOccurred)
        self.entryRemoved.connect(self.__saveTimer.changeOccurred)

        self.__load()

        from .HistoryModel import HistoryModel
        from .HistoryFilterModel import HistoryFilterModel
        from .HistoryTreeModel import HistoryTreeModel

        self.__historyModel = HistoryModel(self, self)
        self.__historyFilterModel = HistoryFilterModel(self.__historyModel, self)
        self.__historyTreeModel = HistoryTreeModel(self.__historyFilterModel, self)

        super(HistoryManager, self).setDefaultInterface(self)
        self.__startFrequencyTimer()

    def close(self):
        """
        Public method to close the history manager.
        """
        # remove history items on application exit
        if self.__daysToExpire == -2:
            self.clear()
        self.__saveTimer.saveIfNeccessary()

    def history(self):
        """
        Public method to return the history.
        
        @return reference to the list of history entries (list of HistoryEntry)
        """
        return self.__history[:]

    def setHistory(self, history, loadedAndSorted=False):
        """
        Public method to set a new history.
        
        @param history reference to the list of history entries to be set
            (list of HistoryEntry)
        @param loadedAndSorted flag indicating that the list is sorted
            (boolean)
        """
        self.__history = history[:]
        if not loadedAndSorted:
            self.__history.sort()

        self.__checkForExpired()

        if loadedAndSorted:
            try:
                self.__lastSavedUrl = self.__history[0].url
            except IndexError:
                self.__lastSavedUrl = ""
        else:
            self.__lastSavedUrl = ""
            self.__saveTimer.changeOccurred()
        self.historyReset.emit()

    def historyContains(self, url):
        """
        Public method to check the history for an entry.
        
        @param url URL to check for (string)
        @return flag indicating success (boolean)
        """
        return self.__historyFilterModel.historyContains(url)

    def _addHistoryEntry(self, itm):
        """
        Protected method to add a history item.
        
        @param itm reference to the history item to add (HistoryEntry)
        """
        globalSettings = QWebSettings.globalSettings()
        if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
            return

        self.__history.insert(0, itm)
        self.entryAdded.emit(itm)
        if len(self.__history) == 1:
            self.__checkForExpired()

    def _removeHistoryEntry(self, itm):
        """
        Protected method to remove a history item.
        
        @param itm reference to the history item to remove (HistoryEntry)
        """
        self.__lastSavedUrl = ""
        self.__history.remove(itm)
        self.entryRemoved.emit(itm)

    def addHistoryEntry(self, url):
        """
        Public method to add a history entry.
        
        @param url URL to be added (string)
        """
        cleanurl = QUrl(url)
        if cleanurl.scheme() not in ["eric", "about"]:
            if cleanurl.password():
                # don't save the password in the history
                cleanurl.setPassword("")
            if cleanurl.host():
                cleanurl.setHost(cleanurl.host().lower())
            itm = HistoryEntry(cleanurl.toString(), QDateTime.currentDateTime())
            self._addHistoryEntry(itm)

    def updateHistoryEntry(self, url, title):
        """
        Public method to update a history entry.
        
        @param url URL of the entry to update (string)
        @param title title of the entry to update (string)
        """
        cleanurl = QUrl(url)
        if cleanurl.scheme() not in ["eric", "about"]:
            for index in range(len(self.__history)):
                if url == self.__history[index].url:
                    self.__history[index].title = title
                    self.__saveTimer.changeOccurred()
                    if not self.__lastSavedUrl:
                        self.__lastSavedUrl = self.__history[index].url
                    self.entryUpdated.emit(index)
                    break

    def removeHistoryEntry(self, url, title=""):
        """
        Public method to remove a history entry.
        
        @param url URL of the entry to remove (QUrl)
        @param title title of the entry to remove (string)
        """
        for index in range(len(self.__history)):
            if url == QUrl(self.__history[index].url) and (not title or title == self.__history[index].title):
                self._removeHistoryEntry(self.__history[index])
                break

    def historyModel(self):
        """
        Public method to get a reference to the history model.
        
        @return reference to the history model (HistoryModel)
        """
        return self.__historyModel

    def historyFilterModel(self):
        """
        Public method to get a reference to the history filter model.
        
        @return reference to the history filter model (HistoryFilterModel)
        """
        return self.__historyFilterModel

    def historyTreeModel(self):
        """
        Public method to get a reference to the history tree model.
        
        @return reference to the history tree model (HistoryTreeModel)
        """
        return self.__historyTreeModel

    def __checkForExpired(self):
        """
        Private slot to check entries for expiration.
        """
        if self.__daysToExpire < 0 or len(self.__history) == 0:
            return

        now = QDateTime.currentDateTime()
        nextTimeout = 0

        while self.__history:
            checkForExpired = QDateTime(self.__history[-1].dateTime)
            checkForExpired.setDate(checkForExpired.date().addDays(self.__daysToExpire))
            if now.daysTo(checkForExpired) > 7:
                nextTimeout = 7 * 86400
            else:
                nextTimeout = now.secsTo(checkForExpired)
            if nextTimeout > 0:
                break

            itm = self.__history.pop(-1)
            self.__lastSavedUrl = ""
            self.entryRemoved.emit(itm)
        self.__saveTimer.saveIfNeccessary()

        if nextTimeout > 0:
            self.__expiredTimer.start(nextTimeout * 1000)

    def daysToExpire(self):
        """
        Public method to get the days for entry expiration.
        
        @return days for entry expiration (integer)
        """
        return self.__daysToExpire

    def setDaysToExpire(self, limit):
        """
        Public method to set the days for entry expiration.
        
        @param limit days for entry expiration (integer)
        """
        if self.__daysToExpire == limit:
            return

        self.__daysToExpire = limit
        self.__checkForExpired()
        self.__saveTimer.changeOccurred()

    def preferencesChanged(self):
        """
        Public method to indicate a change of preferences.
        """
        self.setDaysToExpire(Preferences.getHelp("HistoryLimit"))

    @pyqtSlot()
    def clear(self, period=0):
        """
        Public slot to clear the complete history.
        
        @param period history period in milliseconds to be cleared (integer)
        """
        if period == 0:
            self.__history = []
            self.historyReset.emit()
        else:
            breakMS = QDateTime.currentMSecsSinceEpoch() - period
            while self.__history and (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() > breakMS):
                itm = self.__history.pop(0)
                self.entryRemoved.emit(itm)
        self.__lastSavedUrl = ""
        self.__saveTimer.changeOccurred()
        self.__saveTimer.saveIfNeccessary()
        self.historyCleared.emit()

    def getFileName(self):
        """
        Public method to get the file name of the history file.
        
        @return name of the history file (string)
        """
        return os.path.join(Utilities.getConfigDir(), "browser", "history")

    def reload(self):
        """
        Public method to reload the history.
        """
        self.__load()

    def __load(self):
        """
        Private method to load the saved history entries from disk.
        """
        historyFile = QFile(self.getFileName())
        if not historyFile.exists():
            return
        if not historyFile.open(QIODevice.ReadOnly):
            E5MessageBox.warning(
                None,
                self.tr("Loading History"),
                self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format(
                    historyFile.fileName, historyFile.errorString()
                ),
            )
            return

        history = []

        # double check, that the history file is sorted as it is read
        needToSort = False
        lastInsertedItem = HistoryEntry()
        data = QByteArray(historyFile.readAll())
        stream = QDataStream(data, QIODevice.ReadOnly)
        stream.setVersion(QDataStream.Qt_4_6)
        while not stream.atEnd():
            ver = stream.readUInt32()
            if ver != HISTORY_VERSION:
                continue
            itm = HistoryEntry()
            itm.url = Utilities.readStringFromStream(stream)
            stream >> itm.dateTime
            itm.title = Utilities.readStringFromStream(stream)

            if not itm.dateTime.isValid():
                continue

            if itm == lastInsertedItem:
                if not lastInsertedItem.title and len(history) > 0:
                    history[0].title = itm.title
                continue

            if not needToSort and history and lastInsertedItem < itm:
                needToSort = True

            history.insert(0, itm)
            lastInsertedItem = itm
        historyFile.close()

        if needToSort:
            history.sort()

        self.setHistory(history, True)

        # if the history had to be sorted, rewrite the history sorted
        if needToSort:
            self.__lastSavedUrl = ""
            self.__saveTimer.changeOccurred()

    def save(self):
        """
        Public slot to save the history entries to disk.
        """
        historyFile = QFile(self.getFileName())
        if not historyFile.exists():
            self.__lastSavedUrl = ""

        saveAll = self.__lastSavedUrl == ""
        first = len(self.__history) - 1
        if not saveAll:
            # find the first one to save
            for index in range(len(self.__history)):
                if self.__history[index].url == self.__lastSavedUrl:
                    first = index - 1
                    break
        if first == len(self.__history) - 1:
            saveAll = True

        if saveAll:
            # use a temporary file when saving everything
            f = QTemporaryFile()
            f.setAutoRemove(False)
            opened = f.open()
        else:
            f = historyFile
            opened = f.open(QIODevice.Append)

        if not opened:
            E5MessageBox.warning(
                None,
                self.tr("Saving History"),
                self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format(
                    f.fileName(), f.errorString()
                ),
            )
            return

        for index in range(first, -1, -1):
            data = QByteArray()
            stream = QDataStream(data, QIODevice.WriteOnly)
            stream.setVersion(QDataStream.Qt_4_6)
            itm = self.__history[index]
            stream.writeUInt32(HISTORY_VERSION)
            stream.writeString(itm.url.encode("utf-8"))
            stream << itm.dateTime
            stream.writeString(itm.title.encode("utf-8"))
            f.write(data)

        f.close()
        if saveAll:
            if historyFile.exists() and not historyFile.remove():
                E5MessageBox.warning(
                    None,
                    self.tr("Saving History"),
                    self.tr("""<p>Error removing old history file <b>{0}</b>.""" """<br/>Reason: {1}</p>""").format(
                        historyFile.fileName(), historyFile.errorString()
                    ),
                )
            if not f.copy(historyFile.fileName()):
                E5MessageBox.warning(
                    None,
                    self.tr("Saving History"),
                    self.tr(
                        """<p>Error moving new history file over old one """ """(<b>{0}</b>).<br/>Reason: {1}</p>"""
                    ).format(historyFile.fileName(), f.errorString()),
                )
        self.historySaved.emit()
        try:
            self.__lastSavedUrl = self.__history[0].url
        except IndexError:
            self.__lastSavedUrl = ""

    def __refreshFrequencies(self):
        """
        Private slot to recalculate the refresh frequencies.
        """
        self.__historyFilterModel.recalculateFrequencies()
        self.__startFrequencyTimer()

    def __startFrequencyTimer(self):
        """
        Private method to start the timer to recalculate the frequencies.
        """
        tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0))
        self.__frequencyTimer.start(QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
Пример #36
0
class PasswordManager(QObject):
    """
    Class implementing the password manager.
    
    @signal changed() emitted to indicate a change
    @signal passwordsSaved() emitted after the passwords were saved
    """
    changed = pyqtSignal()
    passwordsSaved = pyqtSignal()
    
    SEPARATOR = "===================="
    FORMS = "=====FORMS====="
    NEVER = "=====NEVER====="
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(PasswordManager, self).__init__(parent)
        
        self.__logins = {}
        self.__loginForms = {}
        self.__never = []
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.changed.connect(self.__saveTimer.changeOccurred)
    
    def clear(self):
        """
        Public slot to clear the saved passwords.
        """
        if not self.__loaded:
            self.__load()
        
        self.__logins = {}
        self.__loginForms = {}
        self.__never = []
        self.__saveTimer.changeOccurred()
        self.__saveTimer.saveIfNeccessary()
        
        self.changed.emit()
    
    def getLogin(self, url, realm):
        """
        Public method to get the login credentials.
        
        @param url URL to get the credentials for (QUrl)
        @param realm realm to get the credentials for (string)
        @return tuple containing the user name (string) and password (string)
        """
        if not self.__loaded:
            self.__load()
        
        key = self.__createKey(url, realm)
        try:
            return self.__logins[key][0], Utilities.crypto.pwConvert(
                self.__logins[key][1], encode=False)
        except KeyError:
            return "", ""
    
    def setLogin(self, url, realm, username, password):
        """
        Public method to set the login credentials.
        
        @param url URL to set the credentials for (QUrl)
        @param realm realm to set the credentials for (string)
        @param username username for the login (string)
        @param password password for the login (string)
        """
        if not self.__loaded:
            self.__load()
        
        key = self.__createKey(url, realm)
        self.__logins[key] = (
            username,
            Utilities.crypto.pwConvert(password, encode=True)
        )
        self.changed.emit()
    
    def __createKey(self, url, realm):
        """
        Private method to create the key string for the login credentials.
        
        @param url URL to get the credentials for (QUrl)
        @param realm realm to get the credentials for (string)
        @return key string (string)
        """
        authority = url.authority()
        if authority.startswith("@"):
            authority = authority[1:]
        if realm:
            key = "{0}://{1} ({2})".format(
                url.scheme(), authority, realm)
        else:
            key = "{0}://{1}".format(url.scheme(), authority)
        return key
    
    def getFileName(self):
        """
        Public method to get the file name of the passwords file.
        
        @return name of the passwords file (string)
        """
        return os.path.join(Utilities.getConfigDir(), "browser", "logins.xml")
    
    def save(self):
        """
        Public slot to save the login entries to disk.
        """
        if not self.__loaded:
            return
        
        from .PasswordWriter import PasswordWriter
        loginFile = self.getFileName()
        writer = PasswordWriter()
        if not writer.write(
                loginFile, self.__logins, self.__loginForms, self.__never):
            E5MessageBox.critical(
                None,
                self.tr("Saving login data"),
                self.tr(
                    """<p>Login data could not be saved to <b>{0}</b></p>"""
                ).format(loginFile))
        else:
            self.passwordsSaved.emit()
    
    def __load(self):
        """
        Private method to load the saved login credentials.
        """
        loginFile = self.getFileName()
        if not os.path.exists(loginFile):
            self.__loadNonXml(os.path.splitext(loginFile)[0])
        else:
            from .PasswordReader import PasswordReader
            reader = PasswordReader()
            self.__logins, self.__loginForms, self.__never = \
                reader.read(loginFile)
            if reader.error() != QXmlStreamReader.NoError:
                E5MessageBox.warning(
                    None,
                    self.tr("Loading login data"),
                    self.tr("""Error when loading login data on"""
                            """ line {0}, column {1}:\n{2}""")
                    .format(reader.lineNumber(),
                            reader.columnNumber(),
                            reader.errorString()))
        
        self.__loaded = True
    
    def __loadNonXml(self, loginFile):
        """
        Private method to load non-XML password files.
        
        This method is to convert from the old, non-XML format to the new
        XML based format.
        
        @param loginFile name of the non-XML password file (string)
        """
        if os.path.exists(loginFile):
            try:
                f = open(loginFile, "r", encoding="utf-8")
                lines = f.read()
                f.close()
            except IOError as err:
                E5MessageBox.critical(
                    None,
                    self.tr("Loading login data"),
                    self.tr("""<p>Login data could not be loaded """
                            """from <b>{0}</b></p>"""
                            """<p>Reason: {1}</p>""")
                    .format(loginFile, str(err)))
                return
            
            data = []
            section = 0
            # 0 = login data, 1 = forms data, 2 = never store info
            for line in lines.splitlines():
                if line == self.FORMS:
                    section = 1
                    continue
                elif line == self.NEVER:
                    section = 2
                    continue
                
                if section == 0:
                    if line != self.SEPARATOR:
                        data.append(line)
                    else:
                        if len(data) != 3:
                            E5MessageBox.critical(
                                None,
                                self.tr("Loading login data"),
                                self.tr(
                                    """<p>Login data could not be loaded """
                                    """from <b>{0}</b></p>"""
                                    """<p>Reason: Wrong input format</p>""")
                                .format(loginFile))
                            return
                        self.__logins[data[0]] = (data[1], data[2])
                        data = []
                
                elif section == 1:
                    if line != self.SEPARATOR:
                        data.append(line)
                    else:
                        from .LoginForm import LoginForm
                        key = data[0]
                        form = LoginForm()
                        form.url = QUrl(data[1])
                        form.name = data[2]
                        form.hasAPassword = data[3] == "True"
                        for element in data[4:]:
                            name, value = element.split(" = ", 1)
                            form.elements.append((name, value))
                        self.__loginForms[key] = form
                        data = []
                
                elif section == 2:
                    self.__never.append(line)
            
            os.remove(loginFile)
        
        self.__loaded = True
        
        # this does the conversion
        self.save()
    
    def reload(self):
        """
        Public method to reload the login data.
        """
        if not self.__loaded:
            return
        
        self.__load()
    
    def close(self):
        """
        Public method to close the passwords manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def removePassword(self, site):
        """
        Public method to remove a password entry.
        
        @param site web site name (string)
        """
        if site in self.__logins:
            del self.__logins[site]
            if site in self.__loginForms:
                del self.__loginForms[site]
            self.changed.emit()
    
    def allSiteNames(self):
        """
        Public method to get a list of all site names.
        
        @return sorted list of all site names (list of strings)
        """
        if not self.__loaded:
            self.__load()
        
        return sorted(self.__logins.keys())
    
    def sitesCount(self):
        """
        Public method to get the number of available sites.
        
        @return number of sites (integer)
        """
        if not self.__loaded:
            self.__load()
        
        return len(self.__logins)
    
    def siteInfo(self, site):
        """
        Public method to get a reference to the named site.
        
        @param site web site name (string)
        @return tuple containing the user name (string) and password (string)
        """
        if not self.__loaded:
            self.__load()
        
        if site not in self.__logins:
            return None
        
        return self.__logins[site][0], Utilities.crypto.pwConvert(
            self.__logins[site][1], encode=False)
    
    def post(self, request, data):
        """
        Public method to check, if the data to be sent contains login data.
        
        @param request reference to the network request (QNetworkRequest)
        @param data data to be sent (QByteArray)
        """
        # shall passwords be saved?
        if not Preferences.getUser("SavePasswords"):
            return
        
        # observe privacy
        if QWebSettings.globalSettings().testAttribute(
                QWebSettings.PrivateBrowsingEnabled):
            return
        
        if not self.__loaded:
            self.__load()
        
        # determine the url
        refererHeader = request.rawHeader(b"Referer")
        if refererHeader.isEmpty():
            return
        url = QUrl.fromEncoded(refererHeader)
        url = self.__stripUrl(url)
        
        # check that url isn't in __never
        if url.toString() in self.__never:
            return
        
        # check the request type
        navType = request.attribute(QNetworkRequest.User + 101)
        if navType is None:
            return
        if navType != QWebPage.NavigationTypeFormSubmitted:
            return
        
        # determine the QWebPage
        webPage = request.attribute(QNetworkRequest.User + 100)
        if webPage is None:
            return
        
        # determine the requests content type
        contentTypeHeader = request.rawHeader(b"Content-Type")
        if contentTypeHeader.isEmpty():
            return
        multipart = contentTypeHeader.startsWith(b"multipart/form-data")
        if multipart:
            boundary = contentTypeHeader.split(" ")[1].split("=")[1]
        else:
            boundary = None
        
        # find the matching form on the web page
        form = self.__findForm(webPage, data, boundary=boundary)
        if not form.isValid():
            return
        form.url = QUrl(url)
        
        # check, if the form has a password
        if not form.hasAPassword:
            return
        
        # prompt, if the form has never be seen
        key = self.__createKey(url, "")
        if key not in self.__loginForms:
            mb = E5MessageBox.E5MessageBox(
                E5MessageBox.Question,
                self.tr("Save password"),
                self.tr(
                    """<b>Would you like to save this password?</b><br/>"""
                    """To review passwords you have saved and remove them, """
                    """use the password management dialog of the Settings"""
                    """ menu."""),
                modal=True)
            neverButton = mb.addButton(
                self.tr("Never for this site"),
                E5MessageBox.DestructiveRole)
            noButton = mb.addButton(
                self.tr("Not now"), E5MessageBox.RejectRole)
            mb.addButton(E5MessageBox.Yes)
            mb.exec_()
            if mb.clickedButton() == neverButton:
                self.__never.append(url.toString())
                return
            elif mb.clickedButton() == noButton:
                return
        
        # extract user name and password
        user = ""
        password = ""
        for index in range(len(form.elements)):
            element = form.elements[index]
            type_ = form.elementTypes[element[0]]
            if user == "" and \
               type_ == "text":
                user = element[1]
            elif password == "" and \
                    type_ == "password":
                password = element[1]
                form.elements[index] = (element[0], "--PASSWORD--")
        if user and password:
            self.__logins[key] = \
                (user, Utilities.crypto.pwConvert(password, encode=True))
            self.__loginForms[key] = form
            self.changed.emit()
    
    def __stripUrl(self, url):
        """
        Private method to strip off all unneeded parts of a URL.
        
        @param url URL to be stripped (QUrl)
        @return stripped URL (QUrl)
        """
        cleanUrl = QUrl(url)
        if qVersion() >= "5.0.0":
            cleanUrl.setQuery("")
        else:
            cleanUrl.setQueryItems([])
        cleanUrl.setUserInfo("")
        
        authority = cleanUrl.authority()
        if authority.startswith("@"):
            authority = authority[1:]
        cleanUrl = QUrl("{0}://{1}{2}".format(
            cleanUrl.scheme(), authority, cleanUrl.path()))
        cleanUrl.setFragment("")
        return cleanUrl
    
    def __findForm(self, webPage, data, boundary=None):
        """
        Private method to find the form used for logging in.
        
        @param webPage reference to the web page (QWebPage)
        @param data data to be sent (QByteArray)
        @keyparam boundary boundary string (QByteArray) for multipart
            encoded data, None for urlencoded data
        @return parsed form (LoginForm)
        """
        from .LoginForm import LoginForm
        form = LoginForm()
        if boundary is not None:
            args = self.__extractMultipartQueryItems(data, boundary)
        else:
            if qVersion() >= "5.0.0":
                from PyQt5.QtCore import QUrlQuery
                argsUrl = QUrl.fromEncoded(
                    QByteArray(b"foo://bar.com/?" + QUrl.fromPercentEncoding(
                        data.replace(b"+", b"%20")).encode("utf-8")))
                encodedArgs = QUrlQuery(argsUrl).queryItems()
            else:
                argsUrl = QUrl.fromEncoded(
                    QByteArray(b"foo://bar.com/?" + data.replace(b"+", b"%20"))
                )
                encodedArgs = argsUrl.queryItems()
            args = set()
            for arg in encodedArgs:
                key = arg[0]
                value = arg[1]
                args.add((key, value))
        
        # extract the forms
        from Helpviewer.JavaScriptResources import parseForms_js
        lst = webPage.mainFrame().evaluateJavaScript(parseForms_js)
        for map in lst:
            formHasPasswords = False
            formName = map["name"]
            formIndex = map["index"]
            if isinstance(formIndex, float) and formIndex.is_integer():
                formIndex = int(formIndex)
            elements = map["elements"]
            formElements = set()
            formElementTypes = {}
            deadElements = set()
            for elementMap in elements:
                try:
                    name = elementMap["name"]
                    value = elementMap["value"]
                    type_ = elementMap["type"]
                except KeyError:
                    continue
                if type_ == "password":
                    formHasPasswords = True
                t = (name, value)
                try:
                    if elementMap["autocomplete"] == "off":
                        deadElements.add(t)
                except KeyError:
                    pass
                if name:
                    formElements.add(t)
                    formElementTypes[name] = type_
            if formElements.intersection(args) == args:
                form.hasAPassword = formHasPasswords
                if not formName:
                    form.name = formIndex
                else:
                    form.name = formName
                args.difference_update(deadElements)
                for elt in deadElements:
                    if elt[0] in formElementTypes:
                        del formElementTypes[elt[0]]
                form.elements = list(args)
                form.elementTypes = formElementTypes
                break
        
        return form
    
    def __extractMultipartQueryItems(self, data, boundary):
        """
        Private method to extract the query items for a post operation.
        
        @param data data to be sent (QByteArray)
        @param boundary boundary string (QByteArray)
        @return set of name, value pairs (set of tuple of string, string)
        """
        args = set()
        
        dataStr = bytes(data).decode()
        boundaryStr = bytes(boundary).decode()
        
        parts = dataStr.split(boundaryStr + "\r\n")
        for part in parts:
            if part.startswith("Content-Disposition"):
                lines = part.split("\r\n")
                name = lines[0].split("=")[1][1:-1]
                value = lines[2]
                args.add((name, value))
        
        return args
    
    def fill(self, page):
        """
        Public slot to fill login forms with saved data.
        
        @param page reference to the web page (QWebPage)
        """
        if page is None or page.mainFrame() is None:
            return
        
        if not self.__loaded:
            self.__load()
        
        url = page.mainFrame().url()
        url = self.__stripUrl(url)
        key = self.__createKey(url, "")
        if key not in self.__loginForms or \
           key not in self.__logins:
            return
        
        form = self.__loginForms[key]
        if form.url != url:
            return
        
        if form.name == "":
            formName = "0"
        else:
            try:
                formName = "{0:d}".format(int(form.name))
            except ValueError:
                formName = '"{0}"'.format(form.name)
        for element in form.elements:
            name = element[0]
            value = element[1]
            
            disabled = page.mainFrame().evaluateJavaScript(
                'document.forms[{0}].elements["{1}"].disabled'.format(
                    formName, name))
            if disabled:
                continue
            
            readOnly = page.mainFrame().evaluateJavaScript(
                'document.forms[{0}].elements["{1}"].readOnly'.format(
                    formName, name))
            if readOnly:
                continue
            
            type_ = page.mainFrame().evaluateJavaScript(
                'document.forms[{0}].elements["{1}"].type'.format(
                    formName, name))
            if type_ == "" or \
               type_ in ["hidden", "reset", "submit"]:
                continue
            if type_ == "password":
                value = Utilities.crypto.pwConvert(
                    self.__logins[key][1], encode=False)
            setType = type_ == "checkbox" and "checked" or "value"
            value = value.replace("\\", "\\\\")
            value = value.replace('"', '\\"')
            javascript = \
                'document.forms[{0}].elements["{1}"].{2}="{3}";'.format(
                    formName, name, setType, value)
            page.mainFrame().evaluateJavaScript(javascript)
    
    def masterPasswordChanged(self, oldPassword, newPassword):
        """
        Public slot to handle the change of the master password.
        
        @param oldPassword current master password (string)
        @param newPassword new master password (string)
        """
        if not self.__loaded:
            self.__load()
        
        progress = E5ProgressDialog(
            self.tr("Re-encoding saved passwords..."),
            None, 0, len(self.__logins), self.tr("%v/%m Passwords"),
            QApplication.activeModalWidget())
        progress.setMinimumDuration(0)
        progress.setWindowTitle(self.tr("Passwords"))
        count = 0
        
        for key in self.__logins:
            progress.setValue(count)
            QCoreApplication.processEvents()
            username, hash = self.__logins[key]
            hash = Utilities.crypto.pwRecode(hash, oldPassword, newPassword)
            self.__logins[key] = (username, hash)
            count += 1
        
        progress.setValue(len(self.__logins))
        QCoreApplication.processEvents()
        self.changed.emit()
Пример #37
0
class NetworkManager(QNetworkAccessManager):
    """
    Class implementing a network manager.
    
    @signal changed() emitted to indicate a change
    """
    changed = pyqtSignal()
    
    def __init__(self, engine, parent=None):
        """
        Constructor
        
        @param engine reference to the help engine (QHelpEngine)
        @param parent reference to the parent object (QObject)
        """
        super(NetworkManager, self).__init__(parent)
        
        from E5Network.E5NetworkProxyFactory import E5NetworkProxyFactory
        
        self.__proxyFactory = E5NetworkProxyFactory()
        if Preferences.getUI("UseSystemProxy"):
            QNetworkProxyFactory.setUseSystemConfiguration(True)
        else:
            QNetworkProxyFactory.setApplicationProxyFactory(
                self.__proxyFactory)
            QNetworkProxyFactory.setUseSystemConfiguration(False)
        
        self.languagesChanged()
        
        if SSL_AVAILABLE:
            self.__sslErrorHandler = E5SslErrorHandler(self)
            self.sslErrors.connect(self.__sslErrorHandler.sslErrorsReplySlot)
        
        self.__temporarilyIgnoredSslErrors = {}
        self.__permanentlyIgnoredSslErrors = {}
        # dictionaries of permanently and temporarily ignored SSL errors
        
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.__save)
        
        self.changed.connect(self.__saveTimer.changeOccurred)
        self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired)
        self.authenticationRequired.connect(
            lambda reply, auth: self.authentication(reply.url(), auth))
        
        from .EricSchemeHandler import EricSchemeHandler
        self.__ericSchemeHandler = EricSchemeHandler()
        WebBrowserWindow.webProfile().installUrlSchemeHandler(
            QByteArray(b"eric"), self.__ericSchemeHandler)
        
        if engine:
            from .QtHelpSchemeHandler import QtHelpSchemeHandler
            self.__qtHelpSchemeHandler = QtHelpSchemeHandler(engine)
            WebBrowserWindow.webProfile().installUrlSchemeHandler(
                QByteArray(b"qthelp"), self.__qtHelpSchemeHandler)
        
        self.__interceptor = NetworkUrlInterceptor(self)
        try:
            WebBrowserWindow.webProfile().setUrlRequestInterceptor(
                self.__interceptor)
        except AttributeError:
            #  Qt < 5.13
            WebBrowserWindow.webProfile().setRequestInterceptor(
                self.__interceptor)
        
        WebBrowserWindow.cookieJar()
    
    def __save(self):
        """
        Private slot to save the permanent SSL error exceptions.
        """
        if not self.__loaded:
            return
        
        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        if not WebBrowserWindow.isPrivate():
            dbString = json.dumps(self.__permanentlyIgnoredSslErrors)
            Preferences.setWebBrowser("SslExceptionsDB", dbString)
    
    def __load(self):
        """
        Private method to load the permanent SSL error exceptions.
        """
        if self.__loaded:
            return
        
        dbString = Preferences.getWebBrowser("SslExceptionsDB")
        if dbString:
            try:
                db = json.loads(dbString)
                self.__permanentlyIgnoredSslErrors = db
            except ValueError:
                # ignore silently
                pass
        
        self.__loaded = True
    
    def shutdown(self):
        """
        Public method to shut down the network manager.
        """
        self.__saveTimer.saveIfNeccessary()
        self.__loaded = False
        self.__temporarilyIgnoredSslErrors = {}
        self.__permanentlyIgnoredSslErrors = {}
        
        # set proxy factory to None to avoid crashes
        QNetworkProxyFactory.setApplicationProxyFactory(None)
    
    def showSslErrorExceptionsDialog(self):
        """
        Public method to show the SSL error exceptions dialog.
        """
        self.__load()
        
        from .SslErrorExceptionsDialog import SslErrorExceptionsDialog
        dlg = SslErrorExceptionsDialog(self.__permanentlyIgnoredSslErrors)
        if dlg.exec_() == QDialog.Accepted:
            self.__permanentlyIgnoredSslErrors = dlg.getSslErrorExceptions()
            self.changed.emit()
    
    def clearSslExceptions(self):
        """
        Public method to clear the permanent SSL certificate error exceptions.
        """
        self.__load()
        
        self.__permanentlyIgnoredSslErrors = {}
        self.changed.emit()
        self.__saveTimer.saveIfNeccessary()
    
    def certificateError(self, error, view):
        """
        Public method to handle SSL certificate errors.
        
        @param error object containing the certificate error information
        @type QWebEngineCertificateError
        @param view reference to a view to be used as parent for the dialog
        @type QWidget
        @return flag indicating to ignore this error
        @rtype bool
        """
        self.__load()
        
        host = error.url().host()
        
        if (
            host in self.__temporarilyIgnoredSslErrors and
            error.error() in self.__temporarilyIgnoredSslErrors[host]
        ):
            return True
        
        if (
            host in self.__permanentlyIgnoredSslErrors and
            error.error() in self.__permanentlyIgnoredSslErrors[host]
        ):
            return True
        
        title = self.tr("SSL Certificate Error")
        msgBox = E5MessageBox.E5MessageBox(
            E5MessageBox.Warning,
            title,
            self.tr("""<b>{0}</b>"""
                    """<p>The page you are trying to access has errors"""
                    """ in the SSL certificate.</p>"""
                    """<ul><li>{1}</li></ul>"""
                    """<p>Would you like to make an exception?</p>""")
            .format(title, error.errorDescription()),
            modal=True, parent=view)
        permButton = msgBox.addButton(self.tr("&Permanent accept"),
                                      E5MessageBox.AcceptRole)
        tempButton = msgBox.addButton(self.tr("&Temporary accept"),
                                      E5MessageBox.AcceptRole)
        msgBox.addButton(self.tr("&Reject"), E5MessageBox.RejectRole)
        msgBox.exec_()
        if msgBox.clickedButton() == permButton:
            if host not in self.__permanentlyIgnoredSslErrors:
                self.__permanentlyIgnoredSslErrors[host] = []
            self.__permanentlyIgnoredSslErrors[host].append(error.error())
            self.changed.emit()
            return True
        elif msgBox.clickedButton() == tempButton:
            if host not in self.__temporarilyIgnoredSslErrors:
                self.__temporarilyIgnoredSslErrors[host] = []
            self.__temporarilyIgnoredSslErrors[host].append(error.error())
            return True
        else:
            return False
    
    def authentication(self, url, auth, page=None):
        """
        Public slot to handle an authentication request.
        
        @param url URL requesting authentication
        @type QUrl
        @param auth reference to the authenticator object
        @type QAuthenticator
        @param page reference to the web page
        @type QWebEnginePage or None
        """
        urlRoot = (
            "{0}://{1}".format(url.scheme(), url.authority())
        )
        realm = auth.realm()
        if not realm and 'realm' in auth.options():
            realm = auth.option("realm")
        if realm:
            info = self.tr(
                "<b>Enter username and password for '{0}', realm '{1}'</b>"
            ).format(urlRoot, realm)
        else:
            info = self.tr(
                "<b>Enter username and password for '{0}'</b>"
            ).format(urlRoot)
        
        from UI.AuthenticationDialog import AuthenticationDialog
        import WebBrowser.WebBrowserWindow
        
        dlg = AuthenticationDialog(info, auth.user(),
                                   Preferences.getUser("SavePasswords"),
                                   Preferences.getUser("SavePasswords"))
        if Preferences.getUser("SavePasswords"):
            username, password = (
                WebBrowser.WebBrowserWindow.WebBrowserWindow
                .passwordManager().getLogin(url, realm)
            )
            if username:
                dlg.setData(username, password)
        if dlg.exec_() == QDialog.Accepted:
            username, password = dlg.getData()
            auth.setUser(username)
            auth.setPassword(password)
            if Preferences.getUser("SavePasswords") and dlg.shallSave():
                (
                    WebBrowser.WebBrowserWindow.WebBrowserWindow
                    .passwordManager().setLogin(
                        url, realm, username, password)
                )
        else:
            if page is not None:
                self.__showAuthenticationErrorPage(page, url)
    
    def __showAuthenticationErrorPage(self, page, url):
        """
        Private method to show an authentication error page.
        
        @param page reference to the page
        @type QWebEnginePage
        @param url reference to the URL requesting authentication
        @type QUrl
        """
        html = readAllFileContents(":/html/authenticationErrorPage.html")
        html = html.replace("@IMAGE@", pixmapToDataUrl(
            qApp.style().standardIcon(QStyle.SP_MessageBoxCritical).pixmap(
                48, 48)).toString())
        html = html.replace("@FAVICON@", pixmapToDataUrl(
            qApp.style() .standardIcon(QStyle.SP_MessageBoxCritical).pixmap(
                16, 16)).toString())
        html = html.replace("@TITLE@", self.tr("Authentication required"))
        html = html.replace("@H1@", self.tr("Authentication required"))
        html = html.replace(
            "@LI-1@",
            self.tr("Authentication is required to access:"))
        html = html.replace(
            "@LI-2@",
            '<a href="{0}">{0}</a>'.format(url.toString()))
        page.setHtml(html, url)
    
    def proxyAuthentication(self, requestUrl, auth, proxyHost):
        """
        Public slot to handle a proxy authentication request.
        
        @param requestUrl requested URL
        @type QUrl
        @param auth reference to the authenticator object
        @type QAuthenticator
        @param proxyHost name of the proxy host
        @type str
        """
        proxy = QNetworkProxy.applicationProxy()
        if proxy.user() and proxy.password():
            auth.setUser(proxy.user())
            auth.setPassword(proxy.password())
            return
        
        proxyAuthenticationRequired(proxy, auth)
    
    def languagesChanged(self):
        """
        Public slot to (re-)load the list of accepted languages.
        """
        from WebBrowser.WebBrowserLanguagesDialog import (
            WebBrowserLanguagesDialog
        )
        languages = Preferences.toList(
            Preferences.Prefs.settings.value(
                "WebBrowser/AcceptLanguages",
                WebBrowserLanguagesDialog.defaultAcceptLanguages()))
        self.__acceptLanguage = WebBrowserLanguagesDialog.httpString(languages)
        
        WebBrowserWindow.webProfile().setHttpAcceptLanguage(
            self.__acceptLanguage)
    
    def installUrlInterceptor(self, interceptor):
        """
        Public method to install an URL interceptor.
        
        @param interceptor URL interceptor to be installed
        @type UrlInterceptor
        """
        self.__interceptor.installUrlInterceptor(interceptor)
    
    def removeUrlInterceptor(self, interceptor):
        """
        Public method to remove an URL interceptor.
        
        @param interceptor URL interceptor to be removed
        @type UrlInterceptor
        """
        self.__interceptor.removeUrlInterceptor(interceptor)
    
    def preferencesChanged(self):
        """
        Public slot to handle a change of preferences.
        """
        self.__interceptor.preferencesChanged()
            
        if Preferences.getUI("UseSystemProxy"):
            QNetworkProxyFactory.setUseSystemConfiguration(True)
        else:
            QNetworkProxyFactory.setApplicationProxyFactory(
                self.__proxyFactory)
            QNetworkProxyFactory.setUseSystemConfiguration(False)
    
    def createRequest(self, op, request, data):
        """
        Public method to launch a network action.
        
        @param op operation to be performed
        @type QNetworkAccessManager.Operation
        @param request request to be operated on
        @type QNetworkRequest
        @param data reference to the data to be sent
        @type QIODevice
        @return reference to the network reply
        @rtype QNetworkReply
        """
        req = QNetworkRequest(request)
        req.setAttribute(QNetworkRequest.SpdyAllowedAttribute, True)
        req.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)
        
        return super(NetworkManager, self).createRequest(op, req, data)
Пример #38
0
class WebIconProvider(QObject):
    """
    Class implementing a web site icon storage.
    
    @signal changed() emitted to indicate a change of the icons database
    """
    changed = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(WebIconProvider, self).__init__(parent)

        self.__encoding = "iso-8859-1"
        self.__iconsFileName = "web_site_icons.json"
        self.__iconDatabasePath = ""  # saving of icons disabled

        self.__iconsDB = {}
        self.__loaded = False

        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)

    def setIconDatabasePath(self, path):
        """
        Public method to set the path for the web site icons store.
        
        @param path path to store the icons file to
        @type str
        """
        if path != self.__iconDatabasePath:
            self.close()

        self.__iconDatabasePath = path

    def iconDatabasePath(self):
        """
        Public method o get the path for the web site icons store.
        
        @return path to store the icons file to
        @rtype str
        """
        return self.__iconDatabasePath

    def close(self):
        """
        Public method to close the web icon provider.
        """
        self.__saveTimer.saveIfNeccessary()
        self.__loaded = False
        self.__iconsDB = {}

    def load(self):
        """
        Public method to load the bookmarks.
        """
        if self.__loaded:
            return

        if self.__iconDatabasePath:
            filename = os.path.join(self.__iconDatabasePath,
                                    self.__iconsFileName)
            try:
                f = open(filename, "r")
                db = json.load(f)
                f.close()
            except (IOError, OSError):
                # ignore silentyl
                db = {}

            self.__iconsDB = {}
            for url, data in db.items():
                self.__iconsDB[url] = QIcon(
                    QPixmap.fromImage(
                        QImage.fromData(
                            QByteArray(data.encode(self.__encoding)))))

        self.__loaded = True

    def save(self):
        """
        Public method to save the zoom values.
        """
        if not self.__loaded:
            return

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        if not WebBrowserWindow.isPrivate() and bool(self.__iconDatabasePath):
            db = {}
            for url, icon in self.__iconsDB.items():
                ba = QByteArray()
                buffer = QBuffer(ba)
                buffer.open(QIODevice.WriteOnly)
                icon.pixmap(32).toImage().save(buffer, "PNG")
                db[url] = bytes(buffer.data()).decode(self.__encoding)

            filename = os.path.join(self.__iconDatabasePath,
                                    self.__iconsFileName)
            try:
                f = open(filename, "w")
                json.dump(db, f)
                f.close()
            except (IOError, OSError):
                # ignore silentyl
                pass

    def saveIcon(self, view):
        """
        Public method to save a web site icon.
        
        @param view reference to the view object
        @type WebBrowserView
        """
        scheme = view.url().scheme()
        if scheme in ["eric", "about", "qthelp", "file", "abp", "ftp"]:
            return

        self.load()

        if view.mainWindow().isPrivate():
            return

        urlStr = self.__urlToString(view.url())
        self.__iconsDB[urlStr] = view.icon()

        self.changed.emit()

    def __urlToString(self, url):
        """
        Private method to convert an URL to a string.
        
        @param url URL to be converted
        @type QUrl
        @return string representation of the URL
        @rtype str
        """
        return url.toString(QUrl.PrettyDecoded | QUrl.RemoveUserInfo
                            | QUrl.RemoveFragment)

    def iconForUrl(self, url):
        """
        Public method to get an icon for an URL.
        
        @param url URL to get icon for
        @type QUrl
        @return icon for the URL
        @rtype QIcon
        """
        scheme = url.scheme()
        if scheme in ["eric", "about"]:
            return UI.PixmapCache.getIcon("ericWeb.png")
        elif scheme == "qthelp":
            return UI.PixmapCache.getIcon("qthelp.png")
        elif scheme == "file":
            return UI.PixmapCache.getIcon("fileMisc.png")
        elif scheme == "abp":
            return UI.PixmapCache.getIcon("adBlockPlus.png")
        elif scheme == "ftp":
            return UI.PixmapCache.getIcon("network-server.png")

        self.load()

        urlStr = self.__urlToString(url)
        for iconUrlStr in self.__iconsDB:
            if iconUrlStr.startswith(urlStr):
                return self.__iconsDB[iconUrlStr]

        # try replacing http scheme with https scheme
        url = QUrl(url)
        if url.scheme() == "http":
            url.setScheme("https")
        urlStr = self.__urlToString(url)
        for iconUrlStr in self.__iconsDB:
            if iconUrlStr.startswith(urlStr):
                return self.__iconsDB[iconUrlStr]

        if scheme == "https":
            return UI.PixmapCache.getIcon("securityHigh32.png")
        else:
            return UI.PixmapCache.getIcon("defaultIcon.png")

    def clear(self):
        """
        Public method to clear the icons cache.
        """
        self.load()
        self.__iconsDB = {}
        self.changed.emit()
        self.__saveTimer.saveIfNeccessary()

    def showWebIconDialog(self):
        """
        Public method to show a dialog to manage the Favicons.
        """
        self.load()

        from .WebIconDialog import WebIconDialog
        dlg = WebIconDialog(self.__iconsDB)
        if dlg.exec_() == QDialog.Accepted:
            changed = False
            urls = dlg.getUrls()
            for url in list(self.__iconsDB.keys())[:]:
                if url not in urls:
                    del self.__iconsDB[url]
                    changed = True
            if changed:
                self.changed.emit()
Пример #39
0
class IrcNetworkManager(QObject):
    """
    Class implementing the IRC identity object.
    
    @signal dataChanged() emitted after some data has changed
    @signal networksChanged() emitted after a network object has changed
    @signal identitiesChanged() emitted after an identity object has changed
    """
    dataChanged = pyqtSignal()
    networksChanged = pyqtSignal()
    identitiesChanged = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(IrcNetworkManager, self).__init__(parent)
        
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.__settings = Preferences.Prefs.settings
        
        self.__networks = {}
        self.__identities = {}
        
        self.dataChanged.connect(self.__saveTimer.changeOccurred)
    
    def close(self):
        """
        Public method to close the open search engines manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def save(self):
        """
        Public slot to save the IRC data.
        """
        if not self.__loaded:
            return
        
        # save IRC data
        self.__settings.beginGroup("IRC")
        
        # identities
        self.__settings.remove("Identities")
        self.__settings.beginGroup("Identities")
        for key in self.__identities:
            self.__settings.beginGroup(key)
            self.__identities[key].save(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()
        
        # networks
        self.__settings.remove("Networks")
        self.__settings.beginGroup("Networks")
        for key in self.__networks:
            self.__settings.beginGroup(key)
            self.__networks[key].save(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()
        
        self.__settings.endGroup()
    
    def __load(self):
        """
        Private slot to load the IRC data.
        """
        if self.__loaded:
            return
        
        # load IRC data
        self.__settings.beginGroup("IRC")
        
        # identities
        self.__settings.beginGroup("Identities")
        for key in self.__settings.childGroups():
            self.__identities[key] = IrcIdentity(key)
            self.__settings.beginGroup(key)
            self.__identities[key].load(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()
        
        # networks
        self.__settings.beginGroup("Networks")
        for key in self.__settings.childGroups():
            self.__networks[key] = IrcNetwork(key)
            self.__settings.beginGroup(key)
            self.__networks[key].load(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()
        
        self.__settings.endGroup()
        
        if not self.__identities or \
           not self.__networks:
            # data structures got corrupted; load defaults
            self.__loadDefaults()
        
        if IrcIdentity.DefaultIdentityName not in self.__identities:
            self.__loadDefaults(identityOnly=True)
        
        self.__loaded = True
    
    def __loadDefaults(self, identityOnly=False):
        """
        Private method to load default values.
        
        @param identityOnly flag indicating to just load the default
            identity (boolean)
        """
        if not identityOnly:
            self.__networks = {}
            self.__identities = {}
        
        # identity
        identity = IrcIdentity.createDefaultIdentity()
        self.__identities[identity.getName()] = identity
        
        if not identityOnly:
            network = IrcNetwork.createDefaultNetwork()
            self.__networks[network.getName()] = network
            network = IrcNetwork.createDefaultNetwork(True)
            self.__networks[network.getName()] = network
        
        self.dataChanged.emit()
    
    ##################################################################
    ## Identity related methods below
    ##################################################################
    
    def getIdentity(self, name, create=False):
        """
        Public method to get an identity object.
        
        @param name name of the identity to get (string)
        @param create flag indicating to create a new object,
            if none exists (boolean)
        @return reference to the identity (IrcIdentity)
        """
        if not name:
            return None
        
        if not self.__loaded:
            self.__load()
        
        if name in self.__identities:
            return self.__identities[name]
        elif create:
            id = IrcIdentity(name)
            self.__identities[name] = id
            
            self.dataChanged.emit()
            
            return id
        else:
            return None
    
    def getIdentities(self):
        """
        Public method to get a copy of all identities.
        
        @return dictionary of all identities (dict of IrcIdentity)
        """
        return copy.deepcopy(self.__identities)
    
    def setIdentities(self, identities):
        """
        Public method to set the identities.
        
        @param identities dictionary of all identities (dict of IrcIdentity)
        """
        self.__identities = copy.deepcopy(identities)
        self.identityChanged()
        
        # Check all networks, if the identity they use is still available.
        # If it isn't, change them to use the default identity.
        for network in self.__networks.values():
            if network.getIdentityName() not in self.__identities:
                network.setIdentityName(IrcIdentity.DefaultIdentityName)
    
    def getIdentityNames(self):
        """
        Public method to get the names of all identities.
        
        @return names of all identities (list of string)
        """
        return list(self.__identities.keys())
    
    def addIdentity(self, identity):
        """
        Public method to add a new identity.
        
        @param identity reference to the identity to add (IrcIdentity)
        """
        name = identity.getName()
        self.__identities[name] = identity
        self.identityChanged()
    
    def deleteIdentity(self, name):
        """
        Public method to delete the given identity.
        
        @param name name of the identity to delete (string)
        """
        if name in self.__identities and \
                name != IrcIdentity.DefaultIdentityName:
            del self.__identities[name]
            self.identityChanged()
    
    def renameIdentity(self, oldName, newName):
        """
        Public method to rename an identity.
        
        @param oldName old name of the identity (string)
        @param newName new name of the identity (string)
        """
        if oldName in self.__identities:
            self.__identities[newName] = self.__identities[oldName]
            del self.__identities[oldName]
            
            for network in self.__networks:
                if network.getIdentityName() == oldName:
                    network.setIdentityName(newName)
            
            self.identityChanged()
    
    def identityChanged(self):
        """
        Public method to indicate a change of an identity object.
        """
        self.dataChanged.emit()
        self.identitiesChanged.emit()
    
    ##################################################################
    ## Network related methods below
    ##################################################################
    
    def getNetwork(self, name):
        """
        Public method to get a network object.
        
        @param name name of the network (string)
        @return reference to the network object (IrcNetwork)
        """
        if not self.__loaded:
            self.__load()
        
        if name in self.__networks:
            return self.__networks[name]
        else:
            return None
    
    def setNetwork(self, network, networkName=""):
        """
        Public method to set a network.
        
        @param network network object to set (IrcNetwork)
        @param networkName name the network was known for (string)
        """
        name = network.getName()
        if networkName and name != networkName:
            # the network name has changed
            self.deleteNetwork(networkName)
            self.addNetwork(network)
        elif name in self.__networks:
            self.__networks[name] = network
            self.networkChanged()
    
    def addNetwork(self, network):
        """
        Public method to add a network.
        
        @param network network object to add (IrcNetwork)
        """
        name = network.getName()
        if name not in self.__networks:
            self.__networks[name] = network
            self.networkChanged()
    
    def deleteNetwork(self, name):
        """
        Public method to delete the given network.
        
        @param name name of the network to delete (string)
        """
        if name in self.__networks:
            del self.__networks[name]
            self.networkChanged()
    
    def networkChanged(self):
        """
        Public method to indicate a change of a network object.
        """
        self.dataChanged.emit()
        self.networksChanged.emit()
    
    def getNetworkNames(self):
        """
        Public method to get a list of all known network names.
        
        @return list of network names (list of string)
        """
        if not self.__loaded:
            self.__load()
        
        return list(sorted(self.__networks.keys()))
Пример #40
0
class DownloadManager(QDialog, Ui_DownloadManager):
    """
    Class implementing the download manager.
    
    @signal downloadsCountChanged() emitted to indicate a change of the
        count of download items
    """
    RemoveNever = 0
    RemoveExit = 1
    RemoveSuccessFullDownload = 2

    UpdateTimerTimeout = 1000

    downloadsCountChanged = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(DownloadManager, self).__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(Qt.Window)

        self.__winTaskbarButton = None

        self.__saveTimer = AutoSaver(self, self.save)

        self.__model = DownloadModel(self)
        self.__manager = WebBrowserWindow.networkManager()

        self.__iconProvider = None
        self.__downloads = []
        self.__downloadDirectory = ""
        self.__loaded = False

        self.__rowHeightMultiplier = 1.1

        self.setDownloadDirectory(Preferences.getUI("DownloadPath"))

        self.downloadsView.setShowGrid(False)
        self.downloadsView.verticalHeader().hide()
        self.downloadsView.horizontalHeader().hide()
        self.downloadsView.setAlternatingRowColors(True)
        self.downloadsView.horizontalHeader().setStretchLastSection(True)
        self.downloadsView.setModel(self.__model)
        self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.downloadsView.customContextMenuRequested.connect(
            self.__customContextMenuRequested)

        self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self)
        self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked)

        self.__load()

        self.__updateTimer = QBasicTimer()

    def __customContextMenuRequested(self, pos):
        """
        Private slot to handle the context menu request for the bookmarks tree.
        
        @param pos position the context menu was requested (QPoint)
        """
        menu = QMenu()

        selectedRowsCount = len(
            self.downloadsView.selectionModel().selectedRows())

        if selectedRowsCount == 1:
            row = self.downloadsView.selectionModel().selectedRows()[0].row()
            itm = self.__downloads[row]
            if itm.downloadedSuccessfully():
                menu.addAction(UI.PixmapCache.getIcon("open.png"),
                               self.tr("Open"), self.__contextMenuOpen)
            elif itm.downloading():
                menu.addAction(UI.PixmapCache.getIcon("stopLoading.png"),
                               self.tr("Cancel"), self.__contextMenuCancel)
                menu.addSeparator()
            menu.addAction(self.tr("Open Containing Folder"),
                           self.__contextMenuOpenFolder)
            menu.addSeparator()
            menu.addAction(self.tr("Go to Download Page"),
                           self.__contextMenuGotoPage)
            menu.addAction(self.tr("Copy Download Link"),
                           self.__contextMenuCopyLink)
            menu.addSeparator()
        menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll)
        if (selectedRowsCount > 1
                or (selectedRowsCount == 1
                    and not self.__downloads[self.downloadsView.selectionModel(
                    ).selectedRows()[0].row()].downloading())):
            menu.addSeparator()
            menu.addAction(self.tr("Remove From List"),
                           self.__contextMenuRemoveSelected)

        menu.exec_(QCursor.pos())

    def shutdown(self):
        """
        Public method to stop the download manager.
        """
        self.save()
        self.close()

    def activeDownloadsCount(self):
        """
        Public method to get the number of active downloads.
        
        @return number of active downloads (integer)
        """
        count = 0

        for download in self.__downloads:
            if download.downloading():
                count += 1
        return count

    def allowQuit(self):
        """
        Public method to check, if it is ok to quit.
        
        @return flag indicating allowance to quit (boolean)
        """
        if self.activeDownloadsCount() > 0:
            res = E5MessageBox.yesNo(
                self,
                self.tr(""),
                self.tr(
                    """There are %n downloads in progress.\n"""
                    """Do you want to quit anyway?""", "",
                    self.activeDownloadsCount()),
                icon=E5MessageBox.Warning)
            if not res:
                self.show()
                return False

        self.close()
        return True

    def __testWebBrowserView(self, view, url):
        """
        Private method to test a web browser view against an URL.
        
        @param view reference to the web browser view to be tested
        @type WebBrowserView
        @param url URL to test against
        @type QUrl
        @return flag indicating, that the view is the one for the URL
        @rtype bool
        """
        if view.tabWidget().count() < 2:
            return False

        page = view.page()
        if page.history().count() != 0:
            return False

        if (not page.url().isEmpty() and page.url().host() == url.host()):
            return True

        requestedUrl = page.requestedUrl()
        if requestedUrl.isEmpty():
            requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text())
        return requestedUrl.isEmpty() or requestedUrl.host() == url.host()

    def __closeDownloadTab(self, url):
        """
        Private method to close an empty tab, that was opened only for loading
        the download URL.
        
        @param url download URL
        @type QUrl
        """
        if self.__testWebBrowserView(
                WebBrowserWindow.getWindow().currentBrowser(), url):
            WebBrowserWindow.getWindow().closeCurrentBrowser()
            return

        for window in WebBrowserWindow.mainWindows():
            for browser in window.browsers():
                if self.__testWebBrowserView(browser, url):
                    window.closeBrowser(browser)
                    return

    def download(self, downloadItem):
        """
        Public method to download a file.
        
        @param downloadItem reference to the download object containing the
        download data.
        @type QWebEngineDownloadItem
        """
        url = downloadItem.url()
        if url.isEmpty():
            return

        self.__closeDownloadTab(url)

        # Safe Browsing
        from WebBrowser.SafeBrowsing.SafeBrowsingManager import (
            SafeBrowsingManager)
        if SafeBrowsingManager.isEnabled():
            threatLists = (
                WebBrowserWindow.safeBrowsingManager().lookupUrl(url)[0])
            if threatLists:
                threatMessages = (WebBrowserWindow.safeBrowsingManager().
                                  getThreatMessages(threatLists))
                res = E5MessageBox.warning(
                    WebBrowserWindow.getWindow(),
                    self.tr("Suspicuous URL detected"),
                    self.tr("<p>The URL <b>{0}</b> was found in the Safe"
                            " Browsing database.</p>{1}").format(
                                url.toString(), "".join(threatMessages)),
                    E5MessageBox.StandardButtons(E5MessageBox.Abort
                                                 | E5MessageBox.Ignore),
                    E5MessageBox.Abort)
                if res == E5MessageBox.Abort:
                    downloadItem.cancel()
                    return

        window = WebBrowserWindow.getWindow()
        if window:
            pageUrl = window.currentBrowser().url()
        else:
            pageUrl = QUrl()
        from .DownloadItem import DownloadItem
        itm = DownloadItem(downloadItem=downloadItem,
                           pageUrl=pageUrl,
                           parent=self)
        self.__addItem(itm)

        if Preferences.getWebBrowser("DownloadManagerAutoOpen"):
            self.show()
        else:
            self.__startUpdateTimer()

    def show(self):
        """
        Public slot to show the download manager dialog.
        """
        self.__startUpdateTimer()

        super(DownloadManager, self).show()
        self.activateWindow()
        self.raise_()

    def __addItem(self, itm, append=False):
        """
        Private method to add a download to the list of downloads.
        
        @param itm reference to the download item
        @type DownloadItem
        @param append flag indicating to append the item
        @type bool
        """
        itm.statusChanged.connect(lambda: self.__updateRow(itm))
        itm.downloadFinished.connect(self.__finished)

        # insert at top of window
        if append:
            row = self.downloadsCount()
        else:
            row = 0
        self.__model.beginInsertRows(QModelIndex(), row, row)
        if append:
            self.__downloads.append(itm)
        else:
            self.__downloads.insert(0, itm)
        self.__model.endInsertRows()

        self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm)
        icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)
        self.downloadsView.setRowHeight(
            row,
            itm.sizeHint().height() * self.__rowHeightMultiplier)
        # just in case the download finished before the constructor returned
        self.__updateRow(itm)
        self.changeOccurred()

        self.downloadsCountChanged.emit()

    def __updateRow(self, itm):
        """
        Private slot to update a download item.
        
        @param itm reference to the download item
        @type DownloadItem
        """
        if itm not in self.__downloads:
            return

        row = self.__downloads.index(itm)

        if self.__iconProvider is None:
            self.__iconProvider = QFileIconProvider()

        icon = self.__iconProvider.icon(QFileInfo(itm.fileName()))
        if icon.isNull():
            icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)

        self.downloadsView.setRowHeight(
            row,
            itm.minimumSizeHint().height() * self.__rowHeightMultiplier)

        remove = False

        if (itm.downloadedSuccessfully() and self.removePolicy()
                == DownloadManager.RemoveSuccessFullDownload):
            remove = True

        if remove:
            self.__model.removeRow(row)

        self.cleanupButton.setEnabled(
            (self.downloadsCount() - self.activeDownloadsCount()) > 0)

        # record the change
        self.changeOccurred()

    def removePolicy(self):
        """
        Public method to get the remove policy.
        
        @return remove policy (integer)
        """
        return Preferences.getWebBrowser("DownloadManagerRemovePolicy")

    def setRemovePolicy(self, policy):
        """
        Public method to set the remove policy.
        
        @param policy policy to be set
            (DownloadManager.RemoveExit, DownloadManager.RemoveNever,
             DownloadManager.RemoveSuccessFullDownload)
        """
        assert policy in (DownloadManager.RemoveExit,
                          DownloadManager.RemoveNever,
                          DownloadManager.RemoveSuccessFullDownload)

        if policy == self.removePolicy():
            return

        Preferences.setWebBrowser("DownloadManagerRemovePolicy", self.policy)

    def save(self):
        """
        Public method to save the download settings.
        """
        if not self.__loaded:
            return

        Preferences.setWebBrowser("DownloadManagerSize", self.size())
        Preferences.setWebBrowser("DownloadManagerPosition", self.pos())
        if self.removePolicy() == DownloadManager.RemoveExit:
            return

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        if WebBrowserWindow.isPrivate():
            return

        downloads = []
        for download in self.__downloads:
            downloads.append(download.getData())
        Preferences.setWebBrowser("DownloadManagerDownloads", downloads)

    def __load(self):
        """
        Private method to load the download settings.
        """
        if self.__loaded:
            return

        size = Preferences.getWebBrowser("DownloadManagerSize")
        if size.isValid():
            self.resize(size)
        pos = Preferences.getWebBrowser("DownloadManagerPosition")
        self.move(pos)

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        if not WebBrowserWindow.isPrivate():
            downloads = Preferences.getWebBrowser("DownloadManagerDownloads")
            for download in downloads:
                if (not download["URL"].isEmpty()
                        and bool(download["Location"])):
                    from .DownloadItem import DownloadItem
                    itm = DownloadItem(parent=self)
                    itm.setData(download)
                    self.__addItem(itm, append=True)
            self.cleanupButton.setEnabled(
                (self.downloadsCount() - self.activeDownloadsCount()) > 0)

        self.__loaded = True

        self.downloadsCountChanged.emit()

    def closeEvent(self, evt):
        """
        Protected event handler for the close event.
        
        @param evt reference to the close event
        @type QCloseEvent
        """
        self.save()

    def cleanup(self):
        """
        Public slot to cleanup the downloads.
        """
        self.on_cleanupButton_clicked()

    @pyqtSlot()
    def on_cleanupButton_clicked(self):
        """
        Private slot to cleanup the downloads.
        """
        if self.downloadsCount() == 0:
            return

        self.__model.removeRows(0, self.downloadsCount())
        if (self.downloadsCount() == 0 and self.__iconProvider is not None):
            self.__iconProvider = None

        self.changeOccurred()

        self.downloadsCountChanged.emit()

    def __finished(self, success):
        """
        Private slot to handle a finished download.
        
        @param success flag indicating a successful download
        @type bool
        """
        if self.isVisible():
            QApplication.alert(self)

        self.downloadsCountChanged.emit()

        if self.activeDownloadsCount() == 0:
            # all active downloads are done
            if success and e5App().activeWindow() is not self:
                if WebBrowserWindow.notificationsEnabled():
                    WebBrowserWindow.showNotification(
                        UI.PixmapCache.getPixmap("downloads48.png"),
                        self.tr("Downloads finished"),
                        self.tr("All files have been downloaded."))
                if not Preferences.getWebBrowser("DownloadManagerAutoClose"):
                    self.raise_()
                    self.activateWindow()

            self.__stopUpdateTimer()
            self.infoLabel.clear()
            self.setWindowTitle(self.tr("Download Manager"))
            if Globals.isWindowsPlatform():
                self.__taskbarButton().progress().hide()

            if Preferences.getWebBrowser("DownloadManagerAutoClose"):
                self.close()

    def setDownloadDirectory(self, directory):
        """
        Public method to set the current download directory.
        
        @param directory current download directory (string)
        """
        self.__downloadDirectory = directory
        if self.__downloadDirectory != "":
            self.__downloadDirectory += "/"

    def downloadDirectory(self):
        """
        Public method to get the current download directory.
        
        @return current download directory (string)
        """
        return self.__downloadDirectory

    def downloadsCount(self):
        """
        Public method to get the number of downloads.
        
        @return number of downloads
        @rtype int
        """
        return len(self.__downloads)

    def downloads(self):
        """
        Public method to get a reference to the downloads.
        
        @return reference to the downloads (list of DownloadItem)
        """
        return self.__downloads

    def changeOccurred(self):
        """
        Public method to signal a change.
        """
        self.__saveTimer.changeOccurred()

    def __taskbarButton(self):
        """
        Private method to get a reference to the task bar button (Windows
        only).
        
        @return reference to the task bar button
        @rtype QWinTaskbarButton or None
        """
        if Globals.isWindowsPlatform():
            from PyQt5.QtWinExtras import QWinTaskbarButton
            if self.__winTaskbarButton is None:
                window = WebBrowserWindow.mainWindow()
                self.__winTaskbarButton = QWinTaskbarButton(
                    window.windowHandle())
                self.__winTaskbarButton.progress().setRange(0, 100)

        return self.__winTaskbarButton

    def timerEvent(self, evt):
        """
        Protected event handler for timer events.
        
        @param evt reference to the timer event
        @type QTimerEvent
        """
        if evt.timerId() == self.__updateTimer.timerId():
            if self.activeDownloadsCount() == 0:
                self.__stopUpdateTimer()
                self.infoLabel.clear()
                self.setWindowTitle(self.tr("Download Manager"))
                if Globals.isWindowsPlatform():
                    self.__taskbarButton().progress().hide()
            else:
                progresses = []
                for itm in self.__downloads:
                    if (itm is None or itm.downloadCanceled()
                            or not itm.downloading()):
                        continue

                    progresses.append(
                        (itm.downloadProgress(), itm.remainingTime(),
                         itm.currentSpeed()))

                if not progresses:
                    return

                remaining = 0
                progress = 0
                speed = 0.0

                for progressData in progresses:
                    if progressData[1] > remaining:
                        remaining = progressData[1]
                    progress += progressData[0]
                    speed += progressData[2]
                progress = progress / len(progresses)

                if self.isVisible():
                    self.infoLabel.setText(
                        self.tr("{0}% of %n file(s) ({1}) {2}", "",
                                len(progresses)).format(
                                    progress,
                                    speedString(speed),
                                    timeString(remaining),
                                ))
                    self.setWindowTitle(self.tr("{0}% - Download Manager"))

                if Globals.isWindowsPlatform():
                    self.__taskbarButton().progress().show()
                    self.__taskbarButton().progress().setValue(progress)

        super(DownloadManager, self).timerEvent(evt)

    def __startUpdateTimer(self):
        """
        Private slot to start the update timer.
        """
        if self.activeDownloadsCount() and not self.__updateTimer.isActive():
            self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self)

    def __stopUpdateTimer(self):
        """
        Private slot to stop the update timer.
        """
        self.__updateTimer.stop()

    ###########################################################################
    ## Context menu related methods below
    ###########################################################################

    def __currentItem(self):
        """
        Private method to get a reference to the current item.
        
        @return reference to the current item (DownloadItem)
        """
        index = self.downloadsView.currentIndex()
        if index and index.isValid():
            row = index.row()
            return self.__downloads[row]

        return None

    def __contextMenuOpen(self):
        """
        Private method to open the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFile()

    def __contextMenuOpenFolder(self):
        """
        Private method to open the folder containing the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFolder()

    def __contextMenuCancel(self):
        """
        Private method to cancel the current download.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.cancelDownload()

    def __contextMenuGotoPage(self):
        """
        Private method to open the download page.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl()
            WebBrowserWindow.mainWindow().openUrl(url, "")

    def __contextMenuCopyLink(self):
        """
        Private method to copy the download link to the clipboard.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl().toDisplayString(QUrl.FullyDecoded)
            QApplication.clipboard().setText(url)

    def __contextMenuSelectAll(self):
        """
        Private method to select all downloads.
        """
        self.downloadsView.selectAll()

    def __contextMenuRemoveSelected(self):
        """
        Private method to remove the selected downloads from the list.
        """
        self.downloadsView.removeSelected()
Пример #41
0
class SpeedDial(QObject):
    """
    Class implementing the speed dial.
    
    @signal pagesChanged() emitted after the list of pages changed
    @signal speedDialSaved() emitted after the speed dial data was saved
    """
    pagesChanged = pyqtSignal()
    speedDialSaved = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(SpeedDial, self).__init__(parent)
        
        self.__regenerateScript = True
        
        self.__webPages = []
        self.__webFrames = []
        
        self.__initialScript = ""
        self.__thumbnailsDirectory = ""
        
        self.__thumbnailers = []
        
        self.__initialize()
        
        self.pagesChanged.connect(self.__pagesChanged)
        
        self.__saveTimer = AutoSaver(self, self.save)
        self.pagesChanged.connect(self.__saveTimer.changeOccurred)
    
    def addWebFrame(self, frame):
        """
        Public method to add a web frame.
        
        @param frame reference to the frame to be added (QWebFrame)
        """
        if frame not in self.__webFrames:
            self.__webFrames.append(frame)
    
    def addPage(self, url, title):
        """
        Public method to add a page for the given data.
        
        @param url URL of the page (QUrl)
        @param title title of the page (string)
        """
        if url.isEmpty():
            return
        
        from .Page import Page
        page = Page(url.toString(), title)
        self.__webPages.append(page)
        
        self.pagesChanged.emit()
    
    def removePage(self, url):
        """
        Public method to remove a page.
        
        @param url URL of the page (QUrl)
        """
        page = self.pageForUrl(url)
        if not page.url:
            return
        
        self.removeImageForUrl(page.url)
        self.__webPages.remove(page)
        
        self.pagesChanged.emit()
    
    def __imageFileName(self, url):
        """
        Private method to generate the image file name for a URL.
        
        @param url URL to generate the file name from (string)
        @return name of the image file (string)
        """
        return os.path.join(
            self.__thumbnailsDirectory,
            str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")),
                QCryptographicHash.Md5).toHex(), encoding="utf-8") + ".png")
    
    def initialScript(self):
        """
        Public method to get the 'initial' JavaScript script.
        
        @return initial JavaScript script (string)
        """
        if self.__regenerateScript:
            self.__regenerateScript = False
            self.__initialScript = ""
            
            for page in self.__webPages:
                if page.broken:
                    imgSource = "qrc:icons/brokenPage.png"
                else:
                    imgSource = self.__imageFileName(page.url)
                    if not os.path.exists(imgSource):
                        self.loadThumbnail(page.url)
                        imgSource = "qrc:icons/loading.gif"
                        
                        if not page.url:
                            imgSource = ""
                    else:
                        imgSource = QUrl.fromLocalFile(imgSource).toString()
                
                self.__initialScript += \
                    "addBox('{0}', '{1}', '{2}');\n".format(
                        page.url, Utilities.html_uencode(page.title),
                        imgSource)
        
        return self.__initialScript
    
    def getFileName(self):
        """
        Public method to get the file name of the user agents file.
        
        @return name of the user agents file (string)
        """
        return os.path.join(
            Utilities.getConfigDir(), "browser", "speedDial.xml")
    
    def __initialize(self):
        """
        Private method to initialize the speed dial.
        """
        self.__thumbnailsDirectory = os.path.join(
            Utilities.getConfigDir(), "browser", "thumbnails")
        # Create directory if it does not exist yet
        if not os.path.exists(self.__thumbnailsDirectory):
            os.makedirs(self.__thumbnailsDirectory)
        
        self.__load()
    
    def reload(self):
        """
        Public method to reload the speed dial data.
        """
        self.__load()
    
    def __load(self):
        """
        Private method to load the speed dial configuration.
        """
        allPages, pagesPerRow, speedDialSize = [], 0, 0
        
        speedDialFile = self.getFileName()
        if os.path.exists(speedDialFile):
            from .SpeedDialReader import SpeedDialReader
            reader = SpeedDialReader()
            allPages, pagesPerRow, speedDialSize = reader.read(speedDialFile)
        
        self.__pagesPerRow = pagesPerRow if pagesPerRow else 4
        self.__speedDialSize = speedDialSize if speedDialSize else 231
        
        if allPages:
            self.__webPages = allPages
            self.pagesChanged.emit()
        else:
            allPages = \
                'url:"http://eric-ide.python-projects.org/"|'\
                'title:"Eric Web Site";'\
                'url:"http://www.riverbankcomputing.com/"|'\
                'title:"PyQt Web Site";'\
                'url:"http://www.qt.io/"|title:"Qt Web Site";'\
                'url:"http://blog.qt.digia.com/"|title:"Qt Blog";'\
                'url:"http://www.python.org"|title:"Python Language Website";'\
                'url:"http://www.google.com"|title:"Google";'
            self.changed(allPages)
    
    def save(self):
        """
        Public method to save the speed dial configuration.
        """
        from .SpeedDialWriter import SpeedDialWriter
        speedDialFile = self.getFileName()
        writer = SpeedDialWriter()
        if not writer.write(speedDialFile, self.__webPages,
                            self.__pagesPerRow, self.__speedDialSize):
            E5MessageBox.critical(
                None,
                self.tr("Saving Speed Dial data"),
                self.tr(
                    """<p>Speed Dial data could not be saved to"""
                    """ <b>{0}</b></p>""").format(speedDialFile))
        else:
            self.speedDialSaved.emit()
    
    def close(self):
        """
        Public method to close the user agents manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def pageForUrl(self, url):
        """
        Public method to get the page for the given URL.
        
        @param url URL to be searched for (QUrl)
        @return page for the URL (Page)
        """
        urlString = url.toString()
        for page in self.__webPages:
            if page.url == urlString:
                return page
        
        from .Page import Page
        return Page()
    
    def urlForShortcut(self, key):
        """
        Public method to get the URL for the given shortcut key.
        
        @param key shortcut key (integer)
        @return URL for the key (QUrl)
        """
        if key < 0 or len(self.__webPages) <= key:
            return QUrl()
        
        return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8"))
    
    @pyqtSlot(str)
    def changed(self, allPages):
        """
        Public slot to react on changed pages.
        
        @param allPages string giving all pages (string)
        """
        if not allPages:
            return
        
        entries = allPages.split('";')
        self.__webPages = []
        
        from .Page import Page
        for entry in entries:
            if not entry:
                continue
            
            tmp = entry.split('"|')
            if len(tmp) == 2:
                broken = False
            elif len(tmp) == 3:
                broken = "brokenPage" in tmp[2][5:]
            else:
                continue
            
            page = Page(tmp[0][5:], tmp[1][7:], broken)
            self.__webPages.append(page)
        
        self.pagesChanged.emit()
    
    @pyqtSlot(str)
    @pyqtSlot(str, bool)
    def loadThumbnail(self, url, loadTitle=False):
        """
        Public slot to load a thumbnail of the given URL.
        
        @param url URL of the thumbnail (string)
        @param loadTitle flag indicating to get the title for the thumbnail
            from the site (boolean)
        """
        if not url:
            return
        
        from .PageThumbnailer import PageThumbnailer
        thumbnailer = PageThumbnailer(self)
        thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8")))
        thumbnailer.setLoadTitle(loadTitle)
        thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated)
        self.__thumbnailers.append(thumbnailer)
        
        thumbnailer.start()

    @pyqtSlot(str)
    def removeImageForUrl(self, url):
        """
        Public slot to remove the image for a URL.
        
        @param url URL to remove the image for (string)
        """
        fileName = self.__imageFileName(url)
        if os.path.exists(fileName):
            os.remove(fileName)

    @pyqtSlot(str, result=str)
    def urlFromUserInput(self, url):
        """
        Public slot to get the URL from user input.
        
        @param url URL entered by the user (string)
        @return sanitized URL (string)
        """
        return QUrl.fromUserInput(url).toString()
    
    @pyqtSlot(str, result=str)
    def unescapeTitle(self, title):
        """
        Public slot to unescape the titel string.
        
        @param title escaped title (string)
        @return un-escaped title (string)
        """
        return Utilities.html_udecode(title)

    @pyqtSlot(int)
    def setPagesInRow(self, count):
        """
        Public slot to set the number of pages per row.
        
        @param count number of pages per row (integer)
        """
        self.__pagesPerRow = count
        self.__saveTimer.changeOccurred()

    def pagesInRow(self):
        """
        Public method to get the number of dials per row.
        
        @return number of dials per row (integer)
        """
        return self.__pagesPerRow
    
    @pyqtSlot(int)
    def setSdSize(self, size):
        """
        Public slot to set the size of the speed dial.
        
        @param size size of the speed dial (integer)
        """
        self.__speedDialSize = size
        self.__saveTimer.changeOccurred()
    
    def sdSize(self):
        """
        Public method to get the speed dial size.
        
        @return speed dial size (integer)
        """
        return self.__speedDialSize
    
    def __thumbnailCreated(self, image):
        """
        Private slot to handle the creation of a thumbnail image.
        
        @param image thumbnail image (QPixmap)
        """
        from .PageThumbnailer import PageThumbnailer
        thumbnailer = self.sender()
        if not isinstance(thumbnailer, PageThumbnailer) or \
           thumbnailer not in self.__thumbnailers:
            return
        
        loadTitle = thumbnailer.loadTitle()
        title = thumbnailer.title()
        url = thumbnailer.url().toString()
        fileName = self.__imageFileName(url)
        
        if image.isNull():
            fileName = "qrc:icons/brokenPage.png"
            title = self.tr("Unable to load")
            loadTitle = True
            page = self.pageForUrl(thumbnailer.url())
            page.broken = True
        else:
            if not image.save(fileName):
                qWarning(
                    "SpeedDial.__thumbnailCreated: Cannot save thumbnail"
                    " to {0}".format(fileName))
            
            fileName = QUrl.fromLocalFile(fileName).toString()
        
        self.__regenerateScript = True
        
        for frame in self.__cleanFrames():
            frame.evaluateJavaScript("setImageToUrl('{0}', '{1}');".format(
                                     url, fileName))
            if loadTitle:
                frame.evaluateJavaScript("setTitleToUrl('{0}', '{1}');".format(
                                         url, Utilities.html_uencode(title)))
        
        thumbnailer.deleteLater()
        self.__thumbnailers.remove(thumbnailer)
    
    def __cleanFrames(self):
        """
        Private method to clean all frames.
        
        @return list of speed dial frames (list of QWebFrame)
        """
        frames = []
        
        for frame in self.__webFrames[:]:
            if frame.url().toString() == "eric:speeddial":
                frames.append(frame)
            else:
                self.__webFrames.remove(frame)
        
        return frames
    
    def __pagesChanged(self):
        """
        Private slot to react on a change of the pages configuration.
        """
        # update all speed dial pages
        self.__regenerateScript = True
        for frame in self.__cleanFrames():
            frame.page().triggerAction(QWebPage.Reload)
Пример #42
0
class HistoryManager(QWebHistoryInterface):
    """
    Class implementing the history manager.
    
    @signal historyCleared() emitted after the history has been cleared
    @signal historyReset() emitted after the history has been reset
    @signal entryAdded(HistoryEntry) emitted after a history entry has been
        added
    @signal entryRemoved(HistoryEntry) emitted after a history entry has been
        removed
    @signal entryUpdated(int) emitted after a history entry has been updated
    @signal historySaved() emitted after the history was saved
    """
    historyCleared = pyqtSignal()
    historyReset = pyqtSignal()
    entryAdded = pyqtSignal(HistoryEntry)
    entryRemoved = pyqtSignal(HistoryEntry)
    entryUpdated = pyqtSignal(int)
    historySaved = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(HistoryManager, self).__init__(parent)

        self.__saveTimer = AutoSaver(self, self.save)
        self.__daysToExpire = Preferences.getHelp("HistoryLimit")
        self.__history = []
        self.__lastSavedUrl = ""

        self.__expiredTimer = QTimer(self)
        self.__expiredTimer.setSingleShot(True)
        self.__expiredTimer.timeout.connect(self.__checkForExpired)

        self.__frequencyTimer = QTimer(self)
        self.__frequencyTimer.setSingleShot(True)
        self.__frequencyTimer.timeout.connect(self.__refreshFrequencies)

        self.entryAdded.connect(self.__saveTimer.changeOccurred)
        self.entryRemoved.connect(self.__saveTimer.changeOccurred)

        self.__load()

        from .HistoryModel import HistoryModel
        from .HistoryFilterModel import HistoryFilterModel
        from .HistoryTreeModel import HistoryTreeModel

        self.__historyModel = HistoryModel(self, self)
        self.__historyFilterModel = \
            HistoryFilterModel(self.__historyModel, self)
        self.__historyTreeModel = \
            HistoryTreeModel(self.__historyFilterModel, self)

        super(HistoryManager, self).setDefaultInterface(self)
        self.__startFrequencyTimer()

    def close(self):
        """
        Public method to close the history manager.
        """
        # remove history items on application exit
        if self.__daysToExpire == -2:
            self.clear()
        self.__saveTimer.saveIfNeccessary()

    def history(self):
        """
        Public method to return the history.
        
        @return reference to the list of history entries (list of HistoryEntry)
        """
        return self.__history[:]

    def setHistory(self, history, loadedAndSorted=False):
        """
        Public method to set a new history.
        
        @param history reference to the list of history entries to be set
            (list of HistoryEntry)
        @param loadedAndSorted flag indicating that the list is sorted
            (boolean)
        """
        self.__history = history[:]
        if not loadedAndSorted:
            self.__history.sort()

        self.__checkForExpired()

        if loadedAndSorted:
            try:
                self.__lastSavedUrl = self.__history[0].url
            except IndexError:
                self.__lastSavedUrl = ""
        else:
            self.__lastSavedUrl = ""
            self.__saveTimer.changeOccurred()
        self.historyReset.emit()

    def historyContains(self, url):
        """
        Public method to check the history for an entry.
        
        @param url URL to check for (string)
        @return flag indicating success (boolean)
        """
        return self.__historyFilterModel.historyContains(url)

    def _addHistoryEntry(self, itm):
        """
        Protected method to add a history item.
        
        @param itm reference to the history item to add (HistoryEntry)
        """
        globalSettings = QWebSettings.globalSettings()
        if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
            return

        self.__history.insert(0, itm)
        self.entryAdded.emit(itm)
        if len(self.__history) == 1:
            self.__checkForExpired()

    def _removeHistoryEntry(self, itm):
        """
        Protected method to remove a history item.
        
        @param itm reference to the history item to remove (HistoryEntry)
        """
        self.__lastSavedUrl = ""
        self.__history.remove(itm)
        self.entryRemoved.emit(itm)

    def addHistoryEntry(self, url):
        """
        Public method to add a history entry.
        
        @param url URL to be added (string)
        """
        cleanurl = QUrl(url)
        if cleanurl.scheme() not in ["eric", "about"]:
            if cleanurl.password():
                # don't save the password in the history
                cleanurl.setPassword("")
            if cleanurl.host():
                cleanurl.setHost(cleanurl.host().lower())
            itm = HistoryEntry(cleanurl.toString(),
                               QDateTime.currentDateTime())
            self._addHistoryEntry(itm)

    def updateHistoryEntry(self, url, title):
        """
        Public method to update a history entry.
        
        @param url URL of the entry to update (string)
        @param title title of the entry to update (string)
        """
        cleanurl = QUrl(url)
        if cleanurl.scheme() not in ["eric", "about"]:
            for index in range(len(self.__history)):
                if url == self.__history[index].url:
                    self.__history[index].title = title
                    self.__saveTimer.changeOccurred()
                    if not self.__lastSavedUrl:
                        self.__lastSavedUrl = self.__history[index].url
                    self.entryUpdated.emit(index)
                    break

    def removeHistoryEntry(self, url, title=""):
        """
        Public method to remove a history entry.
        
        @param url URL of the entry to remove (QUrl)
        @param title title of the entry to remove (string)
        """
        for index in range(len(self.__history)):
            if url == QUrl(self.__history[index].url) and \
               (not title or title == self.__history[index].title):
                self._removeHistoryEntry(self.__history[index])
                break

    def historyModel(self):
        """
        Public method to get a reference to the history model.
        
        @return reference to the history model (HistoryModel)
        """
        return self.__historyModel

    def historyFilterModel(self):
        """
        Public method to get a reference to the history filter model.
        
        @return reference to the history filter model (HistoryFilterModel)
        """
        return self.__historyFilterModel

    def historyTreeModel(self):
        """
        Public method to get a reference to the history tree model.
        
        @return reference to the history tree model (HistoryTreeModel)
        """
        return self.__historyTreeModel

    def __checkForExpired(self):
        """
        Private slot to check entries for expiration.
        """
        if self.__daysToExpire < 0 or len(self.__history) == 0:
            return

        now = QDateTime.currentDateTime()
        nextTimeout = 0

        while self.__history:
            checkForExpired = QDateTime(self.__history[-1].dateTime)
            checkForExpired.setDate(checkForExpired.date().addDays(
                self.__daysToExpire))
            if now.daysTo(checkForExpired) > 7:
                nextTimeout = 7 * 86400
            else:
                nextTimeout = now.secsTo(checkForExpired)
            if nextTimeout > 0:
                break

            itm = self.__history.pop(-1)
            self.__lastSavedUrl = ""
            self.entryRemoved.emit(itm)
        self.__saveTimer.saveIfNeccessary()

        if nextTimeout > 0:
            self.__expiredTimer.start(nextTimeout * 1000)

    def daysToExpire(self):
        """
        Public method to get the days for entry expiration.
        
        @return days for entry expiration (integer)
        """
        return self.__daysToExpire

    def setDaysToExpire(self, limit):
        """
        Public method to set the days for entry expiration.
        
        @param limit days for entry expiration (integer)
        """
        if self.__daysToExpire == limit:
            return

        self.__daysToExpire = limit
        self.__checkForExpired()
        self.__saveTimer.changeOccurred()

    def preferencesChanged(self):
        """
        Public method to indicate a change of preferences.
        """
        self.setDaysToExpire(Preferences.getHelp("HistoryLimit"))

    @pyqtSlot()
    def clear(self, period=0):
        """
        Public slot to clear the complete history.
        
        @param period history period in milliseconds to be cleared (integer)
        """
        if period == 0:
            self.__history = []
            self.historyReset.emit()
        else:
            breakMS = QDateTime.currentMSecsSinceEpoch() - period
            while self.__history and \
                (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() >
                 breakMS):
                itm = self.__history.pop(0)
                self.entryRemoved.emit(itm)
        self.__lastSavedUrl = ""
        self.__saveTimer.changeOccurred()
        self.__saveTimer.saveIfNeccessary()
        self.historyCleared.emit()

    def getFileName(self):
        """
        Public method to get the file name of the history file.
        
        @return name of the history file (string)
        """
        return os.path.join(Utilities.getConfigDir(), "browser", "history")

    def reload(self):
        """
        Public method to reload the history.
        """
        self.__load()

    def __load(self):
        """
        Private method to load the saved history entries from disk.
        """
        historyFile = QFile(self.getFileName())
        if not historyFile.exists():
            return
        if not historyFile.open(QIODevice.ReadOnly):
            E5MessageBox.warning(
                None, self.tr("Loading History"),
                self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>"""
                        """Reason: {1}</p>""").format(
                            historyFile.fileName, historyFile.errorString()))
            return

        history = []

        # double check, that the history file is sorted as it is read
        needToSort = False
        lastInsertedItem = HistoryEntry()
        data = QByteArray(historyFile.readAll())
        stream = QDataStream(data, QIODevice.ReadOnly)
        stream.setVersion(QDataStream.Qt_4_6)
        while not stream.atEnd():
            ver = stream.readUInt32()
            if ver != HISTORY_VERSION:
                continue
            itm = HistoryEntry()
            itm.url = Utilities.readStringFromStream(stream)
            stream >> itm.dateTime
            itm.title = Utilities.readStringFromStream(stream)

            if not itm.dateTime.isValid():
                continue

            if itm == lastInsertedItem:
                if not lastInsertedItem.title and len(history) > 0:
                    history[0].title = itm.title
                continue

            if not needToSort and history and lastInsertedItem < itm:
                needToSort = True

            history.insert(0, itm)
            lastInsertedItem = itm
        historyFile.close()

        if needToSort:
            history.sort()

        self.setHistory(history, True)

        # if the history had to be sorted, rewrite the history sorted
        if needToSort:
            self.__lastSavedUrl = ""
            self.__saveTimer.changeOccurred()

    def save(self):
        """
        Public slot to save the history entries to disk.
        """
        historyFile = QFile(self.getFileName())
        if not historyFile.exists():
            self.__lastSavedUrl = ""

        saveAll = self.__lastSavedUrl == ""
        first = len(self.__history) - 1
        if not saveAll:
            # find the first one to save
            for index in range(len(self.__history)):
                if self.__history[index].url == self.__lastSavedUrl:
                    first = index - 1
                    break
        if first == len(self.__history) - 1:
            saveAll = True

        if saveAll:
            # use a temporary file when saving everything
            f = QTemporaryFile()
            f.setAutoRemove(False)
            opened = f.open()
        else:
            f = historyFile
            opened = f.open(QIODevice.Append)

        if not opened:
            E5MessageBox.warning(
                None, self.tr("Saving History"),
                self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>"""
                        """Reason: {1}</p>""").format(f.fileName(),
                                                      f.errorString()))
            return

        for index in range(first, -1, -1):
            data = QByteArray()
            stream = QDataStream(data, QIODevice.WriteOnly)
            stream.setVersion(QDataStream.Qt_4_6)
            itm = self.__history[index]
            stream.writeUInt32(HISTORY_VERSION)
            stream.writeString(itm.url.encode("utf-8"))
            stream << itm.dateTime
            stream.writeString(itm.title.encode('utf-8'))
            f.write(data)

        f.close()
        if saveAll:
            if historyFile.exists() and not historyFile.remove():
                E5MessageBox.warning(
                    None, self.tr("Saving History"),
                    self.tr(
                        """<p>Error removing old history file <b>{0}</b>."""
                        """<br/>Reason: {1}</p>""").format(
                            historyFile.fileName(), historyFile.errorString()))
            if not f.copy(historyFile.fileName()):
                E5MessageBox.warning(
                    None, self.tr("Saving History"),
                    self.tr(
                        """<p>Error moving new history file over old one """
                        """(<b>{0}</b>).<br/>Reason: {1}</p>""").format(
                            historyFile.fileName(), f.errorString()))
        self.historySaved.emit()
        try:
            self.__lastSavedUrl = self.__history[0].url
        except IndexError:
            self.__lastSavedUrl = ""

    def __refreshFrequencies(self):
        """
        Private slot to recalculate the refresh frequencies.
        """
        self.__historyFilterModel.recalculateFrequencies()
        self.__startFrequencyTimer()

    def __startFrequencyTimer(self):
        """
        Private method to start the timer to recalculate the frequencies.
        """
        tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0))
        self.__frequencyTimer.start(
            QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
Пример #43
0
class UserAgentManager(QObject):
    """
    Class implementing a user agent manager.
    
    @signal changed() emitted to indicate a change
    @signal userAgentSettingsSaved() emitted after the user agent settings
        were saved
    """
    changed = pyqtSignal()
    userAgentSettingsSaved = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(UserAgentManager, self).__init__(parent)
        
        self.__agents = {}
        # dictionary with agent strings indexed by host name
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.changed.connect(self.__saveTimer.changeOccurred)
    
    def getFileName(self):
        """
        Public method to get the file name of the user agents file.
        
        @return name of the user agents file (string)
        """
        return os.path.join(
            Utilities.getConfigDir(), "browser", "userAgentSettings.xml")
    
    def save(self):
        """
        Public slot to save the user agent entries to disk.
        """
        if not self.__loaded:
            return
        
        from .UserAgentWriter import UserAgentWriter
        agentFile = self.getFileName()
        writer = UserAgentWriter()
        if not writer.write(agentFile, self.__agents):
            E5MessageBox.critical(
                None,
                self.tr("Saving user agent data"),
                self.tr(
                    """<p>User agent data could not be saved to"""
                    """ <b>{0}</b></p>""").format(agentFile))
        else:
            self.userAgentSettingsSaved.emit()
    
    def __load(self):
        """
        Private method to load the saved user agent settings.
        """
        agentFile = self.getFileName()
        if not os.path.exists(agentFile):
            self.__loadNonXml(os.path.splitext(agentFile)[0])
        else:
            from .UserAgentReader import UserAgentReader
            reader = UserAgentReader()
            self.__agents = reader.read(agentFile)
            if reader.error() != QXmlStreamReader.NoError:
                E5MessageBox.warning(
                    None,
                    self.tr("Loading user agent data"),
                    self.tr("""Error when loading user agent data on"""
                            """ line {0}, column {1}:\n{2}""")
                    .format(reader.lineNumber(),
                            reader.columnNumber(),
                            reader.errorString()))
        
        self.__loaded = True
    
    def __loadNonXml(self, agentFile):
        """
        Private method to load non-XML user agent files.
        
        This method is to convert from the old, non-XML format to the new
        XML based format.
        
        @param agentFile name of the non-XML user agent file (string)
        """
        if os.path.exists(agentFile):
            try:
                f = open(agentFile, "r", encoding="utf-8")
                lines = f.read()
                f.close()
            except IOError as err:
                E5MessageBox.critical(
                    None,
                    self.tr("Loading user agent data"),
                    self.tr("""<p>User agent data could not be loaded """
                            """from <b>{0}</b></p>"""
                            """<p>Reason: {1}</p>""")
                    .format(agentFile, str(err)))
                return
            
            for line in lines.splitlines():
                if not line or \
                   line.startswith("#") or \
                   "@@" not in line:
                    continue
                
                host, agent = line.split("@@", 1)
                self.__agents[host] = agent
            
            os.remove(agentFile)
        
        self.__loaded = True
        
        # this does the conversion
        self.save()
    
    def reload(self):
        """
        Public method to reload the user agent settings.
        """
        if not self.__loaded:
            return
        
        self.__agents = {}
        self.__load()
    
    def close(self):
        """
        Public method to close the user agents manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def removeUserAgent(self, host):
        """
        Public method to remove a user agent entry.
        
        @param host host name (string)
        """
        if host in self.__agents:
            del self.__agents[host]
            self.changed.emit()
    
    def allHostNames(self):
        """
        Public method to get a list of all host names we a user agent setting
        for.
        
        @return sorted list of all host names (list of strings)
        """
        if not self.__loaded:
            self.__load()
        
        return sorted(self.__agents.keys())
    
    def hostsCount(self):
        """
        Public method to get the number of available user agent settings.
        
        @return number of user agent settings (integer)
        """
        if not self.__loaded:
            self.__load()
        
        return len(self.__agents)
    
    def userAgent(self, host):
        """
        Public method to get the user agent setting for a host.
        
        @param host host name (string)
        @return user agent string (string)
        """
        if not self.__loaded:
            self.__load()
        
        if host not in self.__agents:
            return ""
        
        return self.__agents[host]
    
    def setUserAgent(self, host, agent):
        """
        Public method to set the user agent string for a host.
        
        @param host host name (string)
        @param agent user agent string (string)
        """
        if host != "" and agent != "":
            self.__agents[host] = agent
            self.changed.emit()
    
    def userAgentForUrl(self, url):
        """
        Public method to determine the user agent for the given URL.
        
        @param url URL to determine user agent for (QUrl)
        @return user agent string (string)
        """
        if url.isValid():
            host = url.host()
            return self.userAgent(host)
        
        return ""
    
    def setUserAgentForUrl(self, url, agent):
        """
        Public method to set the user agent string for an URL.
        
        @param url URL to register user agent setting for (QUrl)
        @param agent new current user agent string (string)
        """
        if url.isValid():
            host = url.host()
            self.setUserAgent(host, agent)
Пример #44
0
class TaskViewer(QTreeWidget):
    """
    Class implementing the task viewer.
    
    @signal displayFile(str, int) emitted to go to a file task
    """
    displayFile = pyqtSignal(str, int)

    def __init__(self, parent, project):
        """
        Constructor
        
        @param parent the parent (QWidget)
        @param project reference to the project object
        """
        super(TaskViewer, self).__init__(parent)

        self.setRootIsDecorated(False)
        self.setItemsExpandable(False)
        self.setSortingEnabled(True)

        self.__headerItem = QTreeWidgetItem([
            "", "",
            self.tr("Summary"),
            self.tr("Filename"),
            self.tr("Line"), ""
        ])
        self.__headerItem.setIcon(0,
                                  UI.PixmapCache.getIcon("taskCompleted.png"))
        self.__headerItem.setIcon(1,
                                  UI.PixmapCache.getIcon("taskPriority.png"))
        self.setHeaderItem(self.__headerItem)

        self.header().setSortIndicator(2, Qt.AscendingOrder)
        self.__resizeColumns()

        self.tasks = []
        self.copyTask = None
        self.projectOpen = False
        self.project = project
        self.projectTasksScanFilter = ""

        from .TaskFilter import TaskFilter
        self.taskFilter = TaskFilter()
        self.taskFilter.setActive(False)

        self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks)

        self.__projectTasksMenu = QMenu(self.tr("P&roject Tasks"), self)
        self.__projectTasksMenu.addAction(self.tr("&Regenerate project tasks"),
                                          self.__regenerateProjectTasks)
        self.__projectTasksMenu.addSeparator()
        self.__projectTasksMenu.addAction(
            self.tr("&Configure scan options"),
            self.__configureProjectTasksScanOptions)

        self.__menu = QMenu(self)
        self.__menu.addAction(self.tr("&New Task..."), self.__newTask)
        self.__menu.addSeparator()
        self.projectTasksMenuItem = self.__menu.addMenu(
            self.__projectTasksMenu)
        self.__menu.addSeparator()
        self.gotoItem = self.__menu.addAction(self.tr("&Go To"),
                                              self.__goToTask)
        self.__menu.addSeparator()
        self.copyItem = self.__menu.addAction(self.tr("&Copy"),
                                              self.__copyTask)
        self.pasteItem = self.__menu.addAction(self.tr("&Paste"),
                                               self.__pasteTask)
        self.deleteItem = self.__menu.addAction(self.tr("&Delete"),
                                                self.__deleteTask)
        self.__menu.addSeparator()
        self.markCompletedItem = self.__menu.addAction(
            self.tr("&Mark Completed"), self.__markCompleted)
        self.__menu.addAction(self.tr("Delete Completed &Tasks"),
                              self.__deleteCompleted)
        self.__menu.addSeparator()
        self.__menu.addAction(self.tr("P&roperties..."),
                              self.__editTaskProperties)
        self.__menu.addSeparator()
        self.__menuFilteredAct = self.__menu.addAction(
            self.tr("&Filtered display"))
        self.__menuFilteredAct.setCheckable(True)
        self.__menuFilteredAct.setChecked(False)
        self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter)
        self.__menu.addAction(self.tr("Filter c&onfiguration..."),
                              self.__configureFilter)
        self.__menu.addSeparator()
        self.__menu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns)
        self.__menu.addSeparator()
        self.__menu.addAction(self.tr("Configure..."), self.__configure)

        self.__backMenu = QMenu(self)
        self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask)
        self.__backMenu.addSeparator()
        self.backProjectTasksMenuItem = self.__backMenu.addMenu(
            self.__projectTasksMenu)
        self.__backMenu.addSeparator()
        self.backPasteItem = self.__backMenu.addAction(self.tr("&Paste"),
                                                       self.__pasteTask)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(self.tr("Delete Completed &Tasks"),
                                  self.__deleteCompleted)
        self.__backMenu.addSeparator()
        self.__backMenuFilteredAct = self.__backMenu.addAction(
            self.tr("&Filtered display"))
        self.__backMenuFilteredAct.setCheckable(True)
        self.__backMenuFilteredAct.setChecked(False)
        self.__backMenuFilteredAct.triggered[bool].connect(
            self.__activateFilter)
        self.__backMenu.addAction(self.tr("Filter c&onfiguration..."),
                                  self.__configureFilter)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(self.tr("Resi&ze columns"),
                                  self.__resizeColumns)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(self.tr("Configure..."), self.__configure)

        self.__activating = False

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.__showContextMenu)
        self.itemActivated.connect(self.__taskItemActivated)

        self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))

    def __resort(self):
        """
        Private method to resort the tree.
        """
        self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder())

    def __resizeColumns(self):
        """
        Private method to resize the list columns.
        """
        self.header().resizeSections(QHeaderView.ResizeToContents)
        self.header().setStretchLastSection(True)

    def __refreshDisplay(self):
        """
        Private method to refresh the display.
        """
        for task in self.tasks:
            task.setHidden(not self.taskFilter.showTask(task))
        self.__resort()
        self.__resizeColumns()

    def __taskItemActivated(self, itm, col):
        """
        Private slot to handle the activation of an item.
        
        @param itm reference to the activated item (QTreeWidgetItem)
        @param col column the item was activated in (integer)
        """
        if not self.__activating:
            self.__activating = True
            fn = itm.getFilename()
            if fn:
                self.displayFile.emit(fn, itm.getLineno())
            else:
                self.__editTaskProperties()
            self.__activating = False

    def __showContextMenu(self, coord):
        """
        Private slot to show the context menu of the list.
        
        @param coord the position of the mouse pointer (QPoint)
        """
        itm = self.itemAt(coord)
        coord = self.mapToGlobal(coord)
        if itm is None:
            self.backProjectTasksMenuItem.setEnabled(self.projectOpen)
            if self.copyTask:
                self.backPasteItem.setEnabled(True)
            else:
                self.backPasteItem.setEnabled(False)
            self.__backMenu.popup(coord)
        else:
            self.projectTasksMenuItem.setEnabled(self.projectOpen)
            if itm.getFilename():
                self.gotoItem.setEnabled(True)
                self.deleteItem.setEnabled(True)
                self.markCompletedItem.setEnabled(False)
                self.copyItem.setEnabled(False)
            else:
                self.gotoItem.setEnabled(False)
                self.deleteItem.setEnabled(True)
                self.markCompletedItem.setEnabled(True)
                self.copyItem.setEnabled(True)
            if self.copyTask:
                self.pasteItem.setEnabled(True)
            else:
                self.pasteItem.setEnabled(False)

            self.__menu.popup(coord)

    def setProjectOpen(self, o=False):
        """
        Public slot to set the project status.
        
        @param o flag indicating the project status
        """
        self.projectOpen = o

    def addTask(self,
                summary,
                priority=1,
                filename="",
                lineno=0,
                completed=False,
                _time=0,
                isProjectTask=False,
                taskType=Task.TypeTodo,
                description=""):
        """
        Public slot to add a task.
        
        @param summary summary text of the task (string)
        @param priority priority of the task (0=high, 1=normal, 2=low)
        @param filename filename containing the task (string)
        @param lineno line number containing the task (integer)
        @param completed flag indicating completion status (boolean)
        @param _time creation time of the task (float, if 0 use current time)
        @param isProjectTask flag indicating a task related to the current
            project (boolean)
        @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo,
            Task.TypeWarning, Task.TypeNote)
        @param description explanatory text of the task (string)
        """
        task = Task(summary, priority, filename, lineno, completed, _time,
                    isProjectTask, taskType, self.project, description)
        self.tasks.append(task)
        self.addTopLevelItem(task)
        task.setHidden(not self.taskFilter.showTask(task))
        self.__resort()
        self.__resizeColumns()

        if isProjectTask:
            self.__projectTasksSaveTimer.changeOccurred()

    def addFileTask(self,
                    summary,
                    filename,
                    lineno,
                    taskType=Task.TypeTodo,
                    description=""):
        """
        Public slot to add a file related task.
        
        @param summary summary text of the task (string)
        @param filename filename containing the task (string)
        @param lineno line number containing the task (integer)
        @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo,
            Task.TypeWarning, Task.TypeNote)
        @param description explanatory text of the task (string)
        """
        self.addTask(
            summary,
            filename=filename,
            lineno=lineno,
            isProjectTask=(self.project
                           and self.project.isProjectSource(filename)),
            taskType=taskType,
            description=description)

    def getProjectTasks(self):
        """
        Public method to retrieve all project related tasks.
        
        @return copy of tasks (list of Task)
        """
        tasks = [task for task in self.tasks if task.isProjectTask()]
        return tasks[:]

    def getGlobalTasks(self):
        """
        Public method to retrieve all non project related tasks.
        
        @return copy of tasks (list of Task)
        """
        tasks = [task for task in self.tasks if not task.isProjectTask()]
        return tasks[:]

    def clearTasks(self):
        """
        Public slot to clear all tasks from display.
        """
        self.tasks = []
        self.clear()

    def clearProjectTasks(self, fileOnly=False):
        """
        Public slot to clear project related tasks.
        
        @keyparam fileOnly flag indicating to clear only file related
            project tasks (boolean)
        """
        for task in self.tasks[:]:
            if (fileOnly and task.isProjectFileTask()) or \
               (not fileOnly and task.isProjectTask()):
                if self.copyTask == task:
                    self.copyTask = None
                index = self.indexOfTopLevelItem(task)
                self.takeTopLevelItem(index)
                self.tasks.remove(task)
                del task

    def clearFileTasks(self, filename, conditionally=False):
        """
        Public slot to clear all tasks related to a file.
        
        @param filename name of the file (string)
        @param conditionally flag indicating to clear the tasks of the file
            checking some conditions (boolean)
        """
        if conditionally:
            if self.project and self.project.isProjectSource(filename):
                # project related tasks will not be cleared
                return
            if not Preferences.getTasks("ClearOnFileClose"):
                return
        for task in self.tasks[:]:
            if task.getFilename() == filename:
                if self.copyTask == task:
                    self.copyTask = None
                index = self.indexOfTopLevelItem(task)
                self.takeTopLevelItem(index)
                self.tasks.remove(task)
                if task.isProjectTask:
                    self.__projectTasksSaveTimer.changeOccurred()
                del task

    def __editTaskProperties(self):
        """
        Private slot to handle the "Properties" context menu entry.
        """
        from .TaskPropertiesDialog import TaskPropertiesDialog
        task = self.currentItem()
        dlg = TaskPropertiesDialog(task, self, self.projectOpen)
        ro = task.getFilename() != ""
        if ro:
            dlg.setReadOnly()
        if dlg.exec_() == QDialog.Accepted and not ro:
            data = dlg.getData()
            task.setSummary(data[0])
            task.setPriority(data[1])
            task.setCompleted(data[2])
            task.setProjectTask(data[3])
            task.setDescription(data[4])
            self.__projectTasksSaveTimer.changeOccurred()

    def __newTask(self):
        """
        Private slot to handle the "New Task" context menu entry.
        """
        from .TaskPropertiesDialog import TaskPropertiesDialog
        dlg = TaskPropertiesDialog(None, self, self.projectOpen)
        if dlg.exec_() == QDialog.Accepted:
            data = dlg.getData()
            self.addTask(data[0],
                         data[1],
                         completed=data[2],
                         isProjectTask=data[3],
                         description=data[4])

    def __markCompleted(self):
        """
        Private slot to handle the "Mark Completed" context menu entry.
        """
        task = self.currentItem()
        task.setCompleted(True)

    def __deleteCompleted(self):
        """
        Private slot to handle the "Delete Completed Tasks" context menu entry.
        """
        for task in self.tasks[:]:
            if task.isCompleted():
                if self.copyTask == task:
                    self.copyTask = None
                index = self.indexOfTopLevelItem(task)
                self.takeTopLevelItem(index)
                self.tasks.remove(task)
                if task.isProjectTask:
                    self.__projectTasksSaveTimer.changeOccurred()
                del task
        ci = self.currentItem()
        if ci:
            ind = self.indexFromItem(ci, self.currentColumn())
            self.scrollTo(ind, QAbstractItemView.PositionAtCenter)

    def __copyTask(self):
        """
        Private slot to handle the "Copy" context menu entry.
        """
        task = self.currentItem()
        self.copyTask = task

    def __pasteTask(self):
        """
        Private slot to handle the "Paste" context menu entry.
        """
        if self.copyTask:
            self.addTask(self.copyTask.summary,
                         priority=self.copyTask.priority,
                         completed=self.copyTask.completed,
                         description=self.copyTask.description,
                         isProjectTask=self.copyTask._isProjectTask)

    def __deleteTask(self):
        """
        Private slot to handle the "Delete Task" context menu entry.
        """
        task = self.currentItem()
        if self.copyTask == task:
            self.copyTask = None
        index = self.indexOfTopLevelItem(task)
        self.takeTopLevelItem(index)
        self.tasks.remove(task)
        if task.isProjectTask:
            self.__projectTasksSaveTimer.changeOccurred()
        del task
        ci = self.currentItem()
        if ci:
            ind = self.indexFromItem(ci, self.currentColumn())
            self.scrollTo(ind, QAbstractItemView.PositionAtCenter)

    def __goToTask(self):
        """
        Private slot to handle the "Go To" context menu entry.
        """
        task = self.currentItem()
        self.displayFile.emit(task.getFilename(), task.getLineno())

    def handlePreferencesChanged(self):
        """
        Public slot to react to changes of the preferences.
        """
        for task in self.tasks:
            task.colorizeTask()

    def __activateFilter(self, on):
        """
        Private slot to handle the "Filtered display" context menu entry.
        
        @param on flag indicating the filter state (boolean)
        """
        if on and not self.taskFilter.hasActiveFilter():
            res = E5MessageBox.yesNo(
                self,
                self.tr("Activate task filter"),
                self.tr("""The task filter doesn't have any active filters."""
                        """ Do you want to configure the filter settings?"""),
                yesDefault=True)
            if not res:
                on = False
            else:
                self.__configureFilter()
                on = self.taskFilter.hasActiveFilter()

        self.taskFilter.setActive(on)
        self.__menuFilteredAct.setChecked(on)
        self.__backMenuFilteredAct.setChecked(on)
        self.__refreshDisplay()

    def __configureFilter(self):
        """
        Private slot to handle the "Configure filter" context menu entry.
        """
        from .TaskFilterConfigDialog import TaskFilterConfigDialog
        dlg = TaskFilterConfigDialog(self.taskFilter)
        if dlg.exec_() == QDialog.Accepted:
            dlg.configureTaskFilter(self.taskFilter)
            self.__refreshDisplay()

    def __configureProjectTasksScanOptions(self):
        """
        Private slot to configure scan options for project tasks.
        """
        filter, ok = QInputDialog.getText(
            self, self.tr("Scan Filter Patterns"),
            self.tr("Enter filename patterns of files"
                    " to be excluded separated by a comma:"), QLineEdit.Normal,
            self.projectTasksScanFilter)
        if ok:
            self.projectTasksScanFilter = filter

    def __regenerateProjectTasks(self):
        """
        Private slot to handle the "Regenerated project tasks" context menu
        entry.
        """
        markers = {
            Task.TypeWarning:
            Preferences.getTasks("TasksWarningMarkers").split(),
            Task.TypeNote: Preferences.getTasks("TasksNoteMarkers").split(),
            Task.TypeTodo: Preferences.getTasks("TasksTodoMarkers").split(),
            Task.TypeFixme: Preferences.getTasks("TasksFixmeMarkers").split(),
        }
        files = self.project.pdata["SOURCES"]

        # apply file filter
        filterList = [
            f.strip() for f in self.projectTasksScanFilter.split(",")
            if f.strip()
        ]
        if filterList:
            for filter in filterList:
                files = [f for f in files if not fnmatch.fnmatch(f, filter)]

        # remove all project tasks
        self.clearProjectTasks(fileOnly=True)

        # now process them
        progress = E5ProgressDialog(self.tr("Extracting project tasks..."),
                                    self.tr("Abort"), 0, len(files),
                                    self.tr("%v/%m Files"))
        progress.setMinimumDuration(0)
        progress.setWindowTitle(self.tr("Tasks"))
        count = 0

        for file in files:
            progress.setLabelText(
                self.tr("Extracting project tasks...\n{0}").format(file))
            progress.setValue(count)
            QApplication.processEvents()
            if progress.wasCanceled():
                break

            fn = os.path.join(self.project.ppath, file)
            # read the file and split it into textlines
            try:
                text, encoding = Utilities.readEncodedFile(fn)
                lines = text.splitlines()
            except (UnicodeError, IOError):
                count += 1
                progress.setValue(count)
                continue

            # now search tasks and record them
            lineIndex = 0
            for line in lines:
                lineIndex += 1
                shouldBreak = False

                for taskType, taskMarkers in markers.items():
                    for taskMarker in taskMarkers:
                        index = line.find(taskMarker)
                        if index > -1:
                            task = line[index:]
                            self.addFileTask(task, fn, lineIndex, taskType)
                            shouldBreak = True
                            break
                    if shouldBreak:
                        break

            count += 1

        progress.setValue(len(files))

    def __configure(self):
        """
        Private method to open the configuration dialog.
        """
        e5App().getObject("UserInterface").showPreferences("tasksPage")

    def saveProjectTasks(self):
        """
        Public method to write the project tasks.
        """
        if self.projectOpen and Preferences.getTasks("TasksProjectAutoSave"):
            self.project.writeTasks()
Пример #45
0
class OpenSearchManager(QObject):
    """
    Class implementing a manager for open search engines.
    
    @signal changed() emitted to indicate a change
    @signal currentEngineChanged() emitted to indicate a change of
            the current search engine
    """
    changed = pyqtSignal()
    currentEngineChanged = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        if parent is None:
            parent = e5App()
        super(OpenSearchManager, self).__init__(parent)

        self.__replies = []
        self.__engines = {}
        self.__keywords = {}
        self.__current = ""
        self.__loading = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.changed.connect(self.__saveTimer.changeOccurred)

        self.load()

    def close(self):
        """
        Public method to close the open search engines manager.
        """
        self.__saveTimer.saveIfNeccessary()

    def currentEngineName(self):
        """
        Public method to get the name of the current search engine.
        
        @return name of the current search engine (string)
        """
        return self.__current

    def setCurrentEngineName(self, name):
        """
        Public method to set the current engine by name.
        
        @param name name of the new current engine (string)
        """
        if name not in self.__engines:
            return

        self.__current = name
        self.currentEngineChanged.emit()
        self.changed.emit()

    def currentEngine(self):
        """
        Public method to get a reference to the current engine.
        
        @return reference to the current engine (OpenSearchEngine)
        """
        if not self.__current or self.__current not in self.__engines:
            return None

        return self.__engines[self.__current]

    def setCurrentEngine(self, engine):
        """
        Public method to set the current engine.
        
        @param engine reference to the new current engine (OpenSearchEngine)
        """
        if engine is None:
            return

        for engineName in self.__engines:
            if self.__engines[engineName] == engine:
                self.setCurrentEngineName(engineName)
                break

    def engine(self, name):
        """
        Public method to get a reference to the named engine.
        
        @param name name of the engine (string)
        @return reference to the engine (OpenSearchEngine)
        """
        if name not in self.__engines:
            return None

        return self.__engines[name]

    def engineExists(self, name):
        """
        Public method to check, if an engine exists.
        
        @param name name of the engine (string)
        @return flag indicating an existing engine (boolean)
        """
        return name in self.__engines

    def allEnginesNames(self):
        """
        Public method to get a list of all engine names.
        
        @return sorted list of all engine names (list of strings)
        """
        return sorted(self.__engines.keys())

    def enginesCount(self):
        """
        Public method to get the number of available engines.
        
        @return number of engines (integer)
        """
        return len(self.__engines)

    def addEngine(self, engine):
        """
        Public method to add a new search engine.
        
        @param engine URL of the engine definition file (QUrl) or
            name of a file containing the engine definition (string)
            or reference to an engine object (OpenSearchEngine)
        @return flag indicating success (boolean)
        """
        from .OpenSearchEngine import OpenSearchEngine
        if isinstance(engine, QUrl):
            return self.__addEngineByUrl(engine)
        elif isinstance(engine, OpenSearchEngine):
            return self.__addEngineByEngine(engine)
        else:
            return self.__addEngineByFile(engine)

    def __addEngineByUrl(self, url):
        """
        Private method to add a new search engine given its URL.
        
        @param url URL of the engine definition file (QUrl)
        @return flag indicating success (boolean)
        """
        if not url.isValid():
            return

        from Helpviewer.HelpWindow import HelpWindow

        reply = HelpWindow.networkAccessManager().get(QNetworkRequest(url))
        reply.finished.connect(self.__engineFromUrlAvailable)
        reply.setParent(self)
        self.__replies.append(reply)

        return True

    def __addEngineByFile(self, filename):
        """
        Private method to add a new search engine given a filename.
        
        @param filename name of a file containing the engine definition
            (string)
        @return flag indicating success (boolean)
        """
        file_ = QFile(filename)
        if not file_.open(QIODevice.ReadOnly):
            return False

        from .OpenSearchReader import OpenSearchReader
        reader = OpenSearchReader()
        engine = reader.read(file_)

        if not self.__addEngineByEngine(engine):
            return False

        return True

    def __addEngineByEngine(self, engine):
        """
        Private method to add a new search engine given a reference to an
        engine.
        
        @param engine reference to an engine object (OpenSearchEngine)
        @return flag indicating success (boolean)
        """
        if engine is None:
            return False

        if not engine.isValid():
            return False

        if engine.name() in self.__engines:
            return False

        engine.setParent(self)
        self.__engines[engine.name()] = engine

        self.changed.emit()

        return True

    def removeEngine(self, name):
        """
        Public method to remove an engine.
        
        @param name name of the engine (string)
        """
        if len(self.__engines) <= 1:
            return

        if name not in self.__engines:
            return

        engine = self.__engines[name]
        for keyword in [
                k for k in self.__keywords if self.__keywords[k] == engine
        ]:
            del self.__keywords[keyword]
        del self.__engines[name]

        file_ = QDir(self.enginesDirectory()).filePath(
            self.generateEngineFileName(name))
        QFile.remove(file_)

        if name == self.__current:
            self.setCurrentEngineName(list(self.__engines.keys())[0])

        self.changed.emit()

    def generateEngineFileName(self, engineName):
        """
        Public method to generate a valid engine file name.
        
        @param engineName name of the engine (string)
        @return valid engine file name (string)
        """
        fileName = ""

        # strip special characters
        for c in engineName:
            if c.isspace():
                fileName += '_'
                continue

            if c.isalnum():
                fileName += c

        fileName += ".xml"

        return fileName

    def saveDirectory(self, dirName):
        """
        Public method to save the search engine definitions to files.
        
        @param dirName name of the directory to write the files to (string)
        """
        dir = QDir()
        if not dir.mkpath(dirName):
            return
        dir.setPath(dirName)

        from .OpenSearchWriter import OpenSearchWriter
        writer = OpenSearchWriter()

        for engine in list(self.__engines.values()):
            name = self.generateEngineFileName(engine.name())
            fileName = dir.filePath(name)

            file = QFile(fileName)
            if not file.open(QIODevice.WriteOnly):
                continue

            writer.write(file, engine)

    def save(self):
        """
        Public method to save the search engines configuration.
        """
        if self.__loading:
            return

        self.saveDirectory(self.enginesDirectory())

        Preferences.setHelp("WebSearchEngine", self.__current)
        keywords = []
        for k in self.__keywords:
            if self.__keywords[k]:
                keywords.append((k, self.__keywords[k].name()))
        Preferences.setHelp("WebSearchKeywords", keywords)

    def loadDirectory(self, dirName):
        """
        Public method to load the search engine definitions from files.
        
        @param dirName name of the directory to load the files from (string)
        @return flag indicating success (boolean)
        """
        if not QFile.exists(dirName):
            return False

        success = False

        dir = QDir(dirName)
        for name in dir.entryList(["*.xml"]):
            fileName = dir.filePath(name)
            if self.__addEngineByFile(fileName):
                success = True

        return success

    def load(self):
        """
        Public method to load the search engines configuration.
        """
        self.__loading = True
        self.__current = Preferences.getHelp("WebSearchEngine")
        keywords = Preferences.getHelp("WebSearchKeywords")

        if not self.loadDirectory(self.enginesDirectory()):
            self.restoreDefaults()

        for keyword, engineName in keywords:
            self.__keywords[keyword] = self.engine(engineName)

        if self.__current not in self.__engines and \
           len(self.__engines) > 0:
            self.__current = list(self.__engines.keys())[0]

        self.__loading = False
        self.currentEngineChanged.emit()

    def restoreDefaults(self):
        """
        Public method to restore the default search engines.
        """
        from .OpenSearchReader import OpenSearchReader
        from .DefaultSearchEngines import DefaultSearchEngines_rc  # __IGNORE_WARNING__

        defaultEngineFiles = [
            "YouTube.xml", "Amazoncom.xml", "Bing.xml", "DeEn_Beolingus.xml",
            "Facebook.xml", "Google_Im_Feeling_Lucky.xml", "Google.xml",
            "LEO_DeuEng.xml", "LinuxMagazin.xml", "Reddit.xml", "Wikia_en.xml",
            "Wikia.xml", "Wikipedia.xml", "Wiktionary.xml", "Yahoo.xml"
        ]
        # Keep this list in sync with the contents of the resource file.

        reader = OpenSearchReader()
        for engineFileName in defaultEngineFiles:
            engineFile = QFile(":/" + engineFileName)
            if not engineFile.open(QIODevice.ReadOnly):
                continue
            engine = reader.read(engineFile)
            self.__addEngineByEngine(engine)

    def enginesDirectory(self):
        """
        Public method to determine the directory containing the search engine
        descriptions.
        
        @return directory name (string)
        """
        return os.path.join(Utilities.getConfigDir(), "browser",
                            "searchengines")

    def __confirmAddition(self, engine):
        """
        Private method to confirm the addition of a new search engine.
        
        @param engine reference to the engine to be added (OpenSearchEngine)
        @return flag indicating the engine shall be added (boolean)
        """
        if engine is None or not engine.isValid():
            return False

        host = QUrl(engine.searchUrlTemplate()).host()

        res = E5MessageBox.yesNo(
            None, "",
            self.tr("""<p>Do you want to add the following engine to your"""
                    """ list of search engines?<br/><br/>Name: {0}<br/>"""
                    """Searches on: {1}</p>""").format(engine.name(), host))
        return res

    def __engineFromUrlAvailable(self):
        """
        Private slot to add a search engine from the net.
        """
        reply = self.sender()
        if reply is None:
            return

        if reply.error() != QNetworkReply.NoError:
            reply.close()
            if reply in self.__replies:
                self.__replies.remove(reply)
            return

        from .OpenSearchReader import OpenSearchReader
        reader = OpenSearchReader()
        engine = reader.read(reply)

        reply.close()
        if reply in self.__replies:
            self.__replies.remove(reply)

        if not engine.isValid():
            return

        if self.engineExists(engine.name()):
            return

        if not self.__confirmAddition(engine):
            return

        if not self.__addEngineByEngine(engine):
            return

    def convertKeywordSearchToUrl(self, keywordSearch):
        """
        Public method to get the search URL for a keyword search.
        
        @param keywordSearch search string for keyword search (string)
        @return search URL (QUrl)
        """
        try:
            keyword, term = keywordSearch.split(" ", 1)
        except ValueError:
            return QUrl()

        if not term:
            return QUrl()

        engine = self.engineForKeyword(keyword)
        if engine:
            return engine.searchUrl(term)

        return QUrl()

    def engineForKeyword(self, keyword):
        """
        Public method to get the engine for a keyword.
        
        @param keyword keyword to get engine for (string)
        @return reference to the search engine object (OpenSearchEngine)
        """
        if keyword and keyword in self.__keywords:
            return self.__keywords[keyword]

        return None

    def setEngineForKeyword(self, keyword, engine):
        """
        Public method to set the engine for a keyword.
        
        @param keyword keyword to get engine for (string)
        @param engine reference to the search engine object (OpenSearchEngine)
            or None to remove the keyword
        """
        if not keyword:
            return

        if engine is None:
            try:
                del self.__keywords[keyword]
            except KeyError:
                pass
        else:
            self.__keywords[keyword] = engine

        self.changed.emit()

    def keywordsForEngine(self, engine):
        """
        Public method to get the keywords for a given engine.
        
        @param engine reference to the search engine object (OpenSearchEngine)
        @return list of keywords (list of strings)
        """
        return [k for k in self.__keywords if self.__keywords[k] == engine]

    def setKeywordsForEngine(self, engine, keywords):
        """
        Public method to set the keywords for an engine.
        
        @param engine reference to the search engine object (OpenSearchEngine)
        @param keywords list of keywords (list of strings)
        """
        if engine is None:
            return

        for keyword in self.keywordsForEngine(engine):
            del self.__keywords[keyword]

        for keyword in keywords:
            if not keyword:
                continue

            self.__keywords[keyword] = engine

        self.changed.emit()

    def enginesChanged(self):
        """
        Public slot to tell the search engine manager, that something has
        changed.
        """
        self.changed.emit()
Пример #46
0
    def __init__(self, parent, project):
        """
        Constructor
        
        @param parent the parent (QWidget)
        @param project reference to the project object
        """
        super(TaskViewer, self).__init__(parent)

        self.setRootIsDecorated(False)
        self.setItemsExpandable(False)
        self.setSortingEnabled(True)

        self.__headerItem = QTreeWidgetItem([
            "", "",
            self.tr("Summary"),
            self.tr("Filename"),
            self.tr("Line"), ""
        ])
        self.__headerItem.setIcon(0,
                                  UI.PixmapCache.getIcon("taskCompleted.png"))
        self.__headerItem.setIcon(1,
                                  UI.PixmapCache.getIcon("taskPriority.png"))
        self.setHeaderItem(self.__headerItem)

        self.header().setSortIndicator(2, Qt.AscendingOrder)
        self.__resizeColumns()

        self.tasks = []
        self.copyTask = None
        self.projectOpen = False
        self.project = project
        self.projectTasksScanFilter = ""

        from .TaskFilter import TaskFilter
        self.taskFilter = TaskFilter()
        self.taskFilter.setActive(False)

        self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks)

        self.__projectTasksMenu = QMenu(self.tr("P&roject Tasks"), self)
        self.__projectTasksMenu.addAction(self.tr("&Regenerate project tasks"),
                                          self.__regenerateProjectTasks)
        self.__projectTasksMenu.addSeparator()
        self.__projectTasksMenu.addAction(
            self.tr("&Configure scan options"),
            self.__configureProjectTasksScanOptions)

        self.__menu = QMenu(self)
        self.__menu.addAction(self.tr("&New Task..."), self.__newTask)
        self.__menu.addSeparator()
        self.projectTasksMenuItem = self.__menu.addMenu(
            self.__projectTasksMenu)
        self.__menu.addSeparator()
        self.gotoItem = self.__menu.addAction(self.tr("&Go To"),
                                              self.__goToTask)
        self.__menu.addSeparator()
        self.copyItem = self.__menu.addAction(self.tr("&Copy"),
                                              self.__copyTask)
        self.pasteItem = self.__menu.addAction(self.tr("&Paste"),
                                               self.__pasteTask)
        self.deleteItem = self.__menu.addAction(self.tr("&Delete"),
                                                self.__deleteTask)
        self.__menu.addSeparator()
        self.markCompletedItem = self.__menu.addAction(
            self.tr("&Mark Completed"), self.__markCompleted)
        self.__menu.addAction(self.tr("Delete Completed &Tasks"),
                              self.__deleteCompleted)
        self.__menu.addSeparator()
        self.__menu.addAction(self.tr("P&roperties..."),
                              self.__editTaskProperties)
        self.__menu.addSeparator()
        self.__menuFilteredAct = self.__menu.addAction(
            self.tr("&Filtered display"))
        self.__menuFilteredAct.setCheckable(True)
        self.__menuFilteredAct.setChecked(False)
        self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter)
        self.__menu.addAction(self.tr("Filter c&onfiguration..."),
                              self.__configureFilter)
        self.__menu.addSeparator()
        self.__menu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns)
        self.__menu.addSeparator()
        self.__menu.addAction(self.tr("Configure..."), self.__configure)

        self.__backMenu = QMenu(self)
        self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask)
        self.__backMenu.addSeparator()
        self.backProjectTasksMenuItem = self.__backMenu.addMenu(
            self.__projectTasksMenu)
        self.__backMenu.addSeparator()
        self.backPasteItem = self.__backMenu.addAction(self.tr("&Paste"),
                                                       self.__pasteTask)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(self.tr("Delete Completed &Tasks"),
                                  self.__deleteCompleted)
        self.__backMenu.addSeparator()
        self.__backMenuFilteredAct = self.__backMenu.addAction(
            self.tr("&Filtered display"))
        self.__backMenuFilteredAct.setCheckable(True)
        self.__backMenuFilteredAct.setChecked(False)
        self.__backMenuFilteredAct.triggered[bool].connect(
            self.__activateFilter)
        self.__backMenu.addAction(self.tr("Filter c&onfiguration..."),
                                  self.__configureFilter)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(self.tr("Resi&ze columns"),
                                  self.__resizeColumns)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(self.tr("Configure..."), self.__configure)

        self.__activating = False

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.__showContextMenu)
        self.itemActivated.connect(self.__taskItemActivated)

        self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
Пример #47
0
class SpeedDial(QObject):
    """
    Class implementing the speed dial.
    
    @signal pagesChanged() emitted after the list of pages changed
    @signal speedDialSaved() emitted after the speed dial data was saved
    """
    pagesChanged = pyqtSignal()
    speedDialSaved = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(SpeedDial, self).__init__(parent)

        self.__regenerateScript = True

        self.__webPages = []
        self.__webFrames = []

        self.__initialScript = ""
        self.__thumbnailsDirectory = ""

        self.__thumbnailers = []

        self.__initialize()

        self.pagesChanged.connect(self.__pagesChanged)

        self.__saveTimer = AutoSaver(self, self.save)
        self.pagesChanged.connect(self.__saveTimer.changeOccurred)

    def addWebFrame(self, frame):
        """
        Public method to add a web frame.
        
        @param frame reference to the frame to be added (QWebFrame)
        """
        if frame not in self.__webFrames:
            self.__webFrames.append(frame)

    def addPage(self, url, title):
        """
        Public method to add a page for the given data.
        
        @param url URL of the page (QUrl)
        @param title title of the page (string)
        """
        if url.isEmpty():
            return

        from .Page import Page
        page = Page(url.toString(), title)
        self.__webPages.append(page)

        self.pagesChanged.emit()

    def removePage(self, url):
        """
        Public method to remove a page.
        
        @param url URL of the page (QUrl)
        """
        page = self.pageForUrl(url)
        if not page.url:
            return

        self.removeImageForUrl(page.url)
        self.__webPages.remove(page)

        self.pagesChanged.emit()

    def __imageFileName(self, url):
        """
        Private method to generate the image file name for a URL.
        
        @param url URL to generate the file name from (string)
        @return name of the image file (string)
        """
        return os.path.join(
            self.__thumbnailsDirectory,
            str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")),
                                        QCryptographicHash.Md5).toHex(),
                encoding="utf-8") + ".png")

    def initialScript(self):
        """
        Public method to get the 'initial' JavaScript script.
        
        @return initial JavaScript script (string)
        """
        if self.__regenerateScript:
            self.__regenerateScript = False
            self.__initialScript = ""

            for page in self.__webPages:
                if page.broken:
                    imgSource = "qrc:icons/brokenPage.png"
                else:
                    imgSource = self.__imageFileName(page.url)
                    if not os.path.exists(imgSource):
                        self.loadThumbnail(page.url)
                        imgSource = "qrc:icons/loading.gif"

                        if not page.url:
                            imgSource = ""
                    else:
                        imgSource = QUrl.fromLocalFile(imgSource).toString()

                self.__initialScript += \
                    "addBox('{0}', '{1}', '{2}');\n".format(
                        page.url, Utilities.html_uencode(page.title),
                        imgSource)

        return self.__initialScript

    def getFileName(self):
        """
        Public method to get the file name of the user agents file.
        
        @return name of the user agents file (string)
        """
        return os.path.join(Utilities.getConfigDir(), "browser",
                            "speedDial.xml")

    def __initialize(self):
        """
        Private method to initialize the speed dial.
        """
        self.__thumbnailsDirectory = os.path.join(Utilities.getConfigDir(),
                                                  "browser", "thumbnails")
        # Create directory if it does not exist yet
        if not os.path.exists(self.__thumbnailsDirectory):
            os.makedirs(self.__thumbnailsDirectory)

        self.__load()

    def reload(self):
        """
        Public method to reload the speed dial data.
        """
        self.__load()

    def __load(self):
        """
        Private method to load the speed dial configuration.
        """
        allPages, pagesPerRow, speedDialSize = [], 0, 0

        speedDialFile = self.getFileName()
        if os.path.exists(speedDialFile):
            from .SpeedDialReader import SpeedDialReader
            reader = SpeedDialReader()
            allPages, pagesPerRow, speedDialSize = reader.read(speedDialFile)

        self.__pagesPerRow = pagesPerRow if pagesPerRow else 4
        self.__speedDialSize = speedDialSize if speedDialSize else 231

        if allPages:
            self.__webPages = allPages
            self.pagesChanged.emit()
        else:
            allPages = \
                'url:"http://eric-ide.python-projects.org/"|'\
                'title:"Eric Web Site";'\
                'url:"http://www.riverbankcomputing.com/"|'\
                'title:"PyQt Web Site";'\
                'url:"http://www.qt.io/"|title:"Qt Web Site";'\
                'url:"http://blog.qt.digia.com/"|title:"Qt Blog";'\
                'url:"http://www.python.org"|title:"Python Language Website";'\
                'url:"http://www.google.com"|title:"Google";'
            self.changed(allPages)

    def save(self):
        """
        Public method to save the speed dial configuration.
        """
        from .SpeedDialWriter import SpeedDialWriter
        speedDialFile = self.getFileName()
        writer = SpeedDialWriter()
        if not writer.write(speedDialFile, self.__webPages, self.__pagesPerRow,
                            self.__speedDialSize):
            E5MessageBox.critical(
                None, self.tr("Saving Speed Dial data"),
                self.tr("""<p>Speed Dial data could not be saved to"""
                        """ <b>{0}</b></p>""").format(speedDialFile))
        else:
            self.speedDialSaved.emit()

    def close(self):
        """
        Public method to close the user agents manager.
        """
        self.__saveTimer.saveIfNeccessary()

    def pageForUrl(self, url):
        """
        Public method to get the page for the given URL.
        
        @param url URL to be searched for (QUrl)
        @return page for the URL (Page)
        """
        urlString = url.toString()
        for page in self.__webPages:
            if page.url == urlString:
                return page

        from .Page import Page
        return Page()

    def urlForShortcut(self, key):
        """
        Public method to get the URL for the given shortcut key.
        
        @param key shortcut key (integer)
        @return URL for the key (QUrl)
        """
        if key < 0 or len(self.__webPages) <= key:
            return QUrl()

        return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8"))

    @pyqtSlot(str)
    def changed(self, allPages):
        """
        Public slot to react on changed pages.
        
        @param allPages string giving all pages (string)
        """
        if not allPages:
            return

        entries = allPages.split('";')
        self.__webPages = []

        from .Page import Page
        for entry in entries:
            if not entry:
                continue

            tmp = entry.split('"|')
            if len(tmp) == 2:
                broken = False
            elif len(tmp) == 3:
                broken = "brokenPage" in tmp[2][5:]
            else:
                continue

            page = Page(tmp[0][5:], tmp[1][7:], broken)
            self.__webPages.append(page)

        self.pagesChanged.emit()

    @pyqtSlot(str)
    @pyqtSlot(str, bool)
    def loadThumbnail(self, url, loadTitle=False):
        """
        Public slot to load a thumbnail of the given URL.
        
        @param url URL of the thumbnail (string)
        @param loadTitle flag indicating to get the title for the thumbnail
            from the site (boolean)
        """
        if not url:
            return

        from .PageThumbnailer import PageThumbnailer
        thumbnailer = PageThumbnailer(self)
        thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8")))
        thumbnailer.setLoadTitle(loadTitle)
        thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated)
        self.__thumbnailers.append(thumbnailer)

        thumbnailer.start()

    @pyqtSlot(str)
    def removeImageForUrl(self, url):
        """
        Public slot to remove the image for a URL.
        
        @param url URL to remove the image for (string)
        """
        fileName = self.__imageFileName(url)
        if os.path.exists(fileName):
            os.remove(fileName)

    @pyqtSlot(str, result=str)
    def urlFromUserInput(self, url):
        """
        Public slot to get the URL from user input.
        
        @param url URL entered by the user (string)
        @return sanitized URL (string)
        """
        return QUrl.fromUserInput(url).toString()

    @pyqtSlot(str, result=str)
    def unescapeTitle(self, title):
        """
        Public slot to unescape the titel string.
        
        @param title escaped title (string)
        @return un-escaped title (string)
        """
        return Utilities.html_udecode(title)

    @pyqtSlot(int)
    def setPagesInRow(self, count):
        """
        Public slot to set the number of pages per row.
        
        @param count number of pages per row (integer)
        """
        self.__pagesPerRow = count
        self.__saveTimer.changeOccurred()

    def pagesInRow(self):
        """
        Public method to get the number of dials per row.
        
        @return number of dials per row (integer)
        """
        return self.__pagesPerRow

    @pyqtSlot(int)
    def setSdSize(self, size):
        """
        Public slot to set the size of the speed dial.
        
        @param size size of the speed dial (integer)
        """
        self.__speedDialSize = size
        self.__saveTimer.changeOccurred()

    def sdSize(self):
        """
        Public method to get the speed dial size.
        
        @return speed dial size (integer)
        """
        return self.__speedDialSize

    def __thumbnailCreated(self, image):
        """
        Private slot to handle the creation of a thumbnail image.
        
        @param image thumbnail image (QPixmap)
        """
        from .PageThumbnailer import PageThumbnailer
        thumbnailer = self.sender()
        if not isinstance(thumbnailer, PageThumbnailer) or \
           thumbnailer not in self.__thumbnailers:
            return

        loadTitle = thumbnailer.loadTitle()
        title = thumbnailer.title()
        url = thumbnailer.url().toString()
        fileName = self.__imageFileName(url)

        if image.isNull():
            fileName = "qrc:icons/brokenPage.png"
            title = self.tr("Unable to load")
            loadTitle = True
            page = self.pageForUrl(thumbnailer.url())
            page.broken = True
        else:
            if not image.save(fileName):
                qWarning("SpeedDial.__thumbnailCreated: Cannot save thumbnail"
                         " to {0}".format(fileName))

            fileName = QUrl.fromLocalFile(fileName).toString()

        self.__regenerateScript = True

        for frame in self.__cleanFrames():
            frame.evaluateJavaScript("setImageToUrl('{0}', '{1}');".format(
                url, fileName))
            if loadTitle:
                frame.evaluateJavaScript("setTitleToUrl('{0}', '{1}');".format(
                    url, Utilities.html_uencode(title)))

        thumbnailer.deleteLater()
        self.__thumbnailers.remove(thumbnailer)

    def __cleanFrames(self):
        """
        Private method to clean all frames.
        
        @return list of speed dial frames (list of QWebFrame)
        """
        frames = []

        for frame in self.__webFrames[:]:
            if frame.url().toString() == "eric:speeddial":
                frames.append(frame)
            else:
                self.__webFrames.remove(frame)

        return frames

    def __pagesChanged(self):
        """
        Private slot to react on a change of the pages configuration.
        """
        # update all speed dial pages
        self.__regenerateScript = True
        for frame in self.__cleanFrames():
            frame.page().triggerAction(QWebPage.Reload)
Пример #48
0
class OpenSearchManager(QObject):
    """
    Class implementing a manager for open search engines.
    
    @signal changed() emitted to indicate a change
    @signal currentEngineChanged() emitted to indicate a change of
            the current search engine
    """
    changed = pyqtSignal()
    currentEngineChanged = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        if parent is None:
            parent = e5App()
        super(OpenSearchManager, self).__init__(parent)
        
        self.__replies = []
        self.__engines = {}
        self.__keywords = {}
        self.__current = ""
        self.__loading = False
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.changed.connect(self.__saveTimer.changeOccurred)
        
        self.load()
    
    def close(self):
        """
        Public method to close the open search engines manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def currentEngineName(self):
        """
        Public method to get the name of the current search engine.
        
        @return name of the current search engine (string)
        """
        return self.__current
    
    def setCurrentEngineName(self, name):
        """
        Public method to set the current engine by name.
        
        @param name name of the new current engine (string)
        """
        if name not in self.__engines:
            return
        
        self.__current = name
        self.currentEngineChanged.emit()
        self.changed.emit()
    
    def currentEngine(self):
        """
        Public method to get a reference to the current engine.
        
        @return reference to the current engine (OpenSearchEngine)
        """
        if not self.__current or self.__current not in self.__engines:
            return None
        
        return self.__engines[self.__current]
    
    def setCurrentEngine(self, engine):
        """
        Public method to set the current engine.
        
        @param engine reference to the new current engine (OpenSearchEngine)
        """
        if engine is None:
            return
        
        for engineName in self.__engines:
            if self.__engines[engineName] == engine:
                self.setCurrentEngineName(engineName)
                break
    
    def engine(self, name):
        """
        Public method to get a reference to the named engine.
        
        @param name name of the engine (string)
        @return reference to the engine (OpenSearchEngine)
        """
        if name not in self.__engines:
            return None
        
        return self.__engines[name]
    
    def engineExists(self, name):
        """
        Public method to check, if an engine exists.
        
        @param name name of the engine (string)
        @return flag indicating an existing engine (boolean)
        """
        return name in self.__engines
    
    def allEnginesNames(self):
        """
        Public method to get a list of all engine names.
        
        @return sorted list of all engine names (list of strings)
        """
        return sorted(self.__engines.keys())
    
    def enginesCount(self):
        """
        Public method to get the number of available engines.
        
        @return number of engines (integer)
        """
        return len(self.__engines)
    
    def addEngine(self, engine):
        """
        Public method to add a new search engine.
        
        @param engine URL of the engine definition file (QUrl) or
            name of a file containing the engine definition (string)
            or reference to an engine object (OpenSearchEngine)
        @return flag indicating success (boolean)
        """
        from .OpenSearchEngine import OpenSearchEngine
        if isinstance(engine, QUrl):
            return self.__addEngineByUrl(engine)
        elif isinstance(engine, OpenSearchEngine):
            return self.__addEngineByEngine(engine)
        else:
            return self.__addEngineByFile(engine)
    
    def __addEngineByUrl(self, url):
        """
        Private method to add a new search engine given its URL.
        
        @param url URL of the engine definition file (QUrl)
        @return flag indicating success (boolean)
        """
        if not url.isValid():
            return
        
        from Helpviewer.HelpWindow import HelpWindow

        reply = HelpWindow.networkAccessManager().get(QNetworkRequest(url))
        reply.finished.connect(self.__engineFromUrlAvailable)
        reply.setParent(self)
        self.__replies.append(reply)
        
        return True
    
    def __addEngineByFile(self, filename):
        """
        Private method to add a new search engine given a filename.
        
        @param filename name of a file containing the engine definition
            (string)
        @return flag indicating success (boolean)
        """
        file_ = QFile(filename)
        if not file_.open(QIODevice.ReadOnly):
            return False
        
        from .OpenSearchReader import OpenSearchReader
        reader = OpenSearchReader()
        engine = reader.read(file_)
        
        if not self.__addEngineByEngine(engine):
            return False
        
        return True
    
    def __addEngineByEngine(self, engine):
        """
        Private method to add a new search engine given a reference to an
        engine.
        
        @param engine reference to an engine object (OpenSearchEngine)
        @return flag indicating success (boolean)
        """
        if engine is None:
            return False
        
        if not engine.isValid():
            return False
        
        if engine.name() in self.__engines:
            return False
        
        engine.setParent(self)
        self.__engines[engine.name()] = engine
        
        self.changed.emit()
        
        return True
    
    def removeEngine(self, name):
        """
        Public method to remove an engine.
        
        @param name name of the engine (string)
        """
        if len(self.__engines) <= 1:
            return
        
        if name not in self.__engines:
            return
        
        engine = self.__engines[name]
        for keyword in [k for k in self.__keywords
                        if self.__keywords[k] == engine]:
            del self.__keywords[keyword]
        del self.__engines[name]
        
        file_ = QDir(self.enginesDirectory()).filePath(
            self.generateEngineFileName(name))
        QFile.remove(file_)
        
        if name == self.__current:
            self.setCurrentEngineName(list(self.__engines.keys())[0])
        
        self.changed.emit()
    
    def generateEngineFileName(self, engineName):
        """
        Public method to generate a valid engine file name.
        
        @param engineName name of the engine (string)
        @return valid engine file name (string)
        """
        fileName = ""
        
        # strip special characters
        for c in engineName:
            if c.isspace():
                fileName += '_'
                continue
            
            if c.isalnum():
                fileName += c
        
        fileName += ".xml"
        
        return fileName
    
    def saveDirectory(self, dirName):
        """
        Public method to save the search engine definitions to files.
        
        @param dirName name of the directory to write the files to (string)
        """
        dir = QDir()
        if not dir.mkpath(dirName):
            return
        dir.setPath(dirName)
        
        from .OpenSearchWriter import OpenSearchWriter
        writer = OpenSearchWriter()
        
        for engine in list(self.__engines.values()):
            name = self.generateEngineFileName(engine.name())
            fileName = dir.filePath(name)
            
            file = QFile(fileName)
            if not file.open(QIODevice.WriteOnly):
                continue
            
            writer.write(file, engine)
    
    def save(self):
        """
        Public method to save the search engines configuration.
        """
        if self.__loading:
            return
        
        self.saveDirectory(self.enginesDirectory())
        
        Preferences.setHelp("WebSearchEngine", self.__current)
        keywords = []
        for k in self.__keywords:
            if self.__keywords[k]:
                keywords.append((k, self.__keywords[k].name()))
        Preferences.setHelp("WebSearchKeywords", keywords)
    
    def loadDirectory(self, dirName):
        """
        Public method to load the search engine definitions from files.
        
        @param dirName name of the directory to load the files from (string)
        @return flag indicating success (boolean)
        """
        if not QFile.exists(dirName):
            return False
        
        success = False
        
        dir = QDir(dirName)
        for name in dir.entryList(["*.xml"]):
            fileName = dir.filePath(name)
            if self.__addEngineByFile(fileName):
                success = True
        
        return success
    
    def load(self):
        """
        Public method to load the search engines configuration.
        """
        self.__loading = True
        self.__current = Preferences.getHelp("WebSearchEngine")
        keywords = Preferences.getHelp("WebSearchKeywords")
        
        if not self.loadDirectory(self.enginesDirectory()):
            self.restoreDefaults()
        
        for keyword, engineName in keywords:
            self.__keywords[keyword] = self.engine(engineName)
        
        if self.__current not in self.__engines and \
           len(self.__engines) > 0:
            self.__current = list(self.__engines.keys())[0]
        
        self.__loading = False
        self.currentEngineChanged.emit()
    
    def restoreDefaults(self):
        """
        Public method to restore the default search engines.
        """
        from .OpenSearchReader import OpenSearchReader
        from .DefaultSearchEngines import DefaultSearchEngines_rc   # __IGNORE_WARNING__
        
        defaultEngineFiles = ["YouTube.xml", "Amazoncom.xml", "Bing.xml",
                              "DeEn_Beolingus.xml", "Facebook.xml",
                              "Google_Im_Feeling_Lucky.xml", "Google.xml",
                              "LEO_DeuEng.xml", "LinuxMagazin.xml",
                              "Reddit.xml", "Wikia_en.xml", "Wikia.xml",
                              "Wikipedia.xml", "Wiktionary.xml", "Yahoo.xml"]
        # Keep this list in sync with the contents of the resource file.

        reader = OpenSearchReader()
        for engineFileName in defaultEngineFiles:
            engineFile = QFile(":/" + engineFileName)
            if not engineFile.open(QIODevice.ReadOnly):
                continue
            engine = reader.read(engineFile)
            self.__addEngineByEngine(engine)
    
    def enginesDirectory(self):
        """
        Public method to determine the directory containing the search engine
        descriptions.
        
        @return directory name (string)
        """
        return os.path.join(
            Utilities.getConfigDir(), "browser", "searchengines")
    
    def __confirmAddition(self, engine):
        """
        Private method to confirm the addition of a new search engine.
        
        @param engine reference to the engine to be added (OpenSearchEngine)
        @return flag indicating the engine shall be added (boolean)
        """
        if engine is None or not engine.isValid():
            return False
        
        host = QUrl(engine.searchUrlTemplate()).host()
        
        res = E5MessageBox.yesNo(
            None,
            "",
            self.tr(
                """<p>Do you want to add the following engine to your"""
                """ list of search engines?<br/><br/>Name: {0}<br/>"""
                """Searches on: {1}</p>""").format(engine.name(), host))
        return res
    
    def __engineFromUrlAvailable(self):
        """
        Private slot to add a search engine from the net.
        """
        reply = self.sender()
        if reply is None:
            return
        
        if reply.error() != QNetworkReply.NoError:
            reply.close()
            if reply in self.__replies:
                self.__replies.remove(reply)
            return
        
        from .OpenSearchReader import OpenSearchReader
        reader = OpenSearchReader()
        engine = reader.read(reply)
        
        reply.close()
        if reply in self.__replies:
            self.__replies.remove(reply)
        
        if not engine.isValid():
            return
        
        if self.engineExists(engine.name()):
            return
        
        if not self.__confirmAddition(engine):
            return
        
        if not self.__addEngineByEngine(engine):
            return
    
    def convertKeywordSearchToUrl(self, keywordSearch):
        """
        Public method to get the search URL for a keyword search.
        
        @param keywordSearch search string for keyword search (string)
        @return search URL (QUrl)
        """
        try:
            keyword, term = keywordSearch.split(" ", 1)
        except ValueError:
            return QUrl()
        
        if not term:
            return QUrl()
        
        engine = self.engineForKeyword(keyword)
        if engine:
            return engine.searchUrl(term)
        
        return QUrl()
    
    def engineForKeyword(self, keyword):
        """
        Public method to get the engine for a keyword.
        
        @param keyword keyword to get engine for (string)
        @return reference to the search engine object (OpenSearchEngine)
        """
        if keyword and keyword in self.__keywords:
            return self.__keywords[keyword]
        
        return None
    
    def setEngineForKeyword(self, keyword, engine):
        """
        Public method to set the engine for a keyword.
        
        @param keyword keyword to get engine for (string)
        @param engine reference to the search engine object (OpenSearchEngine)
            or None to remove the keyword
        """
        if not keyword:
            return
        
        if engine is None:
            try:
                del self.__keywords[keyword]
            except KeyError:
                pass
        else:
            self.__keywords[keyword] = engine
        
        self.changed.emit()
    
    def keywordsForEngine(self, engine):
        """
        Public method to get the keywords for a given engine.
        
        @param engine reference to the search engine object (OpenSearchEngine)
        @return list of keywords (list of strings)
        """
        return [k for k in self.__keywords if self.__keywords[k] == engine]
    
    def setKeywordsForEngine(self, engine, keywords):
        """
        Public method to set the keywords for an engine.
        
        @param engine reference to the search engine object (OpenSearchEngine)
        @param keywords list of keywords (list of strings)
        """
        if engine is None:
            return
        
        for keyword in self.keywordsForEngine(engine):
            del self.__keywords[keyword]
        
        for keyword in keywords:
            if not keyword:
                continue
            
            self.__keywords[keyword] = engine
        
        self.changed.emit()
    
    def enginesChanged(self):
        """
        Public slot to tell the search engine manager, that something has
        changed.
        """
        self.changed.emit()
class PasswordManager(QObject):
    """
    Class implementing the password manager.
    
    @signal changed() emitted to indicate a change
    """
    SEPARATOR = "===================="
    FORMS = "=====FORMS====="
    NEVER = "=====NEVER====="
    
    def __init__(self, parent = None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        QObject.__init__(self, parent)
        
        self.__logins = {}
        self.__loginForms = {}
        self.__never = []
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred)
    
    def clear(self):
        """
        Public slot to clear the saved passwords.
        """
        if not self.__loaded:
            self.__load()
        
        self.__logins = {}
        self.__loginForms = {}
        self.__never = []
        self.__saveTimer.changeOccurred()
        self.__saveTimer.saveIfNeccessary()
        
        self.emit(SIGNAL("changed()"))
    
    def getLogin(self, url, realm):
        """
        Public method to get the login credentials.
        
        @param url URL to get the credentials for (QUrl)
        @param realm realm to get the credentials for (string or QString)
        @return tuple containing the user name (string) and password (string)
        """
        if not self.__loaded:
            self.__load()
        
        key = self.__createKey(url, realm)
        try:
            return self.__logins[key][0], Utilities.pwDecode(self.__logins[key][1])
        except KeyError:
            return "", ""
    
    def setLogin(self, url, realm, username, password):
        """
        Public method to set the login credentials.
        
        @param url URL to set the credentials for (QUrl)
        @param realm realm to set the credentials for (string or QString)
        @param username username for the login (string or QString)
        @param password password for the login (string or QString)
        """
        if not self.__loaded:
            self.__load()
        
        key = self.__createKey(url, realm)
        self.__logins[key] = (unicode(username), Utilities.pwEncode(password))
        self.emit(SIGNAL("changed()"))
    
    def __createKey(self, url, realm):
        """
        Private method to create the key string for the login credentials.
        
        @param url URL to get the credentials for (QUrl)
        @param realm realm to get the credentials for (string or QString)
        @return key string (string)
        """
        realm = unicode(realm)
        if realm:
            key = "%s://%s (%s)" % (url.scheme(), url.authority(), realm)
        else:
            key = "%s://%s" % (url.scheme(), url.authority())
        return key
    
    def save(self):
        """
        Public slot to save the login entries to disk.
        """
        if not self.__loaded:
            return
        
        loginFile = os.path.join(Utilities.getConfigDir(), "browser", "logins")
        try:
            f = open(loginFile, "w")
            for key, login in self.__logins.items():
                f.write("%s\n" % key)
                f.write("%s\n" % login[0])
                f.write("%s\n" % login[1])
                f.write("%s\n" % self.SEPARATOR)
            if self.__loginForms:
                f.write("%s\n" % self.FORMS)
                for key, form in self.__loginForms.items():
                    f.write("%s\n" % key)
                    form.save(f)
                    f.write("%s\n" % self.SEPARATOR)
            if self.__never:
                f.write("%s\n" % self.NEVER)
                for key in self.__never:
                    f.write("%s\n") % key
            f.close()
        except IOError, err:
            KQMessageBox.critical(None,
                self.trUtf8("Saving login data"),
                self.trUtf8("""<p>Login data could not be saved to <b>%1</b></p>"""
                            """<p>Reason: %2</p>""").arg(loginFile).arg(str(err)))
            return
Пример #50
0
class AdBlockManager(QObject):
    """
    Class implementing the AdBlock manager.
    
    @signal rulesChanged() emitted after some rule has changed
    """
    rulesChanged = pyqtSignal()
    requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription)
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(AdBlockManager, self).__init__(parent)
        
        self.__loaded = False
        self.__subscriptionsLoaded = False
        self.__enabled = False
        self.__adBlockDialog = None
        self.__adBlockExceptionsDialog = None
        self.__adBlockNetwork = None
        self.__adBlockPage = None
        self.__subscriptions = []
        self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions")
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.__defaultSubscriptionUrlString = \
            "abp:subscribe?location=" \
            "https://easylist-downloads.adblockplus.org/easylist.txt&"\
            "title=EasyList"
        self.__customSubscriptionUrlString = \
            bytes(self.__customSubscriptionUrl().toEncoded()).decode()
        
        self.rulesChanged.connect(self.__saveTimer.changeOccurred)
    
    def close(self):
        """
        Public method to close the open search engines manager.
        """
        self.__adBlockDialog and self.__adBlockDialog.close()
        self.__adBlockExceptionsDialog and \
            self.__adBlockExceptionsDialog.close()
        
        self.__saveTimer.saveIfNeccessary()
    
    def isEnabled(self):
        """
        Public method to check, if blocking ads is enabled.
        
        @return flag indicating the enabled state (boolean)
        """
        if not self.__loaded:
            self.load()
        
        return self.__enabled
    
    def setEnabled(self, enabled):
        """
        Public slot to set the enabled state.
        
        @param enabled flag indicating the enabled state (boolean)
        """
        if self.isEnabled() == enabled:
            return
        
        import Helpviewer.HelpWindow
        self.__enabled = enabled
        for mainWindow in Helpviewer.HelpWindow.HelpWindow.mainWindows():
            mainWindow.adBlockIcon().setEnabled(enabled)
        if enabled:
            self.__loadSubscriptions()
        self.rulesChanged.emit()
    
    def network(self):
        """
        Public method to get a reference to the network block object.
        
        @return reference to the network block object (AdBlockNetwork)
        """
        if self.__adBlockNetwork is None:
            from .AdBlockNetwork import AdBlockNetwork
            self.__adBlockNetwork = AdBlockNetwork(self)
        return self.__adBlockNetwork
    
    def page(self):
        """
        Public method to get a reference to the page block object.
        
        @return reference to the page block object (AdBlockPage)
        """
        if self.__adBlockPage is None:
            from .AdBlockPage import AdBlockPage
            self.__adBlockPage = AdBlockPage(self)
        return self.__adBlockPage
    
    def __customSubscriptionLocation(self):
        """
        Private method to generate the path for custom subscriptions.
        
        @return URL for custom subscriptions (QUrl)
        """
        dataDir = os.path.join(Utilities.getConfigDir(), "browser",
                               "subscriptions")
        if not os.path.exists(dataDir):
            os.makedirs(dataDir)
        fileName = os.path.join(dataDir, "adblock_subscription_custom")
        return QUrl.fromLocalFile(fileName)
    
    def __customSubscriptionUrl(self):
        """
        Private method to generate the URL for custom subscriptions.
        
        @return URL for custom subscriptions (QUrl)
        """
        location = self.__customSubscriptionLocation()
        encodedUrl = bytes(location.toEncoded()).decode()
        url = QUrl("abp:subscribe?location={0}&title={1}".format(
            encodedUrl, self.tr("Custom Rules")))
        return url
    
    def customRules(self):
        """
        Public method to get a subscription for custom rules.
        
        @return subscription object for custom rules (AdBlockSubscription)
        """
        location = self.__customSubscriptionLocation()
        for subscription in self.__subscriptions:
            if subscription.location() == location:
                return subscription
        
        url = self.__customSubscriptionUrl()
        customAdBlockSubscription = AdBlockSubscription(url, True, self)
        self.addSubscription(customAdBlockSubscription)
        return customAdBlockSubscription
    
    def subscriptions(self):
        """
        Public method to get all subscriptions.
        
        @return list of subscriptions (list of AdBlockSubscription)
        """
        if not self.__loaded:
            self.load()
        
        return self.__subscriptions[:]
    
    def subscription(self, location):
        """
        Public method to get a subscription based on its location.
        
        @param location location of the subscription to search for (string)
        @return subscription or None (AdBlockSubscription)
        """
        if location != "":
            for subscription in self.__subscriptions:
                if subscription.location().toString() == location:
                    return subscription
        
        return None
    
    def updateAllSubscriptions(self):
        """
        Public method to update all subscriptions.
        """
        for subscription in self.__subscriptions:
            subscription.updateNow()
    
    def removeSubscription(self, subscription, emitSignal=True):
        """
        Public method to remove an AdBlock subscription.
        
        @param subscription AdBlock subscription to be removed
            (AdBlockSubscription)
        @param emitSignal flag indicating to send a signal (boolean)
        """
        if subscription is None:
            return
        
        if subscription.url().toString().startswith(
            (self.__defaultSubscriptionUrlString,
             self.__customSubscriptionUrlString)):
            return
        
        try:
            self.__subscriptions.remove(subscription)
            rulesFileName = subscription.rulesFileName()
            QFile.remove(rulesFileName)
            requiresSubscriptions = self.getRequiresSubscriptions(subscription)
            for requiresSubscription in requiresSubscriptions:
                self.removeSubscription(requiresSubscription, False)
            if emitSignal:
                self.rulesChanged.emit()
        except ValueError:
            pass
    
    def addSubscription(self, subscription):
        """
        Public method to add an AdBlock subscription.
        
        @param subscription AdBlock subscription to be added
            (AdBlockSubscription)
        """
        if subscription is None:
            return
        
        self.__subscriptions.insert(-1, subscription)
        
        subscription.rulesChanged.connect(self.rulesChanged)
        subscription.changed.connect(self.rulesChanged)
        subscription.enabledChanged.connect(self.rulesChanged)
        
        self.rulesChanged.emit()
    
    def save(self):
        """
        Public method to save the AdBlock subscriptions.
        """
        if not self.__loaded:
            return
        
        Preferences.setHelp("AdBlockEnabled", self.__enabled)
        if self.__subscriptionsLoaded:
            subscriptions = []
            requiresSubscriptions = []
            # intermediate store for subscription requiring others
            for subscription in self.__subscriptions:
                if subscription is None:
                    continue
                urlString = bytes(subscription.url().toEncoded()).decode()
                if "requiresLocation" in urlString:
                    requiresSubscriptions.append(urlString)
                else:
                    subscriptions.append(urlString)
                subscription.saveRules()
            for subscription in requiresSubscriptions:
                subscriptions.insert(-1, subscription)  # custom should be last
            Preferences.setHelp("AdBlockSubscriptions", subscriptions)
    
    def load(self):
        """
        Public method to load the AdBlock subscriptions.
        """
        if self.__loaded:
            return
        
        self.__loaded = True
        
        self.__enabled = Preferences.getHelp("AdBlockEnabled")
        if self.__enabled:
            self.__loadSubscriptions()
    
    def __loadSubscriptions(self):
        """
        Private method to load the set of subscriptions.
        """
        if self.__subscriptionsLoaded:
            return
        
        subscriptions = Preferences.getHelp("AdBlockSubscriptions")
        if subscriptions:
            for subscription in subscriptions:
                if subscription.startswith(
                        self.__defaultSubscriptionUrlString):
                    break
            else:
                subscriptions.insert(0, self.__defaultSubscriptionUrlString)
            for subscription in subscriptions:
                if subscription.startswith(self.__customSubscriptionUrlString):
                    break
            else:
                subscriptions.append(self.__customSubscriptionUrlString)
        else:
            subscriptions = [self.__defaultSubscriptionUrlString,
                             self.__customSubscriptionUrlString]
        for subscription in subscriptions:
            url = QUrl.fromEncoded(subscription.encode("utf-8"))
            adBlockSubscription = AdBlockSubscription(
                url,
                subscription.startswith(self.__customSubscriptionUrlString),
                self,
                subscription.startswith(self.__defaultSubscriptionUrlString))
            adBlockSubscription.rulesChanged.connect(self.rulesChanged)
            adBlockSubscription.changed.connect(self.rulesChanged)
            adBlockSubscription.enabledChanged.connect(self.rulesChanged)
            self.__subscriptions.append(adBlockSubscription)
        
        self.__subscriptionsLoaded = True
    
    def loadRequiredSubscription(self, location, title):
        """
        Public method to load a subscription required by another one.
        
        @param location location of the required subscription (string)
        @param title title of the required subscription (string)
        """
        # Step 1: check, if the subscription is in the list of subscriptions
        urlString = "abp:subscribe?location={0}&title={1}".format(
            location, title)
        for subscription in self.__subscriptions:
            if subscription.url().toString().startswith(urlString):
                # We found it!
                return
        
        # Step 2: if it is not, get it
        url = QUrl.fromEncoded(urlString.encode("utf-8"))
        adBlockSubscription = AdBlockSubscription(url, False, self)
        self.addSubscription(adBlockSubscription)
        self.requiredSubscriptionLoaded.emit(adBlockSubscription)
    
    def getRequiresSubscriptions(self, subscription):
        """
        Public method to get a list of subscriptions, that require the given
        one.
        
        @param subscription subscription to check for (AdBlockSubscription)
        @return list of subscription requiring the given one (list of
            AdBlockSubscription)
        """
        subscriptions = []
        location = subscription.location().toString()
        for subscription in self.__subscriptions:
            if subscription.requiresLocation() == location:
                subscriptions.append(subscription)
        
        return subscriptions
    
    def showDialog(self):
        """
        Public slot to show the AdBlock subscription management dialog.
        
        @return reference to the dialog (AdBlockDialog)
        """
        if self.__adBlockDialog is None:
            from .AdBlockDialog import AdBlockDialog
            self.__adBlockDialog = AdBlockDialog()
        
        self.__adBlockDialog.show()
        return self.__adBlockDialog
    
    def showRule(self):
        """
        Public slot to show an AdBlock rule.
        """
        act = self.sender()
        if act is not None:
            rule = act.data()
            if rule:
                self.showDialog().showRule(rule)
    
    def elementHidingRules(self):
        """
        Public method to get the element hiding rules.
        
        @return element hiding rules (string)
        """
        if not self.__enabled:
            return ""
        
        rules = ""
        
        for subscription in self.__subscriptions:
            rules += subscription.elementHidingRules()
        
        if rules:
            # remove last ",
            rules = rules[:-1]
        
        return rules
    
    def elementHidingRulesForDomain(self, url):
        """
        Public method to get the element hiding rules for a domain.
        
        @param url URL to get hiding rules for (QUrl)
        @return element hiding rules (string)
        """
        if not self.__enabled:
            return ""
        
        rules = ""
        
        for subscription in self.__subscriptions:
            if subscription.elemHideDisabledForUrl(url):
                return ""
            
            rules += subscription.elementHidingRulesForDomain(url.host())
        
        if rules:
            # remove last ",
            rules = rules[:-1]
        
        return rules
    
    def exceptions(self):
        """
        Public method to get a list of excepted hosts.
        
        @return list of excepted hosts (list of string)
        """
        return self.__exceptedHosts
    
    def setExceptions(self, hosts):
        """
        Public method to set the list of excepted hosts.
        
        @param hosts list of excepted hosts (list of string)
        """
        self.__exceptedHosts = hosts[:]
        Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts)
    
    def addException(self, host):
        """
        Public method to add an exception.
        
        @param host to be excepted (string)
        """
        if host and host not in self.__exceptedHosts:
            self.__exceptedHosts.append(host)
            Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts)
    
    def removeException(self, host):
        """
        Public method to remove an exception.
        
        @param host to be removed from the list of exceptions (string)
        """
        if host in self.__exceptedHosts:
            self.__exceptedHosts.remove(host)
            Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts)
    
    def isHostExcepted(self, host):
        """
        Public slot to check, if a host is excepted.
        
        @param host host to check (string)
        @return flag indicating an exception (boolean)
        """
        return host in self.__exceptedHosts
    
    def showExceptionsDialog(self):
        """
        Public method to show the AdBlock Exceptions dialog.
        
        @return reference to the exceptions dialog (AdBlockExceptionsDialog)
        """
        if self.__adBlockExceptionsDialog is None:
            from .AdBlockExceptionsDialog import AdBlockExceptionsDialog
            self.__adBlockExceptionsDialog = AdBlockExceptionsDialog()
        
        self.__adBlockExceptionsDialog.load(self.__exceptedHosts)
        self.__adBlockExceptionsDialog.show()
        return self.__adBlockExceptionsDialog
class BookmarksManager(QObject):
    """
    Class implementing the bookmarks manager.
    
    @signal entryAdded emitted after a bookmark node has been added
    @signal entryRemoved emitted after a bookmark
        node has been removed
    @signal entryChanged emitted after a bookmark node has been changed
    """
    def __init__(self, parent = None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        QObject.__init__(self, parent)
        
        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)
        self.__bookmarkRootNode = None
        self.__toolbar = None
        self.__menu = None
        self.__bookmarksModel = None
        self.__commands = QUndoStack()
        
        self.connect(self, SIGNAL("entryAdded"), 
                     self.__saveTimer.changeOccurred)
        self.connect(self, SIGNAL("entryRemoved"), 
                     self.__saveTimer.changeOccurred)
        self.connect(self, SIGNAL("entryChanged"), 
                     self.__saveTimer.changeOccurred)
    
    def close(self):
        """
        Public method to close the bookmark manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def undoRedoStack(self):
        """
        Public method to get a reference to the undo stack.
        
        @return reference to the undo stack (QUndoStack)
        """
        return self.__commands
    
    def changeExpanded(self):
        """
        Public method to handle a change of the expanded state.
        """
        self.__saveTimer.changeOccurred()
    
    def load(self):
        """
        Public method to load the bookmarks.
        """
        if self.__loaded:
            return
        
        self.__loaded = True
        
        bookmarkFile = os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel")
        if not QFile.exists(bookmarkFile):
            ba = QByteArray(DefaultBookmarks)
            bookmarkFile = QBuffer(ba)
            bookmarkFile.open(QIODevice.ReadOnly)
        
        reader = XbelReader()
        self.__bookmarkRootNode = reader.read(bookmarkFile)
        if reader.error() != QXmlStreamReader.NoError:
            KQMessageBox.warning(None,
                self.trUtf8("Loading Bookmarks"),
                self.trUtf8("""Error when loading bookmarks on line %1, column %2:\n"""
                            """%3""")\
                    .arg(reader.lineNumber())\
                    .arg(reader.columnNumber())\
                    .arg(reader.errorString()))
        
        others = []
        for index in range(len(self.__bookmarkRootNode.children()) - 1, -1, -1):
            node = self.__bookmarkRootNode.children()[index]
            if node.type() == BookmarkNode.Folder:
                if (node.title == self.trUtf8("Toolbar Bookmarks") or \
                    node.title == BOOKMARKBAR) and \
                   self.__toolbar is None:
                    node.title = self.trUtf8(BOOKMARKBAR)
                    self.__toolbar = node
                
                if (node.title == self.trUtf8("Menu") or \
                    node.title == BOOKMARKMENU) and \
                   self.__menu is None:
                    node.title = self.trUtf8(BOOKMARKMENU)
                    self.__menu = node
            else:
                others.append(node)
            self.__bookmarkRootNode.remove(node)
            
        if len(self.__bookmarkRootNode.children()) > 0:
            raise RuntimeError("Error loading bookmarks.")
        
        if self.__toolbar is None:
            self.__toolbar = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode)
            self.__toolbar.title = self.trUtf8(BOOKMARKBAR)
        else:
            self.__bookmarkRootNode.add(self.__toolbar)
        
        if self.__menu is None:
            self.__menu = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode)
            self.__menu.title = self.trUtf8(BOOKMARKMENU)
        else:
            self.__bookmarkRootNode.add(self.__menu)
        
        for node in others:
            self.__menu.add(node)
        
        self.__convertFromOldBookmarks()
    
    def save(self):
        """
        Public method to save the bookmarks.
        """
        if not self.__loaded:
            return
        
        writer = XbelWriter()
        bookmarkFile = os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel")
        
        # save root folder titles in English (i.e. not localized)
        self.__menu.title = BOOKMARKMENU
        self.__toolbar.title = BOOKMARKBAR
        if not writer.write(bookmarkFile, self.__bookmarkRootNode):
            KQMessageBox.warning(None,
                self.trUtf8("Saving Bookmarks"),
                self.trUtf8("""Error saving bookmarks to <b>%1</b>.""").arg(bookmarkFile))
        
        # restore localized titles
        self.__menu.title = self.trUtf8(BOOKMARKMENU)
        self.__toolbar.title = self.trUtf8(BOOKMARKBAR)
    
    def addBookmark(self, parent, node, row = -1):
        """
        Public method to add a bookmark.
        
        @param parent reference to the node to add to (BookmarkNode)
        @param node reference to the node to add (BookmarkNode)
        @param row row number (integer)
        """
        if not self.__loaded:
            return
        
        command = InsertBookmarksCommand(self, parent, node, row)
        self.__commands.push(command)
    
    def removeBookmark(self, node):
        """
        Public method to remove a bookmark.
        
        @param node reference to the node to be removed (BookmarkNode)
        """
        if not self.__loaded:
            return
        
        parent = node.parent()
        row = parent.children().index(node)
        command = RemoveBookmarksCommand(self, parent, row)
        self.__commands.push(command)
    
    def setTitle(self, node, newTitle):
        """
        Public method to set the title of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param newTitle title to be set (QString)
        """
        if not self.__loaded:
            return
        
        command = ChangeBookmarkCommand(self, node, newTitle, True)
        self.__commands.push(command)
    
    def setUrl(self, node, newUrl):
        """
        Public method to set the URL of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param newUrl URL to be set (QString)
        """
        if not self.__loaded:
            return
        
        command = ChangeBookmarkCommand(self, node, newUrl, False)
        self.__commands.push(command)
    
    def bookmarks(self):
        """
        Public method to get a reference to the root bookmark node.
        
        @return reference to the root bookmark node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__bookmarkRootNode
    
    def menu(self):
        """
        Public method to get a reference to the bookmarks menu node.
        
        @return reference to the bookmarks menu node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__menu
    
    def toolbar(self):
        """
        Public method to get a reference to the bookmarks toolbar node.
        
        @return reference to the bookmarks toolbar node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__toolbar
    
    def bookmarksModel(self):
        """
        Public method to get a reference to the bookmarks model.
        
        @return reference to the bookmarks model (BookmarksModel)
        """
        if self.__bookmarksModel is None:
            self.__bookmarksModel = BookmarksModel(self, self)
        return self.__bookmarksModel
    
    def importBookmarks(self):
        """
        Public method to import bookmarks.
        """
        supportedFormats = QStringList() \
            << self.trUtf8("XBEL bookmarks").append(" (*.xbel *.xml)") \
            << self.trUtf8("HTML Netscape bookmarks").append(" (*.html *.htm)")
        
        fileName = KQFileDialog.getOpenFileName(\
            None,
            self.trUtf8("Import Bookmarks"),
            QString(),
            supportedFormats.join(";;"),
            None)
        if fileName.isEmpty():
            return
        
        reader = XbelReader()
        importRootNode = None
        if fileName.endsWith(".html"):
            inFile = QFile(fileName)
            inFile.open(QIODevice.ReadOnly)
            if inFile.openMode == QIODevice.NotOpen:
                KQMessageBox.warning(None,
                    self.trUtf8("Import Bookmarks"),
                    self.trUtf8("""Error opening bookmarks file <b>%1</b>.""")\
                        .arg(fileName))
                return
            
            webpage = QWebPage()
            webpage.mainFrame().setHtml(QString(inFile.readAll()))
            result = webpage.mainFrame().evaluateJavaScript(extract_js).toByteArray()
            buffer_ = QBuffer(result)
            buffer_.open(QIODevice.ReadOnly)
            importRootNode = reader.read(buffer_)
        else:
            importRootNode = reader.read(fileName)
        
        if reader.error() != QXmlStreamReader.NoError:
            KQMessageBox.warning(None,
                self.trUtf8("Import Bookmarks"),
                self.trUtf8("""Error when importing bookmarks on line %1, column %2:\n"""
                            """%3""")\
                    .arg(reader.lineNumber())\
                    .arg(reader.columnNumber())\
                    .arg(reader.errorString()))
            return
        
        importRootNode.setType(BookmarkNode.Folder)
        importRootNode.title = self.trUtf8("Imported %1")\
            .arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
        self.addBookmark(self.menu(), importRootNode)
    
    def exportBookmarks(self):
        """
        Public method to export the bookmarks.
        """
        fileName = KQFileDialog.getSaveFileName(\
            None,
            self.trUtf8("Export Bookmarks"),
            "eric4_bookmarks.xbel",
            self.trUtf8("XBEL bookmarks").append(" (*.xbel *.xml)"))
        if fileName.isEmpty():
            return
        
        writer = XbelWriter()
        if not writer.write(fileName, self.__bookmarkRootNode):
            KQMessageBox.critical(None,
                self.trUtf8("Exporting Bookmarks"),
                self.trUtf8("""Error exporting bookmarks to <b>%1</b>.""")\
                    .arg(fileName))
    
    def __convertFromOldBookmarks(self):
        """
        Private method to convert the old bookmarks into the new ones.
        """
        bmNames = Preferences.Prefs.settings.value('Bookmarks/Names')
        bmFiles = Preferences.Prefs.settings.value('Bookmarks/Files')
        
        if bmNames.isValid() and bmFiles.isValid():
            bmNames = bmNames.toStringList()
            bmFiles = bmFiles.toStringList()
            if len(bmNames) == len(bmFiles):
                convertedRootNode = BookmarkNode(BookmarkNode.Folder)
                convertedRootNode.title = self.trUtf8("Converted %1")\
                    .arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
                for i in range(len(bmNames)):
                    node = BookmarkNode(BookmarkNode.Bookmark, convertedRootNode)
                    node.title = bmNames[i]
                    url = QUrl(bmFiles[i])
                    if url.scheme().isEmpty():
                        url.setScheme("file")
                    node.url = url.toString()
                self.addBookmark(self.menu(), convertedRootNode)
                
                Preferences.Prefs.settings.remove('Bookmarks')
Пример #52
0
class BookmarksManager(QObject):
    """
    Class implementing the bookmarks manager.
    
    @signal entryAdded(BookmarkNode) emitted after a bookmark node has been
        added
    @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a
        bookmark node has been removed
    @signal entryChanged(BookmarkNode) emitted after a bookmark node has been
        changed
    @signal bookmarksSaved() emitted after the bookmarks were saved
    @signal bookmarksReloaded() emitted after the bookmarks were reloaded
    """
    entryAdded = pyqtSignal(BookmarkNode)
    entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode)
    entryChanged = pyqtSignal(BookmarkNode)
    bookmarksSaved = pyqtSignal()
    bookmarksReloaded = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(BookmarksManager, self).__init__(parent)
        
        self.__saveTimer = AutoSaver(self, self.save)
        self.entryAdded.connect(self.__saveTimer.changeOccurred)
        self.entryRemoved.connect(self.__saveTimer.changeOccurred)
        self.entryChanged.connect(self.__saveTimer.changeOccurred)
        
        self.__initialize()
    
    def __initialize(self):
        """
        Private method to initialize some data.
        """
        self.__loaded = False
        self.__bookmarkRootNode = None
        self.__toolbar = None
        self.__menu = None
        self.__bookmarksModel = None
        self.__commands = QUndoStack()
    
    @classmethod
    def getFileName(cls):
        """
        Class method to get the file name of the bookmark file.
        
        @return name of the bookmark file (string)
        """
        return os.path.join(Utilities.getConfigDir(), "web_browser",
                            "bookmarks.xbel")
    
    def close(self):
        """
        Public method to close the bookmark manager.
        """
        self.__saveTimer.saveIfNeccessary()
    
    def undoRedoStack(self):
        """
        Public method to get a reference to the undo stack.
        
        @return reference to the undo stack (QUndoStack)
        """
        return self.__commands
    
    def changeExpanded(self):
        """
        Public method to handle a change of the expanded state.
        """
        self.__saveTimer.changeOccurred()
    
    def reload(self):
        """
        Public method used to initiate a reloading of the bookmarks.
        """
        self.__initialize()
        self.load()
        self.bookmarksReloaded.emit()
    
    def load(self):
        """
        Public method to load the bookmarks.
        
        @exception RuntimeError raised to indicate an error loading the
            bookmarks
        """
        if self.__loaded:
            return
        
        self.__loaded = True
        
        bookmarkFile = self.getFileName()
        if not QFile.exists(bookmarkFile):
            from . import DefaultBookmarks_rc       # __IGNORE_WARNING__
            bookmarkFile = QFile(":/DefaultBookmarks.xbel")
            bookmarkFile.open(QIODevice.ReadOnly)
        
        from .XbelReader import XbelReader
        reader = XbelReader()
        self.__bookmarkRootNode = reader.read(bookmarkFile)
        if reader.error() != QXmlStreamReader.NoError:
            E5MessageBox.warning(
                None,
                self.tr("Loading Bookmarks"),
                self.tr(
                    """Error when loading bookmarks on line {0},"""
                    """ column {1}:\n {2}""")
                .format(reader.lineNumber(),
                        reader.columnNumber(),
                        reader.errorString()))
        
        others = []
        for index in range(
                len(self.__bookmarkRootNode.children()) - 1, -1, -1):
            node = self.__bookmarkRootNode.children()[index]
            if node.type() == BookmarkNode.Folder:
                if (
                    (node.title == self.tr("Toolbar Bookmarks") or
                     node.title == BOOKMARKBAR) and
                    self.__toolbar is None
                ):
                    node.title = self.tr(BOOKMARKBAR)
                    self.__toolbar = node
                
                if (
                    (node.title == self.tr("Menu") or
                     node.title == BOOKMARKMENU) and
                    self.__menu is None
                ):
                    node.title = self.tr(BOOKMARKMENU)
                    self.__menu = node
            else:
                others.append(node)
            self.__bookmarkRootNode.remove(node)
        
        if len(self.__bookmarkRootNode.children()) > 0:
            raise RuntimeError("Error loading bookmarks.")
        
        if self.__toolbar is None:
            self.__toolbar = BookmarkNode(BookmarkNode.Folder,
                                          self.__bookmarkRootNode)
            self.__toolbar.title = self.tr(BOOKMARKBAR)
        else:
            self.__bookmarkRootNode.add(self.__toolbar)
        
        if self.__menu is None:
            self.__menu = BookmarkNode(BookmarkNode.Folder,
                                       self.__bookmarkRootNode)
            self.__menu.title = self.tr(BOOKMARKMENU)
        else:
            self.__bookmarkRootNode.add(self.__menu)
        
        for node in others:
            self.__menu.add(node)
    
    def save(self):
        """
        Public method to save the bookmarks.
        """
        if not self.__loaded:
            return
        
        from .XbelWriter import XbelWriter
        writer = XbelWriter()
        bookmarkFile = self.getFileName()
        
        # save root folder titles in English (i.e. not localized)
        self.__menu.title = BOOKMARKMENU
        self.__toolbar.title = BOOKMARKBAR
        if not writer.write(bookmarkFile, self.__bookmarkRootNode):
            E5MessageBox.warning(
                None,
                self.tr("Saving Bookmarks"),
                self.tr("""Error saving bookmarks to <b>{0}</b>.""")
                .format(bookmarkFile))
        
        # restore localized titles
        self.__menu.title = self.tr(BOOKMARKMENU)
        self.__toolbar.title = self.tr(BOOKMARKBAR)
        
        self.bookmarksSaved.emit()
    
    def addBookmark(self, parent, node, row=-1):
        """
        Public method to add a bookmark.
        
        @param parent reference to the node to add to (BookmarkNode)
        @param node reference to the node to add (BookmarkNode)
        @param row row number (integer)
        """
        if not self.__loaded:
            return
        
        self.setTimestamp(node, BookmarkNode.TsAdded,
                          QDateTime.currentDateTime())
        
        command = InsertBookmarksCommand(self, parent, node, row)
        self.__commands.push(command)
    
    def removeBookmark(self, node):
        """
        Public method to remove a bookmark.
        
        @param node reference to the node to be removed (BookmarkNode)
        """
        if not self.__loaded:
            return
        
        parent = node.parent()
        row = parent.children().index(node)
        command = RemoveBookmarksCommand(self, parent, row)
        self.__commands.push(command)
    
    def setTitle(self, node, newTitle):
        """
        Public method to set the title of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param newTitle title to be set (string)
        """
        if not self.__loaded:
            return
        
        command = ChangeBookmarkCommand(self, node, newTitle, True)
        self.__commands.push(command)
    
    def setUrl(self, node, newUrl):
        """
        Public method to set the URL of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param newUrl URL to be set (string)
        """
        if not self.__loaded:
            return
        
        command = ChangeBookmarkCommand(self, node, newUrl, False)
        self.__commands.push(command)
    
    def setNodeChanged(self, node):
        """
        Public method to signal changes of bookmarks other than title, URL
        or timestamp.
        
        @param node reference to the bookmark (BookmarkNode)
        """
        self.__saveTimer.changeOccurred()
    
    def setTimestamp(self, node, timestampType, timestamp):
        """
        Public method to set the URL of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param timestampType type of the timestamp to set
            (BookmarkNode.TsAdded, BookmarkNode.TsModified,
            BookmarkNode.TsVisited)
        @param timestamp timestamp to set (QDateTime)
        """
        if not self.__loaded:
            return
        
        assert timestampType in [BookmarkNode.TsAdded,
                                 BookmarkNode.TsModified,
                                 BookmarkNode.TsVisited]
        
        if timestampType == BookmarkNode.TsAdded:
            node.added = timestamp
        elif timestampType == BookmarkNode.TsModified:
            node.modified = timestamp
        elif timestampType == BookmarkNode.TsVisited:
            node.visited = timestamp
        self.__saveTimer.changeOccurred()
    
    def incVisitCount(self, node):
        """
        Public method to increment the visit count of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        """
        if not self.__loaded:
            return
        
        if node:
            node.visitCount += 1
            self.__saveTimer.changeOccurred()
    
    def setVisitCount(self, node, count):
        """
        Public method to set the visit count of a bookmark.
        
        @param node reference to the node to be changed (BookmarkNode)
        @param count visit count to be set (int or str)
        """
        try:
            node.visitCount = int(count)
            self.__saveTimer.changeOccurred()
        except ValueError:
            # ignore invalid values
            pass
    
    def bookmarks(self):
        """
        Public method to get a reference to the root bookmark node.
        
        @return reference to the root bookmark node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__bookmarkRootNode
    
    def menu(self):
        """
        Public method to get a reference to the bookmarks menu node.
        
        @return reference to the bookmarks menu node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__menu
    
    def toolbar(self):
        """
        Public method to get a reference to the bookmarks toolbar node.
        
        @return reference to the bookmarks toolbar node (BookmarkNode)
        """
        if not self.__loaded:
            self.load()
        
        return self.__toolbar
    
    def bookmarksModel(self):
        """
        Public method to get a reference to the bookmarks model.
        
        @return reference to the bookmarks model (BookmarksModel)
        """
        if self.__bookmarksModel is None:
            from .BookmarksModel import BookmarksModel
            self.__bookmarksModel = BookmarksModel(self, self)
        return self.__bookmarksModel
    
    def importBookmarks(self):
        """
        Public method to import bookmarks.
        """
        from .BookmarksImportDialog import BookmarksImportDialog
        dlg = BookmarksImportDialog()
        if dlg.exec_() == QDialog.Accepted:
            importRootNode = dlg.getImportedBookmarks()
            if importRootNode is not None:
                self.addBookmark(self.menu(), importRootNode)
    
    def exportBookmarks(self):
        """
        Public method to export the bookmarks.
        """
        fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
            None,
            self.tr("Export Bookmarks"),
            "eric6_bookmarks.xbel",
            self.tr("XBEL bookmarks (*.xbel);;"
                    "XBEL bookmarks (*.xml);;"
                    "HTML Bookmarks (*.html)"))
        if not fileName:
            return
        
        ext = QFileInfo(fileName).suffix()
        if not ext:
            ex = selectedFilter.split("(*")[1].split(")")[0]
            if ex:
                fileName += ex
        
        ext = QFileInfo(fileName).suffix()
        if ext == "html":
            from .NsHtmlWriter import NsHtmlWriter
            writer = NsHtmlWriter()
        else:
            from .XbelWriter import XbelWriter
            writer = XbelWriter()
        if not writer.write(fileName, self.__bookmarkRootNode):
            E5MessageBox.critical(
                None,
                self.tr("Exporting Bookmarks"),
                self.tr("""Error exporting bookmarks to <b>{0}</b>.""")
                .format(fileName))
    
    def faviconChanged(self, url):
        """
        Public slot to update the icon image for an URL.
        
        @param url URL of the icon to update (QUrl or string)
        """
        if isinstance(url, QUrl):
            url = url.toString()
        nodes = self.bookmarksForUrl(url)
        for node in nodes:
            self.bookmarksModel().entryChanged(node)
    
    def bookmarkForUrl(self, url, start=StartRoot):
        """
        Public method to get a bookmark node for a given URL.
        
        @param url URL of the bookmark to search for (QUrl or string)
        @keyparam start indicator for the start of the search
            (StartRoot, StartMenu, StartToolBar)
        @return bookmark node for the given url (BookmarkNode)
        """
        if start == StartMenu:
            startNode = self.__menu
        elif start == StartToolBar:
            startNode = self.__toolbar
        else:
            startNode = self.__bookmarkRootNode
        if startNode is None:
            return None
        
        if isinstance(url, QUrl):
            url = url.toString()
        
        return self.__searchBookmark(url, startNode)
    
    def __searchBookmark(self, url, startNode):
        """
        Private method get a bookmark node for a given URL.
        
        @param url URL of the bookmark to search for (string)
        @param startNode reference to the node to start searching
            (BookmarkNode)
        @return bookmark node for the given url (BookmarkNode)
        """
        bm = None
        for node in startNode.children():
            if node.type() == BookmarkNode.Folder:
                bm = self.__searchBookmark(url, node)
            elif node.type() == BookmarkNode.Bookmark:
                if node.url == url:
                    bm = node
            if bm is not None:
                return bm
        return None
    
    def bookmarksForUrl(self, url, start=StartRoot):
        """
        Public method to get a list of bookmark nodes for a given URL.
        
        @param url URL of the bookmarks to search for (QUrl or string)
        @keyparam start indicator for the start of the search
            (StartRoot, StartMenu, StartToolBar)
        @return list of bookmark nodes for the given url (list of BookmarkNode)
        """
        if start == StartMenu:
            startNode = self.__menu
        elif start == StartToolBar:
            startNode = self.__toolbar
        else:
            startNode = self.__bookmarkRootNode
        if startNode is None:
            return []
        
        if isinstance(url, QUrl):
            url = url.toString()
        
        return self.__searchBookmarks(url, startNode)
    
    def __searchBookmarks(self, url, startNode):
        """
        Private method get a list of bookmark nodes for a given URL.
        
        @param url URL of the bookmarks to search for (string)
        @param startNode reference to the node to start searching
            (BookmarkNode)
        @return list of bookmark nodes for the given url (list of BookmarkNode)
        """
        bm = []
        for node in startNode.children():
            if node.type() == BookmarkNode.Folder:
                bm.extend(self.__searchBookmarks(url, node))
            elif node.type() == BookmarkNode.Bookmark:
                if node.url == url:
                    bm.append(node)
        return bm
Пример #53
0
 def __init__(self, parent, project):
     """
     Constructor
     
     @param parent the parent (QWidget)
     @param project reference to the project object
     """
     super(TaskViewer, self).__init__(parent)
     
     self.setSortingEnabled(True)
     self.setExpandsOnDoubleClick(False)
     
     self.__headerItem = QTreeWidgetItem(
         ["", "", self.tr("Summary"), self.tr("Filename"),
          self.tr("Line"), ""])
     self.__headerItem.setIcon(
         0, UI.PixmapCache.getIcon("taskCompleted.png"))
     self.__headerItem.setIcon(
         1, UI.PixmapCache.getIcon("taskPriority.png"))
     self.setHeaderItem(self.__headerItem)
     
     self.header().setSortIndicator(2, Qt.AscendingOrder)
     self.__resizeColumns()
     
     self.tasks = []
     self.copyTask = None
     self.projectOpen = False
     self.project = project
     self.projectTasksScanFilter = ""
     
     from .TaskFilter import TaskFilter
     self.taskFilter = TaskFilter()
     self.taskFilter.setActive(False)
     
     self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks)
     
     self.__projectTasksMenu = QMenu(
         self.tr("P&roject Tasks"), self)
     self.__projectTasksMenu.addAction(
         self.tr("&Regenerate project tasks"),
         self.__regenerateProjectTasks)
     self.__projectTasksMenu.addSeparator()
     self.__projectTasksMenu.addAction(
         self.tr("&Configure scan options"),
         self.__configureProjectTasksScanOptions)
     
     self.__menu = QMenu(self)
     self.__menu.addAction(self.tr("&New Task..."), self.__newTask)
     self.subtaskItem = self.__menu.addAction(
         self.tr("New &Sub-Task..."), self.__newSubTask)
     self.__menu.addSeparator()
     self.projectTasksMenuItem = self.__menu.addMenu(
         self.__projectTasksMenu)
     self.__menu.addSeparator()
     self.gotoItem = self.__menu.addAction(
         self.tr("&Go To"), self.__goToTask)
     self.__menu.addSeparator()
     self.copyItem = self.__menu.addAction(
         self.tr("&Copy"), self.__copyTask)
     self.pasteItem = self.__menu.addAction(
         self.tr("&Paste"), self.__pasteTask)
     self.pasteMainItem = self.__menu.addAction(
         self.tr("Paste as &Main Task"), self.__pasteMainTask)
     self.deleteItem = self.__menu.addAction(
         self.tr("&Delete"), self.__deleteTask)
     self.__menu.addSeparator()
     self.markCompletedItem = self.__menu.addAction(
         self.tr("&Mark Completed"), self.__markCompleted)
     self.__menu.addAction(
         self.tr("Delete Completed &Tasks"), self.__deleteCompleted)
     self.__menu.addSeparator()
     self.__menu.addAction(
         self.tr("P&roperties..."), self.__editTaskProperties)
     self.__menu.addSeparator()
     self.__menuFilteredAct = self.__menu.addAction(
         self.tr("&Filtered display"))
     self.__menuFilteredAct.setCheckable(True)
     self.__menuFilteredAct.setChecked(False)
     self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter)
     self.__menu.addAction(
         self.tr("Filter c&onfiguration..."), self.__configureFilter)
     self.__menu.addSeparator()
     self.__menu.addAction(
         self.tr("Resi&ze columns"), self.__resizeColumns)
     self.__menu.addSeparator()
     self.__menu.addAction(self.tr("Configure..."), self.__configure)
     
     self.__backMenu = QMenu(self)
     self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask)
     self.__backMenu.addSeparator()
     self.backProjectTasksMenuItem = self.__backMenu.addMenu(
         self.__projectTasksMenu)
     self.__backMenu.addSeparator()
     self.backPasteItem = self.__backMenu.addAction(
         self.tr("&Paste"), self.__pasteTask)
     self.backPasteMainItem = self.__menu.addAction(
         self.tr("Paste as &Main Task"), self.__pasteMainTask)
     self.__backMenu.addSeparator()
     self.__backMenu.addAction(
         self.tr("Delete Completed &Tasks"), self.__deleteCompleted)
     self.__backMenu.addSeparator()
     self.__backMenuFilteredAct = self.__backMenu.addAction(
         self.tr("&Filtered display"))
     self.__backMenuFilteredAct.setCheckable(True)
     self.__backMenuFilteredAct.setChecked(False)
     self.__backMenuFilteredAct.triggered[bool].connect(
         self.__activateFilter)
     self.__backMenu.addAction(
         self.tr("Filter c&onfiguration..."), self.__configureFilter)
     self.__backMenu.addSeparator()
     self.__backMenu.addAction(
         self.tr("Resi&ze columns"), self.__resizeColumns)
     self.__backMenu.addSeparator()
     self.__backMenu.addAction(
         self.tr("Configure..."), self.__configure)
     
     self.__activating = False
     
     self.setContextMenuPolicy(Qt.CustomContextMenu)
     self.customContextMenuRequested.connect(self.__showContextMenu)
     self.itemActivated.connect(self.__taskItemActivated)
     
     self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
     
     self.__generateTopLevelItems()
Пример #54
0
class DownloadManager(QDialog, Ui_DownloadManager):
    """
    Class implementing the download manager.
    """
    RemoveNever = 0
    RemoveExit = 1
    RemoveSuccessFullDownload = 2
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(DownloadManager, self).__init__(parent)
        self.setupUi(self)
        
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.__model = DownloadModel(self)
        self.__manager = Helpviewer.HelpWindow.HelpWindow\
            .networkAccessManager()
        
        self.__iconProvider = None
        self.__downloads = []
        self.__downloadDirectory = ""
        self.__loaded = False
        
        self.setDownloadDirectory(Preferences.getUI("DownloadPath"))
        
        self.downloadsView.setShowGrid(False)
        self.downloadsView.verticalHeader().hide()
        self.downloadsView.horizontalHeader().hide()
        self.downloadsView.setAlternatingRowColors(True)
        self.downloadsView.horizontalHeader().setStretchLastSection(True)
        self.downloadsView.setModel(self.__model)
        self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.downloadsView.customContextMenuRequested.connect(
            self.__customContextMenuRequested)
        
        self.__load()
    
    def __customContextMenuRequested(self, pos):
        """
        Private slot to handle the context menu request for the bookmarks tree.
        
        @param pos position the context menu was requested (QPoint)
        """
        menu = QMenu()
        
        selectedRowsCount = len(
            self.downloadsView.selectionModel().selectedRows())
        
        if selectedRowsCount == 1:
            row = self.downloadsView.selectionModel().selectedRows()[0].row()
            itm = self.__downloads[row]
            if itm.downloadCanceled():
                menu.addAction(
                    UI.PixmapCache.getIcon("restart.png"),
                    self.tr("Retry"), self.__contextMenuRetry)
            else:
                if itm.downloadedSuccessfully():
                    menu.addAction(
                        UI.PixmapCache.getIcon("open.png"),
                        self.tr("Open"), self.__contextMenuOpen)
                elif itm.downloading():
                    menu.addAction(
                        UI.PixmapCache.getIcon("stopLoading.png"),
                        self.tr("Cancel"), self.__contextMenuCancel)
                    menu.addSeparator()
                menu.addAction(
                    self.tr("Open Containing Folder"),
                    self.__contextMenuOpenFolder)
            menu.addSeparator()
            menu.addAction(
                self.tr("Go to Download Page"),
                self.__contextMenuGotoPage)
            menu.addAction(
                self.tr("Copy Download Link"),
                self.__contextMenuCopyLink)
            menu.addSeparator()
        menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll)
        if selectedRowsCount > 1 or \
           (selectedRowsCount == 1 and
            not self.__downloads[
                self.downloadsView.selectionModel().selectedRows()[0].row()]
                .downloading()):
            menu.addSeparator()
            menu.addAction(
                self.tr("Remove From List"),
                self.__contextMenuRemoveSelected)
        
        menu.exec_(QCursor.pos())
    
    def shutdown(self):
        """
        Public method to stop the download manager.
        """
        self.__saveTimer.changeOccurred()
        self.__saveTimer.saveIfNeccessary()
        self.close()
    
    def activeDownloads(self):
        """
        Public method to get the number of active downloads.
        
        @return number of active downloads (integer)
        """
        count = 0
        
        for download in self.__downloads:
            if download.downloading():
                count += 1
        return count
    
    def allowQuit(self):
        """
        Public method to check, if it is ok to quit.
        
        @return flag indicating allowance to quit (boolean)
        """
        if self.activeDownloads() > 0:
            res = E5MessageBox.yesNo(
                self,
                self.tr(""),
                self.tr("""There are %n downloads in progress.\n"""
                        """Do you want to quit anyway?""", "",
                        self.activeDownloads()),
                icon=E5MessageBox.Warning)
            if not res:
                self.show()
                return False
        return True
    
    def download(self, requestOrUrl, requestFileName=False, mainWindow=None):
        """
        Public method to download a file.
        
        @param requestOrUrl reference to a request object (QNetworkRequest)
            or a URL to be downloaded (QUrl)
        @keyparam requestFileName flag indicating to ask for the
            download file name (boolean)
        @keyparam mainWindow reference to the main window (HelpWindow)
        """
        request = QNetworkRequest(requestOrUrl)
        if request.url().isEmpty():
            return
        self.handleUnsupportedContent(
            self.__manager.get(request),
            requestFileName=requestFileName,
            download=True,
            mainWindow=mainWindow)
    
    def handleUnsupportedContent(self, reply, requestFileName=False,
                                 webPage=None, download=False,
                                 mainWindow=None):
        """
        Public method to handle unsupported content by downloading the
        referenced resource.
        
        @param reply reference to the reply object (QNetworkReply)
        @keyparam requestFileName indicating to ask for a filename
            (boolean)
        @keyparam webPage reference to the web page (HelpWebPage)
        @keyparam download flag indicating a download request (boolean)
        @keyparam mainWindow reference to the main window (HelpWindow)
        """
        if reply is None or reply.url().isEmpty():
            return
        
        size = reply.header(QNetworkRequest.ContentLengthHeader)
        if size == 0:
            return
        
        from .DownloadItem import DownloadItem
        itm = DownloadItem(
            reply=reply, requestFilename=requestFileName,
            webPage=webPage, download=download, parent=self,
            mainWindow=mainWindow)
        self.__addItem(itm)
        
        if itm.canceledFileSelect():
            return
        
        if not self.isVisible():
            self.show()
        
        self.activateWindow()
        self.raise_()
    
    def __addItem(self, itm):
        """
        Private method to add a download to the list of downloads.
        
        @param itm reference to the download item (DownloadItem)
        """
        itm.statusChanged.connect(self.__updateRow)
        itm.downloadFinished.connect(self.__finished)
        
        row = len(self.__downloads)
        self.__model.beginInsertRows(QModelIndex(), row, row)
        self.__downloads.append(itm)
        self.__model.endInsertRows()
        
        self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm)
        icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)
        self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5)
        # just in case the download finished before the constructor returned
        self.__updateRow(itm)
        self.changeOccurred()
        self.__updateActiveItemCount()
    
    def __updateRow(self, itm=None):
        """
        Private slot to update a download item.
        
        @param itm reference to the download item (DownloadItem)
        """
        if itm is None:
            itm = self.sender()
        
        if itm not in self.__downloads:
            return
        
        row = self.__downloads.index(itm)
        
        if self.__iconProvider is None:
            self.__iconProvider = QFileIconProvider()
        
        icon = self.__iconProvider.icon(QFileInfo(itm.fileName()))
        if icon.isNull():
            icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)
        
        oldHeight = self.downloadsView.rowHeight(row)
        self.downloadsView.setRowHeight(
            row,
            max(oldHeight, itm.minimumSizeHint().height() * 1.5))
        
        remove = False
        globalSettings = QWebSettings.globalSettings()
        if not itm.downloading() and \
           globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
            remove = True
        
        if itm.downloadedSuccessfully() and \
           self.removePolicy() == DownloadManager.RemoveSuccessFullDownload:
            remove = True
        
        if remove:
            self.__model.removeRow(row)
        
        self.cleanupButton.setEnabled(
            (len(self.__downloads) - self.activeDownloads()) > 0)
        
        # record the change
        self.changeOccurred()
    
    def removePolicy(self):
        """
        Public method to get the remove policy.
        
        @return remove policy (integer)
        """
        return Preferences.getHelp("DownloadManagerRemovePolicy")
    
    def setRemovePolicy(self, policy):
        """
        Public method to set the remove policy.
        
        @param policy policy to be set
            (DownloadManager.RemoveExit, DownloadManager.RemoveNever,
             DownloadManager.RemoveSuccessFullDownload)
        """
        assert policy in (DownloadManager.RemoveExit,
                          DownloadManager.RemoveNever,
                          DownloadManager.RemoveSuccessFullDownload)
        
        if policy == self.removePolicy():
            return
        
        Preferences.setHelp("DownloadManagerRemovePolicy", self.policy)
    
    def save(self):
        """
        Public method to save the download settings.
        """
        if not self.__loaded:
            return
        
        Preferences.setHelp("DownloadManagerSize", self.size())
        Preferences.setHelp("DownloadManagerPosition", self.pos())
        if self.removePolicy() == DownloadManager.RemoveExit:
            return
        
        downloads = []
        for download in self.__downloads:
            downloads.append(download.getData())
        Preferences.setHelp("DownloadManagerDownloads", downloads)
    
    def __load(self):
        """
        Private method to load the download settings.
        """
        if self.__loaded:
            return
        
        size = Preferences.getHelp("DownloadManagerSize")
        if size.isValid():
            self.resize(size)
        pos = Preferences.getHelp("DownloadManagerPosition")
        self.move(pos)
        
        downloads = Preferences.getHelp("DownloadManagerDownloads")
        for download in downloads:
            if not download[0].isEmpty() and \
               download[1] != "":
                from .DownloadItem import DownloadItem
                itm = DownloadItem(parent=self)
                itm.setData(download)
                self.__addItem(itm)
        self.cleanupButton.setEnabled(
            (len(self.__downloads) - self.activeDownloads()) > 0)
        
        self.__loaded = True
        self.__updateActiveItemCount()
    
    def cleanup(self):
        """
        Public slot to cleanup the downloads.
        """
        self.on_cleanupButton_clicked()
    
    @pyqtSlot()
    def on_cleanupButton_clicked(self):
        """
        Private slot cleanup the downloads.
        """
        if len(self.__downloads) == 0:
            return
        
        self.__model.removeRows(0, len(self.__downloads))
        if len(self.__downloads) == 0 and \
           self.__iconProvider is not None:
            self.__iconProvider = None
        
        self.changeOccurred()
        self.__updateActiveItemCount()
    
    def __updateItemCount(self):
        """
        Private method to update the count label.
        """
        count = len(self.__downloads)
        self.countLabel.setText(self.tr("%n Download(s)", "", count))
    
    def __updateActiveItemCount(self):
        """
        Private method to update the window title.
        """
        count = self.activeDownloads()
        if count > 0:
            self.setWindowTitle(
                self.tr("Downloading %n file(s)", "", count))
        else:
            self.setWindowTitle(self.tr("Downloads"))
    
    def __finished(self):
        """
        Private slot to handle a finished download.
        """
        self.__updateActiveItemCount()
        if self.isVisible():
            QApplication.alert(self)
    
    def setDownloadDirectory(self, directory):
        """
        Public method to set the current download directory.
        
        @param directory current download directory (string)
        """
        self.__downloadDirectory = directory
        if self.__downloadDirectory != "":
            self.__downloadDirectory += "/"
    
    def downloadDirectory(self):
        """
        Public method to get the current download directory.
        
        @return current download directory (string)
        """
        return self.__downloadDirectory
    
    def count(self):
        """
        Public method to get the number of downloads.
        
        @return number of downloads (integer)
        """
        return len(self.__downloads)
    
    def downloads(self):
        """
        Public method to get a reference to the downloads.
        
        @return reference to the downloads (list of DownloadItem)
        """
        return self.__downloads
    
    def changeOccurred(self):
        """
        Public method to signal a change.
        """
        self.__saveTimer.changeOccurred()
        self.__updateItemCount()
    
    ###########################################################################
    ## Context menu related methods below
    ###########################################################################
    
    def __currentItem(self):
        """
        Private method to get a reference to the current item.
        
        @return reference to the current item (DownloadItem)
        """
        index = self.downloadsView.currentIndex()
        if index and index.isValid():
            row = index.row()
            return self.__downloads[row]
        
        return None
    
    def __contextMenuRetry(self):
        """
        Private method to retry of the download.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.retry()
    
    def __contextMenuOpen(self):
        """
        Private method to open the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFile()
    
    def __contextMenuOpenFolder(self):
        """
        Private method to open the folder containing the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFolder()
    
    def __contextMenuCancel(self):
        """
        Private method to cancel the current download.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.cancelDownload()
    
    def __contextMenuGotoPage(self):
        """
        Private method to open the download page.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl()
            Helpviewer.HelpWindow.HelpWindow.mainWindow().openUrl(url, "")
    
    def __contextMenuCopyLink(self):
        """
        Private method to copy the download link to the clipboard.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl().toString()
            QApplication.clipboard().setText(url)
    
    def __contextMenuSelectAll(self):
        """
        Private method to select all downloads.
        """
        self.downloadsView.selectAll()
    
    def __contextMenuRemoveSelected(self):
        """
        Private method to remove the selected downloads from the list.
        """
        self.downloadsView.removeSelected()
Пример #55
0
class TaskViewer(QTreeWidget):
    """
    Class implementing the task viewer.
    
    @signal displayFile(str, int) emitted to go to a file task
    """
    displayFile = pyqtSignal(str, int)
    
    def __init__(self, parent, project):
        """
        Constructor
        
        @param parent the parent (QWidget)
        @param project reference to the project object
        """
        super(TaskViewer, self).__init__(parent)
        
        self.setSortingEnabled(True)
        self.setExpandsOnDoubleClick(False)
        
        self.__headerItem = QTreeWidgetItem(
            ["", "", self.tr("Summary"), self.tr("Filename"),
             self.tr("Line"), ""])
        self.__headerItem.setIcon(
            0, UI.PixmapCache.getIcon("taskCompleted.png"))
        self.__headerItem.setIcon(
            1, UI.PixmapCache.getIcon("taskPriority.png"))
        self.setHeaderItem(self.__headerItem)
        
        self.header().setSortIndicator(2, Qt.AscendingOrder)
        self.__resizeColumns()
        
        self.tasks = []
        self.copyTask = None
        self.projectOpen = False
        self.project = project
        self.projectTasksScanFilter = ""
        
        from .TaskFilter import TaskFilter
        self.taskFilter = TaskFilter()
        self.taskFilter.setActive(False)
        
        self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks)
        
        self.__projectTasksMenu = QMenu(
            self.tr("P&roject Tasks"), self)
        self.__projectTasksMenu.addAction(
            self.tr("&Regenerate project tasks"),
            self.__regenerateProjectTasks)
        self.__projectTasksMenu.addSeparator()
        self.__projectTasksMenu.addAction(
            self.tr("&Configure scan options"),
            self.__configureProjectTasksScanOptions)
        
        self.__menu = QMenu(self)
        self.__menu.addAction(self.tr("&New Task..."), self.__newTask)
        self.subtaskItem = self.__menu.addAction(
            self.tr("New &Sub-Task..."), self.__newSubTask)
        self.__menu.addSeparator()
        self.projectTasksMenuItem = self.__menu.addMenu(
            self.__projectTasksMenu)
        self.__menu.addSeparator()
        self.gotoItem = self.__menu.addAction(
            self.tr("&Go To"), self.__goToTask)
        self.__menu.addSeparator()
        self.copyItem = self.__menu.addAction(
            self.tr("&Copy"), self.__copyTask)
        self.pasteItem = self.__menu.addAction(
            self.tr("&Paste"), self.__pasteTask)
        self.pasteMainItem = self.__menu.addAction(
            self.tr("Paste as &Main Task"), self.__pasteMainTask)
        self.deleteItem = self.__menu.addAction(
            self.tr("&Delete"), self.__deleteTask)
        self.__menu.addSeparator()
        self.markCompletedItem = self.__menu.addAction(
            self.tr("&Mark Completed"), self.__markCompleted)
        self.__menu.addAction(
            self.tr("Delete Completed &Tasks"), self.__deleteCompleted)
        self.__menu.addSeparator()
        self.__menu.addAction(
            self.tr("P&roperties..."), self.__editTaskProperties)
        self.__menu.addSeparator()
        self.__menuFilteredAct = self.__menu.addAction(
            self.tr("&Filtered display"))
        self.__menuFilteredAct.setCheckable(True)
        self.__menuFilteredAct.setChecked(False)
        self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter)
        self.__menu.addAction(
            self.tr("Filter c&onfiguration..."), self.__configureFilter)
        self.__menu.addSeparator()
        self.__menu.addAction(
            self.tr("Resi&ze columns"), self.__resizeColumns)
        self.__menu.addSeparator()
        self.__menu.addAction(self.tr("Configure..."), self.__configure)
        
        self.__backMenu = QMenu(self)
        self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask)
        self.__backMenu.addSeparator()
        self.backProjectTasksMenuItem = self.__backMenu.addMenu(
            self.__projectTasksMenu)
        self.__backMenu.addSeparator()
        self.backPasteItem = self.__backMenu.addAction(
            self.tr("&Paste"), self.__pasteTask)
        self.backPasteMainItem = self.__menu.addAction(
            self.tr("Paste as &Main Task"), self.__pasteMainTask)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(
            self.tr("Delete Completed &Tasks"), self.__deleteCompleted)
        self.__backMenu.addSeparator()
        self.__backMenuFilteredAct = self.__backMenu.addAction(
            self.tr("&Filtered display"))
        self.__backMenuFilteredAct.setCheckable(True)
        self.__backMenuFilteredAct.setChecked(False)
        self.__backMenuFilteredAct.triggered[bool].connect(
            self.__activateFilter)
        self.__backMenu.addAction(
            self.tr("Filter c&onfiguration..."), self.__configureFilter)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(
            self.tr("Resi&ze columns"), self.__resizeColumns)
        self.__backMenu.addSeparator()
        self.__backMenu.addAction(
            self.tr("Configure..."), self.__configure)
        
        self.__activating = False
        
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.__showContextMenu)
        self.itemActivated.connect(self.__taskItemActivated)
        
        self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
        
        self.__generateTopLevelItems()
    
    def __generateTopLevelItems(self):
        """
        Private method to generate the 'Extracted Tasks' item.
        """
        self.__extractedItem = QTreeWidgetItem(self,
                                               [self.tr("Extracted Tasks")])
        self.__manualItem = QTreeWidgetItem(self,
                                            [self.tr("Manual Tasks")])
        for itm in [self.__extractedItem, self.__manualItem]:
            itm.setFirstColumnSpanned(True)
            itm.setExpanded(True)
            itm.setHidden(True)
            font = itm.font(0)
            font.setUnderline(True)
            itm.setFont(0, font)
    
    def __checkTopLevelItems(self):
        """
        Private slot to check the 'Extracted Tasks' item for children.
        """
        for itm in [self.__extractedItem, self.__manualItem]:
            visibleCount = itm.childCount()
            for index in range(itm.childCount()):
                if itm.child(index).isHidden():
                    visibleCount -= 1
            itm.setHidden(visibleCount == 0)
    
    def __resort(self):
        """
        Private method to resort the tree.
        """
        self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder())
    
    def __resizeColumns(self):
        """
        Private method to resize the list columns.
        """
        self.header().resizeSections(QHeaderView.ResizeToContents)
        self.header().setStretchLastSection(True)
    
    def findParentTask(self, parentUid):
        """
        Public method to find a parent task by its ID.
        
        @param parentUid uid of the parent task (string)
        @return reference to the task (Task)
        """
        if not parentUid:
            return None
        
        parentTask = None
        for task in self.tasks:
            if task.getUuid() == parentUid:
                parentTask = task
                break
        
        return parentTask
    
    def __refreshDisplay(self):
        """
        Private method to refresh the display.
        """
        for task in self.tasks:
            task.setHidden(not self.taskFilter.showTask(task))
        
        self.__checkTopLevelItems()
        self.__resort()
        self.__resizeColumns()
    
    def __taskItemActivated(self, itm, col):
        """
        Private slot to handle the activation of an item.
        
        @param itm reference to the activated item (QTreeWidgetItem)
        @param col column the item was activated in (integer)
        """
        if not self.__activating and \
                itm is not self.__extractedItem and \
                itm is not self.__manualItem:
            self.__activating = True
            fn = itm.getFilename()
            if fn:
                self.displayFile.emit(fn, itm.getLineno())
            else:
                self.__editTaskProperties()
            self.__activating = False

    def __showContextMenu(self, coord):
        """
        Private slot to show the context menu of the list.
        
        @param coord the position of the mouse pointer (QPoint)
        """
        itm = self.itemAt(coord)
        coord = self.mapToGlobal(coord)
        if itm is None or \
                itm is self.__extractedItem or \
                itm is self.__manualItem:
            self.backProjectTasksMenuItem.setEnabled(self.projectOpen)
            if self.copyTask:
                self.backPasteItem.setEnabled(True)
                self.backPasteMainItem.setEnabled(True)
            else:
                self.backPasteItem.setEnabled(False)
                self.backPasteMainItem.setEnabled(False)
            self.__backMenu.popup(coord)
        else:
            self.projectTasksMenuItem.setEnabled(self.projectOpen)
            if itm.getFilename():
                self.gotoItem.setEnabled(True)
                self.deleteItem.setEnabled(True)
                self.markCompletedItem.setEnabled(False)
                self.copyItem.setEnabled(False)
                self.subtaskItem.setEnabled(False)
            else:
                self.gotoItem.setEnabled(False)
                self.deleteItem.setEnabled(True)
                self.markCompletedItem.setEnabled(True)
                self.copyItem.setEnabled(True)
                self.subtaskItem.setEnabled(True)
            if self.copyTask:
                self.pasteItem.setEnabled(True)
                self.pasteMainItem.setEnabled(True)
            else:
                self.pasteItem.setEnabled(False)
                self.pasteMainItem.setEnabled(False)
            
            self.__menu.popup(coord)
    
    def setProjectOpen(self, o=False):
        """
        Public slot to set the project status.
        
        @param o flag indicating the project status
        """
        self.projectOpen = o
    
    def addTask(self, summary, priority=1, filename="", lineno=0,
                completed=False, _time=0, isProjectTask=False,
                taskType=Task.TypeTodo, description="", uid="",
                parentTask=None):
        """
        Public slot to add a task.
        
        @param summary summary text of the task (string)
        @param priority priority of the task (0=high, 1=normal, 2=low)
        @param filename filename containing the task (string)
        @param lineno line number containing the task (integer)
        @param completed flag indicating completion status (boolean)
        @param _time creation time of the task (float, if 0 use current time)
        @param isProjectTask flag indicating a task related to the current
            project (boolean)
        @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo,
            Task.TypeWarning, Task.TypeNote)
        @param description explanatory text of the task (string)
        @param uid unique id of the task (string)
        @param parentTask reference to the parent task item (Task)
        @return reference to the task item (Task)
        """
        if parentTask:
            parentUid = parentTask.getUuid()
        else:
            parentUid = ""
        task = Task(summary, priority, filename, lineno, completed,
                    _time, isProjectTask, taskType,
                    self.project, description, uid, parentUid)
        self.tasks.append(task)
        if parentTask:
            parentTask.addChild(task)
            parentTask.setExpanded(True)
        elif filename:
            self.__extractedItem.addChild(task)
        else:
            self.__manualItem.addChild(task)
        task.setHidden(not self.taskFilter.showTask(task))
        
        self.__checkTopLevelItems()
        self.__resort()
        self.__resizeColumns()
        
        if isProjectTask:
            self.__projectTasksSaveTimer.changeOccurred()
        
        return task
    
    def addFileTask(self, summary, filename, lineno, taskType=Task.TypeTodo,
                    description=""):
        """
        Public slot to add a file related task.
        
        @param summary summary text of the task (string)
        @param filename filename containing the task (string)
        @param lineno line number containing the task (integer)
        @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo,
            Task.TypeWarning, Task.TypeNote)
        @param description explanatory text of the task (string)
        """
        self.addTask(summary, filename=filename, lineno=lineno,
                     isProjectTask=(
                         self.project and
                         self.project.isProjectSource(filename)),
                     taskType=taskType, description=description)
    
    def getProjectTasks(self):
        """
        Public method to retrieve all project related tasks.
        
        @return copy of tasks (list of Task)
        """
        tasks = [task for task in self.tasks if task.isProjectTask()]
        return tasks[:]
    
    def getGlobalTasks(self):
        """
        Public method to retrieve all non project related tasks.
        
        @return copy of tasks (list of Task)
        """
        tasks = [task for task in self.tasks if not task.isProjectTask()]
        return tasks[:]
    
    def clearTasks(self):
        """
        Public slot to clear all tasks from display.
        """
        self.tasks = []
        self.clear()
        self.__generateTopLevelItems()
    
    def clearProjectTasks(self, fileOnly=False):
        """
        Public slot to clear project related tasks.
        
        @keyparam fileOnly flag indicating to clear only file related
            project tasks (boolean)
        """
        for task in reversed(self.tasks[:]):
            if (fileOnly and task.isProjectFileTask()) or \
               (not fileOnly and task.isProjectTask()):
                if self.copyTask == task:
                    self.copyTask = None
                parent = task.parent()
                parent.removeChild(task)
                self.tasks.remove(task)
                del task
        
        self.__checkTopLevelItems()
        self.__resort()
        self.__resizeColumns()
    
    def clearFileTasks(self, filename, conditionally=False):
        """
        Public slot to clear all tasks related to a file.
        
        @param filename name of the file (string)
        @param conditionally flag indicating to clear the tasks of the file
            checking some conditions (boolean)
        """
        if conditionally:
            if self.project and self.project.isProjectSource(filename):
                # project related tasks will not be cleared
                return
            if not Preferences.getTasks("ClearOnFileClose"):
                return
        for task in self.tasks[:]:
            if task.getFilename() == filename:
                if self.copyTask == task:
                    self.copyTask = None
                self.__extractedItem.removeChild(task)
                self.tasks.remove(task)
                if task.isProjectTask:
                    self.__projectTasksSaveTimer.changeOccurred()
                del task
        
        self.__checkTopLevelItems()
        self.__resort()
        self.__resizeColumns()
    
    def __editTaskProperties(self):
        """
        Private slot to handle the "Properties" context menu entry.
        """
        from .TaskPropertiesDialog import TaskPropertiesDialog
        task = self.currentItem()
        dlg = TaskPropertiesDialog(task, self, self.projectOpen)
        ro = task.getFilename() != ""
        if ro:
            dlg.setReadOnly()
        if dlg.exec_() == QDialog.Accepted and not ro:
            summary, priority, completed, isProjectTask, description = \
                dlg.getData()
            task.setSummary(summary)
            task.setPriority(priority)
            task.setCompleted(completed)
            task.setProjectTask(isProjectTask)
            task.setDescription(description)
            self.__projectTasksSaveTimer.changeOccurred()
    
    def __newTask(self):
        """
        Private slot to handle the "New Task" context menu entry.
        """
        from .TaskPropertiesDialog import TaskPropertiesDialog
        dlg = TaskPropertiesDialog(None, self, self.projectOpen)
        if dlg.exec_() == QDialog.Accepted:
            summary, priority, completed, isProjectTask, description = \
                dlg.getData()
            self.addTask(summary, priority, completed=completed,
                         isProjectTask=isProjectTask, description=description)
    
    def __newSubTask(self):
        """
        Private slot to handle the "New Sub-Task" context menu entry.
        """
        parentTask = self.currentItem()
        projectTask = parentTask.isProjectTask()
        
        from .TaskPropertiesDialog import TaskPropertiesDialog
        dlg = TaskPropertiesDialog(None, self, self.projectOpen)
        dlg.setSubTaskMode(projectTask)
        if dlg.exec_() == QDialog.Accepted:
            summary, priority, completed, isProjectTask, description = \
                dlg.getData()
            self.addTask(summary, priority, completed=completed,
                         isProjectTask=isProjectTask, description=description,
                         parentTask=parentTask)
    
    def __markCompleted(self):
        """
        Private slot to handle the "Mark Completed" context menu entry.
        """
        task = self.currentItem()
        task.setCompleted(True)
    
    def __deleteCompleted(self):
        """
        Private slot to handle the "Delete Completed Tasks" context menu entry.
        """
        for task in reversed(self.tasks[:]):
            if task.isCompleted():
                if self.copyTask == task:
                    self.copyTask = None
                parent = task.parent()
                parent.removeChild(task)
                self.tasks.remove(task)
                if task.isProjectTask:
                    self.__projectTasksSaveTimer.changeOccurred()
                del task
        
        self.__checkTopLevelItems()
        self.__resort()
        self.__resizeColumns()
        
        ci = self.currentItem()
        if ci:
            ind = self.indexFromItem(ci, self.currentColumn())
            self.scrollTo(ind, QAbstractItemView.PositionAtCenter)
    
    def __copyTask(self):
        """
        Private slot to handle the "Copy" context menu entry.
        """
        task = self.currentItem()
        self.copyTask = task
    
    def __pasteTask(self):
        """
        Private slot to handle the "Paste" context menu entry.
        """
        if self.copyTask:
            parent = self.copyTask.parent()
            if not isinstance(parent, Task):
                parent = None
            
            self.addTask(self.copyTask.summary,
                         priority=self.copyTask.priority,
                         completed=self.copyTask.completed,
                         description=self.copyTask.description,
                         isProjectTask=self.copyTask._isProjectTask,
                         parentTask=parent)
    
    def __pasteMainTask(self):
        """
        Private slot to handle the "Paste as Main Task" context menu entry.
        """
        if self.copyTask:
            self.addTask(self.copyTask.summary,
                         priority=self.copyTask.priority,
                         completed=self.copyTask.completed,
                         description=self.copyTask.description,
                         isProjectTask=self.copyTask._isProjectTask)
    
    def __deleteSubTasks(self, task):
        """
        Private method to delete all sub-tasks.
        
        @param task task to delete sub-tasks of (Task)
        """
        for subtask in task.takeChildren():
            if self.copyTask == subtask:
                self.copyTask = None
            if subtask.childCount() > 0:
                self.__deleteSubTasks(subtask)
            self.tasks.remove(subtask)
    
    def __deleteTask(self):
        """
        Private slot to handle the "Delete Task" context menu entry.
        """
        task = self.currentItem()
        if self.copyTask == task:
            self.copyTask = None
        if task.childCount() > 0:
            self.__deleteSubTasks(task)
        parent = task.parent()
        parent.removeChild(task)
        self.tasks.remove(task)
        if task.isProjectTask:
            self.__projectTasksSaveTimer.changeOccurred()
        del task
        
        self.__checkTopLevelItems()
        self.__resort()
        self.__resizeColumns()
        
        ci = self.currentItem()
        if ci:
            ind = self.indexFromItem(ci, self.currentColumn())
            self.scrollTo(ind, QAbstractItemView.PositionAtCenter)
    
    def __goToTask(self):
        """
        Private slot to handle the "Go To" context menu entry.
        """
        task = self.currentItem()
        self.displayFile.emit(task.getFilename(), task.getLineno())

    def handlePreferencesChanged(self):
        """
        Public slot to react to changes of the preferences.
        """
        for task in self.tasks:
            task.colorizeTask()

    def __activateFilter(self, on):
        """
        Private slot to handle the "Filtered display" context menu entry.
        
        @param on flag indicating the filter state (boolean)
        """
        if on and not self.taskFilter.hasActiveFilter():
            res = E5MessageBox.yesNo(
                self,
                self.tr("Activate task filter"),
                self.tr(
                    """The task filter doesn't have any active filters."""
                    """ Do you want to configure the filter settings?"""),
                yesDefault=True)
            if not res:
                on = False
            else:
                self.__configureFilter()
                on = self.taskFilter.hasActiveFilter()
        
        self.taskFilter.setActive(on)
        self.__menuFilteredAct.setChecked(on)
        self.__backMenuFilteredAct.setChecked(on)
        self.__refreshDisplay()
    
    def __configureFilter(self):
        """
        Private slot to handle the "Configure filter" context menu entry.
        """
        from .TaskFilterConfigDialog import TaskFilterConfigDialog
        dlg = TaskFilterConfigDialog(self.taskFilter)
        if dlg.exec_() == QDialog.Accepted:
            dlg.configureTaskFilter(self.taskFilter)
            self.__refreshDisplay()

    def __configureProjectTasksScanOptions(self):
        """
        Private slot to configure scan options for project tasks.
        """
        filter, ok = QInputDialog.getText(
            self,
            self.tr("Scan Filter Patterns"),
            self.tr("Enter filename patterns of files"
                    " to be excluded separated by a comma:"),
            QLineEdit.Normal,
            self.projectTasksScanFilter)
        if ok:
            self.projectTasksScanFilter = filter
    
    def __regenerateProjectTasks(self):
        """
        Private slot to handle the "Regenerated project tasks" context menu
        entry.
        """
        markers = {
            Task.TypeWarning:
            Preferences.getTasks("TasksWarningMarkers").split(),
            Task.TypeNote: Preferences.getTasks("TasksNoteMarkers").split(),
            Task.TypeTodo: Preferences.getTasks("TasksTodoMarkers").split(),
            Task.TypeFixme: Preferences.getTasks("TasksFixmeMarkers").split(),
        }
        files = self.project.pdata["SOURCES"]
        
        # apply file filter
        filterList = [f.strip() for f in self.projectTasksScanFilter.split(",")
                      if f.strip()]
        if filterList:
            for filter in filterList:
                files = [f for f in files if not fnmatch.fnmatch(f, filter)]
        
        # remove all project tasks
        self.clearProjectTasks(fileOnly=True)
        
        # now process them
        progress = E5ProgressDialog(
            self.tr("Extracting project tasks..."),
            self.tr("Abort"), 0, len(files), self.tr("%v/%m Files"))
        progress.setMinimumDuration(0)
        progress.setWindowTitle(self.tr("Tasks"))
        count = 0
        
        for file in files:
            progress.setLabelText(
                self.tr("Extracting project tasks...\n{0}").format(file))
            progress.setValue(count)
            QApplication.processEvents()
            if progress.wasCanceled():
                break
            
            fn = os.path.join(self.project.ppath, file)
            # read the file and split it into textlines
            try:
                text, encoding = Utilities.readEncodedFile(fn)
                lines = text.splitlines()
            except (UnicodeError, IOError):
                count += 1
                progress.setValue(count)
                continue
            
            # now search tasks and record them
            lineIndex = 0
            for line in lines:
                lineIndex += 1
                shouldBreak = False
                
                for taskType, taskMarkers in markers.items():
                    for taskMarker in taskMarkers:
                        index = line.find(taskMarker)
                        if index > -1:
                            task = line[index:]
                            self.addFileTask(task, fn, lineIndex, taskType)
                            shouldBreak = True
                            break
                    if shouldBreak:
                        break
            
            count += 1
            
        progress.setValue(len(files))
    
    def __configure(self):
        """
        Private method to open the configuration dialog.
        """
        e5App().getObject("UserInterface").showPreferences("tasksPage")
    
    def saveProjectTasks(self):
        """
        Public method to write the project tasks.
        """
        if self.projectOpen and Preferences.getTasks("TasksProjectAutoSave"):
            self.project.writeTasks()
Пример #56
0
class CookieJar(QNetworkCookieJar):
    """
    Class implementing a QNetworkCookieJar subclass with various accept policies.
    
    @signal cookiesChanged() emitted after the cookies have been changed
    """
    JAR_VERSION = 23
    
    AcceptAlways                    = 0
    AcceptNever                     = 1
    AcceptOnlyFromSitesNavigatedTo  = 2

    KeepUntilExpire     = 0
    KeepUntilExit       = 1
    KeepUntilTimeLimit  = 2
    
    Allow           = 0
    Block           = 1
    AllowForSession = 2
    
    def __init__(self, parent = None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        QNetworkCookieJar.__init__(self, parent)
        
        self.__loaded = False
        self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo
        self.__saveTimer = AutoSaver(self, self.save)
        
        self.__cookiesFile = os.path.join(Utilities.getConfigDir(), 
                                          "browser", "cookies.ini")
    
    def saveCookies(self, cookiesList):
        """
        Public method to save the cookies.
        
        @param cookiesList list of cookies to be saved
        @return saved cookies as a byte array (QByteArray)
        """
        data = QByteArray()
        stream = QDataStream(data, QIODevice.WriteOnly)
        stream.writeUInt16(self.JAR_VERSION)
        stream.writeUInt32(len(cookiesList))
        for cookie in cookiesList:
            stream << cookie.toRawForm()
        
        return data
    
    def loadCookies(self, cookies):
        """
        Public method to restore the saved cookies.
        
        @param cookies byte array containing the saved cookies (QByteArray)
        @return list of cookies
        """
        if cookies.isEmpty():
            return []
        
        cookiesList = []
        data = QByteArray(cookies)
        stream = QDataStream(data, QIODevice.ReadOnly)
        
        version = stream.readUInt16()
        if version != self.JAR_VERSION:
            return []
        
        noCookies = stream.readUInt32()
        
        rawCookie = QByteArray()
        while not stream.atEnd():
            stream >> rawCookie
            newCookies = QNetworkCookie.parseCookies(rawCookie)
            for newCookie in newCookies:
                cookiesList.append(newCookie)
        
        return cookiesList
    
    def close(self):
        """
        Public slot to close the cookie jar.
        """
        if self.__loaded and self.__keepCookies == self.KeepUntilExit:
            self.clear()
        self.__saveTimer.saveIfNeccessary()
    
    def clear(self):
        """
        Public method to clear all cookies.
        """
        if not self.__loaded:
            self.load()
        
        self.setAllCookies([])
        self.__saveTimer.changeOccurred()
        self.emit(SIGNAL("cookiesChanged()"))
    
    def load(self):
        """
        Public method to load the cookies.
        """
        if self.__loaded:
            return
        
        cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat)
        
        # load cookies
        cookies = cookieSettings.value("Cookies")
        if cookies.isValid():
            cookiesList = self.loadCookies(cookies.toByteArray())
        else:
            cookiesList = []
        self.setAllCookies(cookiesList)
        
        # load exceptions
        self.__exceptionsBlock = \
            cookieSettings.value("Exceptions/block").toStringList()
        self.__exceptionsAllow = \
            cookieSettings.value("Exceptions/allow").toStringList()
        self.__exceptionsAllowForSession = \
            cookieSettings.value("Exceptions/allowForSession").toStringList()
        self.__exceptionsBlock.sort()
        self.__exceptionsAllow.sort()
        self.__exceptionsAllowForSession.sort()
        
        self.__acceptCookies = Preferences.getHelp("AcceptCookies")
        self.__keepCookies = Preferences.getHelp("KeepCookiesUntil")
        if self.__keepCookies == self.KeepUntilExit:
            self.setAllCookies([])
        
        self.__filterTrackingCookies = Preferences.getHelp("FilterTrackingCookies")
        
        self.__loaded = True
        self.emit(SIGNAL("cookiesChanged()"))
    
    def save(self):
        """
        Public method to save the cookies.
        """
        if not self.__loaded:
            return
        
        self.__purgeOldCookies()
        
        cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat)
        
        cookiesList = self.allCookies()
        for index in range(len(cookiesList) -1, -1, -1):
            if cookiesList[index].isSessionCookie():
                del cookiesList[index]
        cookies = self.saveCookies(cookiesList)
        
        cookieSettings.setValue("Cookies", 
                                QVariant(cookies))
        cookieSettings.setValue("Exceptions/block", 
                                QVariant(self.__exceptionsBlock))
        cookieSettings.setValue("Exceptions/allow", 
                                QVariant(self.__exceptionsAllow))
        cookieSettings.setValue("Exceptions/allowForSession", 
                                QVariant(self.__exceptionsAllowForSession))
        
        Preferences.setHelp("AcceptCookies", self.__acceptCookies)
        Preferences.setHelp("KeepCookiesUntil", self.__keepCookies)
        Preferences.setHelp("FilterTrackingCookies", int(self.__filterTrackingCookies))
    
    def __purgeOldCookies(self):
        """
        Private method to purge old cookies
        """
        cookies = self.allCookies()
        if len(cookies) == 0:
            return
        
        oldCount = len(cookies)
        now = QDateTime.currentDateTime()
        for index in range(len(cookies) - 1, -1, -1):
            if not cookies[index].isSessionCookie() and \
               cookies[index].expirationDate() < now:
                del cookies[index]
        if oldCount == len(cookies):
            return
        self.setAllCookies(cookies)
        self.emit(SIGNAL("cookiesChanged()"))
    
    def cookiesForUrl(self, url):
        """
        Public method to get the cookies for a URL.
        
        @param url URL to get cookies for (QUrl)
        @return list of cookies (list of QNetworkCookie)
        """
        if not self.__loaded:
            self.load()
        
        globalSettings = QWebSettings.globalSettings()
        if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
            return []
        
        return QNetworkCookieJar.cookiesForUrl(self, url)
    
    def setCookiesFromUrl(self, cookieList, url):
        """
        Public method to set cookies for a URL.
        
        @param cookieList list of cookies to set (list of QNetworkCookie)
        @param url url to set cookies for (QUrl)
        @return flag indicating cookies were set (boolean)
        """
        if not self.__loaded:
            self.load()
        
        globalSettings = QWebSettings.globalSettings()
        if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
            return False
        
        host = url.host()
        eBlock = self.__isOnDomainList(self.__exceptionsBlock, host)
        eAllow = not eBlock and \
                 self.__isOnDomainList(self.__exceptionsAllow, host)
        eAllowSession = not eBlock and \
                        not eAllow and \
                        self.__isOnDomainList(self.__exceptionsAllowForSession, host)
        
        addedCookies = False
        acceptInitially = self.__acceptCookies != self.AcceptNever
        if (acceptInitially and not eBlock) or \
           (not acceptInitially and (eAllow or eAllowSession)):
            # url domain == cookie domain
            soon = QDateTime.currentDateTime()
            soon = soon.addDays(90)
            for cookie in cookieList:
                lst = []
                if not (self.__filterTrackingCookies and \
                        cookie.name().startsWith("__utm")):
                    if eAllowSession:
                        cookie.setExpirationDate(QDateTime())
                    if self.__keepCookies == self.KeepUntilTimeLimit and \
                       not cookie.isSessionCookie and \
                       cookie.expirationDate() > soon:
                        cookie.setExpirationDate(soon)
                    lst.append(cookie)
                    if QNetworkCookieJar.setCookiesFromUrl(self, lst, url):
                        addedCookies = True
                    elif self.__acceptCookies == self.AcceptAlways:
                        # force it in if wanted
                        cookies = self.allCookies()
                        for ocookie in cookies[:]:
                            # does the cookie exist already?
                            if cookie.name() == ocookie.name() and \
                               cookie.domain() == ocookie.domain() and \
                               cookie.path() == ocookie.path():
                                # found a match
                                cookies.remove(ocookie)
                        
                        cookies.append(cookie)
                        self.setAllCookies(cookies)
                        addedCookies = True
        
        if addedCookies:
            self.__saveTimer.changeOccurred()
            self.emit(SIGNAL("cookiesChanged()"))
        
        return addedCookies
    
    def acceptPolicy(self):
        """
        Public method to get the accept policy.
        
        @return current accept policy
        """
        if not self.__loaded:
            self.load()
        return self.__acceptCookies
    
    def setAcceptPolicy(self, policy):
        """
        Public method to set the accept policy.
        
        @param policy accept policy to be set
        """
        if not self.__loaded:
            self.load()
        if policy > self.AcceptOnlyFromSitesNavigatedTo:
            return
        if policy == self.__acceptCookies:
            return
        self.__acceptCookies = policy
        self.__saveTimer.changeOccurred()
    
    def keepPolicy(self):
        """
        Private method to get the keep policy.
        """
        if not self.__loaded:
            self.load()
        return self.__keepCookies
    
    def setKeepPolicy(self, policy):
        """
        Public method to set the keep policy.
        
        @param policy keep policy to be set
        """
        if not self.__loaded:
            self.load()
        if policy > self.KeepUntilTimeLimit:
            return
        if policy == self.__keepCookies:
            return
        self.__keepCookies = policy
        self.__saveTimer.changeOccurred()
    
    def blockedCookies(self):
        """
        Public method to return the blocked cookies.
        
        @return list of blocked cookies (QStringList)
        """
        if not self.__loaded:
            self.load()
        return self.__exceptionsBlock
    
    def allowedCookies(self):
        """
        Public method to return the allowed cookies.
        
        @return list of allowed cookies (QStringList)
        """
        if not self.__loaded:
            self.load()
        return self.__exceptionsAllow
    
    def allowForSessionCookies(self):
        """
        Public method to return the allowed session cookies.
        
        @return list of allowed session cookies (QStringList)
        """
        if not self.__loaded:
            self.load()
        return self.__exceptionsAllowForSession
    
    def setBlockedCookies(self, list_):
        """
        Public method to set the list of blocked cookies.
        
        @param list_ list of blocked cookies (QStringList)
        """
        if not self.__loaded:
            self.load()
        self.__exceptionsBlock = QStringList(list_)
        self.__exceptionsBlock.sort()
        self.__applyRules()
        self.__saveTimer.changeOccurred()
    
    def setAllowedCookies(self, list_):
        """
        Public method to set the list of allowed cookies.
        
        @param list_ list of allowed cookies (QStringList)
        """
        if not self.__loaded:
            self.load()
        self.__exceptionsAllow = QStringList(list_)
        self.__exceptionsAllow.sort()
        self.__applyRules()
        self.__saveTimer.changeOccurred()
    
    def setAllowForSessionCookies(self, list_):
        """
        Public method to set the list of allowed session cookies.
        
        @param list_ list of allowed session cookies (QStringList)
        """
        if not self.__loaded:
            self.load()
        self.__exceptionsAllowForSession = QStringList(list_)
        self.__exceptionsAllowForSession.sort()
        self.__applyRules()
        self.__saveTimer.changeOccurred()
    
    def filterTrackingCookies(self):
        """
        Public method to get the filter tracking cookies flag.
        
        @return filter tracking cookies flag (boolean)
        """
        return self.__filterTrackingCookies
    
    def setFilterTrackingCookies(self, filterTrackingCookies):
        """
        Public method to set the filter tracking cookies flag.
        
        @param filterTrackingCookies filter tracking cookies flag (boolean)
        """
        self.__filterTrackingCookies = filterTrackingCookies
    
    def __isOnDomainList(self, rules, domain):
        """
        Private method to check, if either the rule matches the domain exactly
        or the domain ends with ".rule".
        
        @param rules list of rules (QStringList)
        @param domain domain name to check (QString)
        @return flag indicating a match (boolean)
        """
        domain = QString(domain)
        for rule in rules:
            if rule.startsWith("."):
                if domain.endsWith(rule):
                    return True
                
                withoutDot = rule.right(rule.size() - 1)
                if domain == withoutDot:
                    return True
            else:
                domainEnding = domain.right(rule.size() + 1)
                if not domainEnding.isEmpty() and \
                   domainEnding[0] == "." and \
                   domain.endsWith(rule):
                    return True
                
                if rule == domain:
                    return True
        
        return False
    
    def __applyRules(self):
        """
        Private method to apply the cookie rules.
        """
        cookiesList = self.allCookies()
        changed = False
        
        for index in range(len(cookiesList) - 1, -1, -1):
            cookie = cookiesList[index]
            if self.__isOnDomainList(self.__exceptionsBlock, cookie.domain()):
                del cookiesList[index]
                changed = True
            elif self.__isOnDomainList(self.__exceptionsAllowForSession, cookie.domain()):
                cookie.setExpirationDate(QDateTime())
                changed = True
        
        if changed:
            self.setAllCookies(cookiesList)
            self.__saveTimer.changeOccurred()
            self.emit(SIGNAL("cookiesChanged()"))
    
    def cookies(self):
        """
        Public method to get the cookies of the cookie jar.
        
        @return list of all cookies (list of QNetworkCookie)
        """
        if not self.__loaded:
            self.load()
        
        return self.allCookies()
    
    def setCookies(self, cookies):
        """
        Public method to set all cookies.
        
        @param cookies list of cookies to be set.
        """
        if not self.__loaded:
            self.load()
        
        self.setAllCookies(cookies)
        self.__saveTimer.changeOccurred()
        self.emit(SIGNAL("cookiesChanged()"))
Пример #57
0
class IrcNetworkManager(QObject):
    """
    Class implementing the IRC identity object.
    
    @signal dataChanged() emitted after some data has changed
    @signal networksChanged() emitted after a network object has changed
    @signal identitiesChanged() emitted after an identity object has changed
    """
    dataChanged = pyqtSignal()
    networksChanged = pyqtSignal()
    identitiesChanged = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(IrcNetworkManager, self).__init__(parent)

        self.__loaded = False
        self.__saveTimer = AutoSaver(self, self.save)

        self.__settings = Preferences.Prefs.settings

        self.__networks = {}
        self.__identities = {}

        self.dataChanged.connect(self.__saveTimer.changeOccurred)

    def close(self):
        """
        Public method to close the open search engines manager.
        """
        self.__saveTimer.saveIfNeccessary()

    def save(self):
        """
        Public slot to save the IRC data.
        """
        if not self.__loaded:
            return

        # save IRC data
        self.__settings.beginGroup("IRC")

        # identities
        self.__settings.remove("Identities")
        self.__settings.beginGroup("Identities")
        for key in self.__identities:
            self.__settings.beginGroup(key)
            self.__identities[key].save(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()

        # networks
        self.__settings.remove("Networks")
        self.__settings.beginGroup("Networks")
        for key in self.__networks:
            self.__settings.beginGroup(key)
            self.__networks[key].save(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()

        self.__settings.endGroup()

    def __load(self):
        """
        Private slot to load the IRC data.
        """
        if self.__loaded:
            return

        # load IRC data
        self.__settings.beginGroup("IRC")

        # identities
        self.__settings.beginGroup("Identities")
        for key in self.__settings.childGroups():
            self.__identities[key] = IrcIdentity(key)
            self.__settings.beginGroup(key)
            self.__identities[key].load(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()

        # networks
        self.__settings.beginGroup("Networks")
        for key in self.__settings.childGroups():
            self.__networks[key] = IrcNetwork(key)
            self.__settings.beginGroup(key)
            self.__networks[key].load(self.__settings)
            self.__settings.endGroup()
        self.__settings.endGroup()

        self.__settings.endGroup()

        if not self.__identities or \
           not self.__networks:
            # data structures got corrupted; load defaults
            self.__loadDefaults()

        if IrcIdentity.DefaultIdentityName not in self.__identities:
            self.__loadDefaults(identityOnly=True)

        self.__loaded = True

    def __loadDefaults(self, identityOnly=False):
        """
        Private method to load default values.
        
        @param identityOnly flag indicating to just load the default
            identity (boolean)
        """
        if not identityOnly:
            self.__networks = {}
            self.__identities = {}

        # identity
        identity = IrcIdentity.createDefaultIdentity()
        self.__identities[identity.getName()] = identity

        if not identityOnly:
            network = IrcNetwork.createDefaultNetwork()
            self.__networks[network.getName()] = network
            network = IrcNetwork.createDefaultNetwork(True)
            self.__networks[network.getName()] = network

        self.dataChanged.emit()

    ##################################################################
    ## Identity related methods below
    ##################################################################

    def getIdentity(self, name, create=False):
        """
        Public method to get an identity object.
        
        @param name name of the identity to get (string)
        @param create flag indicating to create a new object,
            if none exists (boolean)
        @return reference to the identity (IrcIdentity)
        """
        if not name:
            return None

        if not self.__loaded:
            self.__load()

        if name in self.__identities:
            return self.__identities[name]
        elif create:
            id = IrcIdentity(name)
            self.__identities[name] = id

            self.dataChanged.emit()

            return id
        else:
            return None

    def getIdentities(self):
        """
        Public method to get a copy of all identities.
        
        @return dictionary of all identities (dict of IrcIdentity)
        """
        return copy.deepcopy(self.__identities)

    def setIdentities(self, identities):
        """
        Public method to set the identities.
        
        @param identities dictionary of all identities (dict of IrcIdentity)
        """
        self.__identities = copy.deepcopy(identities)
        self.identityChanged()

        # Check all networks, if the identity they use is still available.
        # If it isn't, change them to use the default identity.
        for network in self.__networks.values():
            if network.getIdentityName() not in self.__identities:
                network.setIdentityName(IrcIdentity.DefaultIdentityName)

    def getIdentityNames(self):
        """
        Public method to get the names of all identities.
        
        @return names of all identities (list of string)
        """
        return list(self.__identities.keys())

    def addIdentity(self, identity):
        """
        Public method to add a new identity.
        
        @param identity reference to the identity to add (IrcIdentity)
        """
        name = identity.getName()
        self.__identities[name] = identity
        self.identityChanged()

    def deleteIdentity(self, name):
        """
        Public method to delete the given identity.
        
        @param name name of the identity to delete (string)
        """
        if name in self.__identities and \
                name != IrcIdentity.DefaultIdentityName:
            del self.__identities[name]
            self.identityChanged()

    def renameIdentity(self, oldName, newName):
        """
        Public method to rename an identity.
        
        @param oldName old name of the identity (string)
        @param newName new name of the identity (string)
        """
        if oldName in self.__identities:
            self.__identities[newName] = self.__identities[oldName]
            del self.__identities[oldName]

            for network in self.__networks:
                if network.getIdentityName() == oldName:
                    network.setIdentityName(newName)

            self.identityChanged()

    def identityChanged(self):
        """
        Public method to indicate a change of an identity object.
        """
        self.dataChanged.emit()
        self.identitiesChanged.emit()

    ##################################################################
    ## Network related methods below
    ##################################################################

    def getNetwork(self, name):
        """
        Public method to get a network object.
        
        @param name name of the network (string)
        @return reference to the network object (IrcNetwork)
        """
        if not self.__loaded:
            self.__load()

        if name in self.__networks:
            return self.__networks[name]
        else:
            return None

    def setNetwork(self, network, networkName=""):
        """
        Public method to set a network.
        
        @param network network object to set (IrcNetwork)
        @param networkName name the network was known for (string)
        """
        name = network.getName()
        if networkName and name != networkName:
            # the network name has changed
            self.deleteNetwork(networkName)
            self.addNetwork(network)
        elif name in self.__networks:
            self.__networks[name] = network
            self.networkChanged()

    def addNetwork(self, network):
        """
        Public method to add a network.
        
        @param network network object to add (IrcNetwork)
        """
        name = network.getName()
        if name not in self.__networks:
            self.__networks[name] = network
            self.networkChanged()

    def deleteNetwork(self, name):
        """
        Public method to delete the given network.
        
        @param name name of the network to delete (string)
        """
        if name in self.__networks:
            del self.__networks[name]
            self.networkChanged()

    def networkChanged(self):
        """
        Public method to indicate a change of a network object.
        """
        self.dataChanged.emit()
        self.networksChanged.emit()

    def getNetworkNames(self):
        """
        Public method to get a list of all known network names.
        
        @return list of network names (list of string)
        """
        if not self.__loaded:
            self.__load()

        return list(sorted(self.__networks.keys()))
Пример #58
0
class DownloadManager(QDialog, Ui_DownloadManager):
    """
    Class implementing the download manager.
    """
    RemoveNever = 0
    RemoveExit = 1
    RemoveSuccessFullDownload = 2

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(DownloadManager, self).__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(Qt.Window)

        self.__saveTimer = AutoSaver(self, self.save)

        self.__model = DownloadModel(self)
        self.__manager = Helpviewer.HelpWindow.HelpWindow\
            .networkAccessManager()

        self.__iconProvider = None
        self.__downloads = []
        self.__downloadDirectory = ""
        self.__loaded = False

        self.setDownloadDirectory(Preferences.getUI("DownloadPath"))

        self.downloadsView.setShowGrid(False)
        self.downloadsView.verticalHeader().hide()
        self.downloadsView.horizontalHeader().hide()
        self.downloadsView.setAlternatingRowColors(True)
        self.downloadsView.horizontalHeader().setStretchLastSection(True)
        self.downloadsView.setModel(self.__model)
        self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.downloadsView.customContextMenuRequested.connect(
            self.__customContextMenuRequested)

        self.__load()

    def __customContextMenuRequested(self, pos):
        """
        Private slot to handle the context menu request for the bookmarks tree.
        
        @param pos position the context menu was requested (QPoint)
        """
        menu = QMenu()

        selectedRowsCount = len(
            self.downloadsView.selectionModel().selectedRows())

        if selectedRowsCount == 1:
            row = self.downloadsView.selectionModel().selectedRows()[0].row()
            itm = self.__downloads[row]
            if itm.downloadCanceled():
                menu.addAction(UI.PixmapCache.getIcon("restart.png"),
                               self.tr("Retry"), self.__contextMenuRetry)
            else:
                if itm.downloadedSuccessfully():
                    menu.addAction(UI.PixmapCache.getIcon("open.png"),
                                   self.tr("Open"), self.__contextMenuOpen)
                elif itm.downloading():
                    menu.addAction(UI.PixmapCache.getIcon("stopLoading.png"),
                                   self.tr("Cancel"), self.__contextMenuCancel)
                    menu.addSeparator()
                menu.addAction(self.tr("Open Containing Folder"),
                               self.__contextMenuOpenFolder)
            menu.addSeparator()
            menu.addAction(self.tr("Go to Download Page"),
                           self.__contextMenuGotoPage)
            menu.addAction(self.tr("Copy Download Link"),
                           self.__contextMenuCopyLink)
            menu.addSeparator()
        menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll)
        if selectedRowsCount > 1 or \
           (selectedRowsCount == 1 and
            not self.__downloads[
                self.downloadsView.selectionModel().selectedRows()[0].row()]
                .downloading()):
            menu.addSeparator()
            menu.addAction(self.tr("Remove From List"),
                           self.__contextMenuRemoveSelected)

        menu.exec_(QCursor.pos())

    def shutdown(self):
        """
        Public method to stop the download manager.
        """
        self.__saveTimer.changeOccurred()
        self.__saveTimer.saveIfNeccessary()
        self.close()

    def activeDownloads(self):
        """
        Public method to get the number of active downloads.
        
        @return number of active downloads (integer)
        """
        count = 0

        for download in self.__downloads:
            if download.downloading():
                count += 1
        return count

    def allowQuit(self):
        """
        Public method to check, if it is ok to quit.
        
        @return flag indicating allowance to quit (boolean)
        """
        if self.activeDownloads() > 0:
            res = E5MessageBox.yesNo(
                self,
                self.tr(""),
                self.tr(
                    """There are %n downloads in progress.\n"""
                    """Do you want to quit anyway?""", "",
                    self.activeDownloads()),
                icon=E5MessageBox.Warning)
            if not res:
                self.show()
                return False
        return True

    def download(self, requestOrUrl, requestFileName=False, mainWindow=None):
        """
        Public method to download a file.
        
        @param requestOrUrl reference to a request object (QNetworkRequest)
            or a URL to be downloaded (QUrl)
        @keyparam requestFileName flag indicating to ask for the
            download file name (boolean)
        @keyparam mainWindow reference to the main window (HelpWindow)
        """
        request = QNetworkRequest(requestOrUrl)
        if request.url().isEmpty():
            return
        self.handleUnsupportedContent(self.__manager.get(request),
                                      requestFileName=requestFileName,
                                      download=True,
                                      mainWindow=mainWindow)

    def handleUnsupportedContent(self,
                                 reply,
                                 requestFileName=False,
                                 webPage=None,
                                 download=False,
                                 mainWindow=None):
        """
        Public method to handle unsupported content by downloading the
        referenced resource.
        
        @param reply reference to the reply object (QNetworkReply)
        @keyparam requestFileName indicating to ask for a filename
            (boolean)
        @keyparam webPage reference to the web page (HelpWebPage)
        @keyparam download flag indicating a download request (boolean)
        @keyparam mainWindow reference to the main window (HelpWindow)
        """
        if reply is None or reply.url().isEmpty():
            return

        size = reply.header(QNetworkRequest.ContentLengthHeader)
        if size == 0:
            return

        from .DownloadItem import DownloadItem
        itm = DownloadItem(reply=reply,
                           requestFilename=requestFileName,
                           webPage=webPage,
                           download=download,
                           parent=self,
                           mainWindow=mainWindow)
        self.__addItem(itm)

        if itm.canceledFileSelect():
            return

        if not self.isVisible():
            self.show()

        self.activateWindow()
        self.raise_()

    def __addItem(self, itm):
        """
        Private method to add a download to the list of downloads.
        
        @param itm reference to the download item (DownloadItem)
        """
        itm.statusChanged.connect(self.__updateRow)
        itm.downloadFinished.connect(self.__finished)

        row = len(self.__downloads)
        self.__model.beginInsertRows(QModelIndex(), row, row)
        self.__downloads.append(itm)
        self.__model.endInsertRows()

        self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm)
        icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)
        self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5)
        # just in case the download finished before the constructor returned
        self.__updateRow(itm)
        self.changeOccurred()
        self.__updateActiveItemCount()

    def __updateRow(self, itm=None):
        """
        Private slot to update a download item.
        
        @param itm reference to the download item (DownloadItem)
        """
        if itm is None:
            itm = self.sender()

        if itm not in self.__downloads:
            return

        row = self.__downloads.index(itm)

        if self.__iconProvider is None:
            self.__iconProvider = QFileIconProvider()

        icon = self.__iconProvider.icon(QFileInfo(itm.fileName()))
        if icon.isNull():
            icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)

        oldHeight = self.downloadsView.rowHeight(row)
        self.downloadsView.setRowHeight(
            row, max(oldHeight,
                     itm.minimumSizeHint().height() * 1.5))

        remove = False
        globalSettings = QWebSettings.globalSettings()
        if not itm.downloading() and \
           globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
            remove = True

        if itm.downloadedSuccessfully() and \
           self.removePolicy() == DownloadManager.RemoveSuccessFullDownload:
            remove = True

        if remove:
            self.__model.removeRow(row)

        self.cleanupButton.setEnabled(
            (len(self.__downloads) - self.activeDownloads()) > 0)

        # record the change
        self.changeOccurred()

    def removePolicy(self):
        """
        Public method to get the remove policy.
        
        @return remove policy (integer)
        """
        return Preferences.getHelp("DownloadManagerRemovePolicy")

    def setRemovePolicy(self, policy):
        """
        Public method to set the remove policy.
        
        @param policy policy to be set
            (DownloadManager.RemoveExit, DownloadManager.RemoveNever,
             DownloadManager.RemoveSuccessFullDownload)
        """
        assert policy in (DownloadManager.RemoveExit,
                          DownloadManager.RemoveNever,
                          DownloadManager.RemoveSuccessFullDownload)

        if policy == self.removePolicy():
            return

        Preferences.setHelp("DownloadManagerRemovePolicy", self.policy)

    def save(self):
        """
        Public method to save the download settings.
        """
        if not self.__loaded:
            return

        Preferences.setHelp("DownloadManagerSize", self.size())
        Preferences.setHelp("DownloadManagerPosition", self.pos())
        if self.removePolicy() == DownloadManager.RemoveExit:
            return

        downloads = []
        for download in self.__downloads:
            downloads.append(download.getData())
        Preferences.setHelp("DownloadManagerDownloads", downloads)

    def __load(self):
        """
        Private method to load the download settings.
        """
        if self.__loaded:
            return

        size = Preferences.getHelp("DownloadManagerSize")
        if size.isValid():
            self.resize(size)
        pos = Preferences.getHelp("DownloadManagerPosition")
        self.move(pos)

        downloads = Preferences.getHelp("DownloadManagerDownloads")
        for download in downloads:
            if not download[0].isEmpty() and \
               download[1] != "":
                from .DownloadItem import DownloadItem
                itm = DownloadItem(parent=self)
                itm.setData(download)
                self.__addItem(itm)
        self.cleanupButton.setEnabled(
            (len(self.__downloads) - self.activeDownloads()) > 0)

        self.__loaded = True
        self.__updateActiveItemCount()

    def cleanup(self):
        """
        Public slot to cleanup the downloads.
        """
        self.on_cleanupButton_clicked()

    @pyqtSlot()
    def on_cleanupButton_clicked(self):
        """
        Private slot cleanup the downloads.
        """
        if len(self.__downloads) == 0:
            return

        self.__model.removeRows(0, len(self.__downloads))
        if len(self.__downloads) == 0 and \
           self.__iconProvider is not None:
            self.__iconProvider = None

        self.changeOccurred()
        self.__updateActiveItemCount()

    def __updateItemCount(self):
        """
        Private method to update the count label.
        """
        count = len(self.__downloads)
        self.countLabel.setText(self.tr("%n Download(s)", "", count))

    def __updateActiveItemCount(self):
        """
        Private method to update the window title.
        """
        count = self.activeDownloads()
        if count > 0:
            self.setWindowTitle(self.tr("Downloading %n file(s)", "", count))
        else:
            self.setWindowTitle(self.tr("Downloads"))

    def __finished(self):
        """
        Private slot to handle a finished download.
        """
        self.__updateActiveItemCount()
        if self.isVisible():
            QApplication.alert(self)

    def setDownloadDirectory(self, directory):
        """
        Public method to set the current download directory.
        
        @param directory current download directory (string)
        """
        self.__downloadDirectory = directory
        if self.__downloadDirectory != "":
            self.__downloadDirectory += "/"

    def downloadDirectory(self):
        """
        Public method to get the current download directory.
        
        @return current download directory (string)
        """
        return self.__downloadDirectory

    def count(self):
        """
        Public method to get the number of downloads.
        
        @return number of downloads (integer)
        """
        return len(self.__downloads)

    def downloads(self):
        """
        Public method to get a reference to the downloads.
        
        @return reference to the downloads (list of DownloadItem)
        """
        return self.__downloads

    def changeOccurred(self):
        """
        Public method to signal a change.
        """
        self.__saveTimer.changeOccurred()
        self.__updateItemCount()

    ###########################################################################
    ## Context menu related methods below
    ###########################################################################

    def __currentItem(self):
        """
        Private method to get a reference to the current item.
        
        @return reference to the current item (DownloadItem)
        """
        index = self.downloadsView.currentIndex()
        if index and index.isValid():
            row = index.row()
            return self.__downloads[row]

        return None

    def __contextMenuRetry(self):
        """
        Private method to retry of the download.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.retry()

    def __contextMenuOpen(self):
        """
        Private method to open the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFile()

    def __contextMenuOpenFolder(self):
        """
        Private method to open the folder containing the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFolder()

    def __contextMenuCancel(self):
        """
        Private method to cancel the current download.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.cancelDownload()

    def __contextMenuGotoPage(self):
        """
        Private method to open the download page.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl()
            Helpviewer.HelpWindow.HelpWindow.mainWindow().openUrl(url, "")

    def __contextMenuCopyLink(self):
        """
        Private method to copy the download link to the clipboard.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl().toString()
            QApplication.clipboard().setText(url)

    def __contextMenuSelectAll(self):
        """
        Private method to select all downloads.
        """
        self.downloadsView.selectAll()

    def __contextMenuRemoveSelected(self):
        """
        Private method to remove the selected downloads from the list.
        """
        self.downloadsView.removeSelected()