/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: Jelmer Vernooij
  • Date: 2011-04-10 18:44:39 UTC
  • mto: This revision was merged to the branch mainline in revision 730.
  • Revision ID: jelmer@samba.org-20110410184439-g7hqaacexqtviq13
Move i18n support to a separate file, so gettext files aren't loaded unless bzr-gtk is used.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
"""
6
6
 
7
7
__copyright__ = "Copyright 2005 Canonical Ltd."
8
 
__author__ = "Scott James Remnant <scott@ubuntu.com>"
 
8
__author__    = "Scott James Remnant <scott@ubuntu.com>"
9
9
 
10
10
 
11
11
from cStringIO import StringIO
12
12
 
 
13
import pygtk
 
14
pygtk.require("2.0")
 
15
import gtk
 
16
import pango
13
17
import os
14
18
import re
15
19
import sys
16
20
import inspect
17
21
try:
18
 
    from xml.etree.ElementTree import (
19
 
        Element,
20
 
        SubElement,
21
 
        tostring,
22
 
        )
23
 
    Element, SubElement, tostring  # Hush PEP8 redefinition.
 
22
    from xml.etree.ElementTree import Element, SubElement, tostring
24
23
except ImportError:
25
 
    from elementtree.ElementTree import (
26
 
        Element,
27
 
        SubElement,
28
 
        tostring,
29
 
        )
 
24
    from elementtree.ElementTree import Element, SubElement, tostring
30
25
 
31
 
from gi.repository import Gtk
32
 
from gi.repository import Pango
33
26
try:
34
 
    from gi.repository import GtkSource
 
27
    import gtksourceview2
35
28
    have_gtksourceview = True
36
29
except ImportError:
37
30
    have_gtksourceview = False
38
31
try:
39
 
    from gi.repository import GConf
 
32
    import gconf
40
33
    have_gconf = True
41
34
except ImportError:
42
35
    have_gconf = False
47
40
    osutils,
48
41
    urlutils,
49
42
    workingtree,
50
 
    )
 
43
)
51
44
from bzrlib.diff import show_diff_trees
52
45
from bzrlib.patches import parse_patches
53
46
from bzrlib.trace import warning
73
66
    pass
74
67
 
75
68
 
76
 
class DiffFileView(Gtk.ScrolledWindow):
 
69
class DiffFileView(gtk.ScrolledWindow):
77
70
    """Window for displaying diffs from a diff file"""
78
71
 
79
72
    def __init__(self):
80
 
        super(DiffFileView, self).__init__()
 
73
        gtk.ScrolledWindow.__init__(self)
81
74
        self.construct()
82
75
        self._diffs = {}
83
76
 
84
77
    def construct(self):
85
 
        self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
86
 
        self.set_shadow_type(Gtk.ShadowType.IN)
 
78
        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
79
        self.set_shadow_type(gtk.SHADOW_IN)
87
80
 
88
81
        if have_gtksourceview:
89
 
            self.buffer = GtkSource.Buffer()
90
 
            lang_manager = GtkSource.LanguageManager.get_default()
91
 
            language = lang_manager.guess_language(None, "text/x-patch")
 
82
            self.buffer = gtksourceview2.Buffer()
 
83
            slm = gtksourceview2.LanguageManager()
 
84
            guess_language = getattr(gtksourceview2.LanguageManager, 
 
85
                "guess_language", fallback_guess_language)
 
86
            gsl = guess_language(slm, content_type="text/x-patch")
92
87
            if have_gconf:
93
88
                self.apply_gedit_colors(self.buffer)
94
89
            self.apply_colordiff_colors(self.buffer)
95
 
            self.buffer.set_language(language)
 
90
            self.buffer.set_language(gsl)
96
91
            self.buffer.set_highlight_syntax(True)
97
92
 
98
 
            self.sourceview = GtkSource.View(buffer=self.buffer)
 
93
            self.sourceview = gtksourceview2.View(self.buffer)
99
94
        else:
100
 
            self.buffer = Gtk.TextBuffer()
101
 
            self.sourceview = Gtk.TextView(self.buffer)
 
95
            self.buffer = gtk.TextBuffer()
 
96
            self.sourceview = gtk.TextView(self.buffer)
102
97
 
103
98
        self.sourceview.set_editable(False)
104
 
        self.sourceview.modify_font(Pango.FontDescription("Monospace"))
 
99
        self.sourceview.modify_font(pango.FontDescription("Monospace"))
105
100
        self.add(self.sourceview)
106
101
        self.sourceview.show()
107
102
 
111
106
 
112
107
        This method needs the gconf module.
113
108
 
114
 
        :param buf: a GtkSource.Buffer object.
 
109
        :param buf: a gtksourceview2.Buffer object.
115
110
        """
