~gtg-user/gtg/port-to-gtk3

« back to all changes in this revision

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

  • Committer: Izidor Matušov
  • Date: 2012-12-14 13:53:48 UTC
  • mfrom: (1240.1.17 trunk)
  • Revision ID: izidor.matusov@gmail.com-20121214135348-q7wn21y464xb1q2x
Merging trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
# You should have received a copy of the GNU General Public License along with
17
17
# this program.  If not, see <http://www.gnu.org/licenses/>.
18
18
# -----------------------------------------------------------------------------
19
 
 
20
 
 
21
19
"""
22
20
This class implements a Gtk.TextView but with many other features
23
21
like hyperlink and other stuff special for GTG
39
37
from GTG.gtk.editor import taskviewserial
40
38
from GTG.tools      import urlregex
41
39
 
42
 
separators = [' ', ',', '\n', '\t', '!', '?', ';', '\0','(',')']
 
40
separators = [' ', ',', '\n', '\t', '!', '?', ';', '\0', '(', ')']
43
41
#those separators are only separators if followed by a space. Else, they
44
42
#are part of the word
45
 
specials_separators = ['.','/']
 
43
specials_separators = ['.', '/']
46
44
 
47
45
bullet1_ltr = '→'
48
46
bullet1_rtl = '←'
50
48
 
51
49
class TaskView(Gtk.TextView):
52
50
    __gtype_name__ = 'HyperTextView'
53
 
    __gsignals__ = {'anchor-clicked': (GObject.SignalFlags.RUN_LAST, \
 
51
    __gsignals__ = {'anchor-clicked': (GObject.SignalFlags.RUN_LAST,
54
52
                     None, (str, str, int))}
55
53
    __gproperties__ = {
56
 
        'link': (GObject.TYPE_PYOBJECT, 'link color',\
 
54
        'link': (GObject.TYPE_PYOBJECT, 'link color',
57
55
                  'link color of TextView', GObject.PARAM_READWRITE),
58
 
        'failedlink': (GObject.TYPE_PYOBJECT, 'failed link color',\
 
56
        'failedlink': (GObject.TYPE_PYOBJECT, 'failed link color',
59
57
                  'failed link color of TextView', GObject.PARAM_READWRITE),
60
 
        'active': (GObject.TYPE_PYOBJECT, 'active color', \
 
58
        'active': (GObject.TYPE_PYOBJECT, 'active color',
61
59
                  'active color of TextView', GObject.PARAM_READWRITE),
62
 
        'hover': (GObject.TYPE_PYOBJECT, 'link:hover color', \
 
60
        'hover': (GObject.TYPE_PYOBJECT, 'link:hover color',
63
61
                  'link:hover color of TextView', GObject.PARAM_READWRITE),
64
 
        'tag': (GObject.TYPE_PYOBJECT, 'tag color', \
 
62
        'tag': (GObject.TYPE_PYOBJECT, 'tag color',
65
63
                  'tag color of TextView', GObject.PARAM_READWRITE),
66
 
        'done': (GObject.TYPE_PYOBJECT, 'link color', \
 
64
        'done': (GObject.TYPE_PYOBJECT, 'link color',
67
65
                  'link color of TextView', GObject.PARAM_READWRITE),
68
 
        'indent': (GObject.TYPE_PYOBJECT, 'indent color', \
 
66
        'indent': (GObject.TYPE_PYOBJECT, 'indent color',
69
67
                  'indent color of TextView', GObject.PARAM_READWRITE),
70
68
        }
71
69
 
87
85
        self.buff = self.get_buffer()
88
86
        self.req = requester
89
87
        #Buffer init
90
 
        self.link = {'background': 'white', 'foreground': '#007bff', \
91
 
                      'underline': Pango.Underline.SINGLE, \
92
 
                      'strikethrough': False}
93
 
        self.failedlink = {'background': 'white', 'foreground': '#ff5454', \
94
 
                      'underline': Pango.Underline.NONE, \
95
 
                      'strikethrough': False}
96
 
        self.done   = {'background': 'white', 'foreground': 'gray',\
 
88
        self.link = {'background': 'white', 'foreground': '#007bff',
 
89
                      'underline': Pango.Underline.SINGLE,
 
90
                      'strikethrough': False}
 
91
        self.failedlink = {'background': 'white', 'foreground': '#ff5454',
 
92
                      'underline': Pango.Underline.NONE,
 
93
                      'strikethrough': False}
 
94
        self.done   = {'background': 'white', 'foreground': 'gray',
97
95
                                    'strikethrough': True}
98
 
        self.active = {'background': 'light gray', 'foreground': '#ff1e00',\
 
96
        self.active = {'background': 'light gray', 'foreground': '#ff1e00',
99
97
                                    'underline': Pango.Underline.SINGLE}
100
98
        self.hover  = {'background': 'light gray'}
101
99
        self.tag = {'background': "#FFea00", 'foreground': 'black'}
107
105
        # but set in self.modified)
108
106
        self.table = self.buff.get_tag_table()
109
107
        # Tag for title
110
 
        self.title_tag  = self.buff.create_tag("title", foreground="#007bff", \
 
108
        self.title_tag = self.buff.create_tag("title", foreground="#007bff",
111
109
                            scale=1.6, underline=1)
112
110
        self.title_tag.set_property("pixels-above-lines", 10)
113
111
        self.title_tag.set_property("pixels-below-lines", 10)
124
122
 
125
123
        #Signals
126
124
        self.connect('motion-notify-event', self._motion)
127
 
        self.connect('focus-out-event', lambda w, \
128
 
                     e: self.table.foreach(self.__tag_reset, e.window))
129
 
        self.insert_sigid = self.buff.connect('insert-text', \
 
125
        self.connect('focus-out-event',
 
126
            lambda w, e: self.table.foreach(self.__tag_reset, e.window))
 
127
        self.insert_sigid = self.buff.connect('insert-text',
130
128
                                              self._insert_at_cursor)
131
 
        self.delete_sigid = self.buff.connect("delete-range", self._delete_range)
132
 
        self.connect('copy-clipboard', self.copy_clipboard,"copy")
133
 
        self.connect('cut-clipboard', self.copy_clipboard,"cut")
 
129
        self.delete_sigid = self.buff.connect("delete-range",
 
130
            self._delete_range)
 
131
        self.connect('copy-clipboard', self.copy_clipboard, "copy")
 
132
        self.connect('cut-clipboard', self.copy_clipboard, "cut")
134
133
        self.connect('paste-clipboard', self.paste_clipboard)
135
134
 
136
135
        self.connect('drag-data-received', self.drag_receive)
158
157
        self.get_subtasks = None
159
158
        self.remove_subtask =None
160
159
        self.__refresh_cb = None  # refresh the editor window
161
 
        self.open_task            = None # open another task
 
160
        self.open_task = None # open another task
162
161
        self.new_subtask_callback = None # create a subtask
163
162
        self.save_task = None #This will save the task without refreshing all
164
163
 
187
186
    #editable means that the user can edit the taskview
188
187
    #this is initially set at False and then to True once the editor window
189
188
    #is displayed.
190
 
    #this is used to avoid saving the task when the window is still not displayed
 
189
    #this is used to avoid saving the task when the window is still
 
190
    #not displayed
191
191
    def set_editable(self, boule):
192
192
        self.editable = boule
193
193
 
273
273
 
274
274
        #reconnect
275
275
        if reconnect_insert:
276
 
            self.insert_sigid = self.buff.connect('insert-text', self._insert_at_cursor)
 
276
            self.insert_sigid = self.buff.connect('insert-text',
 
277
                self._insert_at_cursor)
277
278
        if reconnect_modified:
278
279
            self.modified_sigid = self.buff.connect("changed", self.modified)
279
280
 
311
312
        else:
312
313
            return True
313
314
 
314
 
 
315
315
    def create_anchor_tag(self, b, anchor, text=None, typ=None):
316
316
        #We cannot have two tags with the same name
317
317
        #That's why the link tag has no name
329
329
        if linktype == 'link' and not self.check_link(anchor):
330
330
            linktype = 'failedlink'
331
331
 
332
 
        tag = b.create_tag(None, **self.get_property(linktype)) #pylint: disable-msg=W0142
 
332
        #pylint: disable-msg=W0142
 
333
        tag = b.create_tag(None, **self.get_property(linktype))
333
334
        tag.is_anchor = True
334
335
        tag.link = anchor
335
336
        if typ:
352
353
                if ss.begins_tag(t) and ee.ends_tag(t):
353
354
                    already = True
354
355
        if not texttag:
355
 
            texttag = buff.create_tag(None,**self.get_property('tag'))#pylint: disable-msg=W0142
 
356
            #pylint: disable-msg=W0142
 
357
            texttag = buff.create_tag(None,**self.get_property('tag'))
356
358
            texttag.is_tag = True
357
359
            texttag.tagname = tag
358
360
            #This one is for marks
374
376
            tex = buff.get_text(i_s, i_e, True)
375
377
        if len(tex) > 0:
376
378
            self.req.get_task(subtask).set_title(tex)
377
 
            texttag = self.create_anchor_tag(buff, subtask, text=tex, typ="subtask")
 
379
            texttag = self.create_anchor_tag(buff, subtask, text=tex,
 
380
                typ="subtask")
378
381
            texttag.is_subtask = True
379
382
            texttag.child = subtask
380
383
            #This one is for marks
385
388
            buff.delete_mark(e)
386
389
 
387
390
    def create_indent_tag(self, buff, level):
388
 
        tag = buff.create_tag(None, **self.get_property('indent'))#pylint: disable-msg=W0142
 
391
        #pylint: disable-msg=W0142
 
392
        tag = buff.create_tag(None, **self.get_property('indent'))
389
393
        tag.is_indent = True
390
394
        tag.indent_level = level
391
395
        return tag
412
416
                if hasattr(tt, 'is_tag'):
413
417
                    newline = False
414
418
                    firstline.forward_to_line_end()
415
 
                    #Now we should check if the current char is a separator or not
416
 
                    #Currently, we insert a space
 
419
                    # Now we should check if the current char is
 
420
                    # a separator or not
 
421
                    # Currently, we insert a space
417
422
                    self.insert_text(" ", firstline)
418
 
            #Now we check if this newline is empty (it contains only " " and ",")
 
423
            # Now we check if this newline is empty
 
424
            # (it contains only " " and ",")
419
425
    #        if newline:
420
426
    #            endline = firstline.copy()
421
427
    #            if not endline.ends_line():
431
437
                self.insert_text("\n", firstline)
432
438
                firstline = self.buff.get_iter_at_line(1)
433
439
            line_mark = self.buff.create_mark("firstline", firstline, False)
434
 
            #self.tv.insert_at_mark(buf, line_mark,"\n")
 
440
            #self.tv.insert_at_mark(buf, line_mark, "\n")
435
441
            ntags = len(tag_list)
436
442
            for t in tag_list:
437
443
                ntags = ntags - 1
438
444
                self.insert_at_mark(self.buff, line_mark, t)
439
445
                if ntags != 0:
440
 
                    self.insert_at_mark(self.buff, line_mark,",")
 
446
                    self.insert_at_mark(self.buff, line_mark, ",")
441
447
            self.buff.delete_mark(line_mark)
442
448
            self.modified(full=True)
443
449
 
493
499
        stripped = title.strip(' \n\t')
494
500
        return stripped
495
501
 
496
 
### PRIVATE FUNCTIONS ##########################################################
497
 
 
498
 
 
 
502
    ### PRIVATE FUNCTIONS #####################################################
499
503
    #This function is called so frequently that we should optimize it more.
500
504
    def modified(self, buff=None, full=False, refresheditor=True):
501
505
        """Called when the buffer has been modified.
