/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to viz/diffwin.py

  • Committer: Jelmer Vernooij
  • Date: 2006-05-19 16:37:13 UTC
  • Revision ID: jelmer@samba.org-20060519163713-be77b31c72cbc7e8
Move visualisation code to a separate directory, preparing for bundling 
the GTK+ plugins for bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
 
15
15
import gtk
16
16
import pango
17
 
import os
18
 
import re
19
 
import sys
20
17
 
21
18
try:
22
19
    import gtksourceview
23
20
    have_gtksourceview = True
24
21
except ImportError:
25
22
    have_gtksourceview = False
26
 
try:
27
 
    import gconf
28
 
    have_gconf = True
29
 
except ImportError:
30
 
    have_gconf = False
31
 
 
32
 
import bzrlib
33
 
 
 
23
 
 
24
from bzrlib.delta import compare_trees
34
25
from bzrlib.diff import show_diff_trees
35
 
from bzrlib.errors import NoSuchFile
36
 
from bzrlib.trace import warning
37
26
 
38
27
 
39
28
class DiffWindow(gtk.Window):
43
32
    differences between two revisions on a branch.
44
33
    """
45
34
 
46
 
    def __init__(self):
 
35
    def __init__(self, app=None):
47
36
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
48
37
        self.set_border_width(0)
49
38
        self.set_title("bzrk diff")
50
39
 
 
40
        self.app = app
 
41
 
51
42
        # Use two thirds of the screen by default
52
43
        screen = self.get_screen()
53
44
        monitor = screen.get_monitor_geometry(0)
59
50
 
60
51
    def construct(self):
61
52
        """Construct the window contents."""
62
 
        # The   window  consists  of   a  pane   containing:  the
63
 
        # hierarchical list  of files on  the left, and  the diff
64
 
        # for the currently selected file on the right.
65
 
        pane = gtk.HPaned()
66
 
        self.add(pane)
67
 
        pane.show()
 
53
        hbox = gtk.HBox(spacing=6)
 
54
        hbox.set_border_width(12)
 
55
        self.add(hbox)
 
56
        hbox.show()
68
57
 
69
 
        # The file hierarchy: a scrollable treeview
70
58
        scrollwin = gtk.ScrolledWindow()
71
59
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
72
60
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
73
 
        pane.pack1(scrollwin)
 
61
        hbox.pack_start(scrollwin, expand=False, fill=True)
74
62
        scrollwin.show()
75
63
 
76
64
        self.model = gtk.TreeStore(str, str)
88
76
        column.add_attribute(cell, "text", 0)
89
77
        self.treeview.append_column(column)
90
78
 
91
 
        # The diffs of the  selected file: a scrollable source or
92
 
        # text view
 
79
 
93
80
        scrollwin = gtk.ScrolledWindow()
94
81
        scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
95
82
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
96
 
        pane.pack2(scrollwin)
 
83
        hbox.pack_start(scrollwin, expand=True, fill=True)
97
84
        scrollwin.show()
98
85
 
99
86
        if have_gtksourceview:
100
87
            self.buffer = gtksourceview.SourceBuffer()
101
88
            slm = gtksourceview.SourceLanguagesManager()
102
89
            gsl = slm.get_language_from_mime_type("text/x-patch")
103
 
            if have_gconf:
104
 
                self.apply_gedit_colors(gsl)
105
 
            self.apply_colordiff_colors(gsl)
106
90
            self.buffer.set_language(gsl)
107
91
            self.buffer.set_highlight(True)
108
92
 
116
100
        scrollwin.add(sourceview)
117
101
        sourceview.show()
118
102
 
119
 
    def set_diff(self, description, rev_tree, parent_tree):
 
103
    def set_diff(self, branch, revid, parentid):
120
104
        """Set the differences showed by this window.
121
105
 
122
106
        Compares the two trees and populates the window with the
123
107
        differences.