116
111
        GEDIT_SCHEME_PATH = '/apps/gedit-2/preferences/editor/colors/scheme'
117
112
        GEDIT_USER_STYLES_PATH = os.path.expanduser('~/.gnome2/gedit/styles')
118
113
 
119
 
        client = GConf.Client.get_default()
 
114
        client = gconf.client_get_default()
120
115
        style_scheme_name = client.get_string(GEDIT_SCHEME_PATH)
121
116
        if style_scheme_name is not None:
122
 
            style_scheme_mgr = GtkSource.StyleSchemeManager()
 
117
            style_scheme_mgr = gtksourceview2.StyleSchemeManager()
123
118
            style_scheme_mgr.append_search_path(GEDIT_USER_STYLES_PATH)
124
 
 
 
119
            
125
120
            style_scheme = style_scheme_mgr.get_scheme(style_scheme_name)
126
 
 
 
121
            
127
122
            if style_scheme is not None:
128
123
                buf.set_style_scheme(style_scheme)
129
124
 
133
128
 
134
129
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
135
130
 
136
 
        :param buf: a "Diff" GtkSource.Buffer object.
 
131
        :param buf: a "Diff" gtksourceview2.Buffer object.
137
132
        """
138
 
        scheme_manager = GtkSource.StyleSchemeManager()
 
133
        scheme_manager = gtksourceview2.StyleSchemeManager()
139
134
        style_scheme = scheme_manager.get_scheme('colordiff')
140
 
 
 
135
        
141
136
        # if style scheme not found, we'll generate it from colordiffrc
142
137
        # TODO: reload if colordiffrc has changed.
143
138
        if style_scheme is None:
157
152
            if not colors:
158
153
                # ~/.colordiffrc does not exist
159
154
                return
160
 
 
 
155
            
161
156
            mapping = {
162
157
                # map GtkSourceView2 scheme styles to colordiff names
163
158
                # since GSV is richer, accept new names for extra bits,
168
163
                'diff:file': ['file', 'diffstuff'],
169
164
                'diff:special-case': ['specialcase', 'diffstuff'],
170
165
            }
171
 
 
 
166
            
172
167
            converted_colors = {}
173
168
            for name, values in mapping.items():
174
169
                color = None
179
174
                if color is None:
180
175
                    continue
181
176
                converted_colors[name] = color
182
 
 
 
177
            
183
178
            # some xml magic to produce needed style scheme description
184
179
            e_style_scheme = Element('style-scheme')
185
180
            e_style_scheme.set('id', 'colordiff')
189
184
                style = SubElement(e_style_scheme, 'style')
190
185
                style.set('name', name)
191
186
                style.set('foreground', '#%s' % color)
192
 
 
 
187
            
193
188
            scheme_xml = tostring(e_style_scheme, 'UTF-8')
194
 
            if not os.path.exists(os.path.expanduser(
195
 
                '~/.local/share/gtksourceview-2.0/styles')):
196
 
                os.makedirs(os.path.expanduser(
197
 
                    '~/.local/share/gtksourceview-2.0/styles'))
198
 
            file(os.path.expanduser(
199
 
                '~/.local/share/gtksourceview-2.0/styles/colordiff.xml'),
200
 
                'w').write(scheme_xml)
201
 
 
 
189
            if not os.path.exists(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles')):
 
190
                os.makedirs(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles'))
 
191
            file(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles/colordiff.xml'), 'w').write(scheme_xml)
 
192
            
202
193
            scheme_manager.force_rescan()
203
194
            style_scheme = scheme_manager.get_scheme('colordiff')
204
 
 
 
195
        
205
196
        buf.set_style_scheme(style_scheme)
206
197
 
207
198
    @staticmethod
229
220
#        self.parent_tree.lock_read()
230
221
#        self.rev_tree.lock_read()
231
222
#        try:
232
 
#            self.delta = iter_changes_to_status(
233
 
#               self.parent_tree, self.rev_tree)
 
223
#            self.delta = iter_changes_to_status(self.parent_tree, self.rev_tree)
234
224
#            self.path_to_status = {}
235
225
#            self.path_to_diff = {}
236
226
#            source_inv = self.parent_tree.inventory
237
227
#            target_inv = self.rev_tree.inventory
238
228
#            for (file_id, real_path, change_type, display_path) in self.delta:
239
 
#                self.path_to_status[real_path] = u'=== %s %s' % (
240
 
#                    change_type, display_path)
 
229
#                self.path_to_status[real_path] = u'=== %s %s' % (change_type, display_path)
241
230
#                if change_type in ('modified', 'renamed and modified'):
242
231
#                    source_ie = source_inv[file_id]
243
232
#                    target_ie = target_inv[file_id]
245
234
#                    source_ie.diff(internal_diff, *old path, *old_tree,
246
235
#                                   *new_path, target_ie, self.rev_tree,
247
236
#                                   sio)
248
 
#                    self.path_to_diff[real_path] =
 
237
#                    self.path_to_diff[real_path] = 
249
238
#
250
239
#        finally:
251
240
#            self.rev_tree.unlock()
265
254
    """This is the soft and chewy filling for a DiffWindow."""
266
255
 
267
256
    def __init__(self):
268
 
        super(DiffView, self).__init__()
 
257
        DiffFileView.__init__(self)
269
258
        self.rev_tree = None
270
259
        self.parent_tree = None
271
260
 
295
284
        self.buffer.set_text(decoded.encode('UTF-8'))
296
285
 
297
286
 
298
 
class DiffWidget(Gtk.HPaned):
 
287
class DiffWidget(gtk.HPaned):
299
288
    """Diff widget