541
545
        #subt_list = self.get_subtasks()
542
546
        #First, we remove the olds tags
543
547
        tag_list = []
 
548
 
544
549
        def subfunc(texttag, data=None): #pylint: disable-msg=W0613
545
550
            if hasattr(texttag, 'is_subtask'):
546
551
                tag_list.append(texttag)
 
552
 
547
553
        table.foreach(subfunc, None)
548
554
        start, end = buff.get_bounds()
549
555
        for t in tag_list:
586
592
        #First, we remove the olds tags
587
593
        tag_list = []
588
594
        table = buff.get_tag_table()
 
595
 
589
596
        def subfunc(texttag, data=None):
590
597
            if hasattr(texttag, 'is_anchor'):
591
598
                tag_list.append(texttag)
 
599
 
592
600
        table.foreach(subfunc, None)
593
601
        for t in tag_list:
594
602
            buff.remove_tag(t, start, end)
609
617
                    # For short URL we must add http:// prefix
610
618
                    if text == "www":
611
619
                        url = "http://" + url
612
 
                    texttag = self.create_anchor_tag(buff, url, text=None, typ="http")
 
620
                    texttag = self.create_anchor_tag(buff, url, text=None,
 
621
                        typ="http")
613
622
                    it = prev.copy()
614
623
                    it.forward_chars(m.end())