124
108
        """
125
 
        self.rev_tree = rev_tree
126
 
        self.parent_tree = parent_tree
 
109
        self.rev_tree = branch.repository.revision_tree(revid)
 
110
        self.parent_tree = branch.repository.revision_tree(parentid)
127
111
 
128
112
        self.model.clear()
129
 
        delta = self.rev_tree.changes_from(self.parent_tree)
 
113
        delta = compare_trees(self.parent_tree, self.rev_tree)
130
114
 
131
115
        self.model.append(None, [ "Complete Diff", "" ])
132
116
 
144
128
            titer = self.model.append(None, [ "Renamed", None ])
145
129
            for oldpath, newpath, id, kind, text_modified, meta_modified \
146
130
                    in delta.renamed:
147
 
                self.model.append(titer, [ oldpath, newpath ])
 
131
                self.model.append(titer, [ oldpath, oldpath ])
148
132
 
149
133
        if len(delta.modified):
150
134
            titer = self.model.append(None, [ "Modified", None ])
152
136
                self.model.append(titer, [ path, path ])
153
137
 
154
138
        self.treeview.expand_all()
155
 
        self.set_title(description + " - bzrk diff")
156
 
 
157
 
    def set_file(self, file_path):
158
 
        tv_path = None
159
 
        for data in self.model:
160
 
            for child in data.iterchildren():
161
 
                if child[0] == file_path or child[1] == file_path:
162
 
                    tv_path = child.path
163
 
                    break
164
 
        if tv_path is None:
165
 
            raise NoSuchFile(file_path)
166
 
        self.treeview.set_cursor(tv_path)
167
 
        self.treeview.scroll_to_cell(tv_path)
 
139
        self.set_title(revid + " - " + branch.nick + " - bzrk diff")
168
140
 
169
141
    def _treeview_cursor_cb(self, *args):
170
142
        """Callback for when the treeview cursor changes."""
177
149
 
178
150
        s = StringIO()
179
151
        show_diff_trees(self.parent_tree, self.rev_tree, s, specific_files)
180
 
        self.buffer.set_text(s.getvalue().decode(sys.getdefaultencoding(), 'replace'))
181
 
 
182
 
    @staticmethod
183
 
    def apply_gedit_colors(lang):
184
 
        """Set style for lang to that specified in gedit configuration.
185
 
 
186
 
        This method needs the gconf module.
187
 
        
188
 
        :param lang: a gtksourceview.SourceLanguage object.
189
 
        """
190
 
        GEDIT_SYNTAX_PATH = '/apps/gedit-2/preferences/syntax_highlighting'
191
 
        GEDIT_LANG_PATH = GEDIT_SYNTAX_PATH + '/' + lang.get_id()
192
 
 
193
 
        client = gconf.client_get_default()
194
 
        client.add_dir(GEDIT_LANG_PATH, gconf.CLIENT_PRELOAD_NONE)
195
 
 
196
 
        for tag in lang.get_tags():
197
 
            tag_id = tag.get_id()
198
 
            gconf_key = GEDIT_LANG_PATH + '/' + tag_id
199
 
            style_string = client.get_string(gconf_key)
200
 
 
201
 
            if style_string is None:
202
 
                continue
203
 
 
204
 
            # function to get a bool from a string that's either '0' or '1'
205
 
            string_bool = lambda x: bool(int(x))
206
 
 
207
 
            # style_string is a string like "2/#FFCCAA/#000000/0/1/0/0"
208
 
            # values are: mask, fg, bg, italic, bold, underline, strike
209
 
            # this packs them into (str_value, attr_name, conv_func) tuples
210
 
            items = zip(style_string.split('/'), ['mask', 'foreground',
211
 
                'background', 'italic', 'bold', 'underline', 'strikethrough' ],
212
 
                [ int, gtk.gdk.color_parse, gtk.gdk.color_parse, string_bool,
213
 
                    string_bool, string_bool, string_bool ]
214
 
            )
215
 
 
216
 
            style = gtksourceview.SourceTagStyle()
217
 
 
218
 
            # XXX The mask attribute controls whether the present values of
219
 
            # foreground and background color should in fact be used. Ideally
220
 
            # (and that's what gedit does), one could set all three attributes,
221
 
            # and let the TagStyle object figure out which colors to use.
222
 
            # However, in the GtkSourceview python bindings, the mask attribute
223
 
            # is read-only, and it's derived instead from the colors being
224
 
            # set or not. This means that we have to sometimes refrain from
225
 
            # setting fg or bg colors, depending on the value of the mask.
226
 
            # This code could go away if mask were writable.
227
 
            mask = int(items[0][0])
228
 
            if not (mask & 1): # GTK_SOURCE_TAG_STYLE_USE_BACKGROUND
229
 
                items[2:3] = []
230
 
            if not (mask & 2): # GTK_SOURCE_TAG_STYLE_USE_FOREGROUND
231
 
                items[1:2] = []
232
 
            items[0:1] = [] # skip the mask unconditionally
233
 
 
234
 
            for value, attr, func in items:
235
 
                try:
236
 
                    value = func(value)
237
 
                except ValueError:
238
 
                    warning('gconf key %s contains an invalid value: %s'
239
 
                            % gconf_key, value)
240
 
                else:
241
 
                    setattr(style, attr, value)
242
 
 
243
 
            lang.set_tag_style(tag_id, style)
244
 
 
245
 
    @staticmethod
246
 
    def apply_colordiff_colors(lang):
247
 
        """Set style colors for lang using the colordiff configuration file.
