~nmu-sscheel/gtg/rework-task-editor

« back to all changes in this revision

Viewing changes to GTG/gtk/editor/taskview.py

  • Committer: Bertrand Rousseau
  • Date: 2012-05-09 22:33:25 UTC
  • mfrom: (1178 trunk)
  • mto: This revision was merged to the branch mainline in revision 1179.
  • Revision ID: bertrand.rousseau@gmail.com-20120509223325-a53d8nwo0x9g93bc
Merge nimit branch and trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
import gobject
36
36
import pango
37
37
import os
 
38
from webbrowser import open as openurl
38
39
 
39
40
from GTG.gtk.editor import taskviewserial
40
 
from GTG.tools      import openurl
41
41
from GTG.tools      import urlregex
42
42
 
43
 
separators = [' ', ',', '\n', '\t', '!', '?', ';', '\0']
 
43
separators = [' ', ',', '\n', '\t', '!', '?', ';', '\0','(',')']
44
44
#those separators are only separators if followed by a space. Else, they
45
45
#are part of the word
46
46
specials_separators = ['.','/']
163
163
        #The signal emitted each time the buffer is modified
164
164
        #Putting it at the end to avoid doing it too much when starting
165
165
        self.modified_sigid = self.buff.connect("changed", self.modified)
166
 
        self.backspace_sigid = self.connect("backspace",self.backspace)
 
166
        self.backspace_sigid = self.connect("backspace", self.backspace)
167
167
        self.tobe_refreshed = False
168
168
        self.clipboard = clipboard
169
169
 
186
186
    #this is initially set at False and then to True once the editor window
187
187
    #is displayed.
188
188
    #this is used to avoid saving the task when the window is still not displayed
189
 
    def set_editable(self,boule):
 
189
    def set_editable(self, boule):
190
190
        self.editable = boule
 
191
 
191
192
    def get_editable(self):
192
193
        return self.editable
193
194
        
194
 
    #This function is called to refresh the editor 
 
195
    #This function is called to refresh the editor
195
196
    #Specially when we change the title
196
 
    def refresh(self,title) :
197
 
        if self.__refresh_cb :
 
197
    def refresh(self, title):
 
198
        if self.__refresh_cb:
198
199
            self.__refresh_cb(title)
199
200
 
200
 
    def refresh_callback(self,funct) :
 
201
    def refresh_callback(self, funct):
201
202
        self.__refresh_cb = funct
 
203
 
202
204
    #This callback is called to add a new tag
203
 
    def set_add_tag_callback(self,funct) :
 
205
    def set_add_tag_callback(self, funct):
204
206
        self.add_tag_callback = funct
205
207
        
206
208
    #This callback is called to add a new tag
207
 
    def set_remove_tag_callback(self,funct) :
 
209
    def set_remove_tag_callback(self, funct):
208
210
        self.remove_tag_callback = funct
209
211
        
210
212
    #This callback is called to have the list of tags of a task
211
 
    def set_get_tagslist_callback(self,funct) :
 
213
    def set_get_tagslist_callback(self, funct):
212
214
        self.get_tagslist = funct
213
215
        
214
216
    #This callback is called to create a new subtask
215
 
    def set_subtask_callback(self,funct) :
 
217
    def set_subtask_callback(self, funct):
216
218
        self.new_subtask_callback = funct
217
219
    
218
220
    #This callback is called to open another task
219
 
    def open_task_callback(self,funct) :
 
221
    def open_task_callback(self, funct):
220
222
        self.open_task = funct
221
223
        
222
224
    #This was historically a callback but it returns the title
223
 
    def get_subtasktitle(self,tid):
 
225
    def get_subtasktitle(self, tid):
224
226
        task = self.req.get_task(tid)
225
227
        if task:
226
228
            return task.get_title()
228
230
            return None
229
231
    
230
232
    #This callback is called to get the list of tid of subtasks
231
 
    def subtasks_callback(self,funct) :
 
233
    def subtasks_callback(self, funct):
232
234
        self.get_subtasks = funct
233
235
        
234
236
    #This callback is called to remove a subtask by its pid
235
 
    def removesubtask_callback(self,funct) :
 
237
    def removesubtask_callback(self, funct):
236
238
        self.remove_subtask = funct
237
239
        
238
 
    def save_task_callback(self,funct) :
 
240
    def save_task_callback(self, funct):
239
241
        self.save_task = funct
240
242
    
241
243
    #Buffer related functions
244
246
 #### The "Set text" group ########
245
247
    #This set the text of the buffer (and replace any existing one)
246
248
    #without deserializing (used for the title)
247
 
    def set_text(self,stri) :
 
249
    def set_text(self, stri):
248
250
        self.buff.set_text(stri)
249
251
    #This append text at the end of the buffer after deserializing it
250
252
    def insert(self, text, _iter=None):
254
256
        #disable the insert and modified signals
255
257
        reconnect_insert = False
256
258
        reconnect_modified = False
257
 
        if self.insert_sigid :
 
259
        if self.insert_sigid:
258
260
            self.buff.disconnect(self.insert_sigid)
259
261
            self.insert_sigid = False
260
262
            reconnect_insert = True
261
 
        if self.modified_sigid :
 
263
        if self.modified_sigid:
262
264
            self.buff.disconnect(self.modified_sigid)
263
265
            self.modified_sigid = False
264
266
            reconnect_modified = True
267
269
        self.buff.deserialize(self.buff, self.mime_type, _iter, text)
268
270
        
269
271
        #reconnect
270
 
        if reconnect_insert :
 
272
        if reconnect_insert:
271
273
            self.insert_sigid = self.buff.connect('insert-text', self._insert_at_cursor)
272
 
        if reconnect_modified :
273
 
            self.modified_sigid = self.buff.connect("changed" , self.modified)
 
274
        if reconnect_modified:
 
275
            self.modified_sigid = self.buff.connect("changed", self.modified)
274
276
            
275
277
    #This insert raw text without deserializing
276
 
    def insert_text(self,text, _iter=None) :
277
 
        if _iter is None :
 
278
    def insert_text(self, text, _iter=None):
 
279
        if _iter is None:
278
280
            _iter = self.buff.get_end_iter()
279
 
        self.buff.insert(_iter,text)
 
281
        self.buff.insert(_iter, text)
280
282
    
281
283
    #We cannot get an insert in the title
282
 
    def get_insert(self) :
 
284
    def get_insert(self):
283
285
        mark = self.buff.get_insert()
284
286
        itera = self.buff.get_iter_at_mark(mark)
285
 
        if itera.get_line() == 0 :
 
287
        if itera.get_line() == 0:
286
288
            itera.forward_line()
287
289
        return itera
288
290
    
289
 
    def insert_with_anchor(self, text, anchor=None, _iter=None,typ=None):
 
291
    def insert_with_anchor(self, text, anchor=None, _iter=None, typ=None):
290
292
        b = self.get_buffer()
291
293
        if _iter is None:
292
294
            _iter = b.get_end_iter()
293
295
        if anchor is None:
294
296
            anchor = text
295
 
        tag = self.create_anchor_tag(b,anchor,text,typ=typ)
 
297
        tag = self.create_anchor_tag(b, anchor, text, typ=typ)
296
298
        b.insert_with_tags(_iter, text, tag)
297
299
 
298
300
    def check_link(self, url):
307
309
            return True
308
310
 
309
311
 
310
 
    def create_anchor_tag(self,b,anchor,text=None,typ=None):
 
312
    def create_anchor_tag(self, b, anchor, text=None, typ=None):
311
313
        #We cannot have two tags with the same name
312
314
        #That's why the link tag has no name
313
315
        #but it has a "is_anchor" property
314
 
        if typ == "http" :
 
316
        if typ == "http":
315
317
            linktype = 'link'
316
318
        #By default, the type is a subtask
317
 
        else :
 
319
        else:
318
320
            task = self.req.get_task(anchor)
319
 
            if task and task.get_status() == "Active" :
 
321
            if task and task.get_status() == "Active":
320
322
                linktype = 'link'
321
 
            else :
 
323
            else:
322
324
                linktype = 'done'
323
325
 
324
326
        if linktype == 'link' and not self.check_link(anchor):
326
328
 
327
329
        tag = b.create_tag(None, **self.get_property(linktype)) #pylint: disable-msg=W0142
328
330
        tag.set_data('is_anchor', True)
329
 
        tag.set_data('link',anchor)
330
 
        if typ :
331
 
            tag.set_data('type',typ)
332
 
        tag.connect('event', self._tag_event, text, anchor,typ)
 
331
        tag.set_data('link', anchor)
 
332
        if typ:
 
333
            tag.set_data('type', typ)
 
334
        tag.connect('event', self._tag_event, text, anchor, typ)