615
624
                    buff.apply_tag(texttag, prev, it)
625
634
                    nbr = url.split("#")[1]
626
635
                    topoint = None
627
636
                    if url.startswith("bug #") or url.startswith("lp #"):
628
 
                        topoint = "https://launchpad.net/bugs/%s" %nbr
 
637
                        topoint = "https://launchpad.net/bugs/%s" % nbr
629
638
                    elif url.startswith("bgo #"):
630
 
                        topoint = "http://bugzilla.gnome.org/show_bug.cgi?id=%s" %nbr
 
639
                        topoint = "http://bugzilla.gnome.org/" + \
 
640
                            "show_bug.cgi?id=%s" % nbr
631
641
                    elif url.startswith("bko #"):
632
 
                        topoint = "https://bugs.kde.org/show_bug.cgi?id=%s" %nbr
 
642
                        topoint = "https://bugs.kde.org/show_bug.cgi?id=%s" \
 
643
                            % nbr
633
644
                    elif url.startswith("fdo #"):
634
 
                        topoint = "http://bugs.freedesktop.org/show_bug.cgi?id=%s" %nbr
 
645
                        topoint = "http://bugs.freedesktop.org/" + \
 
646
                            "show_bug.cgi?id=%s" % nbr
635
647
                    if topoint:
636
 
                        texttag = self.create_anchor_tag(buff,\
 
648
                        texttag = self.create_anchor_tag(buff,
637
649
                                                topoint, text=None, typ="http")
638
650
                        buff.apply_tag(texttag, prev, it)
639
651
 
662
674
                        mark1 = buff.get_mark(tagname)
663
675
                        if mark1:
664
676
                            offset1 = buff.get_iter_at_mark(mark1).get_offset()
665
 
                            if start.get_offset() <= offset1 <= end.get_offset():
 
677
                            if start.get_offset() <= offset1 <= \
 
678
                                end.get_offset():
666
679
                                buff.delete_mark_by_name(tagname)
667
680
                        mark2 = buff.get_mark("/%s"%tagname)
668
681
                        if mark2:
669
682
                            offset2 = buff.get_iter_at_mark(mark2).get_offset()
670
 
                            if start.get_offset() <= offset2 <= end.get_offset():
 
683
                            if start.get_offset() <= offset2 <= \
 
684
                                end.get_offset():
671
685
                                buff.delete_mark_by_name("/%s"%tagname)
672
686
            it.forward_char()
673
687
 
674
688
        # Set iterators for word
675
689
        word_start = start.copy()
676
 
        word_end   = start.copy()
 
690
        word_end = start.copy()
677
691
 
678
692
        # Set iterators for char
679
693
        char_start = start.copy()
680
 
        char_end   = start.copy()
 
694
        char_end = start.copy()
681
695
        char_end.forward_char()
682
696
        last_char = None
683
697
 
684
698
        # Iterate over characters of the line to get words
685
699
        while char_end.compare(end) <= 0:
686
700
            do_word_check = False
687
 
            my_char       = buff.get_text(char_start, char_end, True)
 
701
            my_char = buff.get_text(char_start, char_end, True)
688
702
            if my_char not in separators:
689
703
                last_char = my_char
690
704
                word_end = char_end.copy()
710
724
                    #and it shouldn't start with @@ (bug 531553)
711
725
                    if len(my_word) > 1 and my_word[0] == '@' \
712
726
                       and not my_word[1] == '@':
713
 
                        #self.apply_tag_tag(buff, my_word, word_start, word_end)
 
727
                        #self.apply_tag_tag(buff, my_word, word_start,
 
728
                        #   word_end)
714
729
                        #We will add mark where tag should be applied
715
730
                        buff.create_mark(my_word, word_start, True)
716
731
                        buff.create_mark("/%s"%my_word, word_end, False)
721
736
 
722
737
                # We set new word boundaries
723
738
                word_start = char_end.copy()
724
 
                word_end   = char_end.copy()
 
739
                word_end = char_end.copy()
725
740
 
726
741
            # Stop loop if we are at the end
727
742
            if char_end.compare(end) == 0:
739
754
 
740
755
    def is_at_title(self, buff, itera):
741
756
        to_return = False
 
757
 
742
758
        if itera.get_line() == 0:
743
759
            to_return = True
744
760
        #We are at a line with the title tag applied
747
763
        #else, we look if there's something between us and buffer start
748
764
        elif not buff.get_text(buff.get_start_iter(), itera, True).strip('\n\t '):
749
765
            to_return = True
 
766
 
750
767
        return to_return
751
768
 
752
769
    #When the user removes a selection, we remove subtasks and @tags
771
788
#                buff.remove_tag(ta, start, endindent)
772
789
        #Now we delete all, char after char
773
790
        it = start.copy()
774
 
        while (it.get_offset() <= end.get_offset()) and (it.get_char() != '\0'):
 
791
        while it.get_offset() <= end.get_offset() and it.get_char() != '\0':
775
792
            if it.begins_tag():
776
793
                tags = it.get_tags()
777
794
                for ta in tags:
801
818
        #now we really delete the selected stuffs
802
819
        selec = self.buff.get_selection_bounds()
803
820
#        if selec:
804
 
#            print "deleted text is ##%s##" %self.buff.get_text(selec[0], selec[1])#(start, end)
 
821
#            print "deleted text is ##%s##" %self.buff.get_text(selec[0],
 
822
#                selec[1])#(start, end)
805
823
#        self.buff.disconnect(self.delete_sigid)
806
824
#        self.disconnect(self.backspace_sigid)
807
825
#        self.buff.stop_emission("delete-range")
810
828
#        else:
811
829
#            end.forward_char()
812
830
#            self.buff.backspace(end, False, True)
813
 
#        self.delete_sigid = self.buff.connect("delete-range", self._delete_range)
 
831
#        self.delete_sigid = self.buff.connect("delete-range",
 
832
#            self._delete_range)
814
833
#        self.backspace_sigid = self.connect("backspace", self.backspace)
815
834
        #We return false so the parent still get the signal
816
835
        return False
817
836
 
818
 
    #Apply the title and return an iterator after that title.buff.get_iter_at_mar
819
837
    def _apply_title(self, buff, refresheditor=True):
820
 
        start     = buff.get_start_iter()
821
 
        end       = buff.get_end_iter()
822
 
        line_nbr  = 1
 
838
        """
 
839
        Apply the title and return an iterator after that
 
840
        title.buff.get_iter_at_mar
 
841
        """
 
842
        start = buff.get_start_iter()
 
843
        end = buff.get_end_iter()
 
844
        line_nbr = 1
823
845
        linecount = buff.get_line_count()
824
846
 
825
847
        # Apply the title tag on the first line
831
853
            # Applying title on the first line
832
854
            title_end = buff.get_iter_at_line(line_nbr-1)
833
855
            title_end.forward_to_line_end()
834
 
            stripped  = buff.get_text(title_start, title_end, True).strip('\n\t ')
 
856
            stripped = buff.get_text(title_start, title_end, True)
 
857
            stripped = stripped.strip('\n\t ')
835
858
            # Here we ignore lines that are blank
836
859
            # Title is the first written line
837
860
            while line_nbr <= linecount and not stripped:
838
 
                line_nbr  += 1
839
 
                title_end  = buff.get_iter_at_line(line_nbr-1)
 
861
                line_nbr += 1
 
862
                title_end = buff.get_iter_at_line(line_nbr-1)
840
863
                title_end.forward_to_line_end()
841
 
                stripped   = buff.get_text(title_start, title_end, True).strip('\n\t ')
 
864
                stripped = buff.get_text(title_start, title_end, True)
 
865
                stripped = stripped.strip('\n\t ')
842
866
        # Or to all the buffer if there is only one line
843
867
        else:
844
868
            title_end = end.copy()
849
873
            self.refresh(buff.get_text(title_start, title_end, True).strip('\n\t'))
850
874
        return title_end
851
875
 
852
 
 
853
 
 
854
876
    def __newsubtask(self, buff, title, line_nbr, level=1):
855
877
        anchor = self.new_subtask_callback(title)
856
878
        end_i = self.write_subtask(buff, line_nbr, anchor, level=level)
878
900
        #be in the subtask title
879
901
        start_i = buff.get_iter_at_line(line_nbr)
880
902
        start_i.forward_to_line_end()
881
 
        buff.insert(start_i,"\n")
 
903
        buff.insert(start_i, "\n")
882
904
        #Ok, now we can start working
883
905
        start_i = buff.get_iter_at_line(line_nbr)
884
 
        end_i   = start_i.copy()
 
906
        end_i = start_i.copy()
885
907
        #We go back at the end of the previous line
886
908
#        start_i.backward_char()
887
909
#        #But only if this is not the title.
889
911
#        if start_i.has_tag(self.title_tag):
890
912
#            start_i.forward_char()
891
913
#            insert_enter = False
892
 
        start   = buff.create_mark("start", start_i, True)
 
914
        start = buff.create_mark("start", start_i, True)
893
915
        end_i.forward_line()
894
 
        end     = buff.create_mark("end", end_i, False)
 
916
        end = buff.create_mark("end", end_i, False)
895
917
        buff.delete(start_i, end_i)
896
918
        start_i = buff.get_iter_at_mark(start)
897
919
        self.insert_indent(buff, start_i, level, enter=insert_enter)
908
930
        #buff.delete_mark(end)
909
931
 
910
932
        if reconnect_insert:
911
 
            self.insert_sigid = self.buff.connect('insert-text', self._insert_at_cursor)
 
933
            self.insert_sigid = self.buff.connect('insert-text',
 
934
                self._insert_at_cursor)
912
935
        if reconnect_modified:
913
936
            self.modified_sigid = self.buff.connect("changed", self.modified)
914
937
        return end_i
939
962
        if line == self.buff.get_line_count():
940
963
            itera.forward_to_line_end()
941
964
            mark = self.buff.create_mark(None, itera, True)
942
 
            self.buff.insert(itera,"\n")
 
965
            self.buff.insert(itera, "\n")
943
966
            itera = self.buff.get_iter_at_mark(mark)
944
967
            self.buff.delete_mark(mark)
945
968
 
949
972
        enter = True
950
973
        if itera.starts_line():
951
974
            mark = self.buff.create_mark(None, itera, True)
952
 
            self.buff.insert(itera,"\n")
 
975
            self.buff.insert(itera, "\n")
953
976
            itera = self.buff.get_iter_at_mark(mark)
954
977
            self.buff.delete_mark(mark)
955
978
            enter = False
980
1003
        #It will be later replaced by the good one with right gravity
981
1004
        temp_mark = self.buff.create_mark("temp", start_i, True)
982
1005
 
983
 
        end     = buff.create_mark("end", start_i, False)
 
1006
        end = buff.create_mark("end", start_i, False)
984
1007
        if enter:
985
 
            buff.insert(start_i,"\n")
 
1008
            buff.insert(start_i, "\n")
986
1009
 
987
1010
        #Moving the end of subtask mark to the position of the temp mark
988
1011
        if stag:
997
1020
        #This is normally not needed and purely defensive
998
1021
        if itera.get_line() <= 0:
999
1022
            itera = buff.get_iter_at_line(1)
1000
 
        start   = buff.create_mark("start", itera, True)
 
1023
        start = buff.create_mark("start", itera, True)
1001
1024
        indentation = ""
1002
1025
        #adding two spaces by level
1003
1026
        spaces = "  "
1010
1033
        self.__apply_tag_to_mark(start, end, tag=indenttag)
1011
1034
        return end
1012
1035
 
1013
 
 
1014
1036
    def __apply_tag_to_mark(self, start, end, tag=None, name=None):
1015
1037
        start_i = self.buff.get_iter_at_mark(start)
1016
1038
        end_i = self.buff.get_iter_at_mark(end)
1030
1052
        else:
1031
1053
            buff.insert(ite, text)
1032
1054
 
1033
 
 
1034
1055
    def _get_indent_level(self, itera):
1035
 
        line_nbr   = itera.get_line()
 
1056
        line_nbr = itera.get_line()
1036
1057
        start_line = itera.copy()
1037
1058
        start_line.set_line(line_nbr)
1038
1059
        tags = start_line.get_tags()
1049
1070
 
1050
1071
        #First, we analyse the selection to put in our own
1051
1072
        #GTG clipboard a selection with description of subtasks
1052
 
        bounds =  self.buff.get_selection_bounds()
 
1073
        bounds = self.buff.get_selection_bounds()
1053
1074
        if not bounds:
1054
1075
            return
1055
 
        start, stop =  self.buff.get_selection_bounds()
 
1076
        start, stop = self.buff.get_selection_bounds()
1056
1077
 
1057
1078
        self.clipboard.copy(start, stop, bullet=self.bullet1)
1058
1079
 
1108
1129
 
1109
1130
            #First, we will get the actual indentation value
1110
1131
            #The nbr just before the \n
1111
 
            line_nbr   = itera.get_line()
 
1132
            line_nbr = itera.get_line()
1112
1133
            start_line = itera.copy()
1113
1134
            start_line.set_line(line_nbr)
1114
 
            end_line   = itera.copy()
 
1135
            end_line = itera.copy()
1115
1136
            tags = start_line.get_tags()
1116
1137
            subtask_nbr = None
1117
1138
            current_indent = self._get_indent_level(itera)
1160
1181
                    #the "-" might be after a space
1161
1182
                    #Python 2.5 should allow both tests in one
1162
1183
                    if current_indent == 0:
1163
 
                        if (line.startswith('-') or line.startswith(' -')) and line.lstrip(' -').strip() != "":
 
1184
                        if (line.startswith('-') or line.startswith(' -')) \
 
1185
                            and line.lstrip(' -').strip() != "":
1164
1186
                            line = line.lstrip(' -')
1165
 
                            end_i = self.__newsubtask(self.buff, line, line_nbr)
 
1187
                            end_i = self.__newsubtask(self.buff, line,
 
1188
                                line_nbr)
1166
1189
                            #Here, we should increment indent level
1167
1190
                            #If we inserted enter in the middle of a line
1168
1191
                            if restofline and restofline.strip() != "":
1169
1192
                                #it means we have two subtask to create
1170
1193
                                if self.buff.get_line_count() > line_nbr+1:
1171
1194
                                    #but don't merge with the next line
1172
 
                                    itera = self.buff.get_iter_at_line(line_nbr+1)
1173
 
                                    self.buff.insert(itera,"\n\n")
1174
 
                                self.__newsubtask(self.buff, restofline,\
 
1195
                                    itera = self.buff.get_iter_at_line(
 
1196
                                        line_nbr + 1)
 
1197
                                    self.buff.insert(itera, "\n\n")
 
1198
                                self.__newsubtask(self.buff, restofline,
1175
1199
                                                            line_nbr+1)
1176
1200
                            else:
1177
 
                                self.insert_indent(self.buff, end_i, 1, enter=True)
 
1201
                                self.insert_indent(self.buff, end_i, 1,
 
1202
                                    enter=True)
1178
1203
                            tv.emit_stop_by_name('insert-text')
1179
1204
                        else:
1180
 
                            self.buff.insert(itera,"\n")
 
1205
                            self.buff.insert(itera, "\n")
1181
1206
                            tv.emit_stop_by_name('insert-text')
1182
1207
 
1183
1208
                    #Then, if indent > 0, we increment it
1194
1219
                            else:
1195
1220
                                #we first put the subtask one line below
1196
1221
                                itera2 = self.buff.get_iter_at_line(line_nbr)
1197
 
                                self.buff.insert(itera2,"\n")
 
1222
                                self.buff.insert(itera2, "\n")
1198
1223
                                #and increment the new white line
1199
1224
                                itera2 = self.buff.get_iter_at_line(line_nbr)
1200
 
                                self.insert_indent(self.buff, itera2, current_indent, enter=False)
 
1225
                                self.insert_indent(self.buff, itera2,
 
1226
                                    current_indent, enter=False)
1201
1227
                        elif current_indent == 1:
1202
 
                            self.insert_indent(self.buff, itera, current_indent)
 
1228
                            self.insert_indent(self.buff, itera,
 
1229
                                current_indent)
1203
1230
                        #we stop the signal in all cases
1204
1231
                        tv.emit_stop_by_name('insert-text')
1205
1232
                    #Then we close the tag tag
1206
1233
                    if closed_tag:
1207
1234
                        insert_mark = self.buff.get_mark("insert_point")
1208
1235
                        insert_iter = self.buff.get_iter_at_mark(insert_mark)
1209
 
                        self.buff.move_mark_by_name("/%s"%closed_tag, insert_iter)
 
1236
                        self.buff.move_mark_by_name("/%s" % closed_tag,
 
1237
                            insert_iter)
1210
1238
                        self.buff.delete_mark(insert_mark)
1211
1239
                        if cutting_subtask:
1212
 
                            cursor = self.buff.get_iter_at_mark(self.buff.get_insert())
 
1240
                            cursor = self.buff.get_iter_at_mark(
 
1241
                                self.buff.get_insert())
1213
1242
                            endl = cursor.copy()
1214
1243
                            if not endl.ends_line():
1215
1244
                                endl.forward_to_line_end()
1225
1254
                    if itera.starts_line():
1226
1255
                        #we are at the start of an existing subtask
1227
1256
                        #we simply move that subtask down
1228
 
                        self.buff.insert(itera,"\n")
 
1257
                        self.buff.insert(itera, "\n")
1229
1258
                        itera2 = self.buff.get_iter_at_line(line_nbr)
1230
1259
                        self.buff.insert(itera2, tex)
1231
1260
                        itera3 = self.buff.get_iter_at_line(line_nbr)
1233
1262
                        self.buff.place_cursor(itera3)
1234
1263
                        tv.emit_stop_by_name('insert-text')
1235
1264
                    else:
1236
 
                        #self.__newsubtask(self.buff, tex, line_nbr, level=current_indent)
 
1265
                        #self.__newsubtask(self.buff, tex, line_nbr,
 
1266
                        #   level=current_indent)
1237
1267
                        anchor = self.new_subtask_callback(tex)
1238
1268
                        self.buff.create_mark(anchor, itera, True)
1239
1269
                        self.buff.create_mark("/%s"%anchor, itera, False)
1240
 
            self.insert_sigid = self.buff.connect('insert-text', self._insert_at_cursor)
 
1270
            self.insert_sigid = self.buff.connect('insert-text',
 
1271
                self._insert_at_cursor)
1241
1272
            self.connect('key_press_event', self._keypress)
1242
1273
            self.modified_sigid = self.buff.connect("changed", self.modified)
1243
1274
 
1244
1275
    def _keypress(self, widget, event):
1245
1276
        # Check for Ctrl-Return/Enter
1246
 
        if event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter):
 
1277
        if event.get_state() & Gdk.ModifierType.CONTROL_MASK and \
 
1278
            event.keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter):