300
289
 
301
290
    """
302
 
 
303
 
    SHOW_WIDGETS = True
304
 
 
305
291
    def __init__(self):
306
292
        super(DiffWidget, self).__init__()
307
293
 
308
294
        # The file hierarchy: a scrollable treeview
309
 
        scrollwin = Gtk.ScrolledWindow()
310
 
        scrollwin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
311
 
        scrollwin.set_shadow_type(Gtk.ShadowType.IN)
 
295
        scrollwin = gtk.ScrolledWindow()
 
296
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
297
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
312
298
        self.pack1(scrollwin)
313
 
        if self.SHOW_WIDGETS:
314
 
            scrollwin.show()
315
 
 
316
 
        self.model = Gtk.TreeStore(str, str)
317
 
        self.treeview = Gtk.TreeView(model=self.model)
 
299
        scrollwin.show()
 
300
        
 
301
        self.model = gtk.TreeStore(str, str)
 
302
        self.treeview = gtk.TreeView(self.model)
318
303
        self.treeview.set_headers_visible(False)
319
304
        self.treeview.set_search_column(1)
320
305
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
321
306
        scrollwin.add(self.treeview)
322
 
        if self.SHOW_WIDGETS:
323
 
            self.treeview.show()
 
307
        self.treeview.show()
324
308
 
325
 
        cell = Gtk.CellRendererText()
 
309
        cell = gtk.CellRendererText()
326
310
        cell.set_property("width-chars", 20)
327
 
        column = Gtk.TreeViewColumn()
328
 
        column.pack_start(cell, True)
 
311
        column = gtk.TreeViewColumn()
 
312
        column.pack_start(cell, expand=True)
329
313
        column.add_attribute(cell, "text", 0)
330
314
        self.treeview.append_column(column)
331
315
 
341
325
        if getattr(self, 'diff_view', None) is None:
342
326
            self.diff_view = DiffFileView()
343
327
            self.pack2(self.diff_view)
344
 
        if self.SHOW_WIDGETS:
345
 
            self.diff_view.show()
 
328
        self.diff_view.show()
346
329
        for oldname, newname, patch in sections:
347
330
            self.diff_view._diffs[newname] = str(patch)
348
331
            if newname is None:
359
342
        if getattr(self, 'diff_view', None) is None:
360
343
            self.diff_view = DiffView()
361
344
            self.pack2(self.diff_view)
362
 
        if self.SHOW_WIDGETS:
363
 
            self.diff_view.show()
 
345
        self.diff_view.show()
364
346
        self.diff_view.set_trees(rev_tree, parent_tree)
365
347
        self.rev_tree = rev_tree
366
348
        self.parent_tree = parent_tree
368
350
        self.model.clear()
369
351
        delta = self.rev_tree.changes_from(self.parent_tree)
370
352
 
371
 
        self.model.append(None, ["Complete Diff", ""])
 
353
        self.model.append(None, [ "Complete Diff", "" ])
372
354
 
373
355
        if len(delta.added):
374
 
            titer = self.model.append(None, ["Added", None])
 
356
            titer = self.model.append(None, [ "Added", None ])
375
357
            for path, id, kind in delta.added:
376
 
                self.model.append(titer, [path, path])
 
358
                self.model.append(titer, [ path, path ])
377
359
 
378
360
        if len(delta.removed):
379
 
            titer = self.model.append(None, ["Removed", None])
 
361
            titer = self.model.append(None, [ "Removed", None ])
380
362
            for path, id, kind in delta.removed:
381
 
                self.model.append(titer, [path, path])
 
363
                self.model.append(titer, [ path, path ])
382
364
 
383
365
        if len(delta.renamed):
384
 
            titer = self.model.append(None, ["Renamed", None])
 
366
            titer = self.model.append(None, [ "Renamed", None ])
385
367
            for oldpath, newpath, id, kind, text_modified, meta_modified \
386
368
                    in delta.renamed:
387
 
                self.model.append(titer, [oldpath, newpath])
 
369
                self.model.append(titer, [ oldpath, newpath ])
388
370
 
389
371
        if len(delta.modified):
390
 
            titer = self.model.append(None, ["Modified", None])
 
372
            titer = self.model.append(None, [ "Modified", None ])
391
373
            for path, id, kind, text_modified, meta_modified in delta.modified:
392
 
                self.model.append(titer, [path, path])
 
374
                self.model.append(titer, [ path, path ])
393
375
 
394
376
        self.treeview.expand_all()
395
377
        self.diff_view.show_diff(None)
404
386
                    break
405
387
        if tv_path is None:
406
388
            raise errors.NoSuchFile(file_path)
407
 
        self.treeview.set_cursor(tv_path, None, False)
 
389
        self.treeview.set_cursor(tv_path)
408
390
        self.treeview.scroll_to_cell(tv_path)
409
391
 
410
392
    def _treeview_cursor_cb(self, *args):
411
393
        """Callback for when the treeview cursor changes."""
412
394
        (path, col) = self.treeview.get_cursor()
413
 
        if path is None:
414
 
            return
415
 
        specific_files = [self.model[path][1]]
416
 
        if specific_files == [None]:
417
 
            return
418
 
        elif specific_files == [""]:
 
395
        specific_files = [ self.model[path][1] ]
 
396
        if specific_files == [ None ]:
 
397
            return
 
398
        elif specific_files == [ "" ]:
419
399
            specific_files = None
420
 
 
 
400
        
421
401
        self.diff_view.show_diff(specific_files)
422
 
 
 
402
    
423
403
    def _on_wraplines_toggled(self, widget=None, wrap=False):
424
404
        """Callback for when the wrap lines checkbutton is toggled"""
425
405
        if wrap or widget.get_active():
426
 
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.WORD)
 
406
            self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_WORD)
427
407
        else:
428
 
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.NONE)
429
 
 
 
408
            self.diff_view.sourceview.set_wrap_mode(gtk.WRAP_NONE)
430
409
 
431
410
class DiffWindow(Window):
432
411
    """Diff window.