333
335
        self.__tags.append(tag)
334
336
        return tag
335
337
        
336
338
    #Apply the tag tag to a set of TextMarks (not Iter)
337
 
    def apply_tag_tag(self,buff,tag,s,e) :
 
339
    def apply_tag_tag(self, buff, tag, s, e):
338
340
        ss = buff.get_iter_at_mark(s)
339
341
        ee = buff.get_iter_at_mark(e)
340
342
        #If the tag is already applied, we do nothing
341
343
        t_list = ss.get_tags()
342
344
        texttag = None
343
345
        already = False
344
 
        for t in t_list :
345
 
            if t.get_data('is_tag') and t.get_data('tagname') == tag :
 
346
        for t in t_list:
 
347
            if t.get_data('is_tag') and t.get_data('tagname') == tag:
346
348
                texttag = t
347
 
                if ss.begins_tag(t) and ee.ends_tag(t) :
 
349
                if ss.begins_tag(t) and ee.ends_tag(t):
348
350
                    already = True
349
 
        if not texttag :
 
351
        if not texttag:
350
352
            texttag = buff.create_tag(None,**self.get_property('tag'))#pylint: disable-msg=W0142
351
353
            texttag.set_data('is_tag', True)
352
 
            texttag.set_data('tagname',tag)
 
354
            texttag.set_data('tagname', tag)
353
355
            #This one is for marks
354
 
        if not already :
355
 
            self.__apply_tag_to_mark(s,e,tag=texttag)
 
356
        if not already:
 
357
            self.__apply_tag_to_mark(s, e, tag=texttag)
356
358
        
357
359
    #Apply the tag tag to a set of TextMarks (not Iter)
358
360
    #Also change the subtask title if needed
359
 
    def apply_subtask_tag(self,buff,subtask,s,e) :
 
361
    def apply_subtask_tag(self, buff, subtask, s, e):
360
362
        i_s = buff.get_iter_at_mark(s)
361
363
        i_e = buff.get_iter_at_mark(e)
362
 
        tex = buff.get_text(i_s,i_e)
 
364
        tex = buff.get_text(i_s, i_e)
363
365
        #we don't accept \n in a subtask title
364
 
        if "\n" in tex :
 
366
        if "\n" in tex:
365
367
            i_e = i_s.copy()
366
 
            while i_e.get_char() != "\n" :
 
368
            while i_e.get_char() != "\n":
367
369
                i_e.forward_char()
368
 
            buff.move_mark(e,i_e)
369
 
            tex = buff.get_text(i_s,i_e)
370
 
        if len(tex) > 0 :
 
370
            buff.move_mark(e, i_e)
 
371
            tex = buff.get_text(i_s, i_e)
 
372
        if len(tex) > 0:
371
373
            self.req.get_task(subtask).set_title(tex)
372
 
            texttag = self.create_anchor_tag(buff,subtask,text=tex,typ="subtask")
 
374
            texttag = self.create_anchor_tag(buff, subtask, text=tex, typ="subtask")
373
375
            texttag.set_data('is_subtask', True)
374
 
            texttag.set_data('child',subtask)
 
376
            texttag.set_data('child', subtask)
375
377
            #This one is for marks
376
 
            self.__apply_tag_to_mark(s,e,tag=texttag)
377
 
        else :
 
378
            self.__apply_tag_to_mark(s, e, tag=texttag)
 
379
        else:
378
380
            self.remove_subtask(subtask)
379
381
            buff.delete_mark(s)
380
382
            buff.delete_mark(e)
381
383
        
382
 
    def create_indent_tag(self,buff,level) :
 
384
    def create_indent_tag(self, buff, level):
383
385
        tag = buff.create_tag(None, **self.get_property('indent'))#pylint: disable-msg=W0142
384
 
        tag.set_data('is_indent',True)
385
 
        tag.set_data('indent_level',level)
 
386
        tag.set_data('is_indent', True)
 
387
        tag.set_data('indent_level', level)
386
388
        return tag
387
389
    
388
390
    #Insert a list of subtasks at the end of the buffer
389
 
    def insert_subtasks(self,st_list) :
390
 
        for tid in st_list :
 
391
    def insert_subtasks(self, st_list):
 
392
        for tid in st_list:
391
393
            line_nbr = self.buff.get_end_iter().get_line()
392
394
            #Warning, we have to take the next line !
393
 
            self.write_subtask(self.buff,line_nbr+1,tid)
 
395
            self.write_subtask(self.buff, line_nbr+1, tid)
394
396
            
395
397
    #Insert a list of tag in the first line of the buffer
396
 
    def insert_tags(self,tag_list) :
 
398
    def insert_tags(self, tag_list):
397
399
        #First, we don't insert tags that are already present
398
 
        for t in self.get_tagslist() :
399
 
            if t in tag_list :
 
400
        for t in self.get_tagslist():
 
401
            if t in tag_list:
400
402
                tag_list.remove(t)
401
 
        if len(tag_list) > 0 :
 
403
        if len(tag_list) > 0:
402
404
            #We insert them just after the title
403
405
            #We use the current first line if it begins with a tag
404
406
            firstline = self.buff.get_iter_at_line(1)
405
407
            newline = True
406
 
            for tt in firstline.get_tags() :
407
 
                if tt.get_data('is_tag') :
 
408
            for tt in firstline.get_tags():
 
409
                if tt.get_data('is_tag'):
408
410
                    newline = False
409
411
                    firstline.forward_to_line_end()
410
412
                    #Now we should check if the current char is a separator or not
411
413
                    #Currently, we insert a space
412
 
                    self.insert_text(" ",firstline)
 
414
                    self.insert_text(" ", firstline)
413
415
            #Now we check if this newline is empty (it contains only " " and ",")
414
 
    #        if newline :
 
416
    #        if newline:
415
417
    #            endline = firstline.copy()
416
 
    #            if not endline.ends_line() :
 
418
    #            if not endline.ends_line():
417
419
    #                endline.forward_to_line_end()
418
 
    #            text = self.buff.get_text(firstline,endline)
419
 
    #            if not text.strip(", ") :
 
420
    #            text = self.buff.get_text(firstline, endline)
 
421
    #            if not text.strip(", "):
420
422
    #                newline = False
421
423
    #                firstline.forward_to_line_end()
422
424
            #Now we can process
423
 
            if newline :
 
425
            if newline:
424
426
                firstline = self.buff.get_iter_at_line(0)
425
427
                firstline.forward_to_line_end()
426
 
                self.insert_text("\n",firstline)
 
428
                self.insert_text("\n", firstline)
427
429
                firstline = self.buff.get_iter_at_line(1)
428
 
            line_mark = self.buff.create_mark("firstline",firstline,False)
429
 
            #self.tv.insert_at_mark(buf,line_mark,"\n")
 
430
            line_mark = self.buff.create_mark("firstline", firstline, False)
 
431
            #self.tv.insert_at_mark(buf, line_mark,"\n")
430
432
            ntags = len(tag_list)
431
 
            for t in tag_list :
 
433
            for t in tag_list:
432
434
                ntags = ntags - 1
433
 
                self.insert_at_mark(self.buff,line_mark,t)
 
435
                self.insert_at_mark(self.buff, line_mark, t)
434
436
                if ntags != 0:
435
 
                    self.insert_at_mark(self.buff,line_mark,",")
 
437
                    self.insert_at_mark(self.buff, line_mark,",")
436
438
            self.buff.delete_mark(line_mark)
437
439
            self.modified(full=True)
438
440
        
446
448
        self.insert_at_mark(self.buff, line_mark, tag)
447
449
        
448
450
    #this function select and highligth the title (first line)
449
 
    def select_title(self) :
 
451
    def select_title(self):
450
452
        start = self.buff.get_start_iter()
451
453
        stop = start.copy()
452
454
        stop.forward_to_line_end()
455
457
 ##### The "Get text" group #########
456
458
    #Get the complete serialized text
457
459
    #But without the title
458
 
    def get_text(self) :
 
460
    def get_text(self):
459
461
        #we get the text
460
462
        start = self.buff.get_start_iter()
461
463
        start.forward_to_line_end()
462
464
        conti = True
463
 
        while conti and not start.ends_tag(self.table.lookup("title")) :
 
465
        while conti and not start.ends_tag(self.table.lookup("title")):
464
466
            conti = start.forward_line()
465
 
            if conti :
 
467
            if conti:
466
468
                conti = start.forward_to_line_end()
467
469
        #we go to the next line, just after the title
468
470
        start.forward_line()
471
473
        
472
474
        return texte
473
475
    #Get the title of the task (aka the first line of the buffer)
474
 
    def get_title(self) :
 
476
    def get_title(self):
475
477
        start = self.buff.get_start_iter()
476
478
        end = self.buff.get_start_iter()
