|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a tree widget for the AdBlock configuration dialog. |
|
8 """ |
|
9 |
|
10 from PyQt4.QtCore import Qt |
|
11 from PyQt4.QtGui import QAbstractItemView, QFont, QTreeWidgetItem, QInputDialog, \ |
|
12 QLineEdit, QMenu, QApplication, QColor |
|
13 |
|
14 from E5Gui.E5TreeWidget import E5TreeWidget |
|
15 |
|
16 from .AdBlockRule import AdBlockRule |
|
17 |
|
18 |
|
19 class AdBlockTreeWidget(E5TreeWidget): |
|
20 """ |
|
21 Class implementing a tree widget for the AdBlock configuration dialog. |
|
22 """ |
|
23 def __init__(self, subscription, parent=None): |
|
24 """ |
|
25 Constructor |
|
26 |
|
27 @param subscription reference to the subscription (AdBlockSubscription) |
|
28 @param parenbt reference to the parent widget (QWidget) |
|
29 """ |
|
30 super().__init__(parent) |
|
31 |
|
32 self.__subscription = subscription |
|
33 self.__topItem = None |
|
34 self.__ruleToBeSelected = "" |
|
35 self.__itemChangingBlock = False |
|
36 |
|
37 self.setContextMenuPolicy(Qt.CustomContextMenu) |
|
38 self.setDefaultItemShowMode(E5TreeWidget.ItemsExpanded) |
|
39 self.setHeaderHidden(True) |
|
40 self.setAlternatingRowColors(True) |
|
41 |
|
42 self.customContextMenuRequested.connect(self.__contextMenuRequested) |
|
43 self.itemChanged.connect(self.__itemChanged) |
|
44 self.__subscription.changed.connect(self.__subscriptionChanged) |
|
45 self.__subscription.rulesChanged.connect(self.__subscriptionChanged) |
|
46 |
|
47 def subscription(self): |
|
48 """ |
|
49 Public method to get a reference to the subscription. |
|
50 |
|
51 @return reference to the subscription (AdBlockSubscription) |
|
52 """ |
|
53 return self.__subscription |
|
54 |
|
55 def showRule(self, rule): |
|
56 """ |
|
57 Public method to highlight the given rule. |
|
58 |
|
59 @param rule AdBlock rule to be shown (AdBlockRule) |
|
60 """ |
|
61 if rule: |
|
62 self.__ruleToBeSelected = rule.filter() |
|
63 if not self.__topItem: |
|
64 return |
|
65 if self.__ruleToBeSelected: |
|
66 items = self.findItems(self.__ruleToBeSelected, Qt.MatchRecursive) |
|
67 if items: |
|
68 item = items[0] |
|
69 self.setCurrentItem(item) |
|
70 self.scrollToItem(item, QAbstractItemView.PositionAtCenter) |
|
71 |
|
72 self.__ruleToBeSelected = "" |
|
73 |
|
74 def refresh(self): |
|
75 """ |
|
76 Public method to refresh the tree. |
|
77 """ |
|
78 QApplication.setOverrideCursor(Qt.WaitCursor) |
|
79 self.__itemChangingBlock = True |
|
80 self.clear() |
|
81 |
|
82 boldFont = QFont() |
|
83 boldFont.setBold(True) |
|
84 |
|
85 self.__topItem = QTreeWidgetItem(self) |
|
86 self.__topItem.setText(0, self.__subscription.title()) |
|
87 self.__topItem.setFont(0, boldFont) |
|
88 self.addTopLevelItem(self.__topItem) |
|
89 |
|
90 allRules = self.__subscription.allRules() |
|
91 |
|
92 index = 0 |
|
93 for rule in allRules: |
|
94 item = QTreeWidgetItem(self.__topItem) |
|
95 item.setText(0, rule.filter()) |
|
96 item.setData(0, Qt.UserRole, index) |
|
97 if self.__subscription.canEditRules(): |
|
98 item.setFlags(item.flags() | Qt.ItemIsEditable) |
|
99 self.__adjustItemFeatures(item, rule) |
|
100 index += 1 |
|
101 |
|
102 self.expandAll() |
|
103 self.showRule(None) |
|
104 self.__itemChangingBlock = False |
|
105 QApplication.restoreOverrideCursor() |
|
106 QApplication.processEvents() |
|
107 |
|
108 def addRule(self): |
|
109 """ |
|
110 Public slot to add a new rule. |
|
111 """ |
|
112 if not self.__subscription.canEditRules(): |
|
113 return |
|
114 |
|
115 filter = QInputDialog.getText( |
|
116 self, |
|
117 self.trUtf8("Add Custom Rule"), |
|
118 self.trUtf8("Write your rule here:"), |
|
119 QLineEdit.Normal) |
|
120 if filter == "": |
|
121 return |
|
122 |
|
123 rule = AdBlockRule(filter, self.__subscription) |
|
124 offset = self.__subscription.addRule(rule) |
|
125 |
|
126 item = QTreeWidgetItem() |
|
127 item.setText(0, filter) |
|
128 item.setData(0, Qt.UserRole, offset) |
|
129 item.setFlags(item.flags() | Qt.ItemIsEditable) |
|
130 |
|
131 self.__itemChangingBlock = True |
|
132 self.__topItem.addChild(item) |
|
133 self.__itemChangingBlock = False |
|
134 |
|
135 self.__adjustItemFeatures(item, rule) |
|
136 |
|
137 def removeRule(self): |
|
138 """ |
|
139 Public slot to remove the current rule. |
|
140 """ |
|
141 item = self.currentItem() |
|
142 if item is None or \ |
|
143 not self.__subscription.canEditRules() or \ |
|
144 item == self.__topItem: |
|
145 return |
|
146 |
|
147 offset = item.data(0, Qt.UserRole) |
|
148 self.__subscription.removeRule(offset) |
|
149 self.deleteItem(item) |
|
150 |
|
151 def __contextMenuRequested(self, pos): |
|
152 """ |
|
153 Private slot to show the context menu. |
|
154 |
|
155 @param pos position for the menu (QPoint) |
|
156 """ |
|
157 if not self.__subscription.canEditRules(): |
|
158 return |
|
159 |
|
160 item = self.itemAt(pos) |
|
161 if item is None: |
|
162 return |
|
163 |
|
164 menu = QMenu() |
|
165 menu.addAction(self.trUtf8("Add Rule"), self.addRule) |
|
166 menu.addSeparator() |
|
167 act = menu.addAction(self.trUtf8("Remove Rule"), self.removeRule) |
|
168 if item.parent() is None: |
|
169 act.setDisabled(True) |
|
170 |
|
171 menu.exec_(self.viewport().mapToGlobal(pos)) |
|
172 |
|
173 def __itemChanged(self, itm): |
|
174 """ |
|
175 Private slot to handle the change of an item. |
|
176 |
|
177 @param itm changed item (QTreeWidgetItem) |
|
178 """ |
|
179 if itm is None or self.__itemChangingBlock: |
|
180 return |
|
181 |
|
182 self.__itemChangingBlock = True |
|
183 |
|
184 offset = itm.data(0, Qt.UserRole) |
|
185 oldRule = self.__subscription.rule(offset) |
|
186 |
|
187 if itm.checkState(0) == Qt.Unchecked and oldRule.isEnabled(): |
|
188 # Disable rule |
|
189 rule = self.__subscription.setRuleEnabled(offset, False) |
|
190 self.__adjustItemFeatures(itm, rule) |
|
191 elif itm.checkState(0) == Qt.Checked and not oldRule.isEnabled(): |
|
192 # Enable rule |
|
193 rule = self.__subscription.setRuleEnabled(offset, True) |
|
194 self.__adjustItemFeatures(itm, rule) |
|
195 elif self.__subscription.canEditRules(): |
|
196 # Custom rule has been changed |
|
197 rule = self.__subscription.replaceRule( |
|
198 AdBlockRule(itm.text(0), self.__subscription), offset) |
|
199 self.__adjustItemFeatures(itm, rule) |
|
200 |
|
201 self.__itemChangingBlock = False |
|
202 |
|
203 def __copyFilter(self): |
|
204 """ |
|
205 Private slot to copy the current filter to the clipboard. |
|
206 """ |
|
207 item = self.currentItem() |
|
208 if item is not None: |
|
209 QApplication.clipboard().setText(item.text(0)) |
|
210 |
|
211 def __subscriptionChanged(self): |
|
212 """ |
|
213 Private slot handling a subscription change. |
|
214 """ |
|
215 self.refresh() |
|
216 |
|
217 self.__itemChangingBlock = True |
|
218 self.__topItem.setText(0, |
|
219 self.trUtf8("{0} (recently updated)").format(self.__subscription.title())) |
|
220 self.__itemChangingBlock = False |
|
221 |
|
222 def __adjustItemFeatures(self, itm, rule): |
|
223 """ |
|
224 Private method to adjust an item. |
|
225 |
|
226 @param itm item to be adjusted (QTreeWidgetItem) |
|
227 @param rule rule for the adjustment (AdBlockRule) |
|
228 """ |
|
229 if not rule.isEnabled(): |
|
230 font = QFont() |
|
231 font.setItalic(True) |
|
232 itm.setForeground(0, QColor(Qt.gray)) |
|
233 |
|
234 if not rule.isComment(): |
|
235 itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable) |
|
236 itm.setCheckState(0, Qt.Unchecked) |
|
237 itm.setFont(0, font) |
|
238 |
|
239 return |
|
240 |
|
241 itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable) |
|
242 itm.setCheckState(0, Qt.Checked) |
|
243 |
|
244 if rule.isCSSRule(): |
|
245 itm.setForeground(0, QColor(Qt.darkBlue)) |
|
246 itm.setFont(0, QFont()) |
|
247 elif rule.isException(): |
|
248 itm.setForeground(0, QColor(Qt.darkGreen)) |
|
249 itm.setFont(0, QFont()) |
|
250 else: |
|
251 itm.setForeground(0, QColor()) |
|
252 itm.setFont(0, QFont()) |
|
253 |
|
254 def keyPressEvent(self, evt): |
|
255 """ |
|
256 Protected method handling key presses. |
|
257 |
|
258 @param evt key press event (QKeyEvent) |
|
259 """ |
|
260 if evt.key() == Qt.Key_C and \ |
|
261 evt.modifiers() & Qt.ControlModifier: |
|
262 self.__copyFilter() |
|
263 elif evt.key() == Qt.Key_Delete: |
|
264 self.removeRule() |
|
265 else: |
|
266 super().keyPressEvent(evt) |