|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2012 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a personal information manager used to complete form |
|
8 fields. |
|
9 """ |
|
10 |
|
11 from __future__ import unicode_literals |
|
12 |
|
13 from PyQt5.QtCore import Qt, QObject |
|
14 from PyQt5.QtWidgets import QDialog, QMenu |
|
15 |
|
16 import Preferences |
|
17 import UI.PixmapCache |
|
18 |
|
19 |
|
20 class PersonalInformationManager(QObject): |
|
21 """ |
|
22 Class implementing the personal information manager used to complete form |
|
23 fields. |
|
24 """ |
|
25 FullName = 0 |
|
26 LastName = 1 |
|
27 FirstName = 2 |
|
28 Email = 3 |
|
29 Mobile = 4 |
|
30 Phone = 5 |
|
31 Address = 6 |
|
32 City = 7 |
|
33 Zip = 8 |
|
34 State = 9 |
|
35 Country = 10 |
|
36 HomePage = 11 |
|
37 Special1 = 12 |
|
38 Special2 = 13 |
|
39 Special3 = 14 |
|
40 Special4 = 15 |
|
41 Max = 16 |
|
42 Invalid = 256 |
|
43 |
|
44 def __init__(self, parent=None): |
|
45 """ |
|
46 Constructor |
|
47 |
|
48 @param parent reference to the parent object (QObject) |
|
49 """ |
|
50 super(PersonalInformationManager, self).__init__(parent) |
|
51 |
|
52 self.__loaded = False |
|
53 self.__allInfo = {} |
|
54 self.__infoMatches = {} |
|
55 self.__translations = {} |
|
56 |
|
57 self.__view = None |
|
58 self.__element = None |
|
59 |
|
60 def __loadSettings(self): |
|
61 """ |
|
62 Private method to load the settings. |
|
63 """ |
|
64 self.__allInfo[self.FullName] = Preferences.getHelp("PimFullName") |
|
65 self.__allInfo[self.LastName] = Preferences.getHelp("PimLastName") |
|
66 self.__allInfo[self.FirstName] = Preferences.getHelp("PimFirstName") |
|
67 self.__allInfo[self.Email] = Preferences.getHelp("PimEmail") |
|
68 self.__allInfo[self.Mobile] = Preferences.getHelp("PimMobile") |
|
69 self.__allInfo[self.Phone] = Preferences.getHelp("PimPhone") |
|
70 self.__allInfo[self.Address] = Preferences.getHelp("PimAddress") |
|
71 self.__allInfo[self.City] = Preferences.getHelp("PimCity") |
|
72 self.__allInfo[self.Zip] = Preferences.getHelp("PimZip") |
|
73 self.__allInfo[self.State] = Preferences.getHelp("PimState") |
|
74 self.__allInfo[self.Country] = Preferences.getHelp("PimCountry") |
|
75 self.__allInfo[self.HomePage] = Preferences.getHelp("PimHomePage") |
|
76 self.__allInfo[self.Special1] = Preferences.getHelp("PimSpecial1") |
|
77 self.__allInfo[self.Special2] = Preferences.getHelp("PimSpecial2") |
|
78 self.__allInfo[self.Special3] = Preferences.getHelp("PimSpecial3") |
|
79 self.__allInfo[self.Special4] = Preferences.getHelp("PimSpecial4") |
|
80 |
|
81 self.__translations[self.FullName] = self.tr("Full Name") |
|
82 self.__translations[self.LastName] = self.tr("Last Name") |
|
83 self.__translations[self.FirstName] = self.tr("First Name") |
|
84 self.__translations[self.Email] = self.tr("E-mail") |
|
85 self.__translations[self.Mobile] = self.tr("Mobile") |
|
86 self.__translations[self.Phone] = self.tr("Phone") |
|
87 self.__translations[self.Address] = self.tr("Address") |
|
88 self.__translations[self.City] = self.tr("City") |
|
89 self.__translations[self.Zip] = self.tr("ZIP Code") |
|
90 self.__translations[self.State] = self.tr("State/Region") |
|
91 self.__translations[self.Country] = self.tr("Country") |
|
92 self.__translations[self.HomePage] = self.tr("Home Page") |
|
93 self.__translations[self.Special1] = self.tr("Custom 1") |
|
94 self.__translations[self.Special2] = self.tr("Custom 2") |
|
95 self.__translations[self.Special3] = self.tr("Custom 3") |
|
96 self.__translations[self.Special4] = self.tr("Custom 4") |
|
97 |
|
98 self.__infoMatches[self.FullName] = ["fullname", "realname"] |
|
99 self.__infoMatches[self.LastName] = ["lastname", "surname"] |
|
100 self.__infoMatches[self.FirstName] = ["firstname", "name"] |
|
101 self.__infoMatches[self.Email] = ["email", "e-mail", "mail"] |
|
102 self.__infoMatches[self.Mobile] = ["mobile", "mobilephone"] |
|
103 self.__infoMatches[self.Phone] = ["phone", "telephone"] |
|
104 self.__infoMatches[self.Address] = ["address"] |
|
105 self.__infoMatches[self.City] = ["city"] |
|
106 self.__infoMatches[self.Zip] = ["zip"] |
|
107 self.__infoMatches[self.State] = ["state", "region"] |
|
108 self.__infoMatches[self.Country] = ["country"] |
|
109 self.__infoMatches[self.HomePage] = ["homepage", "www"] |
|
110 |
|
111 self.__loaded = True |
|
112 |
|
113 def showConfigurationDialog(self): |
|
114 """ |
|
115 Public method to show the configuration dialog. |
|
116 """ |
|
117 from .PersonalDataDialog import PersonalDataDialog |
|
118 dlg = PersonalDataDialog() |
|
119 if dlg.exec_() == QDialog.Accepted: |
|
120 dlg.storeData() |
|
121 self.__loadSettings() |
|
122 |
|
123 def createSubMenu(self, menu, view, hitTestResult): |
|
124 """ |
|
125 Public method to create the personal information sub-menu. |
|
126 |
|
127 @param menu reference to the main menu (QMenu) |
|
128 @param view reference to the view (HelpBrowser) |
|
129 @param hitTestResult reference to the hit test result |
|
130 (QWebHitTestResult) |
|
131 """ |
|
132 self.__view = view |
|
133 self.__element = hitTestResult.element() |
|
134 |
|
135 if not hitTestResult.isContentEditable(): |
|
136 return |
|
137 |
|
138 if not self.__loaded: |
|
139 self.__loadSettings() |
|
140 |
|
141 submenu = QMenu(self.tr("Insert Personal Information"), menu) |
|
142 submenu.setIcon(UI.PixmapCache.getIcon("pim.png")) |
|
143 |
|
144 for key, info in sorted(self.__allInfo.items()): |
|
145 if info: |
|
146 act = submenu.addAction(self.__translations[key]) |
|
147 act.setData(info) |
|
148 act.triggered.connect(lambda: self.__insertData(act)) |
|
149 |
|
150 submenu.addSeparator() |
|
151 submenu.addAction(self.tr("Edit Personal Information"), |
|
152 self.showConfigurationDialog) |
|
153 |
|
154 menu.addMenu(submenu) |
|
155 menu.addSeparator() |
|
156 |
|
157 def __insertData(self, act): |
|
158 """ |
|
159 Private slot to insert the selected personal information. |
|
160 |
|
161 @param act reference to the action that triggered |
|
162 @type QAction |
|
163 """ |
|
164 if not self.__element or self.__element.isNull(): |
|
165 return |
|
166 |
|
167 info = act.data() |
|
168 info = info.replace('"', '\\"') |
|
169 self.__element.evaluateJavaScript( |
|
170 'var newVal = this.value.substring(0, this.selectionStart) +' |
|
171 ' "{0}" + this.value.substring(this.selectionEnd); this.value =' |
|
172 ' newVal;'.format(info)) |
|
173 |
|
174 def viewKeyPressEvent(self, view, evt): |
|
175 """ |
|
176 Protected method to handle key press events we are interested in. |
|
177 |
|
178 @param view reference to the view (HelpBrowser) |
|
179 @param evt reference to the key event (QKeyEvent) |
|
180 @return flag indicating handling of the event (boolean) |
|
181 """ |
|
182 if view is None: |
|
183 return False |
|
184 |
|
185 isEnter = evt.key() in [Qt.Key_Return, Qt.Key_Enter] |
|
186 if not isEnter or evt.modifiers() != Qt.ControlModifier: |
|
187 return False |
|
188 |
|
189 if not self.__loaded: |
|
190 self.__loadSettings() |
|
191 |
|
192 document = view.page().mainFrame().documentElement() |
|
193 elements = document.findAll('input[type="text"]') |
|
194 |
|
195 for element in elements: |
|
196 name = element.attribute("name") |
|
197 if name == "": |
|
198 continue |
|
199 |
|
200 match = self.__nameMatch(name) |
|
201 if match != self.Invalid: |
|
202 element.evaluateJavaScript( |
|
203 'this.value = "{0}"'.format(self.__allInfo[match])) |
|
204 |
|
205 return True |
|
206 |
|
207 def __nameMatch(self, name): |
|
208 """ |
|
209 Private method to find the information entry for the given field. |
|
210 |
|
211 @param name name of the form field (string) |
|
212 @return value of the information entry (integer) |
|
213 """ |
|
214 for index in range(self.Max): |
|
215 if self.__allInfo[index]: |
|
216 for n in self.__infoMatches[index]: |
|
217 if name == n or n in name: |
|
218 return index |
|
219 |
|
220 return self.Invalid |
|
221 |
|
222 def connectPage(self, page): |
|
223 """ |
|
224 Public method to allow the personal information manager to connect to |
|
225 the page. |
|
226 |
|
227 @param page reference to the web page (HelpWebPage) |
|
228 """ |
|
229 page.loadFinished.connect(lambda ok: self.__pageLoadFinished(ok, page)) |
|
230 |
|
231 def __pageLoadFinished(self, ok, page): |
|
232 """ |
|
233 Private slot to handle the completion of a page load. |
|
234 |
|
235 @param ok flag indicating a successful load |
|
236 @type bool |
|
237 @param page reference to the web page object |
|
238 @type HelpWebPage |
|
239 """ |
|
240 if page is None or not ok: |
|
241 return |
|
242 |
|
243 if not self.__loaded: |
|
244 self.__loadSettings() |
|
245 |
|
246 document = page.mainFrame().documentElement() |
|
247 elements = document.findAll('input[type="text"]') |
|
248 |
|
249 for element in elements: |
|
250 name = element.attribute("name") |
|
251 if name == "": |
|
252 continue |
|
253 |
|
254 match = self.__nameMatch(name) |
|
255 if match != self.Invalid: |
|
256 element.setStyleProperty( |
|
257 "-webkit-appearance", "none") |
|
258 element.setStyleProperty( |
|
259 "-webkit-box-shadow", "inset 0 0 2px 1px #0000EE") |