477
479
        end.forward_to_line_end()
478
480
        #The boolean stays True as long as we are in the buffer
479
481
        conti = True
480
 
        while conti and not end.ends_tag(self.table.lookup("title")) :
 
482
        while conti and not end.ends_tag(self.table.lookup("title")):
481
483
            conti = end.forward_line()
482
 
            if conti :
 
484
            if conti:
483
485
                conti = end.forward_to_line_end()
484
486
        #We don't want to deserialize the title
485
487
        #Let's get the pure text directly
486
 
        title = unicode(self.buff.get_text(start,end))
 
488
        title = unicode(self.buff.get_text(start, end))
487
489
        #Let's strip blank lines
488
490
        stripped = title.strip(' \n\t')
489
491
        return stripped
491
493
### PRIVATE FUNCTIONS ##########################################################
492
494
 
493
495
        
494
 
    #This function is called so frequently that we should optimize it more.    
495
 
    def modified(self,buff=None,full=False,refresheditor=True):
 
496
    #This function is called so frequently that we should optimize it more.
 
497
    def modified(self, buff=None, full=False, refresheditor=True):
496
498
        """Called when the buffer has been modified.
497
499
 
498
500
        It reflects the changes by:
501
503
          2. Changing the name of the window if title change
502
504
        """
503
505
        tags_before = self.get_tagslist()
504
 
        if not buff: 
505
 
            buff = self.buff   
 
506
        if not buff:
 
507
            buff = self.buff
506
508
        cursor_mark = buff.get_insert()
507
509
        cursor_iter = buff.get_iter_at_mark(cursor_mark)
508
510
        table = buff.get_tag_table()
509
511
        #This should be called only if we are on the title line
510
512
        #As an optimisation
511
513
        #But we should still get the title_end iter
512
 
        if full or self.is_at_title(buff,cursor_iter) :
 
514
        if full or self.is_at_title(buff, cursor_iter):
513
515
            #The apply title is very expensive because
514
516
            #It involves refreshing the whole task tree
515
 
            title_end = self._apply_title(buff,refresheditor)
 
517
            title_end = self._apply_title(buff, refresheditor)
516
518
 
517
 
        if full :
 
519
        if full:
518
520
            local_start = title_end.copy()
519
521
            local_end = buff.get_end_iter()
520
 
        else :
 
522
        else:
521
523
            #We analyse only the current line
522
524
            local_start = cursor_iter.copy()
523
525
            local_start.set_line(local_start.get_line())
528
530
        #The following 3 lines are a quick ugly fix for bug #359469
529
531
#        temp = buff.get_iter_at_line(1)
530
532
#        temp.backward_char()
531
 
#        self._detect_tag(buff,temp,buff.get_end_iter())
 
533
#        self._detect_tag(buff, temp, buff.get_end_iter())
532
534
        #This should be the good line
533
 
        self._detect_tag(buff,local_start,local_end)
534
 
        self._detect_url(buff,local_start,local_end)
 
535
        self._detect_tag(buff, local_start, local_end)
 
536
        self._detect_url(buff, local_start, local_end)
535
537
        
536
538
        #subt_list = self.get_subtasks()
537
539
        #First, we remove the olds tags
538
540
        tag_list = []
539
 
        def subfunc(texttag,data=None): #pylint: disable-msg=W0613
 
541
        def subfunc(texttag, data=None): #pylint: disable-msg=W0613
540
542
            if texttag.get_data('is_subtask'):
541
543
                tag_list.append(texttag)
542
544
        table.foreach(subfunc)
543
 
        start,end = buff.get_bounds()
544
 
        for t in tag_list :
545
 
            buff.remove_tag(t,start,end)
 
545
        start, end = buff.get_bounds()
 
546
        for t in tag_list:
 
547
            buff.remove_tag(t, start, end)
546
548
            table.remove(t)
547
549
        
548
550
        #We apply the hyperlink tag to subtask
549
 
        for s in self.get_subtasks() :
 
551
        for s in self.get_subtasks():
550
552
            start_mark = buff.get_mark(s)
551
 
            # "applying %s to %s - %s"%(s,start_mark,end_mark)
552
 
            if start_mark :
 
553
            # "applying %s to %s - %s"%(s, start_mark, end_mark)
 
554
            if start_mark:
553
555
                #In fact, the subtask mark always go to the end of line.
554
556
                start_i = buff.get_iter_at_mark(start_mark)
555
557
                if self._get_indent_level(start_i) > 0:
556
558
                    start_i.forward_to_line_end()
557
 
                    end_mark = buff.create_mark("/%s"%s,start_i,False)
558
 
                    self.apply_subtask_tag(buff,s,start_mark,end_mark)
 
559
                    end_mark = buff.create_mark("/%s"%s, start_i, False)
 
560
                    self.apply_subtask_tag(buff, s, start_mark, end_mark)
559
561
                else:
560
562
                    self.remove_subtask(s)
561
563
    
562
564
                
563
565
        #Now we apply the tag tag to the marks
564
 
        for t in self.get_tagslist() :
 
566
        for t in self.get_tagslist():
565
567
            start_mark = buff.get_mark(t)
566
568
            end_mark = buff.get_mark("/%s"%t)
567
 
            # "applying %s to %s - %s"%(t,start_mark,end_mark)
568
 
            if start_mark and end_mark :
569
 
                self.apply_tag_tag(buff,t,start_mark,end_mark)
 
569
            # "applying %s to %s - %s"%(t, start_mark, end_mark)
 
570
            if start_mark and end_mark:
 
571
                self.apply_tag_tag(buff, t, start_mark, end_mark)
570
572
        
571
573
        #Ok, we took care of the modification
572
574
        self.buff.set_modified(False)
573
575
        #Else we save the task anyway (but without refreshing all)
574
 
        if self.save_task :
 
576
        if self.save_task:
575
577
            self.save_task()
576
578
            
577
579
    #Detect URL in the tasks
578
580
    #It's ugly...
579
 
    def _detect_url(self,buff,start,end) :
 
581
    def _detect_url(self, buff, start, end):
580
582
        #subt_list = self.get_subtasks()
581
583
        #First, we remove the olds tags
582
584
        tag_list = []
583
585
        table = buff.get_tag_table()
584
 
        def subfunc(texttag,data=None):
 
586
        def subfunc(texttag, data=None):
585
587
            if texttag.get_data('is_anchor'):
586
588
                tag_list.append(texttag)
587
589
        table.foreach(subfunc)
588
 
        for t in tag_list :
589
 
            buff.remove_tag(t,start,end)
 
590
        for t in tag_list:
 
591
            buff.remove_tag(t, start, end)
590
592
        #Now we add the tag URL
591
593
        it = start.copy()
592
594
        prev = start.copy()
593
 
        while (it.get_offset() < end.get_offset()) and (it.get_char() != '\0') :
 
595
        while (it.get_offset() < end.get_offset()) and (it.get_char() != '\0'):
594
596
            it.forward_word_end()
595
597
            prev = it.copy()
596
598
            prev.backward_word_start()
597
 
            text = buff.get_text(prev,it)
 
599
            text = buff.get_text(prev, it)
598
600
            
599
601
            if text in ["http", "https", "www", "file"]:
600
602
                isurl = buff.get_text(prev, buff.get_end_iter())
601
603
                m = urlregex.match(isurl)
602
604
                if m is not None:
603
 
                    url = isurl[:m.end()] 
 
605
                    url = isurl[:m.end()]
604
606
                    # For short URL we must add http:// prefix
605
607
                    if text == "www":
606
608
                        url = "http://" + url
607
609
                    texttag = self.create_anchor_tag(buff, url, text=None, typ="http")
608
610
                    it = prev.copy()
609
611
                    it.forward_chars(m.end())
610
 
                    buff.apply_tag(texttag, prev , it)
 
612
                    buff.apply_tag(texttag, prev, it)
611
613
 
612
 
            elif text in ["bug", "lp", "bgo", "fdo", "bko"] :
613
 
                if it.get_char() == " " :
614
 
                    it.forward_char()
615
 
                if it.get_char() == "#" :
616
 
                    it.forward_char()
617
 
                    while it.get_char().isdigit() and (it.get_char() != '\0') :
 
614
            elif text in ["bug", "lp", "bgo", "fdo", "bko"]:
 
615
                if it.get_char() == " ":
 
616
                    it.forward_char()
 
617
                if it.get_char() == "#":
 
618
                    it.forward_char()
 
619
                    while it.get_char().isdigit() and (it.get_char() != '\0'):
618
620
                        it.forward_char()
619
 
                    url = buff.get_text(prev,it)
 
621
                    url = buff.get_text(prev, it)
620
622
                    nbr = url.split("#")[1]
621
623
                    topoint = None
