~iferca/yape/trunk

« back to all changes in this revision

Viewing changes to addins/tasks/tasks.py

  • Committer: Israel Fernández Cabrera
  • Date: 2008-10-03 21:12:17 UTC
  • Revision ID: iferca@gmail.com-20081003211217-uu1df2ucq3wd67nd
YaPe project moved to the new YaPe project structure. Old one will work fine but it was not suitable for the next development phases

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
 
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>"
6
 
 
7
 
import os
8
 
import threading
9
 
import gtk
10
 
import utils
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
16
 
try:
17
 
    from taskstorage import Task, TaskStorageError
18
 
    TASK_ENABLED = True
19
 
except ImportError, e:
20
 
    TASK_ENABLED = False
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)
24
 
 
25
 
 
26
 
def getAddin():
27
 
    return TaskPanel
28
 
 
29
 
COLUMN_TASK_ID = 4
30
 
 
31
 
class TaskPanel(AddinBase, gtk.VBox):
32
 
    """TaskPanel(orientation)
33
 
    where: orientation is one of "Vertical"/"Horizontal"
34
 
    
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
37
 
    """
38
 
    def __init__(self, orientation):
39
 
        super(TaskPanel, self).__init__()
40
 
        try:
41
 
            self._sc = SignalConnector.getInstance()
42
 
            self._sc.registerSignalObserver("UpdateProjectBrowser", self)
43
 
            self._sc.registerSignalObserver("EditorChanged", self)
44
 
        except Exception, e:
45
 
            print e
46
 
        try:
47
 
            self._taskStorage = None
48
 
            self._pm = ProjectManager.getInstance()
49
 
            if self._pm.isProjectOpen():
50
 
                if TASK_ENABLED:
51
 
                    self._loadTasks()
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)
59
 
 
60
 
    def _loadTasks(self):
61
 
        self._taskView.set_model(None)
62
 
        self._taskModel = gtk.ListStore(str, bool, str, str, str)
63
 
        tasks = Task.all()
64
 
        for task in tasks:
65
 
            self._taskModel.append((task.icon(), task.isDone(), 
66
 
                                    task.name, task.status(), str(task.tid)))
67
 
        self._taskView.set_model(self._taskModel)
68
 
 
69
 
    def _initGUIContent(self, orientation):
70
 
        if TASK_ENABLED:
71
 
            container = None
72
 
            if orientation == "Vertical":
73
 
                container = gtk.VPaned()
74
 
            else:
75
 
                container = gtk.HPaned()
76
 
            container.pack1(self._initTodoView())
77
 
            container.pack2(self._initTaskView(), False, False)
78
 
            self.pack_start(container)
79
 
        else:
80
 
            self.pack_start(self._initTodoView())
81
 
        self.show_all()
82
 
 
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)
87
 
        
88
 
        column = YTreeViewColumn()
89
 
        column.addContent(colType="stock")
90
 
        self._taskView.addColumn(column)
91
 
        
92
 
        column = YTreeViewColumn("Done")
93
 
        column.addContent(colType="bool", extraArgs=self._onToggleTask)
94
 
        self._taskView.addColumn(column)
95
 
        
96
 
        column = YTreeViewColumn("Task")
97
 
        column.addContent(colType="text", resizable=True, reorderable=True)
98
 
        self._taskView.addColumn(column)
99
 
        
100
 
        column = YTreeViewColumn("Status")
101
 
        column.addContent(colType="text", resizable=True, reorderable=True)
102
 
        self._taskView.addColumn(column)
103
 
        
104
 
        column = YTreeViewColumn("Task")
105
 
        column.addContent(colType="text", visible=False)
106
 
        self._taskView.addColumn(column)
107
 
        
108
 
        sw = gtk.ScrolledWindow()
109
 
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
110
 
        sw.add(self._taskView)
111
 
        self._initTaskPopupMenu()
112
 
        return sw
113
 
 
114
 
    def _initTaskPopupMenu(self):
115
 
        uiManager = gtk.UIManager()
116
 
        menuItems = """<ui>
117
 
        <popup name = "Popup">
118
 
            <menuitem action = "New" />
119
 
            <menuitem action = "Edit" />
120
 
            <menuitem action = "Remove" />
121
 
        </popup>
122
 
        </ui>
123
 
        """
124
 
        actions = (
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),
128
 
        )
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')
134
 
 
135
 
    def _onRemoveTask(self, widget):
136
 
        tId = self._taskView.getSelectedValue(COLUMN_TASK_ID)
137
 
        if tId:
138
 
            self.__removeTask(tId)
139
 
 
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:
149
 
            task.remove(tId)
