|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2005 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a task viewer and associated classes. |
|
8 |
|
9 Tasks can be defined manually or automatically. Automatically |
|
10 generated tasks are derived from a comment with a special |
|
11 introductory text. This text is configurable. |
|
12 """ |
|
13 |
|
14 import os |
|
15 import sys |
|
16 import time |
|
17 |
|
18 from PyQt4.QtCore import * |
|
19 from PyQt4.QtGui import * |
|
20 |
|
21 from E4Gui.E4Application import e4App |
|
22 |
|
23 from TaskPropertiesDialog import TaskPropertiesDialog |
|
24 from TaskFilterConfigDialog import TaskFilterConfigDialog |
|
25 |
|
26 import UI.PixmapCache |
|
27 |
|
28 import Preferences |
|
29 import Utilities |
|
30 |
|
31 class Task(QTreeWidgetItem): |
|
32 """ |
|
33 Class implementing the task data structure. |
|
34 """ |
|
35 def __init__(self, description, priority = 1, filename = "", lineno = 0, |
|
36 completed = False, _time = 0, isProjectTask = False, |
|
37 isBugfixTask = False, ppath = "", longtext = ""): |
|
38 """ |
|
39 Constructor |
|
40 |
|
41 @param parent parent widget of the task (QWidget) |
|
42 @param description descriptive text of the task (string) |
|
43 @param priority priority of the task (0=high, 1=normal, 2=low) |
|
44 @param filename filename containing the task (string) |
|
45 @param lineno line number containing the task (integer) |
|
46 @param completed flag indicating completion status (boolean) |
|
47 @param _time creation time of the task (float, if 0 use current time) |
|
48 @param isProjectTask flag indicating a task related to the current project |
|
49 (boolean) |
|
50 @param isBugfixTask flag indicating a bugfix task (boolean) |
|
51 @param ppath the project path (string) |
|
52 @param longtext explanatory text of the task (string) |
|
53 """ |
|
54 self.description = description |
|
55 self.longtext = longtext |
|
56 if priority in [0, 1, 2]: |
|
57 self.priority = priority |
|
58 else: |
|
59 self.priority = 1 |
|
60 self.filename = filename |
|
61 self.lineno = lineno |
|
62 self.completed = completed |
|
63 self.created = _time and _time or time.time() |
|
64 self._isProjectTask = isProjectTask |
|
65 self.isBugfixTask = isBugfixTask |
|
66 self.ppath = ppath |
|
67 |
|
68 if isProjectTask: |
|
69 self.filename = self.filename.replace(self.ppath + os.sep, "") |
|
70 |
|
71 QTreeWidgetItem.__init__(self, ["", "", self.description, self.filename, |
|
72 (self.lineno and "%6d" % self.lineno or "")]) |
|
73 |
|
74 if self.completed: |
|
75 self.setIcon(0, UI.PixmapCache.getIcon("taskCompleted.png")) |
|
76 else: |
|
77 self.setIcon(0, UI.PixmapCache.getIcon("empty.png")) |
|
78 |
|
79 if self.priority == 1: |
|
80 self.setIcon(1, UI.PixmapCache.getIcon("empty.png")) |
|
81 elif self.priority == 0: |
|
82 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioHigh.png")) |
|
83 elif self.priority == 2: |
|
84 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioLow.png")) |
|
85 else: |
|
86 self.setIcon(1, UI.PixmapCache.getIcon("empty.png")) |
|
87 |
|
88 self.colorizeTask() |
|
89 self.setTextAlignment(4, Qt.AlignRight) |
|
90 |
|
91 def colorizeTask(self): |
|
92 """ |
|
93 Public slot to set the colors of the task item. |
|
94 """ |
|
95 for col in range(5): |
|
96 if self.isBugfixTask: |
|
97 self.setTextColor(col, Preferences.getTasks("TasksBugfixColour")) |
|
98 else: |
|
99 self.setTextColor(col, Preferences.getTasks("TasksColour")) |
|
100 if self._isProjectTask: |
|
101 self.setBackgroundColor(col, Preferences.getTasks("TasksProjectBgColour")) |
|
102 else: |
|
103 self.setBackgroundColor(col, Preferences.getTasks("TasksBgColour")) |
|
104 |
|
105 def setDescription(self, description): |
|
106 """ |
|
107 Public slot to update the description. |
|
108 |
|
109 @param longtext explanatory text of the task (string) |
|
110 """ |
|
111 self.description = description |
|
112 self.setText(2, self.description) |
|
113 |
|
114 def setLongText(self, longtext): |
|
115 """ |
|
116 Public slot to update the longtext field. |
|
117 |
|
118 @param longtext descriptive text of the task (string) |
|
119 """ |
|
120 self.longtext = longtext |
|
121 |
|
122 def setPriority(self, priority): |
|
123 """ |
|
124 Public slot to update the priority. |
|
125 |
|
126 @param priority priority of the task (0=high, 1=normal, 2=low) |
|
127 """ |
|
128 if priority in [0, 1, 2]: |
|
129 self.priority = priority |
|
130 else: |
|
131 self.priority = 1 |
|
132 |
|
133 if self.priority == 1: |
|
134 self.setIcon(1, UI.PixmapCache.getIcon("empty.png")) |
|
135 elif self.priority == 0: |
|
136 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioHigh.png")) |
|
137 elif self.priority == 2: |
|
138 self.setIcon(1, UI.PixmapCache.getIcon("taskPrioLow.png")) |
|
139 else: |
|
140 self.setIcon(1, UI.PixmapCache.getIcon("empty.png")) |
|
141 |
|
142 def setCompleted(self, completed): |
|
143 """ |
|
144 Public slot to update the completed flag. |
|
145 |
|
146 @param completed flag indicating completion status (boolean) |
|
147 """ |
|
148 self.completed = completed |
|
149 if self.completed: |
|
150 self.setIcon(0, UI.PixmapCache.getIcon("taskCompleted.png")) |
|
151 else: |
|
152 self.setIcon(0, UI.PixmapCache.getIcon("empty.png")) |
|
153 |
|
154 def isCompleted(self): |
|
155 """ |
|
156 Public slot to return the completion status. |
|
157 |
|
158 @return flag indicating the completion status (boolean) |
|
159 """ |
|
160 return self.completed |
|
161 |
|
162 def getFilename(self): |
|
163 """ |
|
164 Public method to retrieve the tasks filename. |
|
165 |
|
166 @return filename (string) |
|
167 """ |
|
168 if self._isProjectTask and self.filename: |
|
169 return os.path.join(self.ppath, self.filename) |
|
170 else: |
|
171 return self.filename |
|
172 |
|
173 def getLineno(self): |
|
174 """ |
|
175 Public method to retrieve the tasks linenumber. |
|
176 |
|
177 @return linenumber (integer) |
|
178 """ |
|
179 return self.lineno |
|
180 |
|
181 def setProjectTask(self, pt): |
|
182 """ |
|
183 Public method to set the project relation flag. |
|
184 |
|
185 @param pt flag indicating a project task (boolean) |
|
186 """ |
|
187 self._isProjectTask = pt |
|
188 self.colorizeTask() |
|
189 |
|
190 def isProjectTask(self): |
|
191 """ |
|
192 Public slot to return the project relation status. |
|
193 |
|
194 @return flag indicating the project relation status (boolean) |
|
195 """ |
|
196 return self._isProjectTask |
|
197 |
|
198 class TaskFilter(object): |
|
199 """ |
|
200 Class implementing a filter for tasks. |
|
201 """ |
|
202 def __init__(self): |
|
203 """ |
|
204 Constructor |
|
205 """ |
|
206 self.active = False |
|
207 |
|
208 self.descriptionFilter = None |
|
209 self.filenameFilter = None |
|
210 self.typeFilter = None # standard (False) or bugfix (True) |
|
211 self.scopeFilter = None # global (False) or project (True) |
|
212 self.statusFilter = None # uncompleted (False) or completed (True) |
|
213 self.prioritiesFilter = None # list of priorities [0 (high), 1 (normal), 2 (low)] |
|
214 |
|
215 def setActive(self, enabled): |
|
216 """ |
|
217 Public method to activate the filter. |
|
218 |
|
219 @param enabled flag indicating the activation state (boolean) |
|
220 """ |
|
221 self.active = enabled |
|
222 |
|
223 def setDescriptionFilter(self, filter): |
|
224 """ |
|
225 Public method to set the description filter. |
|
226 |
|
227 @param filter a regular expression for the description filter |
|
228 to set (string) or None |
|
229 """ |
|
230 if not filter: |
|
231 self.descriptionFilter = None |
|
232 else: |
|
233 self.descriptionFilter = QRegExp(filter) |
|
234 |
|
235 def setFileNameFilter(self, filter): |
|
236 """ |
|
237 Public method to set the filename filter. |
|
238 |
|
239 @param filter a wildcard expression for the filename filter |
|
240 to set (string) or None |
|
241 """ |
|
242 if not filter: |
|
243 self.filenameFilter = None |
|
244 else: |
|
245 self.filenameFilter = QRegExp(filter) |
|
246 self.filenameFilter.setPatternSyntax(QRegExp.Wildcard) |
|
247 |
|
248 def setTypeFilter(self, type_): |
|
249 """ |
|
250 Public method to set the type filter. |
|
251 |
|
252 @param type_ flag indicating a bugfix task (boolean) or None |
|
253 """ |
|
254 self.typeFilter = type_ |
|
255 |
|
256 def setScopeFilter(self, scope): |
|
257 """ |
|
258 Public method to set the scope filter. |
|
259 |
|
260 @param scope flag indicating a project task (boolean) or None |
|
261 """ |
|
262 self.scopeFilter = scope |
|
263 |
|
264 def setStatusFilter(self, status): |
|
265 """ |
|
266 Public method to set the status filter. |
|
267 |
|
268 @param status flag indicating a completed task (boolean) or None |
|
269 """ |
|
270 self.statusFilter = status |
|
271 |
|
272 def setPrioritiesFilter(self, priorities): |
|
273 """ |
|
274 Public method to set the priorities filter. |
|
275 |
|
276 @param priorities list of task priorities (list of integer) or None |
|
277 """ |
|
278 self.prioritiesFilter = priorities |
|
279 |
|
280 def hasActiveFilter(self): |
|
281 """ |
|
282 Public method to check for active filters. |
|
283 |
|
284 @return flag indicating an active filter was found (boolean) |
|
285 """ |
|
286 return self.descriptionFilter is not None or \ |
|
287 self.filenameFilter is not None or \ |
|
288 self.typeFilter is not None or \ |
|
289 self.scopeFilter is not None or \ |
|
290 self.statusFilter is not None or \ |
|
291 self.prioritiesFilter is not None |
|
292 |
|
293 def showTask(self, task): |
|
294 """ |
|
295 Public method to check, if a task should be shown. |
|
296 |
|
297 @param task reference to the task object to check (Task) |
|
298 @return flag indicating whether the task should be shown (boolean) |
|
299 """ |
|
300 if not self.active: |
|
301 return True |
|
302 |
|
303 if self.descriptionFilter and \ |
|
304 self.descriptionFilter.indexIn(task.description) == -1: |
|
305 return False |
|
306 |
|
307 if self.filenameFilter and \ |
|
308 not self.filenameFilter.exactMatch(task.filename): |
|
309 return False |
|
310 |
|
311 if self.typeFilter is not None and \ |
|
312 self.typeFilter != task.isBugfixTask: |
|
313 return False |
|
314 |
|
315 if self.scopeFilter is not None and \ |
|
316 self.scopeFilter != task._isProjectTask: |
|
317 return False |
|
318 |
|
319 if self.statusFilter is not None and \ |
|
320 self.statusFilter != task.completed: |
|
321 return False |
|
322 |
|
323 if self.prioritiesFilter is not None and \ |
|
324 not task.priority in self.prioritiesFilter: |
|
325 return False |
|
326 |
|
327 return True |
|
328 |
|
329 class TaskViewer(QTreeWidget): |
|
330 """ |
|
331 Class implementing the task viewer. |
|
332 |
|
333 @signal displayFile(string, integer) emitted to go to a file task |
|
334 """ |
|
335 def __init__(self, parent, project): |
|
336 """ |
|
337 Constructor |
|
338 |
|
339 @param parent the parent (QWidget) |
|
340 @param project reference to the project object |
|
341 """ |
|
342 QTreeWidget.__init__(self, parent) |
|
343 |
|
344 self.setRootIsDecorated(False) |
|
345 self.setItemsExpandable(False) |
|
346 self.setSortingEnabled(True) |
|
347 |
|
348 self.__headerItem = QTreeWidgetItem(["", "", self.trUtf8("Summary"), |
|
349 self.trUtf8("Filename"), self.trUtf8("Line"), ""]) |
|
350 self.__headerItem.setIcon(0, UI.PixmapCache.getIcon("taskCompleted.png")) |
|
351 self.__headerItem.setIcon(1, UI.PixmapCache.getIcon("taskPriority.png")) |
|
352 self.setHeaderItem(self.__headerItem) |
|
353 |
|
354 self.header().setSortIndicator(2, Qt.AscendingOrder) |
|
355 self.__resizeColumns() |
|
356 |
|
357 self.tasks = [] |
|
358 self.copyTask = None |
|
359 self.projectOpen = False |
|
360 self.project = project |
|
361 |
|
362 self.taskFilter = TaskFilter() |
|
363 self.taskFilter.setActive(False) |
|
364 |
|
365 self.__menu = QMenu(self) |
|
366 self.__menu.addAction(self.trUtf8("&New Task..."), self.__newTask) |
|
367 self.__menu.addSeparator() |
|
368 self.regenerateProjectTasksItem = \ |
|
369 self.__menu.addAction(self.trUtf8("&Regenerate project tasks"), |
|
370 self.__regenerateProjectTasks) |
|
371 self.__menu.addSeparator() |
|
372 self.gotoItem = self.__menu.addAction(self.trUtf8("&Go To"), self.__goToTask) |
|
373 self.__menu.addSeparator() |
|
374 self.copyItem = self.__menu.addAction(self.trUtf8("&Copy"), self.__copyTask) |
|
375 self.pasteItem = self.__menu.addAction(self.trUtf8("&Paste"), self.__pasteTask) |
|
376 self.deleteItem = self.__menu.addAction(self.trUtf8("&Delete"), self.__deleteTask) |
|
377 self.__menu.addSeparator() |
|
378 self.markCompletedItem = self.__menu.addAction(self.trUtf8("&Mark Completed"), |
|
379 self.__markCompleted) |
|
380 self.__menu.addAction(self.trUtf8("Delete Completed &Tasks"), |
|
381 self.__deleteCompleted) |
|
382 self.__menu.addSeparator() |
|
383 self.__menu.addAction(self.trUtf8("P&roperties..."), self.__editTaskProperties) |
|
384 self.__menu.addSeparator() |
|
385 self.__menuFilteredAct = self.__menu.addAction(self.trUtf8("&Filtered display")) |
|
386 self.__menuFilteredAct.setCheckable(True) |
|
387 self.__menuFilteredAct.setChecked(False) |
|
388 self.connect(self.__menuFilteredAct, SIGNAL("triggered(bool)"), |
|
389 self.__activateFilter) |
|
390 self.__menu.addAction(self.trUtf8("Filter c&onfiguration..."), |
|
391 self.__configureFilter) |
|
392 self.__menu.addSeparator() |
|
393 self.__menu.addAction(self.trUtf8("Resi&ze columns"), self.__resizeColumns) |
|
394 self.__menu.addSeparator() |
|
395 self.__menu.addAction(self.trUtf8("Configure..."), self.__configure) |
|
396 |
|
397 self.__backMenu = QMenu(self) |
|
398 self.__backMenu.addAction(self.trUtf8("&New Task..."), self.__newTask) |
|
399 self.__backMenu.addSeparator() |
|
400 self.backRegenerateProjectTasksItem = \ |
|
401 self.__backMenu.addAction(self.trUtf8("&Regenerate project tasks"), |
|
402 self.__regenerateProjectTasks) |
|
403 self.__backMenu.addSeparator() |
|
404 self.backPasteItem = self.__backMenu.addAction(self.trUtf8("&Paste"), |
|
405 self.__pasteTask) |
|
406 self.__backMenu.addSeparator() |
|
407 self.__backMenu.addAction(self.trUtf8("Delete Completed &Tasks"), |
|
408 self.__deleteCompleted) |
|
409 self.__backMenu.addSeparator() |
|
410 self.__backMenuFilteredAct = \ |
|
411 self.__backMenu.addAction(self.trUtf8("&Filtered display")) |
|
412 self.__backMenuFilteredAct.setCheckable(True) |
|
413 self.__backMenuFilteredAct.setChecked(False) |
|
414 self.connect(self.__backMenuFilteredAct, SIGNAL("triggered(bool)"), |
|
415 self.__activateFilter) |
|
416 self.__backMenu.addAction(self.trUtf8("Filter c&onfiguration..."), |
|
417 self.__configureFilter) |
|
418 self.__backMenu.addSeparator() |
|
419 self.__backMenu.addAction(self.trUtf8("Resi&ze columns"), self.__resizeColumns) |
|
420 self.__backMenu.addSeparator() |
|
421 self.__backMenu.addAction(self.trUtf8("Configure..."), self.__configure) |
|
422 |
|
423 self.setContextMenuPolicy(Qt.CustomContextMenu) |
|
424 self.connect(self, SIGNAL("customContextMenuRequested(const QPoint &)"), |
|
425 self.__showContextMenu) |
|
426 self.connect(self, SIGNAL("itemActivated(QTreeWidgetItem *, int)"), |
|
427 self.__taskItemActivated) |
|
428 |
|
429 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) |
|
430 |
|
431 def __resort(self): |
|
432 """ |
|
433 Private method to resort the tree. |
|
434 """ |
|
435 self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder()) |
|
436 |
|
437 def __resizeColumns(self): |
|
438 """ |
|
439 Private method to resize the list columns. |
|
440 """ |
|
441 self.header().resizeSections(QHeaderView.ResizeToContents) |
|
442 self.header().setStretchLastSection(True) |
|
443 |
|
444 def __refreshDisplay(self): |
|
445 """ |
|
446 Private method to refresh the display. |
|
447 """ |
|
448 for task in self.tasks: |
|
449 index = self.indexOfTopLevelItem(task) |
|
450 if self.taskFilter.showTask(task): |
|
451 # show the task |
|
452 if index == -1: |
|
453 self.addTopLevelItem(task) |
|
454 else: |
|
455 # hide the task |
|
456 if index != -1: |
|
457 self.takeTopLevelItem(index) |
|
458 self.__resort() |
|
459 self.__resizeColumns() |
|
460 |
|
461 def __taskItemActivated(self, itm, col): |
|
462 """ |
|
463 Private slot to handle the activation of an item. |
|
464 |
|
465 @param itm reference to the activated item (QTreeWidgetItem) |
|
466 @param col column the item was activated in (integer) |
|
467 """ |
|
468 fn = itm.getFilename() |
|
469 if fn: |
|
470 self.emit(SIGNAL("displayFile"), fn, itm.getLineno()) |
|
471 else: |
|
472 self.__editTaskProperties() |
|
473 |
|
474 def __showContextMenu(self, coord): |
|
475 """ |
|
476 Private slot to show the context menu of the list. |
|
477 |
|
478 @param coord the position of the mouse pointer (QPoint) |
|
479 """ |
|
480 itm = self.itemAt(coord) |
|
481 coord = self.mapToGlobal(coord) |
|
482 if itm is None: |
|
483 self.backRegenerateProjectTasksItem.setEnabled(self.projectOpen) |
|
484 if self.copyTask: |
|
485 self.backPasteItem.setEnabled(True) |
|
486 else: |
|
487 self.backPasteItem.setEnabled(False) |
|
488 self.__backMenu.popup(coord) |
|
489 else: |
|
490 self.regenerateProjectTasksItem.setEnabled(self.projectOpen) |
|
491 if itm.getFilename(): |
|
492 self.gotoItem.setEnabled(True) |
|
493 self.deleteItem.setEnabled(True) |
|
494 self.markCompletedItem.setEnabled(False) |
|
495 self.copyItem.setEnabled(False) |
|
496 else: |
|
497 self.gotoItem.setEnabled(False) |
|
498 self.deleteItem.setEnabled(True) |
|
499 self.markCompletedItem.setEnabled(True) |
|
500 self.copyItem.setEnabled(True) |
|
501 if self.copyTask: |
|
502 self.pasteItem.setEnabled(True) |
|
503 else: |
|
504 self.pasteItem.setEnabled(False) |
|
505 |
|
506 self.__menu.popup(coord) |
|
507 |
|
508 def setProjectOpen(self, o = False): |
|
509 """ |
|
510 Public slot to set the project status. |
|
511 |
|
512 @param o flag indicating the project status |
|
513 """ |
|
514 self.projectOpen = o |
|
515 |
|
516 def addTask(self, description, priority = 1, filename = "", lineno = 0, |
|
517 completed = False, _time = 0, isProjectTask = False, |
|
518 isBugfixTask = False, longtext = ""): |
|
519 """ |
|
520 Public slot to add a task. |
|
521 |
|
522 @param description descriptive text of the task (string) |
|
523 @param priority priority of the task (0=high, 1=normal, 2=low) |
|
524 @param filename filename containing the task (string) |
|
525 @param lineno line number containing the task (integer) |
|
526 @param completed flag indicating completion status (boolean) |
|
527 @param _time creation time of the task (float, if 0 use current time) |
|
528 @param isProjectTask flag indicating a task related to the current |
|
529 project (boolean) |
|
530 @param isBugfixTask flag indicating a bugfix task (boolean) |
|
531 @param longtext explanatory text of the task (string) |
|
532 """ |
|
533 task = Task(description, priority, filename, lineno, completed, |
|
534 _time, isProjectTask, isBugfixTask, |
|
535 self.project and self.project.ppath or "", |
|
536 longtext) |
|
537 self.tasks.append(task) |
|
538 if self.taskFilter.showTask(task): |
|
539 self.addTopLevelItem(task) |
|
540 self.__resort() |
|
541 self.__resizeColumns() |
|
542 |
|
543 def addFileTask(self, description, filename, lineno, isBugfixTask = False, |
|
544 longtext = ""): |
|
545 """ |
|
546 Public slot to add a file related task. |
|
547 |
|
548 @param description descriptive text of the task (string) |
|
549 @param filename filename containing the task (string) |
|
550 @param lineno line number containing the task (integer) |
|
551 @param isBugfixTask flag indicating a bugfix task (boolean) |
|
552 @param longtext explanatory text of the task (string) |
|
553 """ |
|
554 self.addTask(description, filename = filename, lineno = lineno, |
|
555 isProjectTask = \ |
|
556 self.project and self.project.isProjectSource(filename), |
|
557 isBugfixTask = isBugfixTask, longtext = longtext) |
|
558 |
|
559 def getProjectTasks(self): |
|
560 """ |
|
561 Public method to retrieve all project related tasks. |
|
562 |
|
563 @return copy of tasks (list of Task) |
|
564 """ |
|
565 tasks = [task for task in self.tasks if task.isProjectTask()] |
|
566 return tasks[:] |
|
567 |
|
568 def getGlobalTasks(self): |
|
569 """ |
|
570 Public method to retrieve all non project related tasks. |
|
571 |
|
572 @return copy of tasks (list of Task) |
|
573 """ |
|
574 tasks = [task for task in self.tasks if not task.isProjectTask()] |
|
575 return tasks[:] |
|
576 |
|
577 def clearTasks(self): |
|
578 """ |
|
579 Public slot to clear all tasks from display. |
|
580 """ |
|
581 self.tasks = [] |
|
582 self.clear() |
|
583 |
|
584 def clearProjectTasks(self): |
|
585 """ |
|
586 Public slot to clear project related tasks. |
|
587 """ |
|
588 for task in self.tasks[:]: |
|
589 if task.isProjectTask(): |
|
590 if self.copyTask == task: |
|
591 self.copyTask = None |
|
592 index = self.indexOfTopLevelItem(task) |
|
593 self.takeTopLevelItem(index) |
|
594 self.tasks.remove(task) |
|
595 del task |
|
596 |
|
597 def clearFileTasks(self, filename): |
|
598 """ |
|
599 Public slot to clear all tasks related to a file. |
|
600 |
|
601 @param filename name of the file (string) |
|
602 """ |
|
603 for task in self.tasks[:]: |
|
604 if task.getFilename() == filename: |
|
605 if self.copyTask == task: |
|
606 self.copyTask = None |
|
607 index = self.indexOfTopLevelItem(task) |
|
608 self.takeTopLevelItem(index) |
|
609 self.tasks.remove(task) |
|
610 del task |
|
611 |
|
612 def __editTaskProperties(self): |
|
613 """ |
|
614 Private slot to handle the "Properties" context menu entry |
|
615 """ |
|
616 task = self.currentItem() |
|
617 dlg = TaskPropertiesDialog(task, self, self.projectOpen) |
|
618 ro = task.getFilename() != "" |
|
619 if ro: |
|
620 dlg.setReadOnly() |
|
621 if dlg.exec_() == QDialog.Accepted and not ro: |
|
622 data = dlg.getData() |
|
623 task.setDescription(data[0]) |
|
624 task.setPriority(data[1]) |
|
625 task.setCompleted(data[2]) |
|
626 task.setProjectTask(data[3]) |
|
627 task.setLongText(data[4]) |
|
628 |
|
629 def __newTask(self): |
|
630 """ |
|
631 Private slot to handle the "New Task" context menu entry. |
|
632 """ |
|
633 dlg = TaskPropertiesDialog(None, self, self.projectOpen) |
|
634 if dlg.exec_() == QDialog.Accepted: |
|
635 data = dlg.getData() |
|
636 self.addTask(data[0], data[1], completed = data[2], isProjectTask = data[3], |
|
637 longtext = data[4]) |
|
638 |
|
639 def __markCompleted(self): |
|
640 """ |
|
641 Private slot to handle the "Mark Completed" context menu entry. |
|
642 """ |
|
643 task = self.currentItem() |
|
644 task.setCompleted(True) |
|
645 |
|
646 def __deleteCompleted(self): |
|
647 """ |
|
648 Private slot to handle the "Delete Completed Tasks" context menu entry. |
|
649 """ |
|
650 for task in self.tasks[:]: |
|
651 if task.isCompleted(): |
|
652 if self.copyTask == task: |
|
653 self.copyTask = None |
|
654 index = self.indexOfTopLevelItem(task) |
|
655 self.takeTopLevelItem(index) |
|
656 self.tasks.remove(task) |
|
657 del task |
|
658 ci = self.currentItem() |
|
659 if ci: |
|
660 ind = self.indexFromItem(ci, self.currentColumn()) |
|
661 self.scrollTo(ind, QAbstractItemView.PositionAtCenter) |
|
662 |
|
663 def __copyTask(self): |
|
664 """ |
|
665 Private slot to handle the "Copy" context menu entry. |
|
666 """ |
|
667 task = self.currentItem() |
|
668 self.copyTask = task |
|
669 |
|
670 def __pasteTask(self): |
|
671 """ |
|
672 Private slot to handle the "Paste" context menu entry. |
|
673 """ |
|
674 if self.copyTask: |
|
675 self.addTask(self.copyTask.description, |
|
676 priority = self.copyTask.priority, |
|
677 completed = self.copyTask.completed, |
|
678 longtext = self.copyTask.longtext, |
|
679 isProjectTask = self.copyTask._isProjectTask) |
|
680 |
|
681 def __deleteTask(self): |
|
682 """ |
|
683 Private slot to handle the "Delete Task" context menu entry. |
|
684 """ |
|
685 task = self.currentItem() |
|
686 if self.copyTask == task: |
|
687 self.copyTask = None |
|
688 index = self.indexOfTopLevelItem(task) |
|
689 self.takeTopLevelItem(index) |
|
690 self.tasks.remove(task) |
|
691 del task |
|
692 ci = self.currentItem() |
|
693 if ci: |
|
694 ind = self.indexFromItem(ci, self.currentColumn()) |
|
695 self.scrollTo(ind, QAbstractItemView.PositionAtCenter) |
|
696 |
|
697 def __goToTask(self): |
|
698 """ |
|
699 Private slot to handle the "Go To" context menu entry. |
|
700 """ |
|
701 task = self.currentItem() |
|
702 self.emit(SIGNAL('displayFile'), task.getFilename(), task.getLineno()) |
|
703 |
|
704 def handlePreferencesChanged(self): |
|
705 """ |
|
706 Public slot to react to changes of the preferences. |
|
707 """ |
|
708 for task in self.tasks: |
|
709 task.colorizeTask() |
|
710 |
|
711 def __activateFilter(self, on): |
|
712 """ |
|
713 Private slot to handle the "Filtered display" context menu entry. |
|
714 |
|
715 @param on flag indicating the filter state (boolean) |
|
716 """ |
|
717 if on and not self.taskFilter.hasActiveFilter(): |
|
718 res = QMessageBox.information(None, |
|
719 self.trUtf8("Activate task filter"), |
|
720 self.trUtf8("""The task filter doesn't have any active filters.""" |
|
721 """ Do you want to configure the filter settings?"""), |
|
722 QMessageBox.StandardButtons(\ |
|
723 QMessageBox.No | \ |
|
724 QMessageBox.Yes), |
|
725 QMessageBox.Yes) |
|
726 if res != QMessageBox.Yes: |
|
727 on = False |
|
728 else: |
|
729 self.__configureFilter() |
|
730 on = self.taskFilter.hasActiveFilter() |
|
731 |
|
732 self.taskFilter.setActive(on) |
|
733 self.__menuFilteredAct.setChecked(on) |
|
734 self.__backMenuFilteredAct.setChecked(on) |
|
735 self.__refreshDisplay() |
|
736 |
|
737 def __configureFilter(self): |
|
738 """ |
|
739 Private slot to handle the "Configure filter" context menu entry. |
|
740 """ |
|
741 dlg = TaskFilterConfigDialog(self.taskFilter) |
|
742 if dlg.exec_() == QDialog.Accepted: |
|
743 dlg.configureTaskFilter(self.taskFilter) |
|
744 self.__refreshDisplay() |
|
745 |
|
746 def __regenerateProjectTasks(self): |
|
747 """ |
|
748 Private slot to handle the "Regenerated project tasks" context menu entry. |
|
749 """ |
|
750 todoMarkers = Preferences.getTasks("TasksMarkers").split() |
|
751 bugfixMarkers = Preferences.getTasks("TasksMarkersBugfix").split() |
|
752 files = self.project.pdata["SOURCES"] |
|
753 |
|
754 # remove all project tasks |
|
755 self.clearProjectTasks() |
|
756 |
|
757 # now process them |
|
758 progress = QProgressDialog(self.trUtf8("Extracting project tasks..."), |
|
759 self.trUtf8("Abort"), 0, len(files)) |
|
760 progress.setMinimumDuration(0) |
|
761 count = 0 |
|
762 |
|
763 for file in files: |
|
764 progress.setLabelText(\ |
|
765 self.trUtf8("Extracting project tasks...\n{0}").format(file)) |
|
766 progress.setValue(count) |
|
767 QApplication.processEvents() |
|
768 if progress.wasCanceled(): |
|
769 break |
|
770 |
|
771 fn = os.path.join(self.project.ppath, file) |
|
772 # read the file and split it into textlines |
|
773 try: |
|
774 f = open(fn, 'rb') |
|
775 text, encoding = Utilities.decode(f.read()) |
|
776 lines = text.splitlines() |
|
777 f.close() |
|
778 except IOError: |
|
779 count += 1 |
|
780 self.progress.setValue(count) |
|
781 continue |
|
782 |
|
783 # now search tasks and record them |
|
784 lineIndex = 0 |
|
785 for line in lines: |
|
786 lineIndex += 1 |
|
787 shouldContinue = False |
|
788 # normal tasks first |
|
789 for tasksMarker in todoMarkers: |
|
790 index = line.find(tasksMarker) |
|
791 if index > -1: |
|
792 task = line[index:] |
|
793 self.addFileTask(task, fn, lineIndex, False) |
|
794 shouldContinue = True |
|
795 break |
|
796 if shouldContinue: |
|
797 continue |
|
798 |
|
799 # bugfix tasks second |
|
800 for tasksMarker in bugfixMarkers: |
|
801 index = line.find(tasksMarker) |
|
802 if index > -1: |
|
803 task = line[index:] |
|
804 self.addFileTask(task, fn, lineIndex, True) |
|
805 shouldContinue = True |
|
806 break |
|
807 |
|
808 count += 1 |
|
809 |
|
810 progress.setValue(len(files)) |
|
811 |
|
812 def __configure(self): |
|
813 """ |
|
814 Private method to open the configuration dialog. |
|
815 """ |
|
816 e4App().getObject("UserInterface").showPreferences("tasksPage") |