622
 
                    if url.startswith("bug #") or url.startswith("lp #") :
 
624
                    if url.startswith("bug #") or url.startswith("lp #"):
623
625
                        topoint = "https://launchpad.net/bugs/%s" %nbr
624
 
                    elif url.startswith("bgo #") :
 
626
                    elif url.startswith("bgo #"):
625
627
                        topoint = "http://bugzilla.gnome.org/show_bug.cgi?id=%s" %nbr
626
 
                    elif url.startswith("bko #") :
 
628
                    elif url.startswith("bko #"):
627
629
                        topoint = "https://bugs.kde.org/show_bug.cgi?id=%s" %nbr
628
 
                    elif url.startswith("fdo #") :
 
630
                    elif url.startswith("fdo #"):
629
631
                        topoint = "http://bugs.freedesktop.org/show_bug.cgi?id=%s" %nbr
630
 
                    if topoint :
 
632
                    if topoint:
631
633
                        texttag = self.create_anchor_tag(buff,\
632
 
                                                topoint,text=None,typ="http")
633
 
                        buff.apply_tag(texttag, prev , it)
 
634
                                                topoint, text=None, typ="http")
 
635
                        buff.apply_tag(texttag, prev, it)
634
636
        
635
637
 
636
638
    #Detect tags in buff in the region between start iter and end iter
637
 
    def _detect_tag(self,buff,start,end) :
 
639
    def _detect_tag(self, buff, start, end):
638
640
        # Removing already existing tag in the current selection
639
641
        # out of the tag table
640
642
        it = start.copy()
644
646
        #We must be strictly < than the end_offset. If not, we might
645
647
        #find the beginning of a tag on the nextline
646
648
        while (it.get_offset() < end.get_offset()) and (it.get_char() != '\0'):
647
 
            if it.begins_tag() :
 
649
            if it.begins_tag():
648
650
                tags = it.get_toggled_tags(True)
649
 
                for ta in tags :
 
651
                for ta in tags:
650
652
                    #removing deleted tags
651
 
                    if ta.get_data('is_tag') :
 
653
                    if ta.get_data('is_tag'):
652
654
                        tagname = ta.get_data('tagname')
653
655
                        old_tags.append(tagname)
654
 
                        buff.remove_tag(ta,start,end)
 
656
                        buff.remove_tag(ta, start, end)
655
657
                        table.remove(ta)
656
658
                        #Removing the marks if they exist
657
659
                        mark1 = buff.get_mark(tagname)
658
 
                        if mark1 :
 
660
                        if mark1:
659
661
                            offset1 = buff.get_iter_at_mark(mark1).get_offset()
660
 
                            if start.get_offset() <= offset1 <= end.get_offset() :
 
662
                            if start.get_offset() <= offset1 <= end.get_offset():
661
663
                                buff.delete_mark_by_name(tagname)
662
664
                        mark2 = buff.get_mark("/%s"%tagname)
663
 
                        if mark2 :
 
665
                        if mark2:
664
666
                            offset2 = buff.get_iter_at_mark(mark2).get_offset()
665
667
                            if start.get_offset() <= offset2 <= end.get_offset():
666
668
                                buff.delete_mark_by_name("/%s"%tagname)
680
682
        while char_end.compare(end) <= 0:
681
683
            do_word_check = False
682
684
            my_char       = buff.get_text(char_start, char_end)
683
 
            if my_char not in separators :
 
685
            if my_char not in separators:
684
686
                last_char = my_char
685
687
                word_end = char_end.copy()
686
688
                #If a special case is at the end of the document
705
707
                    #and it shouldn't start with @@ (bug 531553)
706
708
                    if len(my_word) > 1 and my_word[0] == '@' \
707
709
                       and not my_word[1] == '@':
708
 
                        #self.apply_tag_tag(buff,my_word,word_start,word_end)
 
710
                        #self.apply_tag_tag(buff, my_word, word_start, word_end)
709
711
                        #We will add mark where tag should be applied
710
 
                        buff.create_mark(my_word,word_start,True)
711
 
                        buff.create_mark("/%s"%my_word,word_end,False)
 
712
                        buff.create_mark(my_word, word_start, True)
 
713
                        buff.create_mark("/%s"%my_word, word_end, False)
712
714
                        #adding tag to a local list
713
715
                        new_tags.append(my_word)
714
716
                        #adding tag to the model
719
721
                word_end   = char_end.copy()
720
722
 
721
723
            # Stop loop if we are at the end
722
 
            if char_end.compare(end) == 0: 
 
724
            if char_end.compare(end) == 0:
723
725
                break
724
726
            
725
727
            # We search the next word
726
728
            char_start = char_end.copy()
727
729
            char_end.forward_char()
728
730
            
729
 
        # Update tags in model : 
 
731
        # Update tags in model:
730
732
        # we remove tags that are not in the description anymore
731
 
        for t in old_tags :
732
 
            if not t in new_tags :
 
733
        for t in old_tags:
 
734
            if not t in new_tags:
733
735
                self.remove_tag_callback(t)
734
736
                
735
 
    def is_at_title(self,buff,itera) :
 
737
    def is_at_title(self, buff, itera):
736
738
        to_return = False
737
 
        if itera.get_line() == 0 :
 
739
        if itera.get_line() == 0:
738
740
            to_return = True
739
741
        #We are at a line with the title tag applied
740
 
        elif self.title_tag in itera.get_tags() :
 
742
        elif self.title_tag in itera.get_tags():
741
743
            to_return = True
742
744
        #else, we look if there's something between us and buffer start
743
 
        elif not buff.get_text(buff.get_start_iter(),itera).strip('\n\t ') :
 
745
        elif not buff.get_text(buff.get_start_iter(), itera).strip('\n\t '):
744
746
            to_return = True
745
747
        return to_return
746
748
        
747
749
    #When the user removes a selection, we remove subtasks and @tags
748
750
    #from this selection
749
 
    def _delete_range(self,buff,start,end) :
 
751
    def _delete_range(self, buff, start, end):
750
752
#        #If we are at the beginning of a mark, put this mark at the end
751
753
#        marks = start.get_marks()
752
 
#        for m in marks :
 
754
#        for m in marks:
753
755
#            print m.get_name()
754
 
#            buff.move_mark(m,end)
 
756
#            buff.move_mark(m, end)
755
757
        #If the begining of the selection is in the middle of an indent
756
758
        #We want to start at the begining
757
759
        tags = start.get_tags()+start.get_toggled_tags(False)
758
 
        for ta in tags :
 
760
        for ta in tags:
759
761
            if (ta.get_data('is_indent')):
760
762
                line = start.get_line()
761
763
                start = self.buff.get_iter_at_line(line)
763
765
#                #start.backward_to_tag_toggle(ta)
764
766
#                endindent = start.copy()
765
767
#                endindent.forward_to_tag_toggle(ta)
766
 
#                buff.remove_tag(ta,start,endindent)
 
768
#                buff.remove_tag(ta, start, endindent)
767
769
        #Now we delete all, char after char
768
770
        it = start.copy()
769
771
        while (it.get_offset() <= end.get_offset()) and (it.get_char() != '\0'):
770
 
            if it.begins_tag() :
 
772
            if it.begins_tag():
771
773
                tags = it.get_tags()
772
 
                for ta in tags :
 
774
                for ta in tags:
773
775
                    #removing deleted subtasks
774
 
                    if ta.get_data('is_subtask') and it.begins_tag(ta) :
 
776
                    if ta.get_data('is_subtask') and it.begins_tag(ta):
775
777
                        target = ta.get_data('child')
776
778
                        #print "removing task %s" %target
777
779
                        self.remove_subtask(target)
779
781
                    if ta.get_data('is_tag') and it.begins_tag(ta):
780
782
                        tagname = ta.get_data('tagname')
781
783
                        self.remove_tag_callback(tagname)
782
 
                        if buff.get_mark(tagname) :
 
784
                        if buff.get_mark(tagname):
783
785
                            buff.delete_mark_by_name(tagname)
784
 
                        if buff.get_mark("/%s"%tagname) :
 
786
                        if buff.get_mark("/%s"%tagname):
785
787
                            buff.delete_mark_by_name("/%s"%tagname)
786
 
                    if ta.get_data('is_indent') :
 
788
                    if ta.get_data('is_indent'):
787
789
                        #Because the indent tag is read only
788
790
                        #we will remove it
789
791
                        endtag = it.copy()
790
792
                        endtag.forward_to_tag_toggle(ta)
791
 
                        buff.remove_tag(ta,it,endtag)
 
793
                        buff.remove_tag(ta, it, endtag)
792
794
                        #Also, we want to delete the indent completely,
793
795
                        #Even if the selection was in the middle of an indent
794
796
                        
