/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
1
#!/usr/bin/python
2
# -*- coding: UTF-8 -*-
3
"""Difference window.
4
5
This module contains the code to manage the diff window which shows
6
the changes made between two revisions on a branch.
7
"""
8
9
__copyright__ = "Copyright © 2005 Canonical Ltd."
10
__author__    = "Scott James Remnant <scott@ubuntu.com>"
11
12
13
from cStringIO import StringIO
14
15
import gtk
16
import pango
232.1.1 by Adeodato Simó
Read ~/.colordiffrc to set colors in the diff window.
17
import os
18
import re
76 by Jelmer Vernooij
Replace non-UTF8 characters rather than generating an exception (fixes #44677).
19
import sys
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
20
21
try:
22
    import gtksourceview
23
    have_gtksourceview = True
24
except ImportError:
25
    have_gtksourceview = False
26
71 by Szilveszter Farkas (Phanatic)
(phanatic) compare_trees deprecated in 0.9
27
import bzrlib
28
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
29
from bzrlib.diff import show_diff_trees
59.2.4 by Aaron Bentley
Teach gdiff to accept a single file argument
30
from bzrlib.errors import NoSuchFile
232.1.1 by Adeodato Simó
Read ~/.colordiffrc to set colors in the diff window.
31
from bzrlib.trace import warning
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
32
33
34
class DiffWindow(gtk.Window):
35
    """Diff window.
36
37
    This object represents and manages a single window containing the
38
    differences between two revisions on a branch.
39
    """
40
51 by Jelmer Vernooij
Rework some of the parameters to DiffWindow.set_diff() to be
41
    def __init__(self):
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
42
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
43
        self.set_border_width(0)
44
        self.set_title("bzrk diff")
45
46
        # Use two thirds of the screen by default
47
        screen = self.get_screen()
48
        monitor = screen.get_monitor_geometry(0)
49
        width = int(monitor.width * 0.66)
50
        height = int(monitor.height * 0.66)
51
        self.set_default_size(width, height)
52
53
        self.construct()
54
55
    def construct(self):
56
        """Construct the window contents."""
75 by Jelmer Vernooij
Use HPaned rather than HBox so long filenames can be viewed (fixes #56993).
57
        # The   window  consists  of   a  pane   containing:  the
58
        # hierarchical list  of files on  the left, and  the diff
59
        # for the currently selected file on the right.
60
        pane = gtk.HPaned()
61
        self.add(pane)
62
        pane.show()
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
63
75 by Jelmer Vernooij
Use HPaned rather than HBox so long filenames can be viewed (fixes #56993).
64
        # The file hierarchy: a scrollable treeview
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
65
        scrollwin = gtk.ScrolledWindow()
66
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
67
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
75 by Jelmer Vernooij
Use HPaned rather than HBox so long filenames can be viewed (fixes #56993).
68
        pane.pack1(scrollwin)
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
69
        scrollwin.show()
70
71
        self.model = gtk.TreeStore(str, str)
72
        self.treeview = gtk.TreeView(self.model)
73
        self.treeview.set_headers_visible(False)
74
        self.treeview.set_search_column(1)
75
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
76
        scrollwin.add(self.treeview)
77
        self.treeview.show()
78
79
        cell = gtk.CellRendererText()
80
        cell.set_property("width-chars", 20)
81
        column = gtk.TreeViewColumn()
82
        column.pack_start(cell, expand=True)
83
        column.add_attribute(cell, "text", 0)
84
        self.treeview.append_column(column)
85
75 by Jelmer Vernooij
Use HPaned rather than HBox so long filenames can be viewed (fixes #56993).
86
        # The diffs of the  selected file: a scrollable source or
87
        # text view
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
88
        scrollwin = gtk.ScrolledWindow()
89
        scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
90
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
75 by Jelmer Vernooij
Use HPaned rather than HBox so long filenames can be viewed (fixes #56993).
91
        pane.pack2(scrollwin)
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
92
        scrollwin.show()
93
94
        if have_gtksourceview:
95
            self.buffer = gtksourceview.SourceBuffer()
96
            slm = gtksourceview.SourceLanguagesManager()
97
            gsl = slm.get_language_from_mime_type("text/x-patch")
232.1.1 by Adeodato Simó
Read ~/.colordiffrc to set colors in the diff window.
98
            self.apply_colordiffrc(gsl)
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
99
            self.buffer.set_language(gsl)
100
            self.buffer.set_highlight(True)
101
102
            sourceview = gtksourceview.SourceView(self.buffer)
103
        else:
104
            self.buffer = gtk.TextBuffer()
105
            sourceview = gtk.TextView(self.buffer)
106
107
        sourceview.set_editable(False)
108
        sourceview.modify_font(pango.FontDescription("Monospace"))
109
        scrollwin.add(sourceview)
110
        sourceview.show()
111
51 by Jelmer Vernooij
Rework some of the parameters to DiffWindow.set_diff() to be
112
    def set_diff(self, description, rev_tree, parent_tree):
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
113
        """Set the differences showed by this window.
114
115
        Compares the two trees and populates the window with the
116
        differences.
117
        """
51 by Jelmer Vernooij
Rework some of the parameters to DiffWindow.set_diff() to be
118
        self.rev_tree = rev_tree
119
        self.parent_tree = parent_tree
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
120
121
        self.model.clear()
77 by Jelmer Vernooij
Prepare for 0.10.0
122
        delta = self.rev_tree.changes_from(self.parent_tree)
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
123
11 by Scott James Remnant
Add a default "Complete Diff" option to the top of the diff window
124
        self.model.append(None, [ "Complete Diff", "" ])
125
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
126
        if len(delta.added):
127
            titer = self.model.append(None, [ "Added", None ])
128
            for path, id, kind in delta.added:
129
                self.model.append(titer, [ path, path ])
130
131
        if len(delta.removed):
132
            titer = self.model.append(None, [ "Removed", None ])
133
            for path, id, kind in delta.removed:
134
                self.model.append(titer, [ path, path ])
135
136
        if len(delta.renamed):
137
            titer = self.model.append(None, [ "Renamed", None ])
138
            for oldpath, newpath, id, kind, text_modified, meta_modified \
139
                    in delta.renamed:
63 by Aaron Bentley
Accept either side of a rename for DiffWindow.set_file
140
                self.model.append(titer, [ oldpath, newpath ])
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
141
142
        if len(delta.modified):
143
            titer = self.model.append(None, [ "Modified", None ])
144
            for path, id, kind, text_modified, meta_modified in delta.modified:
145
                self.model.append(titer, [ path, path ])
146
147
        self.treeview.expand_all()
51 by Jelmer Vernooij
Rework some of the parameters to DiffWindow.set_diff() to be
148
        self.set_title(description + " - bzrk diff")
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
149
59.2.3 by Aaron Bentley
Gannotate-launched diffs now jump to correct file
150
    def set_file(self, file_path):
151
        tv_path = None
152
        for data in self.model:
153
            for child in data.iterchildren():
63 by Aaron Bentley
Accept either side of a rename for DiffWindow.set_file
154
                if child[0] == file_path or child[1] == file_path:
59.2.3 by Aaron Bentley
Gannotate-launched diffs now jump to correct file
155
                    tv_path = child.path
156
                    break
59.2.4 by Aaron Bentley
Teach gdiff to accept a single file argument
157
        if tv_path is None:
158
            raise NoSuchFile(file_path)
59.2.3 by Aaron Bentley
Gannotate-launched diffs now jump to correct file
159
        self.treeview.set_cursor(tv_path)
64 by Aaron Bentley
Scroll to the appropriate cell when file selected
160
        self.treeview.scroll_to_cell(tv_path)
59.2.3 by Aaron Bentley
Gannotate-launched diffs now jump to correct file
161
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
162
    def _treeview_cursor_cb(self, *args):
163
        """Callback for when the treeview cursor changes."""
164
        (path, col) = self.treeview.get_cursor()
11 by Scott James Remnant
Add a default "Complete Diff" option to the top of the diff window
165
        specific_files = [ self.model[path][1] ]
166
        if specific_files == [ None ]:
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
167
            return
11 by Scott James Remnant
Add a default "Complete Diff" option to the top of the diff window
168
        elif specific_files == [ "" ]:
169
            specific_files = []
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
170
171
        s = StringIO()
11 by Scott James Remnant
Add a default "Complete Diff" option to the top of the diff window
172
        show_diff_trees(self.parent_tree, self.rev_tree, s, specific_files)
66.2.10 by Jelmer Vernooij
Merge some fixes from Alexander Belchenko
173
        self.buffer.set_text(s.getvalue().decode(sys.getdefaultencoding(), 'replace'))
232.1.1 by Adeodato Simó
Read ~/.colordiffrc to set colors in the diff window.
174
175
    @staticmethod
176
    def apply_colordiffrc(lang):
177
        """Set style colors in lang to that specified in colordiff config file.
178
179
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
180
181
        :param lang: a gtksourceview.SourceLanguage object.
182
        """
183
        def parse_colordiffrc(fileobj):
184
            """Parse fileobj as a colordiff configuration file.
185
            
186
            :return: A dict with the key -> value pairs.
187
            """
188
            colors = {}
189
            for line in fileobj:
190
                if re.match(r'^\s*#', line):
191
                    continue
192
                key, val = line.split('=')
193
                colors[key.strip()] = val.strip()
194
            return colors
195
196
        colors = {}
197
198
        for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
199
            f = os.path.expanduser(f)
200
            if os.path.exists(f):
201
                try:
202
                    f = file(f)
203
                except IOError, e:
204
                    warning('could not open file %s: %s' % (f, str(e)))
205
                else:
206
                    colors.update(parse_colordiffrc(f))
207
                    f.close()
208
209
        if not colors:
210
            # ~/.colordiffrc does not exist
211
            return
212
213
        mapping = {
214
                # map GtkSourceView tags to colordiff names
215
                # since GSV is richer, accept new names for extra bits,
216
                # defaulting to old names if they're not present
217
                'Added@32@line': ['newtext'],
218
                'Removed@32@line': ['oldtext'],
219
                'Location': ['location', 'diffstuff'],
220
                'Diff@32@file': ['file', 'diffstuff'],
221
                'Special@32@case': ['specialcase', 'diffstuff'],
222
        }
223
224
        for tag in lang.get_tags():
225
            tag_id = tag.get_id()
226
            keys = mapping.get(tag_id, [])
227
            color = None
228
229
            for key in keys:
230
                color = colors.get(key, None)
231
                if color is not None:
232
                    break
233
234
            if color is None:
235
                continue
236
237
            style = gtksourceview.SourceTagStyle()
238
            try:
239
                style.foreground = gtk.gdk.color_parse(color)
240
            except ValueError:
241
                warning('not a valid color: %s' % color)
242
            else:
243
                lang.set_tag_style(tag_id, style)