436
415
    """
437
416
 
438
417
    def __init__(self, parent=None, operations=None):
439
 
        super(DiffWindow, self).__init__(parent=parent)
 
418
        Window.__init__(self, parent)
440
419
        self.set_border_width(0)
441
420
        self.set_title("bzrk diff")
442
421
 
450
429
 
451
430
    def construct(self, operations):
452
431
        """Construct the window contents."""
453
 
        self.vbox = Gtk.VBox()
 
432
        self.vbox = gtk.VBox()
454
433
        self.add(self.vbox)
455
434
        self.vbox.show()
456
435
        self.diff = DiffWidget()
462
441
        hbox = self._get_button_bar(operations)
463
442
        if hbox is not None:
464
443
            self.vbox.pack_start(hbox, False, True, 0)
465
 
 
 
444
        
 
445
    
466
446
    def _get_menu_bar(self):
467
 
        menubar = Gtk.MenuBar()
 
447
        menubar = gtk.MenuBar()
468
448
        # View menu
469
 
        mb_view = Gtk.MenuItem.new_with_mnemonic(_i18n("_View"))
470
 
        mb_view_menu = Gtk.Menu()
471
 
        mb_view_wrapsource = Gtk.CheckMenuItem.new_with_mnemonic(
472
 
            _i18n("Wrap _Long Lines"))
 
449
        mb_view = gtk.MenuItem(_i18n("_View"))
 
450
        mb_view_menu = gtk.Menu()
 
451
        mb_view_wrapsource = gtk.CheckMenuItem(_i18n("Wrap _Long Lines"))
473
452
        mb_view_wrapsource.connect('activate', self.diff._on_wraplines_toggled)
474
453
        mb_view_wrapsource.show()
475
454
        mb_view_menu.append(mb_view_wrapsource)
479
458
        menubar.append(mb_view)
480
459
        menubar.show()
481
460
        return menubar
482
 
 
 
461
    
483
462
    def _get_button_bar(self, operations):
484
463
        """Return a button bar to use.
