1
# -*- coding: utf-8 -*-
3
__license__ = "GPL V.2 or (at your option) any later version"
4
__copyright__ = "2007, Israel Fernández Cabrera"
5
__author__ = "Israel Fernández Cabrera <iferca@gmail.com>"
11
from addinbase import AddinBase
12
from signalhandler import SignalConnector
13
from localgtk import YTreeView, YTreeViewColumn
14
from dialog import TaskDialog
15
from project.projectmanager import ProjectManager
17
from taskstorage import Task, TaskStorageError
19
except ImportError, e:
21
utils.showErrorMessageEx("Missing dependency",
22
"YaPe found a missing dependency while starting up!",
23
"Eather «pysqlite» is not installed on your system or is not properly configured. You'll be unable to manipulate tasks in the Task Addin. Please, install support for pysqlite in your system and restart YaPe.", e)
31
class TaskPanel(AddinBase, gtk.VBox):
32
"""TaskPanel(orientation)
33
where: orientation is one of "Vertical"/"Horizontal"
35
Panel that shows and organize the task «hard codded» in the project's modules.
36
The project's modules are scanned searching for: #TODO, #FIXME
38
def __init__(self, orientation):
39
super(TaskPanel, self).__init__()
41
self._sc = SignalConnector.getInstance()
42
self._sc.registerSignalObserver("UpdateProjectBrowser", self)
43
self._sc.registerSignalObserver("EditorChanged", self)
47
self._taskStorage = None
48
self._pm = ProjectManager.getInstance()
49
if self._pm.isProjectOpen():
52
self.notifyUpdateProjectBrowser()
53
except TaskStorageError, e:
54
utils.showMessage(title="Error initializing tasks",
55
primaryMessage="YaPe found an error initializing tasks.",
56
secondaryMessage="%s. You'll be unable to use the tasks from the Task addin. This is probable an bug in the addin. Please report." % str(e),
57
dialogType = gtk.MESSAGE_ERROR)
58
self._initGUIContent(orientation)
61
self._taskView.set_model(None)
62
self._taskModel = gtk.ListStore(str, bool, str, str, str)
65
self._taskModel.append((task.icon(), task.isDone(),
66
task.name, task.status(), str(task.tid)))
67
self._taskView.set_model(self._taskModel)
69
def _initGUIContent(self, orientation):
72
if orientation == "Vertical":
73
container = gtk.VPaned()
75
container = gtk.HPaned()
76
container.pack1(self._initTodoView())
77
container.pack2(self._initTaskView(), False, False)
78
self.pack_start(container)
80
self.pack_start(self._initTodoView())
83
def _initTaskView(self):
84
self._taskView = YTreeView()
85
self._taskView.connect('button-press-event', self._onTaskViewClick)
86
self._taskView.connect('row-activated', self._onTaskRowActivated)
88
column = YTreeViewColumn()
89
column.addContent(colType="stock")
90
self._taskView.addColumn(column)
92
column = YTreeViewColumn("Done")
93
column.addContent(colType="bool", extraArgs=self._onToggleTask)
94
self._taskView.addColumn(column)
96
column = YTreeViewColumn("Task")
97
column.addContent(colType="text", resizable=True, reorderable=True)
98
self._taskView.addColumn(column)
100
column = YTreeViewColumn("Status")
101
column.addContent(colType="text", resizable=True, reorderable=True)
102
self._taskView.addColumn(column)
104
column = YTreeViewColumn("Task")
105
column.addContent(colType="text", visible=False)
106
self._taskView.addColumn(column)
108
sw = gtk.ScrolledWindow()
109
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
110
sw.add(self._taskView)
111
self._initTaskPopupMenu()
114
def _initTaskPopupMenu(self):
115
uiManager = gtk.UIManager()
117
<popup name = "Popup">
118
<menuitem action = "New" />
119
<menuitem action = "Edit" />
120
<menuitem action = "Remove" />
125
("New", gtk.STOCK_NEW, "_New Task", None, "Create new task", self._onCreateNewTask),
126
("Edit", gtk.STOCK_EDIT, "_Edit Task", None, "Edit selected task", self._onEditTask),
127
("Remove", gtk.STOCK_REMOVE, "_Remove Task", None, "Remove selected task", self._onRemoveTask),
129
actionGroup = gtk.ActionGroup('PopupActions')
130
actionGroup.add_actions(actions)
131
uiManager.insert_action_group(actionGroup, 0)
132
uiManager.add_ui_from_string(menuItems)
133
self._taskPopupMenu = uiManager.get_widget('/Popup')
135
def _onRemoveTask(self, widget):
136
tId = self._taskView.getSelectedValue(COLUMN_TASK_ID)
138
self.__removeTask(tId)
140
def __removeTask(self, tId):
141
task = Task.load(tId)
142
response = utils.showMessage(title="Confirm Task Deletion",
143
primaryMessage="Are you sure you want to delete task: %s" % task.name,
144
secondaryMessage="Deleting this task will permanently destroy it.",
145
dialogType = gtk.MESSAGE_QUESTION,
146
buttons = (gtk.STOCK_NO, gtk.RESPONSE_NO, gtk.STOCK_YES, gtk.RESPONSE_YES),
147
defaultResponse = gtk.RESPONSE_NO)
148
if response == gtk.RESPONSE_YES:
152
def _onEditTask(self, widget):
153
tId = self._taskView.getSelectedValue(COLUMN_TASK_ID)
155
task = Task.load(tId)
156
self.__editTask(task)
158
def _onCreateNewTask(self, widget):
161
def __editTask(self, task=None):
165
if d.run() != 2 or not d.isValid():
177
if d.run() == 1 or not d.isValid():
185
def _initTodoView(self):
186
self._todoView = YTreeView()
187
self._todoView.connect('button-press-event', self._onTodoViewClick)
188
self._todoView.connect('row-activated', self._onTodoRowActivated)
189
column = YTreeViewColumn(title="TODOs/FXMEs")
190
column.addContent(colType="stock")
191
column.addContent(colType="text")
192
self._todoView.addColumn(column)
193
column = YTreeViewColumn(title="File")
194
column.addContent(colType="text")
195
self._todoView.addColumn(column)
196
column = YTreeViewColumn()
197
column.addContent(colType="text", visible=False)
198
self._todoView.addColumn(column)
199
sw = gtk.ScrolledWindow()
200
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
201
sw.add(self._todoView)
202
self._initTodoPopupMenu()
205
def _initTodoPopupMenu(self):
206
uiManager = gtk.UIManager()
208
<popup name = "Popup">
209
<menuitem action = "Refresh" />
214
("Refresh", gtk.STOCK_REFRESH, "Update Tasks", None, "Update Tasks", self.notifyUpdateProjectBrowser),
216
actionGroup = gtk.ActionGroup('PopupActions')
217
actionGroup.add_actions(actions)
218
uiManager.insert_action_group(actionGroup, 0)
219
uiManager.add_ui_from_string(menuItems)
220
self._todoPopupMenu = uiManager.get_widget('/Popup')
222
def _onToggleTask(self, cell, path):
224
newValue = not self._taskModel[path][1]
225
self._taskModel[path][1] = newValue
226
tId = self._taskModel.get_value(self._taskModel.get_iter(path), COLUMN_TASK_ID)
227
task = Task.load(tId)
228
task.markFinished(newValue)
232
def _onTodoViewClick(self, widget, event):
233
if event.button != 3:
235
self._todoPopupMenu.popup(None, None, None, event.button, event.time)
237
def _onTaskViewClick(self, widget, event):
238
if event.button != 3:
240
self._taskPopupMenu.popup(None, None, None, event.button, event.time)
242
def _onTodoRowActivated(self, treeview, path, column):
244
model = treeview.get_model()
245
currentIter = model.get_iter(path)
246
lineNo = int(model.get_value(currentIter, 3))
247
fileName = model.get_value(currentIter, 2)
248
self._sc.notifyObservers("OpenEditor", filename=fileName)
249
#FIXME: When a new editor is opened the current line does not move to lineNo
250
self._sc.notifyObservers("GotoEditorLine", line=lineNo)
254
def _onTaskRowActivated(self, treeview, path, column):
255
tId = self._taskView.getSelectedValue(COLUMN_TASK_ID)
257
self.__editTask(Task.load(tId))
259
def notifyEditorChanged(self, editor):
261
self.notifyUpdateProjectBrowser()
263
def notifyUpdateProjectBrowser(self, widget=None):
264
if not self._pm.isProjectOpen():
266
t = threading.Thread(target=self._doUpdateProjectBrowser)
269
def _doUpdateProjectBrowser(self):
270
self._treestore = gtk.TreeStore(str, str, str, str)
271
self._hasTODORoot = False
272
self._hasFIXMERoot = False
273
gtk.gdk.threads_enter()
275
projectLocation = self._pm.projectLocation()
277
os.path.walk(projectLocation, self._checkFile, None)
279
self._sc.notifyObservers("MessageEmited", level = 1,
280
message = e.__class__.__name__,
285
self._todoView.set_model(self._treestore)
286
self._todoView.expand_all()
287
gtk.gdk.threads_leave()
289
def _checkFile(self, param, dirName, files):
290
for fileName in files:
291
fullPath = os.path.join(dirName, fileName)
292
if fullPath.endswith("py"):
293
self._findTasksInFile(fullPath)
295
def _findTasksInFile(self, fileName):
296
archive = open(fileName, 'r')
299
lineUpper = line.upper()
300
if not line.strip().startswith("#"):
303
if "TODO:" in lineUpper:
304
self._addTODONode(line, fileName, index)
305
elif "FIXME:" in lineUpper:
306
self._addFIXMENode(line, fileName, index)
309
def _addTODONode(self, content, fileName, lineNo):
310
if not self._hasTODORoot:
311
self._TODORootIter = self._treestore.append(None, (gtk.STOCK_INFO, "TODO's:", None, None))
312
self._hasTODORoot = True
313
content = content.strip()
314
content = content.split("TODO:")[1].strip()
315
self._treestore.append(self._TODORootIter, (None, content, fileName, lineNo))
317
def _addFIXMENode(self, content, fileName, lineNo):
318
if not self._hasFIXMERoot:
319
self._FIXMERootIter = self._treestore.append(None, (gtk.STOCK_DIALOG_WARNING, "FIXME's:", None, None))
320
self._hasFIXMERoot = True
321
content = content.strip()
322
content = content.split("FIXME:")[1].strip()
323
self._treestore.append(self._FIXMERootIter, (None, content, fileName, lineNo))
326
def createNewInstance(cls, orientation):
327
return TaskPanel(orientation)
330
def description(cls):