796
798
        #now we really delete the selected stuffs
797
799
        selec = self.buff.get_selection_bounds()
798
800
#        if selec:
799
 
#            print "deleted text is ##%s##" %self.buff.get_text(selec[0],selec[1])#(start,end)
 
801
#            print "deleted text is ##%s##" %self.buff.get_text(selec[0], selec[1])#(start, end)
800
802
#        self.buff.disconnect(self.delete_sigid)
801
803
#        self.disconnect(self.backspace_sigid)
802
804
#        self.buff.stop_emission("delete-range")
803
 
#        if self.buff.get_has_selection() :
804
 
#            self.buff.delete_selection(False,True)
805
 
#        else :
 
805
#        if self.buff.get_has_selection():
 
806
#            self.buff.delete_selection(False, True)
 
807
#        else:
806
808
#            end.forward_char()
807
 
#            self.buff.backspace(end,False,True)
808
 
#        self.delete_sigid = self.buff.connect("delete-range",self._delete_range)
809
 
#        self.backspace_sigid = self.connect("backspace",self.backspace)
 
809
#            self.buff.backspace(end, False, True)
 
810
#        self.delete_sigid = self.buff.connect("delete-range", self._delete_range)
 
811
#        self.backspace_sigid = self.connect("backspace", self.backspace)
810
812
        #We return false so the parent still get the signal
811
813
        return False
812
814
        
813
815
    #Apply the title and return an iterator after that title.buff.get_iter_at_mar
814
 
    def _apply_title(self,buff,refresheditor=True) :
 
816
    def _apply_title(self, buff, refresheditor=True):
815
817
        start     = buff.get_start_iter()
816
818
        end       = buff.get_end_iter()
817
819
        line_nbr  = 1
818
820
        linecount = buff.get_line_count()
819
821
    
820
 
        # Apply the title tag on the first line 
 
822
        # Apply the title tag on the first line
821
823
        #---------------------------------------
822
824
        
823
825
        # Determine the iterators for title
824
 
        title_start = start.copy() 
825
 
        if linecount > line_nbr :
 
826
        title_start = start.copy()
 
827
        if linecount > line_nbr:
826
828
            # Applying title on the first line
827
829
            title_end = buff.get_iter_at_line(line_nbr-1)
828
830
            title_end.forward_to_line_end()
829
 
            stripped  = buff.get_text(title_start,title_end).strip('\n\t ')
 
831
            stripped  = buff.get_text(title_start, title_end).strip('\n\t ')
830
832
            # Here we ignore lines that are blank
831
833
            # Title is the first written line
832
 
            while line_nbr <= linecount and not stripped :
 
834
            while line_nbr <= linecount and not stripped:
833
835
                line_nbr  += 1
834
836
                title_end  = buff.get_iter_at_line(line_nbr-1)
835
837
                title_end.forward_to_line_end()
836
838
                stripped   = buff.get_text(title_start, title_end).strip('\n\t ')
837
839
        # Or to all the buffer if there is only one line
838
 
        else :
 
840
        else:
839
841
            title_end = end.copy()
840
 
        buff.apply_tag_by_name('title', title_start , title_end)
841
 
        buff.remove_tag_by_name('title', title_end   , end)
 
842
        buff.apply_tag_by_name('title', title_start, title_end)
 
843
        buff.remove_tag_by_name('title', title_end, end)
842
844
        # Refresh title of the window
843
845
        if refresheditor:
844
 
            self.refresh(buff.get_text(title_start,title_end).strip('\n\t'))
 
846
            self.refresh(buff.get_text(title_start, title_end).strip('\n\t'))
845
847
        return title_end
846
848
    
847
849
            
848
850
        
849
 
    def __newsubtask(self,buff,title,line_nbr, level=1) :
 
851
    def __newsubtask(self, buff, title, line_nbr, level=1):
850
852
        anchor = self.new_subtask_callback(title)
851
 
        end_i = self.write_subtask(buff,line_nbr,anchor,level=level)
 
853
        end_i = self.write_subtask(buff, line_nbr, anchor, level=level)
852
854
        return end_i
853
855
    
854
856
    #Write the subtask then return the iterator at the end of the line
855
 
    def write_subtask(self,buff,line_nbr,anchor,level=1) :
 
857
    def write_subtask(self, buff, line_nbr, anchor, level=1):
856
858
        #disable the insert signal to avoid recursion
857
859
        #firstly, we check that the subtask exists !
858
860
        if not self.req.has_task(anchor):
859
861
            return False
860
862
        reconnect_insert = False
861
863
        reconnect_modified = False
862
 
        if self.insert_sigid :
 
864
        if self.insert_sigid:
863
865
            self.buff.disconnect(self.insert_sigid)
864
866
            self.insert_sigid = False
865
867
            reconnect_insert = True
866
 
        if self.modified_sigid :
 
868
        if self.modified_sigid:
867
869
            self.buff.disconnect(self.modified_sigid)
868
870
            self.modified_sigid = False
869
871
            reconnect_modified = True
881
883
#        start_i.backward_char()
882
884
#        #But only if this is not the title.
883
885
        insert_enter = False
884
 
#        if start_i.has_tag(self.title_tag) :
 
886
#        if start_i.has_tag(self.title_tag):
885
887
#            start_i.forward_char()
886
888
#            insert_enter = False
887
 
        start   = buff.create_mark("start",start_i,True)
 
889
        start   = buff.create_mark("start", start_i, True)
888
890
        end_i.forward_line()
889
 
        end     = buff.create_mark("end",end_i,False)
890
 
        buff.delete(start_i,end_i)
 
891
        end     = buff.create_mark("end", end_i, False)
 
892
        buff.delete(start_i, end_i)
891
893
        start_i = buff.get_iter_at_mark(start)
892
 
        self.insert_indent(buff,start_i,level, enter=insert_enter)
 
894
        self.insert_indent(buff, start_i, level, enter=insert_enter)
893
895
        newline = self.get_subtasktitle(anchor)
894
896
        end_i = buff.get_iter_at_mark(end)
895
 
        startm = buff.create_mark(anchor,end_i,True)
 
897
        startm = buff.create_mark(anchor, end_i, True)
896
898
        #Putting the subtask marks around the title
897
 
        self.insert_at_mark(buff,end,newline)
 
899
        self.insert_at_mark(buff, end, newline)
898
900
        end_i = buff.get_iter_at_mark(end)
899
 
        endm = buff.create_mark("/%s"%anchor,end_i,False)
 
901
        endm = buff.create_mark("/%s"%anchor, end_i, False)
900
902
        #put the tag on the marks
901
 
        self.apply_subtask_tag(buff,anchor,startm,endm)
 
903
        self.apply_subtask_tag(buff, anchor, startm, endm)
902
904
        #buff.delete_mark(start)
903
905
        #buff.delete_mark(end)
904
906
        
905
 
        if reconnect_insert :
 
907
        if reconnect_insert:
906
908
            self.insert_sigid = self.buff.connect('insert-text', self._insert_at_cursor)
907
 
        if reconnect_modified :
908
 
            self.modified_sigid = self.buff.connect("changed" , self.modified)
 
909
        if reconnect_modified:
 
910
            self.modified_sigid = self.buff.connect("changed", self.modified)
909
911
        return end_i
910
912
        
911
 
    def insert_newtask(self,fitera=None) :
912
 
        if not fitera :
 
913
    def insert_newtask(self, fitera=None):
 
914
        if not fitera:
913
915
            fitera = self.get_insert()
914
916
        #First, find a line without subtask
915
917
        line = fitera.get_line()
916
918
        #Avoid the title at all cost
917
 
        if line <= 0 :
 
919
        if line <= 0:
918
920
            line = 1
919
921
        startl = self.buff.get_iter_at_line(line)
920
922
        itera = None
921
 
        while not itera :
 
923
        while not itera:
922
924
            found = True
923
 
            for t in startl.get_tags() :
924
 
                if t.get_data('is_indent') :
 
925
            for t in startl.get_tags():
 
926
                if t.get_data('is_indent'):
925
927
                    line += 1
926
928
                    startl = self.buff.get_iter_at_line(line)
927
 
                    if line < self.buff.get_line_count() :
 
929
                    if line < self.buff.get_line_count():
928
930
                        found = False
929
 
            if found :
930
 
                itera = startl 
 
931
            if found:
 
932
                itera = startl
931
933
                
932
934
        #if the last line is indented, then insert a new line
933
935
        #at the end
934
 
        if line == self.buff.get_line_count() :
 
936
        if line == self.buff.get_line_count():
935
937
            itera.forward_to_line_end()
936
 
            mark = self.buff.create_mark(None,itera,True)
 
938
            mark = self.buff.create_mark(None, itera, True)
937
939
            self.buff.insert(itera,"\n")