150
 
        self._loadTasks()
151
 
 
152
 
    def _onEditTask(self, widget):
153
 
        tId = self._taskView.getSelectedValue(COLUMN_TASK_ID)
154
 
        if tId:
155
 
            task = Task.load(tId)
156
 
            self.__editTask(task)
157
 
 
158
 
    def _onCreateNewTask(self, widget):
159
 
        self.__newTask()
160
 
 
161
 
    def __editTask(self, task=None):
162
 
        d = None
163
 
        try:
164
 
            d = TaskDialog(task)
165
 
            if d.run() != 2 or not d.isValid():
166
 
                return
167
 
            t = d.getTask()
168
 
            t.save()
169
 
        finally:
170
 
            if d: d.close()
171
 
        self._loadTasks()
172
 
 
173
 
    def __newTask(self):
174
 
        d = None
175
 
        try:
176
 
            d = TaskDialog()
177
 
            if d.run() == 1 or not d.isValid():
178
 
                return
179
 
            t = d.getTask()
180
 
            t.save()
181
 
        finally:
182
 
            if d: d.close()
183
 
        self._loadTasks()
184
 
 
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()
203
 
        return sw
204
 
 
205
 
    def _initTodoPopupMenu(self):
206
 
        uiManager = gtk.UIManager()
207
 
        menuItems = """<ui>
208
 
        <popup name = "Popup">
209
 
            <menuitem action = "Refresh" />
210
 
        </popup>
211
 
        </ui>
212
 
        """
213
 
        actions = (
214
 
            ("Refresh", gtk.STOCK_REFRESH, "Update Tasks", None, "Update Tasks", self.notifyUpdateProjectBrowser),
215
 
        )
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')
221
 
 
222
 
    def _onToggleTask(self, cell, path):
223
 
        if self._taskModel:
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)
229
 
            task.save()
230
 
            self._loadTasks()
231
 
 
232
 
    def _onTodoViewClick(self,  widget, event):
233
 
        if event.button != 3:
234
 
            return False
235
 
        self._todoPopupMenu.popup(None, None, None, event.button, event.time)
236
 
 
237
 
    def _onTaskViewClick(self, widget, event):
238
 
        if event.button != 3:
239
 
            return False
240
 
        self._taskPopupMenu.popup(None, None, None, event.button, event.time)
241
 
 
242
 
    def _onTodoRowActivated(self, treeview, path, column):
243
 
        try:
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)
251
 
        except TypeError:
252
 
            pass
253
 
 
254
 
    def _onTaskRowActivated(self, treeview, path, column):
255
 
        tId = self._taskView.getSelectedValue(COLUMN_TASK_ID)
256
 
        if tId:
257
 
            self.__editTask(Task.load(tId))
258
 
 
259
 
    def notifyEditorChanged(self, editor):
260
 
        if editor:
261
 
            self.notifyUpdateProjectBrowser()
262
 
 
263
 
    def notifyUpdateProjectBrowser(self, widget=None):
264
 
        if not self._pm.isProjectOpen():
265
 
            return
266
 
        t = threading.Thread(target=self._doUpdateProjectBrowser)
267
 
        t.start()
268
 
        
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()
274
 
        self._loadTasks()
275
 
        projectLocation = self._pm.projectLocation()
276
 
        try:
277
 
            os.path.walk(projectLocation, self._checkFile, None)
278
 
        except Exception, e:
279
 
            self._sc.notifyObservers("MessageEmited", level = 1,
280
 
                               message = e.__class__.__name__,
281
 
                               detail = str(e),
282
 
                               filename = None, 
283
 
                               lineNumber = None)
284
 
        finally:
285
 
            self._todoView.set_model(self._treestore)
286
 
            self._todoView.expand_all()
287
 
            gtk.gdk.threads_leave()
288
 
        
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)
294
 
                
295
 
    def _findTasksInFile(self, fileName):
296
 
        archive = open(fileName, 'r')
297
 
        index = 1
298
 
        for line in archive:
299
 
            lineUpper = line.upper()
300
 
            if not line.strip().startswith("#"):
301
 
                index += 1
302
 
                continue
303
 
            if "TODO:" in lineUpper:
304
 
                self._addTODONode(line, fileName, index)
305
 
            elif "FIXME:" in lineUpper:
306
 
                self._addFIXMENode(line, fileName, index)
307
 
            index += 1
308
 
 
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))
316
 
    
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))
324
 
 
325
 
    @classmethod
326
 
    def createNewInstance(cls, orientation):
327
 
        return TaskPanel(orientation)
328
 
 
329
 
    @classmethod
330
 
    def description(cls):
331
 
        return "Tasks"