1247
1279
            buff = self.buff
1248
1280
            cursor_mark = buff.get_insert()
1249
1281
            cursor_iter = buff.get_iter_at_mark(cursor_mark)
1250
1282
            local_start = cursor_iter.copy()
1251
1283
 
1252
1284
            for tag in local_start.get_tags():
1253
 
                anchor =  tag.link
1254
 
                typ =  tag.type
 
1285
                anchor = tag.link
 
1286
                typ = tag.type
1255
1287
                if(anchor):
1256
1288
                    if typ == "subtask":
1257
1289
                        self.open_task(anchor)
1281
1313
        self.buff.disconnect(self.delete_sigid)
1282
1314
        #print "deintdent-delete: %s" %self.buff.get_text(startline, itera)
1283
1315
        self.buff.delete(startline, itera)
1284
 
        self.delete_sigid = self.buff.connect("delete-range", \
1285
 
                                               self._delete_range)
1286
1316
        #For the day when we will have different indent levels
1287
1317
        #newiter = self.buff.get_iter_at_mark(tempm)
1288
1318
        #self.buff.delete_mark(tempm)
1289
1319
        #self.insert_indent(self.buff, newiter, newlevel, enter=False)
 
1320
        self.delete_sigid = self.buff.connect("delete-range",
 
1321
                                               self._delete_range)