938
940
            itera = self.buff.get_iter_at_mark(mark)
939
941
            self.buff.delete_mark(mark)
942
944
        #but if we are at the start of line, then create the subtask
943
945
        #before the current line
944
946
        enter = True
945
 
        if itera.starts_line() :
946
 
            mark = self.buff.create_mark(None,itera,True)
 
947
        if itera.starts_line():
 
948
            mark = self.buff.create_mark(None, itera, True)
947
949
            self.buff.insert(itera,"\n")
948
950
            itera = self.buff.get_iter_at_mark(mark)
949
951
            self.buff.delete_mark(mark)
950
952
            enter = False
951
 
        elif not itera.ends_line() :
 
953
        elif not itera.ends_line():
952
954
            itera.forward_to_line_end()
953
 
        endm = self.insert_indent(self.buff,itera,1,enter=enter)
 
955
        endm = self.insert_indent(self.buff, itera, 1, enter=enter)
954
956
        end = self.buff.get_iter_at_mark(endm)
955
957
        self.buff.place_cursor(end)
956
958
        
957
 
    def insert_indent(self,buff,start_i,level,enter=True) :
 
959
    def insert_indent(self, buff, start_i, level, enter=True):
958
960
        #We will close the current subtask tag
959
961
        list_stag = start_i.get_toggled_tags(False)
960
962
        stag = None
961
 
        for t in list_stag :
962
 
            if t.get_data('is_subtask') :
 
963
        for t in list_stag:
 
964
            if t.get_data('is_subtask'):
963
965
                stag = t
964
966
        #maybe the tag was not toggled off here but we were in the middle
965
 
        if not stag :
 
967
        if not stag:
966
968
            list_stag = start_i.get_tags()
967
 
            for t in list_stag :
968
 
                if t.get_data('is_subtask') :
 
969
            for t in list_stag:
 
970
                if t.get_data('is_subtask'):
969
971
                    stag = t
970
 
        if stag :
 
972
        if stag:
971
973
            #We will remove the tag from the whole text
972
974
            subtid = stag.get_data('child')
973
975
        #We move the end_subtask mark to here
974
976
        #We have to create a temporary mark with left gravity
975
977
        #It will be later replaced by the good one with right gravity
976
 
        temp_mark = self.buff.create_mark("temp",start_i,True)
 
978
        temp_mark = self.buff.create_mark("temp", start_i, True)
977
979
        
978
 
        end     = buff.create_mark("end",start_i,False)
979
 
        if enter : 
 
980
        end     = buff.create_mark("end", start_i, False)
 
981
        if enter:
980
982
            buff.insert(start_i,"\n")
981
983
        
982
984
        #Moving the end of subtask mark to the position of the temp mark
983
 
        if stag :
 
985
        if stag:
984
986
            itera = buff.get_iter_at_mark(temp_mark)
985
 
            buff.move_mark_by_name("/%s"%subtid,itera)
 
987
            buff.move_mark_by_name("/%s"%subtid, itera)
986
988
        buff.delete_mark(temp_mark)
987
989
        #The mark has right gravity but because we put it on the left
988
990
        #of the newly inserted \n, it will not move anymore.
990
992
        itera = buff.get_iter_at_mark(end)
991
993
        #We should never have an indentation at 0.
992
994
        #This is normally not needed and purely defensive
993
 
        if itera.get_line() <= 0 :
 
995
        if itera.get_line() <= 0:
994
996
            itera = buff.get_iter_at_line(1)
995
 
        start   = buff.create_mark("start",itera,True)
 
997
        start   = buff.create_mark("start", itera, True)
996
998
        indentation = ""
997
999
        #adding two spaces by level
998
1000
        spaces = "  "
999
1001
        indentation = indentation + (level-1)*spaces
1000
 
        #adding the symbol 
1001
 
        if level == 1 :
1002
 
            indentation = "%s%s "%(indentation,self.bullet1)
1003
 
        buff.insert(itera,indentation)
1004
 
        indenttag = self.create_indent_tag(buff,level)
1005
 
        self.__apply_tag_to_mark(start,end,tag=indenttag)
 
1002
        #adding the symbol
 
1003
        if level == 1:
 
1004
            indentation = "%s%s "%(indentation, self.bullet1)
 
1005
        buff.insert(itera, indentation)
 
1006
        indenttag = self.create_indent_tag(buff, level)
 
1007
        self.__apply_tag_to_mark(start, end, tag=indenttag)
1006
1008
        return end
1007
1009
 
1008
1010
        
1009
 
    def __apply_tag_to_mark(self,start,end,tag=None,name=None) :
 
1011
    def __apply_tag_to_mark(self, start, end, tag=None, name=None):
1010
1012
        start_i = self.buff.get_iter_at_mark(start)
1011
1013
        end_i = self.buff.get_iter_at_mark(end)
1012
1014
        #we should apply the tag only if the mark are separated
1013
 
        if end_i.get_offset() - start_i.get_offset() > 0 :
1014
 
            if tag :
1015
 
                self.buff.apply_tag(tag,start_i,end_i)
1016
 
            elif name :
1017
 
                self.buff.apply_tag_by_name(name,start_i,end_i)
1018
 
        elif tag :
1019
 
            self.buff.remove_tag(tag,start_i,end_i)
 
1015
        if end_i.get_offset() - start_i.get_offset() > 0:
 
1016
            if tag:
 
1017
                self.buff.apply_tag(tag, start_i, end_i)
 
1018
            elif name:
 
1019
                self.buff.apply_tag_by_name(name, start_i, end_i)
 
1020
        elif tag:
 
1021
            self.buff.remove_tag(tag, start_i, end_i)
1020
1022
    
1021
 
    def insert_at_mark(self,buff,mark,text,anchor=None) :
 
1023
    def insert_at_mark(self, buff, mark, text, anchor=None):
1022
1024
        ite = buff.get_iter_at_mark(mark)
1023
 
        if anchor :
1024
 
            self.insert_with_anchor(text,anchor,_iter=ite,typ="subtask")
1025
 
        else :
1026
 
            buff.insert(ite,text)
1027
 
            
1028
 
            
1029
 
    def _get_indent_level(self,itera) :
 
1025
        if anchor:
 
1026
            self.insert_with_anchor(text, anchor, _iter=ite, typ="subtask")
 
1027
        else:
 
1028
            buff.insert(ite, text)
 
1029
            
 
1030
            
 
1031
    def _get_indent_level(self, itera):
1030
1032
        line_nbr   = itera.get_line()
1031
1033
        start_line = itera.copy()
1032
1034
        start_line.set_line(line_nbr)
1033
1035
        tags = start_line.get_tags()
1034
1036
        current_indent = 0
1035
 
        for ta in tags :
1036
 
            if ta.get_data('is_indent') :
 
1037
        for ta in tags:
 
1038
            if ta.get_data('is_indent'):
1037
1039
                current_indent = ta.get_data('indent_level')
1038
1040
        return current_indent
1039
 
    
 
1041
 
1040
1042
    #Method called on copy and cut actions
1041
1043
    #param is either "cut" or "copy"
1042
 
    def copy_clipboard(self,widget,param=None):
 
1044
    def copy_clipboard(self, widget, param=None):
1043
1045
        clip = gtk.clipboard_get(gdk.SELECTION_CLIPBOARD)
1044
 
        
 
1046
 
1045
1047
        #First, we analyse the selection to put in our own
1046
1048
        #GTG clipboard a selection with description of subtasks
1047
1049
        bounds =  self.buff.get_selection_bounds()
1049
1051
            return
1050
1052
        start, stop =  self.buff.get_selection_bounds()
1051
1053
        
1052
 
        self.clipboard.copy(start,stop,bullet=self.bullet1)
 
1054
        self.clipboard.copy(start, stop, bullet=self.bullet1)
1053
1055
        
1054
1056
        clip.set_text(self.clipboard.paste_text())
1055
1057
        clip.store()
1056
1058
        
1057
 
        if param == "cut" :
1058
 
            self.buff.delete_selection(False,True)
 
1059
        if param == "cut":
 
1060
            self.buff.delete_selection(False, True)
1059
1061
            self.stop_emission("cut_clipboard")
1060
 
        else :
 
1062
        else:
1061
1063
            self.stop_emission("copy_clipboard")
1062
1064
        
1063
1065
    #Called on paste.
1064
 
    def paste_clipboard(self,widget,param=None):
 
1066
    def paste_clipboard(self, widget, param=None):
1065
1067
        clip = gtk.clipboard_get(gdk.SELECTION_CLIPBOARD)
1066
1068
        #if the clipboard text is the same are our own internal
1067
1069
        #clipboard text, it means that we can paste from our own clipboard
1068
1070
        #else, that we can empty it.