248
 
 
249
 
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
250
 
 
251
 
        :param lang: a "Diff" gtksourceview.SourceLanguage object.
252
 
        """
253
 
        def parse_colordiffrc(fileobj):
254
 
            """Parse fileobj as a colordiff configuration file.
255
 
            
256
 
            :return: A dict with the key -> value pairs.
257
 
            """
258
 
            colors = {}
259
 
            for line in fileobj:
260
 
                if re.match(r'^\s*#', line):
261
 
                    continue
262
 
                key, val = line.split('=')
263
 
                colors[key.strip()] = val.strip()
264
 
            return colors
265
 
 
266
 
        colors = {}
267
 
 
268
 
        for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
269
 
            f = os.path.expanduser(f)
270
 
            if os.path.exists(f):
271
 
                try:
272
 
                    f = file(f)
273
 
                except IOError, e:
274
 
                    warning('could not open file %s: %s' % (f, str(e)))
275
 
                else:
276
 
                    colors.update(parse_colordiffrc(f))
277
 
                    f.close()
278
 
 
279
 
        if not colors:
280
 
            # ~/.colordiffrc does not exist
281
 
            return
282
 
 
283
 
        mapping = {
284
 
                # map GtkSourceView tags to colordiff names
285
 
                # since GSV is richer, accept new names for extra bits,
286
 
                # defaulting to old names if they're not present
287
 
                'Added@32@line': ['newtext'],
288
 
                'Removed@32@line': ['oldtext'],
289
 
                'Location': ['location', 'diffstuff'],
290
 
                'Diff@32@file': ['file', 'diffstuff'],
291
 
                'Special@32@case': ['specialcase', 'diffstuff'],
292
 
        }
293
 
 
294
 
        for tag in lang.get_tags():
295
 
            tag_id = tag.get_id()
296
 
            keys = mapping.get(tag_id, [])
297
 
            color = None
298
 
 
299
 
            for key in keys:
300
 
                color = colors.get(key, None)
301
 
                if color is not None:
302
 
                    break
303
 
 
304
 
            if color is None:
305
 
                continue
306
 
 
307
 
            style = gtksourceview.SourceTagStyle()
308
 
            try:
309
 
                style.foreground = gtk.gdk.color_parse(color)
310
 
            except ValueError:
311
 
                warning('not a valid color: %s' % color)
312
 
            else:
313
 
                lang.set_tag_style(tag_id, style)
 
152
        self.buffer.set_text(s.getvalue())