--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/WebBrowser/PersonalInformationManager/PersonalInformationManager.py Sat May 15 18:45:04 2021 +0200 @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a personal information manager used to complete form +fields. +""" + +import functools + +from PyQt5.QtCore import Qt, QObject, QPoint +from PyQt5.QtWidgets import QDialog, QMenu + +import Preferences +import UI.PixmapCache + +from ..WebBrowserPage import WebBrowserPage + + +class PersonalInformationManager(QObject): + """ + Class implementing the personal information manager used to complete form + fields. + """ + FullName = 0 + LastName = 1 + FirstName = 2 + Email = 3 + Mobile = 4 + Phone = 5 + Address = 6 + City = 7 + Zip = 8 + State = 9 + Country = 10 + HomePage = 11 + Special1 = 12 + Special2 = 13 + Special3 = 14 + Special4 = 15 + Max = 16 + Invalid = 256 + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent object (QObject) + """ + super().__init__(parent) + + self.__loaded = False + self.__allInfo = {} + self.__infoMatches = {} + self.__translations = {} + + self.__view = None + self.__clickedPos = QPoint() + + def __loadSettings(self): + """ + Private method to load the settings. + """ + self.__allInfo[self.FullName] = Preferences.getWebBrowser( + "PimFullName") + self.__allInfo[self.LastName] = Preferences.getWebBrowser( + "PimLastName") + self.__allInfo[self.FirstName] = Preferences.getWebBrowser( + "PimFirstName") + self.__allInfo[self.Email] = Preferences.getWebBrowser("PimEmail") + self.__allInfo[self.Mobile] = Preferences.getWebBrowser("PimMobile") + self.__allInfo[self.Phone] = Preferences.getWebBrowser("PimPhone") + self.__allInfo[self.Address] = Preferences.getWebBrowser("PimAddress") + self.__allInfo[self.City] = Preferences.getWebBrowser("PimCity") + self.__allInfo[self.Zip] = Preferences.getWebBrowser("PimZip") + self.__allInfo[self.State] = Preferences.getWebBrowser("PimState") + self.__allInfo[self.Country] = Preferences.getWebBrowser("PimCountry") + self.__allInfo[self.HomePage] = Preferences.getWebBrowser( + "PimHomePage") + self.__allInfo[self.Special1] = Preferences.getWebBrowser( + "PimSpecial1") + self.__allInfo[self.Special2] = Preferences.getWebBrowser( + "PimSpecial2") + self.__allInfo[self.Special3] = Preferences.getWebBrowser( + "PimSpecial3") + self.__allInfo[self.Special4] = Preferences.getWebBrowser( + "PimSpecial4") + + self.__translations[self.FullName] = self.tr("Full Name") + self.__translations[self.LastName] = self.tr("Last Name") + self.__translations[self.FirstName] = self.tr("First Name") + self.__translations[self.Email] = self.tr("E-mail") + self.__translations[self.Mobile] = self.tr("Mobile") + self.__translations[self.Phone] = self.tr("Phone") + self.__translations[self.Address] = self.tr("Address") + self.__translations[self.City] = self.tr("City") + self.__translations[self.Zip] = self.tr("ZIP Code") + self.__translations[self.State] = self.tr("State/Region") + self.__translations[self.Country] = self.tr("Country") + self.__translations[self.HomePage] = self.tr("Home Page") + self.__translations[self.Special1] = self.tr("Custom 1") + self.__translations[self.Special2] = self.tr("Custom 2") + self.__translations[self.Special3] = self.tr("Custom 3") + self.__translations[self.Special4] = self.tr("Custom 4") + + self.__infoMatches[self.FullName] = ["fullname", "realname"] + self.__infoMatches[self.LastName] = ["lastname", "surname"] + self.__infoMatches[self.FirstName] = ["firstname", "name"] + self.__infoMatches[self.Email] = ["email", "e-mail", "mail"] + self.__infoMatches[self.Mobile] = ["mobile", "mobilephone"] + self.__infoMatches[self.Phone] = ["phone", "telephone"] + self.__infoMatches[self.Address] = ["address"] + self.__infoMatches[self.City] = ["city"] + self.__infoMatches[self.Zip] = ["zip"] + self.__infoMatches[self.State] = ["state", "region"] + self.__infoMatches[self.Country] = ["country"] + self.__infoMatches[self.HomePage] = ["homepage", "www"] + + self.__loaded = True + + def showConfigurationDialog(self): + """ + Public method to show the configuration dialog. + """ + from .PersonalDataDialog import PersonalDataDialog + dlg = PersonalDataDialog() + if dlg.exec() == QDialog.DialogCode.Accepted: + dlg.storeData() + self.__loadSettings() + + def createSubMenu(self, menu, view, hitTestResult): + """ + Public method to create the personal information sub-menu. + + @param menu reference to the main menu (QMenu) + @param view reference to the view (HelpBrowser) + @param hitTestResult reference to the hit test result + (WebHitTestResult) + """ + self.__view = view + self.__clickedPos = hitTestResult.pos() + + if not hitTestResult.isContentEditable(): + return + + if not self.__loaded: + self.__loadSettings() + + submenu = QMenu(self.tr("Insert Personal Information"), menu) + submenu.setIcon(UI.PixmapCache.getIcon("pim")) + + for key, info in sorted(self.__allInfo.items()): + if info: + act = submenu.addAction(self.__translations[key]) + act.setData(info) + act.triggered.connect( + functools.partial(self.__insertData, act)) + + submenu.addSeparator() + submenu.addAction(self.tr("Edit Personal Information"), + self.showConfigurationDialog) + + menu.addMenu(submenu) + menu.addSeparator() + + def __insertData(self, act): + """ + Private slot to insert the selected personal information. + + @param act reference to the action that triggered + @type QAction + """ + if self.__view is None or self.__clickedPos.isNull(): + return + + info = act.data() + info = info.replace('"', '\\"') + + source = """ + var e = document.elementFromPoint({0}, {1}); + if (e) {{ + var v = e.value.substring(0, e.selectionStart); + v += "{2}" + e.value.substring(e.selectionEnd); + e.value = v; + }}""".format(self.__clickedPos.x(), self.__clickedPos.y(), info) + self.__view.page().runJavaScript(source, WebBrowserPage.SafeJsWorld) + + def viewKeyPressEvent(self, view, evt): + """ + Protected method to handle key press events we are interested in. + + @param view reference to the view (HelpBrowser) + @param evt reference to the key event (QKeyEvent) + @return flag indicating handling of the event (boolean) + """ + if view is None: + return False + + isEnter = evt.key() in [Qt.Key.Key_Return, Qt.Key.Key_Enter] + isControlModifier = ( + evt.modifiers() & Qt.KeyboardModifier.ControlModifier + ) + if not isEnter or not isControlModifier: + return False + + if not self.__loaded: + self.__loadSettings() + + source = """ + var inputs = document.getElementsByTagName('input'); + var table = {0}; + for (var i = 0; i < inputs.length; ++i) {{ + var input = inputs[i]; + if (input.type != 'text' || input.name == '') + continue; + for (var key in table) {{ + if (!table.hasOwnProperty(key)) + continue; + if (key == input.name || input.name.indexOf(key) != -1) {{ + input.value = table[key]; + break; + }} + }} + }}""".format(self.__matchingJsTable()) + view.page().runJavaScript(source, WebBrowserPage.SafeJsWorld) + + return True + + def connectPage(self, page): + """ + Public method to allow the personal information manager to connect to + the page. + + @param page reference to the web page + @type WebBrowserPage + """ + page.loadFinished.connect(lambda ok: self.__pageLoadFinished(ok, page)) + + def __pageLoadFinished(self, ok, page): + """ + Private slot to handle the completion of a page load. + + @param ok flag indicating a successful load + @type bool + @param page reference to the web page object + @type WebBrowserPage + """ + if page is None or not ok: + return + + if not self.__loaded: + self.__loadSettings() + + source = """ + var inputs = document.getElementsByTagName('input'); + var table = {0}; + for (var i = 0; i < inputs.length; ++i) {{ + var input = inputs[i]; + if (input.type != 'text' || input.name == '') + continue; + for (var key in table) {{ + if (!table.hasOwnProperty(key) || table[key] == '') + continue; + if (key == input.name || input.name.indexOf(key) != -1) {{ + input.style['-webkit-appearance'] = 'none'; + input.style['-webkit-box-shadow'] = + 'inset 0 0 2px 1px #000EEE'; + break; + }} + }} + }}""".format(self.__matchingJsTable()) + page.runJavaScript(source, WebBrowserPage.SafeJsWorld) + + def __matchingJsTable(self): + """ + Private method to create the common part of the JavaScript sources. + + @return JavaScript source + @rtype str + """ + values = [] + for key, names in self.__infoMatches.items(): + for name in names: + value = self.__allInfo[key].replace('"', '\\"') + values.append('"{0}":"{1}"'.format(name, value)) + return "{{ {0} }}".format(",".join(values))