1069
1071
        our_paste = self.clipboard.paste_text()
1070
 
        if our_paste != None and clip.wait_for_text() == our_paste :
 
1072
        if our_paste != None and clip.wait_for_text() == our_paste:
1071
1073
            #first, we delete the current selection
1072
 
            self.buff.delete_selection(False,True)
 
1074
            self.buff.delete_selection(False, True)
1073
1075
            for line in self.clipboard.paste():
1074
1076
                if line[0] == 'text':
1075
1077
                    self.buff.insert_at_cursor(line[1])
1081
1083
                    #we must paste the \n before inserting the subtask
1082
1084
                    #else, we will start another subtask
1083
1085
                    self.buff.insert_at_cursor("\n")
1084
 
                    self.write_subtask(self.buff,line_nbr,tid)
 
1086
                    self.write_subtask(self.buff, line_nbr, tid)
1085
1087
 
1086
1088
            #we handle ourselves the pasting
1087
1089
            self.stop_emission("paste_clipboard")
1090
1092
            #we keep the normal pasting by not interupting the signal
1091
1093
            self.clipboard.clear()
1092
1094
        
1093
 
    #Function called each time the user inputs a letter   
1094
 
    def _insert_at_cursor(self, tv, itera, tex, leng) :
 
1095
    #Function called each time the user inputs a letter
 
1096
    def _insert_at_cursor(self, tv, itera, tex, leng):
1095
1097
        #We don't paste the bullet
1096
 
        if tex.strip() != self.bullet1 :
1097
 
            #print "text ###%s### inserted length = %s" %(tex,leng)
1098
 
            #disable the insert signal to avoid recursion 
 
1098
        if tex.strip() != self.bullet1:
 
1099
            #print "text ###%s### inserted length = %s" %(tex, leng)
 
1100
            #disable the insert signal to avoid recursion
1099
1101
            self.buff.disconnect(self.insert_sigid)
1100
1102
            self.insert_sigid = False
1101
1103
            self.buff.disconnect(self.modified_sigid)
1111
1113
            subtask_nbr = None
1112
1114
            current_indent = self._get_indent_level(itera)
1113
1115
            tags = itera.get_tags()
1114
 
            for ta in tags :
1115
 
                if ta.get_data('is_subtask') :
 
1116
            for ta in tags:
 
1117
                if ta.get_data('is_subtask'):
1116
1118
                    subtask_nbr = ta.get_data('child')
1117
1119
            #Maybe we are simply at the end of the tag
1118
1120
            if not subtask_nbr and itera.ends_tag():
1119
 
                for ta in itera.get_toggled_tags(False) :
1120
 
                    if ta.get_data('is_subtask') :
 
1121
                for ta in itera.get_toggled_tags(False):
 
1122
                    if ta.get_data('is_subtask'):
1121
1123
                        subtask_nbr = ta.get_data('child')
1122
1124
 
1123
 
            #New line : the user pressed enter !
 
1125
            #New line: the user pressed enter !
1124
1126
            #If the line begins with "-", it's a new subtask !
1125
 
            if tex == '\n' :
 
1127
            if tex == '\n':
1126
1128
                self.buff.create_mark("insert_point", itera, True)
1127
1129
                #First, we close tag tags.
1128
1130
                #If we are at the end of a tag, we look for closed tags
1129
1131
                closed_tag = None
1130
1132
                cutting_subtask = False
1131
 
                if itera.ends_tag() :
 
1133
                if itera.ends_tag():
1132
1134
                    list_stag = itera.get_toggled_tags(False)
1133
1135
                #Or maybe we are in the middle of a tag
1134
 
                else :
 
1136
                else:
1135
1137
                    list_stag = itera.get_tags()
1136
 
                for t in list_stag :
1137
 
                    if t.get_data('is_tag') :
 
1138
                for t in list_stag:
 
1139
                    if t.get_data('is_tag'):
1138
1140
                        closed_tag = t.get_data('tagname')
1139
 
                    elif t.get_data('is_subtask') :
 
1141
                    elif t.get_data('is_subtask'):
1140
1142
                        cutting_subtask = True
1141
1143
                        closed_tag = t.get_data('child')
1142
1144
                #We add a bullet list but not on the first line
1143
1145
                #Because it's the title
1144
 
                if line_nbr > 0 :
 
1146
                if line_nbr > 0:
1145
1147
                    line = start_line.get_slice(end_line)
1146
1148
                    #the part after the enter
1147
1149
                    realend = end_line.copy()
1154
1156
                    #If indent is 0, We check if we created a new task
1155
1157
                    #the "-" might be after a space
1156
1158
                    #Python 2.5 should allow both tests in one
1157
 
                    if current_indent == 0 :
1158
 
                        if line.startswith('-') or line.startswith(' -') :
 
1159
                    if current_indent == 0:
 
1160
                        if (line.startswith('-') or line.startswith(' -')) and line.lstrip(' -').strip() != "":
1159
1161
                            line = line.lstrip(' -')
1160
 
                            end_i = self.__newsubtask(self.buff,line,line_nbr)
 
1162
                            end_i = self.__newsubtask(self.buff, line, line_nbr)
1161
1163
                            #Here, we should increment indent level
1162
1164
                            #If we inserted enter in the middle of a line
1163
 
                            if restofline and restofline.strip() != "" :
 
1165
                            if restofline and restofline.strip() != "":
1164
1166
                                #it means we have two subtask to create
1165
1167
                                if self.buff.get_line_count() > line_nbr+1:
1166
1168
                                    #but don't merge with the next line
1167
1169
                                    itera = self.buff.get_iter_at_line(line_nbr+1)
1168
1170
                                    self.buff.insert(itera,"\n\n")