485
464
 
487
466
        """
488
467
        if operations is None:
489
468
            return None
490
 
        hbox = Gtk.HButtonBox()
491
 
        hbox.set_layout(Gtk.ButtonBoxStyle.START)
 
469
        hbox = gtk.HButtonBox()
 
470
        hbox.set_layout(gtk.BUTTONBOX_START)
492
471
        for title, method in operations:
493
 
            merge_button = Gtk.Button(title)
 
472
            merge_button = gtk.Button(title)
494
473
            merge_button.show()
495
 
            merge_button.set_relief(Gtk.ReliefStyle.NONE)
 
474
            merge_button.set_relief(gtk.RELIEF_NONE)
496
475
            merge_button.connect("clicked", method)
497
476
            hbox.pack_start(merge_button, expand=False, fill=True)
498
477
        hbox.show()
499
478
        return hbox
500
479
 
501
480
    def _get_merge_target(self):
502
 
        d = Gtk.FileChooserDialog('Merge branch', self,
503
 
                                  Gtk.FileChooserAction.SELECT_FOLDER,
504
 
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
505
 
                                           Gtk.STOCK_CANCEL,
506
 
                                           Gtk.ResponseType.CANCEL,))
 
481
        d = gtk.FileChooserDialog('Merge branch', self,
 
482
                                  gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
 
483
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
 
484
                                           gtk.STOCK_CANCEL,
 
485
                                           gtk.RESPONSE_CANCEL,))
507
486
        try:
508
487
            result = d.run()
509
 
            if result != Gtk.ResponseType.OK:
 
488
            if result != gtk.RESPONSE_OK:
510
489
                raise SelectCancelled()
511
490
            return d.get_current_folder_uri()
512
491
        finally:
526
505
        error_dialog('Error', str(e))
527
506
 
528
507
    def _get_save_path(self, basename):
529
 
        d = Gtk.FileChooserDialog('Save As', self,
530
 
                                  Gtk.FileChooserAction.SAVE,
531
 
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
532
 
                                           Gtk.STOCK_CANCEL,
533
 
                                           Gtk.ResponseType.CANCEL,))
 
508
        d = gtk.FileChooserDialog('Save As', self,
 
509
                                  gtk.FILE_CHOOSER_ACTION_SAVE,
 
510
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
 
511
                                           gtk.STOCK_CANCEL,
 
512
                                           gtk.RESPONSE_CANCEL,))
534
513
        d.set_current_name(basename)
535
514
        try:
536
515
            result = d.run()
537
 
            if result != Gtk.ResponseType.OK:
 
516
            if result != gtk.RESPONSE_OK:
538
517
                raise SelectCancelled()
539
518
            return urlutils.local_path_from_url(d.get_uri())
540
519
        finally:
602
581
class MergeDirectiveController(DiffController):
603
582
 
604
583
    def __init__(self, path, directive, window=None):
605
 
        super(MergeDirectiveController, self).__init__(
606
 
            path, directive.patch.splitlines(True), window)
 
584
        DiffController.__init__(self, path, directive.patch.splitlines(True),
 
585
                                window)
607
586
        self.directive = directive
608
587
        self.merge_target = None
609
588