/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 diff.py

  • Committer: Mateusz Korniak
  • Date: 2007-07-21 13:16:33 UTC
  • mto: This revision was merged to the branch mainline in revision 248.
  • Revision ID: matkor@laptop-hp-20070721131633-t40kxs20j1q2fvvc
Context menu "Remove and delete added"
Acts like "Remove" but also deletes file locally.

Show diffs side-by-side

added added

removed removed

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