1169
 
                                self.__newsubtask(self.buff,restofline,\
 
1171
                                self.__newsubtask(self.buff, restofline,\
1170
1172
                                                            line_nbr+1)
1171
1173
                            else:
1172
 
                                self.insert_indent(self.buff,end_i,1,enter=True)
 
1174
                                self.insert_indent(self.buff, end_i, 1, enter=True)
1173
1175
                            tv.emit_stop_by_name('insert-text')
1174
 
                        else :
 
1176
                        else:
1175
1177
                            self.buff.insert(itera,"\n")
1176
1178
                            tv.emit_stop_by_name('insert-text')
1177
1179
                            
1178
1180
                    #Then, if indent > 0, we increment it
1179
 
                    #First step : we preserve it.
1180
 
                    else :
 
1181
                    #First step: we preserve it.
 
1182
                    else:
1181
1183
                        if not line.lstrip("%s "%self.bullet1):
1182
1184
                            #if we didn't write a task, we remove the indent
1183
 
                            #we check if the iterator is well at the end of 
 
1185
                            #we check if the iterator is well at the end of
1184
1186
                            #the line
1185
1187
                            if end_line.ends_line():
1186
 
                                self.deindent(itera,newlevel=0)
1187
 
                            #else, it means that we pressed enter before 
 
1188
                                self.deindent(itera, newlevel=0)
 
1189
                            #else, it means that we pressed enter before
1188
1190
                            #a subtask title
1189
 
                            else :
 
1191
                            else:
1190
1192
                                #we first put the subtask one line below
1191
1193
                                itera2 = self.buff.get_iter_at_line(line_nbr)
1192
1194
                                self.buff.insert(itera2,"\n")
1193
1195
                                #and increment the new white line
1194
1196
                                itera2 = self.buff.get_iter_at_line(line_nbr)
1195
 
                                self.insert_indent(self.buff,itera2,current_indent,enter=False)
1196
 
                        elif current_indent == 1 :
1197
 
                            self.insert_indent(self.buff,itera,current_indent)
 
1197
                                self.insert_indent(self.buff, itera2, current_indent, enter=False)
 
1198
                        elif current_indent == 1:
 
1199
                            self.insert_indent(self.buff, itera, current_indent)
1198
1200
                        #we stop the signal in all cases
1199
1201
                        tv.emit_stop_by_name('insert-text')
1200
1202
                    #Then we close the tag tag
1201
 
                    if closed_tag :
 
1203
                    if closed_tag:
1202
1204
                        insert_mark = self.buff.get_mark("insert_point")
1203
1205
                        insert_iter = self.buff.get_iter_at_mark(insert_mark)
1204
 
                        self.buff.move_mark_by_name("/%s"%closed_tag,insert_iter)
 
1206
                        self.buff.move_mark_by_name("/%s"%closed_tag, insert_iter)
1205
1207
                        self.buff.delete_mark(insert_mark)
1206
 
                        if cutting_subtask :
 
1208
                        if cutting_subtask:
1207
1209
                            cursor = self.buff.get_iter_at_mark(self.buff.get_insert())
1208
1210
                            endl = cursor.copy()
1209
 
                            if not endl.ends_line() :
 
1211
                            if not endl.ends_line():
1210
1212
                                endl.forward_to_line_end()
1211
 
                            text = self.buff.get_text(cursor,endl)
 
1213
                            text = self.buff.get_text(cursor, endl)
1212
1214
                            anchor = self.new_subtask_callback(text)
1213
 
                            self.buff.create_mark(anchor,cursor,True)
1214
 
                            self.buff.create_mark("/%s"%anchor,endl,False)
 
1215
                            self.buff.create_mark(anchor, cursor, True)
 
1216
                            self.buff.create_mark("/%s"%anchor, endl, False)
1215
1217
                        self.modified(full=True)
1216
1218
            #The user entered something else than \n
1217
 
            elif tex :
 
1219
            elif tex:
1218
1220
                #We are on an indented line without subtask ? Create it !
1219
 
                if current_indent > 0 and not subtask_nbr :
 
1221
                if current_indent > 0 and not subtask_nbr:
1220
1222
                    if itera.starts_line():
1221
1223
                        #we are at the start of an existing subtask
1222
1224
                        #we simply move that subtask down
1223
1225
                        self.buff.insert(itera,"\n")
1224
1226
                        itera2 = self.buff.get_iter_at_line(line_nbr)
1225
 
                        self.buff.insert(itera2,tex)
 
1227
                        self.buff.insert(itera2, tex)
1226
1228
                        itera3 = self.buff.get_iter_at_line(line_nbr)
1227
1229
                        itera3.forward_to_line_end()
1228
1230
                        self.buff.place_cursor(itera3)
1229
1231
                        tv.emit_stop_by_name('insert-text')
1230
1232
                    else:
1231
 
                        #self.__newsubtask(self.buff,tex,line_nbr, level=current_indent)
 
1233
                        #self.__newsubtask(self.buff, tex, line_nbr, level=current_indent)
1232
1234
                        anchor = self.new_subtask_callback(tex)
1233
 
                        self.buff.create_mark(anchor,itera,True)
1234
 
                        self.buff.create_mark("/%s"%anchor,itera,False)
 
1235
                        self.buff.create_mark(anchor, itera, True)
 
1236
                        self.buff.create_mark("/%s"%anchor, itera, False)
1235
1237
            self.insert_sigid = self.buff.connect('insert-text', self._insert_at_cursor)
1236
1238
            self.connect('key_press_event', self._keypress)
1237
 
            self.modified_sigid = self.buff.connect("changed" , self.modified)
 
1239
            self.modified_sigid = self.buff.connect("changed", self.modified)
1238
1240
        
1239
1241
    def _keypress(self, widget, event):
1240
1242
        # Check for Ctrl-Return/Enter
1248
1250
                anchor =  tag.get_data('link')
1249
1251
                typ =  tag.get_data('type')
1250
1252
                if(anchor):
1251
 
                    if typ == "subtask" :
 
1253
                    if typ == "subtask":
1252
1254
                        self.open_task(anchor)
1253
1255
                    elif typ == "http" and self.check_link(anchor):
1254
 
                        openurl.openurl(anchor)
 
1256
                        openurl(anchor)
1255
1257
 
1256
1258
            return True
1257
1259
 
1260
1262
    def deindent(self, itera, newlevel=-1):
1261
1263
        line = itera.get_line()
1262
1264
        startline = self.buff.get_iter_at_line(line)
1263
 
        if newlevel < 0 :
1264
 
            for t in itera.get_toggled_tags(False) :
1265
 
                if t.get_data('is_indent') :
 
1265
        if newlevel < 0:
 
1266
            for t in itera.get_toggled_tags(False):
 
1267
                if t.get_data('is_indent'):
1266
1268
                    newlevel = t.get_data('indent_level')
1267
1269
                    
1268
 
            if newlevel > 0 :
 
1270
            if newlevel > 0:
1269
1271
                newlevel -= 1
1270
1272
        #If it's still < 0
1271
 
        if newlevel < 0 :
1272
 
            print "bug : no is_indent tag on that line"
 
1273
        if newlevel < 0:
 
1274
            print "bug: no is_indent tag on that line"
1273
1275
        #startline.backward_char()
1274
1276
        #We make a temp mark where we should insert the new indent
1275
 
        #tempm = self.buff.create_mark("temp",startline)
 
1277
        #tempm = self.buff.create_mark("temp", startline)
1276
1278
        self.buff.disconnect(self.delete_sigid)
1277
 
        #print "deintdent-delete : %s" %self.buff.get_text(startline,itera)
1278
 
        self.buff.delete(startline,itera)
 
1279
        #print "deintdent-delete: %s" %self.buff.get_text(startline, itera)
 
1280
        self.buff.delete(startline, itera)
1279
1281
        self.delete_sigid = self.buff.connect("delete-range", \
1280
1282
                                               self._delete_range)
1281
1283
        #For the day when we will have different indent levels
1282
1284
        #newiter = self.buff.get_iter_at_mark(tempm)
1283
1285
        #self.buff.delete_mark(tempm)
1284
 
        #self.insert_indent(self.buff,newiter,newlevel,enter=False)
 
1286
        #self.insert_indent(self.buff, newiter, newlevel, enter=False)
1285
1287
        
1286
1288
    def backspace(self, tv):
1287
1289
        self.buff.disconnect(self.insert_sigid)
1288
1290
        insert_mark = self.buff.get_insert()
1289
1291
        insert_iter = self.buff.get_iter_at_mark(insert_mark)
1290
1292
        #All this crap to find if we are at the end of an indent tag
1291
 
        if insert_iter.ends_tag() :
1292
 
            for t in insert_iter.get_toggled_tags(False) :
1293
 
                if t.get_data('is_indent') :
 
1293
        if insert_iter.ends_tag():
 
1294
            for t in insert_iter.get_toggled_tags(False):
 
1295
                if t.get_data('is_indent'):
1294
1296
                    self.deindent(insert_iter)
1295
1297
                    tv.emit_stop_by_name('backspace')
1296
 
                    #we stopped the signal, don't forget to erase 
 
1298
                    #we stopped the signal, don't forget to erase
1297
1299
                    #the selection if one
1298
 
                    self.buff.delete_selection(True,True)
 
1300
                    self.buff.delete_selection(True, True)
1299
1301
        self.insert_sigid = self.buff.connect('insert-text', \
1300
1302
                                               self._insert_at_cursor)
1301
1303
 
1309
1311
            if tag.get_data('is_anchor'):
1310
1312
                for t in set(self.__tags) - set([tag]):
1311
1313
                    self.__tag_reset(t, window)
1312
 
                self.__set_anchor(window, tag, gtk.gdk.Cursor(gtk.gdk.HAND2), 
 
1314
                self.__set_anchor(window, tag, gtk.gdk.Cursor(gtk.gdk.HAND2),
1313
1315
                                  self.get_property('hover'))
1314
1316
                break
1315
1317
        else:
1317
1319
            tag_table.foreach(self.__tag_reset, window)
1318
1320
 
1319
1321
    #We clicked on a link
1320
 
    def _tag_event(self, tag, view, ev, _iter, text, anchor,typ): #pylint: disable-msg=W0613
 
1322
    def _tag_event(self, tag, view, ev, _iter, text, anchor, typ): #pylint: disable-msg=W0613
1321
1323
        _type = ev.type
1322
1324
        if _type == gtk.gdk.MOTION_NOTIFY:
1323
1325
            return
1325
1327
            button = ev.button
1326
1328
            cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)
1327
1329
            if _type == gtk.gdk.BUTTON_RELEASE:
1328
 
                if typ == "subtask" :
 
1330
                if typ == "subtask":
1329
1331
                    self.open_task(anchor)
1330
1332
                elif typ == "http":
1331
 
                    if button == 1 and self.check_link(anchor):
1332
 
                        openurl.openurl(anchor)
1333
 
                else :
 
1333
                    if button == 1 and self.check_link(anchor) and self.buff.get_has_selection() == False:
 
1334
                        openurl(anchor)
 
1335
                else:
1334
1336
                    print "Unknown link type for %s" %anchor
1335
1337
                self.emit('anchor-clicked', text, anchor, button)
1336
1338
                self.__set_anchor(ev.window, tag, cursor, self.get_property('hover'))
1341
1343
        if tag.get_data('is_anchor'):
1342
1344
            #We need to get the normal cursor back
1343
1345
            editing_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
1344
 
            if tag.get_property('strikethrough') : 
 
1346
            if tag.get_property('strikethrough'):
1345
1347
                linktype = 'done'
1346
 
            else: 
 
1348
            else:
1347
1349
                anchor = tag.get_data('link')
1348
1350
                if self.check_link(anchor):
1349
1351
                    linktype = 'link'