1290
1322
 
1291
1323
    def backspace(self, tv):
1292
1324
        self.buff.disconnect(self.insert_sigid)
1301
1333
                    #we stopped the signal, don't forget to erase
1302
1334
                    #the selection if one
1303
1335
                    self.buff.delete_selection(True, True)
1304
 
        self.insert_sigid = self.buff.connect('insert-text', \
 
1336
        self.insert_sigid = self.buff.connect('insert-text',
1305
1337
                                               self._insert_at_cursor)
1306
1338
 
1307
1339
    #The mouse is moving. We must change it to a hand when hovering over a link
1321
1353
            tag_table = self.buff.get_tag_table()
1322
1354
            tag_table.foreach(self.__tag_reset, window)
1323
1355
 
1324
 
    #We clicked on a link
1325
 
    def _tag_event(self, tag, view, ev, _iter, text, anchor, typ): #pylint: disable-msg=W0613
 
1356
    def _tag_event(self, tag, view, ev, _iter, text, anchor, typ):
 
1357
        """
 
1358
        We clicked on a link
 
1359
        """
 
1360
        #pylint: disable-msg=W0613
1326
1361
        _type = ev.type
1327
1362
        if _type == Gdk.EventType.MOTION_NOTIFY:
1328
1363
            return
1333
1368
                if typ == "subtask":
1334
1369
                    self.open_task(anchor)
1335
1370
                elif typ == "http":
1336
 
                    if button == 1 and self.check_link(anchor) and self.buff.get_has_selection() == False:
 
1371
                    if button == 1 and self.check_link(anchor) and \
 
1372
                        not self.buff.get_has_selection():
1337
1373
                        openurl(anchor)
1338
1374
                else:
1339
1375
                    print "Unknown link type for %s" %anchor
1340
1376
                self.emit('anchor-clicked', text, anchor, button)
1341
 
                self.__set_anchor(ev.window, tag, cursor, self.get_property('hover'))
 
1377
                self.__set_anchor(ev.window, tag, cursor,
 
1378
                    self.get_property('hover'))
1342
1379
            elif button in [1, 2]:
1343
 
                self.__set_anchor(ev.window, tag, cursor, self.get_property('active'))
 
1380
                self.__set_anchor(ev.window, tag, cursor,
 
1381
                    self.get_property('active'))
1344
1382
 
1345
1383
    def __tag_reset(self, tag, window):
1346
1384
        if hasattr(tag, 'is_anchor'):
1353
1391
                    linktype = 'link'
1354
1392
                else:
1355
1393
                    linktype = 'failedlink'
1356
 
            self.__set_anchor(window, tag, editing_cursor, self.get_property(linktype))
 
1394
            self.__set_anchor(window, tag, editing_cursor,
 
1395
                self.get_property(linktype))
1357
1396
 
1358
1397
    def __set_anchor(self, window, tag, cursor, prop):
1359
1398
        window.set_cursor(cursor)