/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
1
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
2
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
7
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
12
#
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
try:
18
    import pygtk
19
    pygtk.require("2.0")
20
except:
21
    pass
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
22
0.13.11 by Jelmer Vernooij
Bunch of small fixes, cleanups and simplifications.
23
import gtk
24
import gobject
25
import pango
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
26
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
27
import os.path
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
28
import re
126.1.10 by Szilveszter Farkas (Phanatic)
Allow to commit single files from the context menu (Fixed: #54983)
29
248 by Jelmer Vernooij
Merge fixes for #127392 and #127381)
30
from bzrlib import errors, osutils
235.1.5 by Mateusz Korniak
Missing mutter import added.
31
from bzrlib.trace import mutter
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
32
153 by Jelmer Vernooij
Fix references to dialog.
33
from dialog import error_dialog, question_dialog
132 by Jelmer Vernooij
Use decorator for catching and showing bzr-gtk errors graphically. Eventually, this should go away and should be handled by the ui factory.
34
from errors import show_bzr_error
93.1.6 by Alexander Belchenko
detecting name of glade file doing in separate module (olive.gladefile)
35
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
36
try:
37
    import dbus
38
    import dbus.glib
180 by Jelmer Vernooij
Don't obtain handle to network manager until it's actually needed.
39
    have_dbus = True
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
40
except ImportError:
180 by Jelmer Vernooij
Don't obtain handle to network manager until it's actually needed.
41
    have_dbus = False
158 by Jelmer Vernooij
If available, use NetworkManager to find out whether a commit should be local or not.
42
278.1.4 by John Arbash Meinel
Just playing around.
43
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
44
def pending_revisions(wt):
45
    """Return a list of pending merges or None if there are none of them.
46
47
    Arguably this should be a core function, and
48
    ``bzrlib.status.show_pending_merges`` should be built on top of it.
49
50
    :return: [(rev, [children])]
51
    """
52
    parents = wt.get_parent_ids()
53
    if len(parents) < 2:
54
        return None
55
56
    # The basic pending merge algorithm uses the same algorithm as
57
    # bzrlib.status.show_pending_merges
58
    pending = parents[1:]
59
    branch = wt.branch
60
    last_revision = parents[0]
61
62
    if last_revision is not None:
63
        try:
64
            ignore = set(branch.repository.get_ancestry(last_revision,
65
                                                        topo_sorted=False))
66
        except errors.NoSuchRevision:
67
            # the last revision is a ghost : assume everything is new
68
            # except for it
69
            ignore = set([None, last_revision])
70
    else:
71
        ignore = set([None])
72
73
    pm = []
74
    for merge in pending:
75
        ignore.add(merge)
76
        try:
77
            rev = branch.repository.get_revision(merge)
78
            children = []
79
            pm.append((rev, children))
80
81
            # This does need to be topo sorted, so we search backwards
82
            inner_merges = branch.repository.get_ancestry(merge)
83
            assert inner_merges[0] is None
84
            inner_merges.pop(0)
85
            for mmerge in reversed(inner_merges):
86
                if mmerge in ignore:
87
                    continue
88
                rev = branch.repository.get_revision(mmerge)
89
                children.append(rev)
90
91
                ignore.add(mmerge)
92
        except errors.NoSuchRevision:
93
            print "DEBUG: NoSuchRevision:", merge
94
95
    return pm
96
97
135 by Jelmer Vernooij
Throw out the old CommitDialog code and use the new code instead, also for 'gcommit'.
98
class CommitDialog(gtk.Dialog):
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
99
    """Implementation of Commit."""
100
101
    def __init__(self, wt, selected=None, parent=None):
102
         gtk.Dialog.__init__(self, title="Commit - Olive",
103
                                   parent=parent,
104
                                   flags=0,
105
                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
106
         self._wt = wt
107
         self._selected = selected
108
109
         self.setup_params()
110
         self.construct()
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
111
         # These could potentially be set based on the size of your monitor.
112
         # But for now, they seem like a reasonable default
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
113
         self.set_default_size(800, 600)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
114
         self._hpane.set_position(300)
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
115
         self.fill_in_data()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
116
117
    def setup_params(self):
118
        """Setup the member variables for state."""
119
        self._basis_tree = self._wt.basis_tree()
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
120
        self._delta = None
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
121
        self._pending = pending_revisions(self._wt)
122
123
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
124
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
125
    def fill_in_data(self):
126
        # Now that we are built, handle changes to the view based on the state
127
        self._fill_in_pending()
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
128
        self._fill_in_files()
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
129
130
    def _fill_in_pending(self):
131
        if not self._pending:
132
            self._pending_box.hide()
133
            return
134
135
        # TODO: We'd really prefer this to be a nested list
136
        for rev, children in self._pending:
137
            rev_info = self._rev_to_pending_info(rev)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
138
            self._pending_store.append([
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
139
                rev_info['revision_id'],
140
                rev_info['date'],
141
                rev_info['committer'],
142
                rev_info['summary'],
143
                ])
144
            for child in children:
145
                rev_info = self._rev_to_pending_info(child)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
146
                self._pending_store.append([
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
147
                    rev_info['revision_id'],
148
                    rev_info['date'],
149
                    rev_info['committer'],
150
                    rev_info['summary'],
151
                    ])
152
        self._pending_box.show()
153
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
154
    def _fill_in_files(self):
155
        # We should really use _iter_changes, and then add a progress bar of
156
        # some kind.
157
        self._compute_delta()
158
159
        # While we fill in the view, hide the store
160
        store = self._files_store
161
        self._treeview_files.set_model(None)
162
163
        added = _('added')
164
        removed = _('removed')
165
        renamed = _('renamed')
166
        renamed_and_modified = _('renamed and modified')
167
        modified = _('modified')
168
169
        # [file_id, real path, checkbox, display path, changes type]
170
        for path, file_id, kind in self._delta.added:
171
            marker = osutils.kind_marker(kind)
172
            store.append([file_id, path, True, path+marker, added])
173
174
        for path, file_id, kind in self._delta.removed:
175
            marker = osutils.kind_marker(kind)
176
            store.append([file_id, path, True, path+marker, removed])
177
178
        for oldpath, newpath, file_id, kind, text_mod, meta_mod in self._delta.renamed:
179
            marker = osutils.kind_marker(kind)
180
            if text_mod or meta_mod:
181
                changes = renamed_and_modified
182
            else:
183
                changes = renamed
184
            store.append([file_id, newpath, True,
185
                          oldpath+marker + '  =>  ' + newpath+marker,
186
                          changes,
187
                         ])
188
189
        for path, file_id, kind, text_mod, meta_mod in self._delta.modified:
190
            marker = osutils.kind_marker(kind)
191
            store.append([file_id, path, True, path+marker, modified])
192
193
        self._treeview_files.set_model(store)
194
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
195
    def _compute_delta(self):
196
        self._delta = self._wt.changes_from(self._basis_tree)
197
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
198
    def construct(self):
199
        """Build up the dialog widgets."""
200
        # The primary pane which splits it into left and right (adjustable)
201
        # sections.
278.1.4 by John Arbash Meinel
Just playing around.
202
        self._hpane = gtk.HPaned()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
203
204
        self._construct_left_pane()
205
        self._construct_right_pane()
206
207
        self.vbox.pack_start(self._hpane)
208
        self._hpane.show()
209
        self.set_focus(self._global_message_text_view)
210
211
    def _construct_left_pane(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
212
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
213
        self._construct_file_list()
214
        self._construct_pending_list()
215
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
216
        self._hpane.pack1(self._left_pane_box, resize=False, shrink=False)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
217
        self._left_pane_box.show()
218
219
    def _construct_right_pane(self):
220
        # TODO: I really want to make it so the diff view gets more space than
221
        # the global commit message, and the per-file commit message gets even
222
        # less. When I did it with wxGlade, I set it to 4 for diff, 2 for
223
        # commit, and 1 for file commit, and it looked good. But I don't seem
224
        # to have a way to do that with the gtk boxes... :( (Which is extra
225
        # weird since wx uses gtk on Linux...)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
226
        self._right_pane_table = gtk.Table(rows=10, columns=1, homogeneous=False)
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
227
        self._right_pane_table.set_row_spacings(5)
228
        self._right_pane_table.set_col_spacings(5)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
229
        self._right_pane_table_row = 0
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
230
        self._construct_diff_view()
231
        self._construct_file_message()
232
        self._construct_global_message()
233
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
234
        self._right_pane_table.show()
235
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
236
237
    def _add_to_right_table(self, widget, weight, expanding=False):
238
        """Add another widget to the table
239
240
        :param widget: The object to add
241
        :param weight: How many rows does this widget get to request
242
        :param expanding: Should expand|fill|shrink be set?
243
        """
244
        end_row = self._right_pane_table_row + weight
245
        options = 0
246
        expand_opts = gtk.EXPAND | gtk.FILL | gtk.SHRINK
247
        if expanding:
248
            options = expand_opts
249
        self._right_pane_table.attach(widget, 0, 1,
250
                                      self._right_pane_table_row, end_row,
251
                                      xoptions=expand_opts, yoptions=options)
252
        self._right_pane_table_row = end_row
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
253
254
    def _construct_file_list(self):
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
255
        self._files_box = gtk.VBox(homogeneous=False, spacing=0)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
256
        file_label = gtk.Label()
257
        file_label.set_markup(_('<b>Files</b>'))
258
        file_label.show()
259
        self._files_box.pack_start(file_label, expand=False)
260
261
        scroller = gtk.ScrolledWindow()
262
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
263
        self._treeview_files = gtk.TreeView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
264
        self._treeview_files.show()
265
        scroller.add(self._treeview_files)
266
        scroller.show()
267
        scroller.set_shadow_type(gtk.SHADOW_IN)
268
        self._files_box.pack_start(scroller,
269
                                   expand=True, fill=True)
270
        self._files_box.show()
271
        self._left_pane_box.pack_start(self._files_box)
272
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
273
        liststore = gtk.ListStore(
274
            gobject.TYPE_STRING,  # [0] file_id
275
            gobject.TYPE_STRING,  # [1] real path
276
            gobject.TYPE_BOOLEAN, # [2] checkbox
277
            gobject.TYPE_STRING,  # [3] display path
278
            gobject.TYPE_STRING,  # [4] changes type
279
            )
280
        self._files_store = liststore
281
        self._treeview_files.set_model(liststore)
282
        crt = gtk.CellRendererToggle()
283
        crt.set_property("activatable", True) # not bool(self._pending))
284
        crt.connect("toggled", self._toggle_commit, self._files_store)
285
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
286
                                           crt, active=2))
287
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
288
                                           gtk.CellRendererText(), text=3))
289
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
290
                                           gtk.CellRendererText(), text=4))
291
292
    def _toggle_commit(self, cell, path, model):
293
        model[path][2] = not model[path][2]
294
        return
295
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
296
    def _construct_pending_list(self):
297
        # Pending information defaults to hidden, we put it all in 1 box, so
298
        # that we can show/hide all of them at once
299
        self._pending_box = gtk.VBox()
300
        self._pending_box.hide()
301
302
        pending_message = gtk.Label()
303
        pending_message.set_markup(
304
            _('<i>Cannot select specific files when merging</i>'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
305
        self._pending_box.pack_start(pending_message, expand=False, padding=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
306
        pending_message.show()
307
308
        pending_label = gtk.Label()
309
        pending_label.set_markup(_('<b>Pending Revisions</b>'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
310
        self._pending_box.pack_start(pending_label, expand=False, padding=0)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
311
        pending_label.show()
312
313
        scroller = gtk.ScrolledWindow()
314
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
315
        self._treeview_pending = gtk.TreeView()
316
        scroller.add(self._treeview_pending)
317
        scroller.show()
318
        scroller.set_shadow_type(gtk.SHADOW_IN)
319
        self._pending_box.pack_start(scroller,
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
320
                                     expand=True, fill=True, padding=5)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
321
        self._treeview_pending.show()
322
        self._left_pane_box.pack_start(self._pending_box)
323
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
324
        liststore = gtk.ListStore(gobject.TYPE_STRING, # revision_id
325
                                  gobject.TYPE_STRING, # date
326
                                  gobject.TYPE_STRING, # committer
327
                                  gobject.TYPE_STRING, # summary
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
328
                                 )
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
329
        self._pending_store = liststore
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
330
        self._treeview_pending.set_model(liststore)
331
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Date'),
332
                                             gtk.CellRendererText(), text=1))
333
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Committer'),
334
                                             gtk.CellRendererText(), text=2))
335
        self._treeview_pending.append_column(gtk.TreeViewColumn(_('Summary'),
336
                                             gtk.CellRendererText(), text=3))
337
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
338
    def _construct_diff_view(self):
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
339
        from diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
340
341
        self._diff_label = gtk.Label(_('Diff for whole tree'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
342
        self._diff_label.set_alignment(0, 0)
343
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
344
        self._add_to_right_table(self._diff_label, 1, False)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
345
        self._diff_label.show()
346
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
347
        self._diff_view = DiffView()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
348
        self._add_to_right_table(self._diff_view, 4, True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
349
        self._diff_view.show()
350
351
    def _construct_file_message(self):
352
        file_message_box = gtk.VBox()
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
353
        scroller = gtk.ScrolledWindow()
354
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
355
356
        self._file_message_text_view = gtk.TextView()
357
        scroller.add(self._file_message_text_view)
358
        scroller.show()
359
        scroller.set_shadow_type(gtk.SHADOW_IN)
360
        file_message_box.pack_start(scroller, expand=True, fill=True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
361
362
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
363
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
364
        self._file_message_text_view.set_accepts_tab(False)
365
        self._file_message_text_view.show()
366
367
        self._file_message_expander = gtk.Expander(_('Message for XXX'))
368
        self._file_message_expander.add(file_message_box)
369
        file_message_box.show()
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
370
        self._add_to_right_table(self._file_message_expander, 1, False)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
371
        self._file_message_expander.show()
372
373
    def _construct_global_message(self):
374
        self._global_message_label = gtk.Label(_('Global Commit Message'))
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
375
        self._global_message_label.set_alignment(0, 0)
376
        self._right_pane_table.set_row_spacing(self._right_pane_table_row, 0)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
377
        self._add_to_right_table(self._global_message_label, 1, False)
278.1.11 by John Arbash Meinel
Worked out the rest of the spacing.
378
        # Can we remove the spacing between the label and the box?
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
379
        self._global_message_label.show()
380
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
381
        scroller = gtk.ScrolledWindow()
382
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
383
384
        self._global_message_text_view = gtk.TextView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
385
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
278.1.9 by John Arbash Meinel
Move all text entry boxes into a ScrolledWindow, so that they don't change size constantly.
386
        scroller.add(self._global_message_text_view)
387
        scroller.show()
388
        scroller.set_shadow_type(gtk.SHADOW_IN)
278.1.10 by John Arbash Meinel
To get the space weighting I wanted, I turned to a Table.
389
        self._add_to_right_table(scroller, 2, True)
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
390
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
391
        self._file_message_text_view.set_accepts_tab(False)
392
        self._global_message_text_view.show()
393
394
    @staticmethod
395
    def _rev_to_pending_info(rev):
396
        """Get the information from a pending merge."""
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
397
        from bzrlib.osutils import format_date
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
398
399
        rev_dict = {}
400
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
401
        rev_dict['summary'] = rev.get_summary()
402
        rev_dict['date'] = format_date(rev.timestamp,
403
                                       rev.timezone or 0,
404
                                       'original', date_fmt="%Y-%m-%d",
405
                                       show_offset=False)
406
        rev_dict['revision_id'] = rev.revision_id
407
        return rev_dict
408
409
410
# class CommitDialog(gtk.Dialog):
411
#     """ New implementation of the Commit dialog. """
412
#     def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
413
#         """ Initialize the Commit Dialog. """
414
#         gtk.Dialog.__init__(self, title="Commit - Olive",
415
#                                   parent=parent,
416
#                                   flags=0,
417
#                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
418
#         
419
#         # Get arguments
420
#         self.wt = wt
421
#         self.wtpath = wtpath
422
#         self.notbranch = notbranch
423
#         self.selected = selected
424
#         
425
#         # Set the delta
426
#         self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
427
#         self.delta = self.wt.changes_from(self.old_tree)
428
#         
429
#         # Get pending merges
430
#         self.pending = self._pending_merges(self.wt)
431
#         
432
#         # Do some preliminary checks
433
#         self._is_checkout = False
434
#         self._is_pending = False
435
#         if self.wt is None and not self.notbranch:
436
#             error_dialog(_('Directory does not have a working tree'),
437
#                          _('Operation aborted.'))
438
#             self.close()
439
#             return
440
# 
441
#         if self.notbranch:
442
#             error_dialog(_('Directory is not a branch'),
443
#                          _('You can perform this action only in a branch.'))
444
#             self.close()
445
#             return
446
#         else:
447
#             if self.wt.branch.get_bound_location() is not None:
448
#                 # we have a checkout, so the local commit checkbox must appear
449
#                 self._is_checkout = True
450
#             
451
#             if self.pending:
452
#                 # There are pending merges, file selection not supported
453
#                 self._is_pending = True
454
#         
455
#         # Create the widgets
456
#         # This is the main horizontal box, which is used to separate the commit
457
#         # info from the diff window.
458
#         self._hpane = gtk.HPaned()
459
#         self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
460
#         self._expander_files = gtk.Expander(_("File(s) to commit"))
461
#         self._vpaned_main = gtk.VPaned()
462
#         self._scrolledwindow_files = gtk.ScrolledWindow()
463
#         self._scrolledwindow_message = gtk.ScrolledWindow()
464
#         self._treeview_files = gtk.TreeView()
465
#         self._vbox_message = gtk.VBox()
466
#         self._label_message = gtk.Label(_("Commit message:"))
467
#         self._textview_message = gtk.TextView()
468
#         
469
#         if self._is_pending:
470
#             self._expander_merges = gtk.Expander(_("Pending merges"))
471
#             self._vpaned_list = gtk.VPaned()
472
#             self._scrolledwindow_merges = gtk.ScrolledWindow()
473
#             self._treeview_merges = gtk.TreeView()
474
# 
475
#         # Set callbacks
476
#         self._button_commit.connect('clicked', self._on_commit_clicked)
477
#         self._treeview_files.connect('cursor-changed', self._on_treeview_files_cursor_changed)
478
#         self._treeview_files.connect('row-activated', self._on_treeview_files_row_activated)
479
#         
480
#         # Set properties
481
#         self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
482
#                                               gtk.POLICY_AUTOMATIC)
483
#         self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
484
#                                                 gtk.POLICY_AUTOMATIC)
485
#         self._textview_message.modify_font(pango.FontDescription("Monospace"))
486
#         self.set_default_size(500, 500)
487
#         self._vpaned_main.set_position(200)
488
#         self._button_commit.set_flags(gtk.CAN_DEFAULT)
489
# 
490
#         if self._is_pending:
491
#             self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
492
#                                                    gtk.POLICY_AUTOMATIC)
493
#             self._treeview_files.set_sensitive(False)
494
#         
495
#         # Construct the dialog
496
#         self.action_area.pack_end(self._button_commit)
497
#         
498
#         self._scrolledwindow_files.add(self._treeview_files)
499
#         self._scrolledwindow_message.add(self._textview_message)
500
#         
501
#         self._expander_files.add(self._scrolledwindow_files)
502
#         
503
#         self._vbox_message.pack_start(self._label_message, False, False)
504
#         self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
505
#         
506
#         if self._is_pending:        
507
#             self._expander_merges.add(self._scrolledwindow_merges)
508
#             self._scrolledwindow_merges.add(self._treeview_merges)
509
#             self._vpaned_list.add1(self._expander_files)
510
#             self._vpaned_list.add2(self._expander_merges)
511
#             self._vpaned_main.add1(self._vpaned_list)
512
#         else:
513
#             self._vpaned_main.add1(self._expander_files)
514
# 
515
#         self._vpaned_main.add2(self._vbox_message)
516
#         
517
#         self._hpane.pack1(self._vpaned_main)
518
#         self.vbox.pack_start(self._hpane, expand=True, fill=True)
519
#         if self._is_checkout: 
520
#             self._check_local = gtk.CheckButton(_("_Only commit locally"),
521
#                                                 use_underline=True)
522
#             self.vbox.pack_start(self._check_local, False, False)
523
#             if have_dbus:
524
#                 bus = dbus.SystemBus()
525
#                 proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
526
#                               '/org/freedesktop/NetworkManager')
527
#                 dbus_iface = dbus.Interface(
528
#                         proxy_obj, 'org.freedesktop.NetworkManager')
529
#                 try:
530
#                     # 3 is the enum value for STATE_CONNECTED
531
#                     self._check_local.set_active(dbus_iface.state() != 3)
532
#                 except dbus.DBusException, e:
533
#                     # Silently drop errors. While DBus may be 
534
#                     # available, NetworkManager doesn't necessarily have to be
535
#                     mutter("unable to get networkmanager state: %r" % e)
536
#                 
537
#         # Create the file list
538
#         self._create_file_view()
539
#         # Create the pending merges
540
#         self._create_pending_merges()
541
#         self._create_diff_view()
542
#         
543
#         # Expand the corresponding expander
544
#         if self._is_pending:
545
#             self._expander_merges.set_expanded(True)
546
#         else:
547
#             self._expander_files.set_expanded(True)
548
#         
549
#         # Display dialog
550
#         self.vbox.show_all()
551
#         
552
#         # Default to Commit button
553
#         self._button_commit.grab_default()
554
#     
555
#     def _show_diff_view(self, treeview):
556
#         # FIXME: the diff window freezes for some reason
557
#         treeselection = treeview.get_selection()
558
#         (model, iter) = treeselection.get_selected()
559
# 
560
#         if iter is not None:
561
#             selected = model.get_value(iter, 3) # Get the real_path attribute
562
#             self._diff_display.show_diff([selected])
563
# 
564
#     def _on_treeview_files_cursor_changed(self, treeview):
565
#         self._show_diff_view(treeview)
566
#         
567
#     def _on_treeview_files_row_activated(self, treeview, path, view_column):
568
#         self._show_diff_view(treeview)
569
#     
570
#     @show_bzr_error
571
#     def _on_commit_clicked(self, button):
572
#         """ Commit button clicked handler. """
573
#         textbuffer = self._textview_message.get_buffer()
574
#         start, end = textbuffer.get_bounds()
575
#         message = textbuffer.get_text(start, end).decode('utf-8')
576
#         
577
#         if not self.pending:
578
#             specific_files = self._get_specific_files()
579
#         else:
580
#             specific_files = None
581
# 
582
#         if message == '':
583
#             response = question_dialog(_('Commit with an empty message?'),
584
#                                        _('You can describe your commit intent in the message.'))
585
#             if response == gtk.RESPONSE_NO:
586
#                 # Kindly give focus to message area
587
#                 self._textview_message.grab_focus()
588
#                 return
589
# 
590
#         if self._is_checkout:
591
#             local = self._check_local.get_active()
592
#         else:
593
#             local = False
594
# 
595
#         if list(self.wt.unknowns()) != []:
596
#             response = question_dialog(_("Commit with unknowns?"),
597
#                _("Unknown files exist in the working tree. Commit anyway?"))
598
#             if response == gtk.RESPONSE_NO:
599
#                 return
600
#         
601
#         try:
602
#             self.wt.commit(message,
603
#                        allow_pointless=False,
604
#                        strict=False,
605
#                        local=local,
606
#                        specific_files=specific_files)
607
#         except errors.PointlessCommit:
608
#             response = question_dialog(_('Commit with no changes?'),
609
#                                        _('There are no changes in the working tree.'))
610
#             if response == gtk.RESPONSE_YES:
611
#                 self.wt.commit(message,
612
#                                allow_pointless=True,
613
#                                strict=False,
614
#                                local=local,
615
#                                specific_files=specific_files)
616
#         self.response(gtk.RESPONSE_OK)
617
# 
618
#     def _pending_merges(self, wt):
619
#         """ Return a list of pending merges or None if there are none of them. """
620
#         parents = wt.get_parent_ids()
621
#         if len(parents) < 2:
622
#             return None
623
#         
624
#         import re
625
#         from bzrlib.osutils import format_date
626
#         
627
#         pending = parents[1:]
628
#         branch = wt.branch
629
#         last_revision = parents[0]
630
#         
631
#         if last_revision is not None:
632
#             try:
633
#                 ignore = set(branch.repository.get_ancestry(last_revision))
634
#             except errors.NoSuchRevision:
635
#                 # the last revision is a ghost : assume everything is new 
636
#                 # except for it
637
#                 ignore = set([None, last_revision])
638
#         else:
639
#             ignore = set([None])
640
#         
641
#         pm = []
642
#         for merge in pending:
643
#             ignore.add(merge)
644
#             try:
645
#                 m_revision = branch.repository.get_revision(merge)
646
#                 
647
#                 rev = {}
648
#                 rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
649
#                 rev['summary'] = m_revision.get_summary()
650
#                 rev['date'] = format_date(m_revision.timestamp,
651
#                                           m_revision.timezone or 0, 
652
#                                           'original', date_fmt="%Y-%m-%d",
653
#                                           show_offset=False)
654
#                 
655
#                 pm.append(rev)
656
#                 
657
#                 inner_merges = branch.repository.get_ancestry(merge)
658
#                 assert inner_merges[0] is None
659
#                 inner_merges.pop(0)
660
#                 inner_merges.reverse()
661
#                 for mmerge in inner_merges:
662
#                     if mmerge in ignore:
663
#                         continue
664
#                     mm_revision = branch.repository.get_revision(mmerge)
665
#                     
666
#                     rev = {}
667
#                     rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
668
#                     rev['summary'] = mm_revision.get_summary()
669
#                     rev['date'] = format_date(mm_revision.timestamp,
670
#                                               mm_revision.timezone or 0, 
671
#                                               'original', date_fmt="%Y-%m-%d",
672
#                                               show_offset=False)
673
#                 
674
#                     pm.append(rev)
675
#                     
676
#                     ignore.add(mmerge)
677
#             except errors.NoSuchRevision:
678
#                 print "DEBUG: NoSuchRevision:", merge
679
#         
680
#         return pm
681
# 
682
#     def _create_file_view(self):
683
#         self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
684
#                                          gobject.TYPE_STRING,    # [1] path to display
685
#                                          gobject.TYPE_STRING,    # [2] changes type
686
#                                          gobject.TYPE_STRING)    # [3] real path
687
#         self._treeview_files.set_model(self._file_store)
688
#         crt = gtk.CellRendererToggle()
689
#         crt.set_property("activatable", True)
690
#         crt.connect("toggled", self._toggle_commit, self._file_store)
691
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
692
#                                      crt, active=0))
693
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
694
#                                      gtk.CellRendererText(), text=1))
695
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
696
#                                      gtk.CellRendererText(), text=2))
697
# 
698
#         for path, id, kind in self.delta.added:
699
#             marker = osutils.kind_marker(kind)
700
#             if self.selected is not None:
701
#                 if path == os.path.join(self.wtpath, self.selected):
702
#                     self._file_store.append([ True, path+marker, _('added'), path ])
703
#                 else:
704
#                     self._file_store.append([ False, path+marker, _('added'), path ])
705
#             else:
706
#                 self._file_store.append([ True, path+marker, _('added'), path ])
707
# 
708
#         for path, id, kind in self.delta.removed:
709
#             marker = osutils.kind_marker(kind)
710
#             if self.selected is not None:
711
#                 if path == os.path.join(self.wtpath, self.selected):
712
#                     self._file_store.append([ True, path+marker, _('removed'), path ])
713
#                 else:
714
#                     self._file_store.append([ False, path+marker, _('removed'), path ])
715
#             else:
716
#                 self._file_store.append([ True, path+marker, _('removed'), path ])
717
# 
718
#         for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
719
#             marker = osutils.kind_marker(kind)
720
#             if text_modified or meta_modified:
721
#                 changes = _('renamed and modified')
722
#             else:
723
#                 changes = _('renamed')
724
#             if self.selected is not None:
725
#                 if newpath == os.path.join(self.wtpath, self.selected):
726
#                     self._file_store.append([ True,
727
#                                               oldpath+marker + '  =>  ' + newpath+marker,
728
#                                               changes,
729
#                                               newpath
730
#                                             ])
731
#                 else:
732
#                     self._file_store.append([ False,
733
#                                               oldpath+marker + '  =>  ' + newpath+marker,
734
#                                               changes,
735
#                                               newpath
736
#                                             ])
737
#             else:
738
#                 self._file_store.append([ True,
739
#                                           oldpath+marker + '  =>  ' + newpath+marker,
740
#                                           changes,
741
#                                           newpath
742
#                                         ])
743
# 
744
#         for path, id, kind, text_modified, meta_modified in self.delta.modified:
745
#             marker = osutils.kind_marker(kind)
746
#             if self.selected is not None:
747
#                 if path == os.path.join(self.wtpath, self.selected):
748
#                     self._file_store.append([ True, path+marker, _('modified'), path ])
749
#                 else:
750
#                     self._file_store.append([ False, path+marker, _('modified'), path ])
751
#             else:
752
#                 self._file_store.append([ True, path+marker, _('modified'), path ])
753
#     
754
#     def _create_pending_merges(self):
755
#         if not self.pending:
756
#             return
757
#         
758
#         liststore = gtk.ListStore(gobject.TYPE_STRING,
759
#                                   gobject.TYPE_STRING,
760
#                                   gobject.TYPE_STRING)
761
#         self._treeview_merges.set_model(liststore)
762
#         
763
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
764
#                                             gtk.CellRendererText(), text=0))
765
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
766
#                                             gtk.CellRendererText(), text=1))
767
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
768
#                                             gtk.CellRendererText(), text=2))
769
#         
770
#         for item in self.pending:
771
#             liststore.append([ item['date'],
772
#                                item['committer'],
773
#                                item['summary'] ])
774
#     
775
# 
776
#     def _create_diff_view(self):
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
777
#         from diff import DiffView
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
778
# 
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
779
#         self._diff_display = DiffView()
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
780
#         self._diff_display.set_trees(self.wt, self.wt.basis_tree())
781
#         self._diff_display.show_diff(None)
782
#         self._diff_display.show()
783
#         self._hpane.pack2(self._diff_display)
784
# 
785
#     def _get_specific_files(self):
786
#         ret = []
787
#         it = self._file_store.get_iter_first()
788
#         while it:
789
#             if self._file_store.get_value(it, 0):
790
#                 # get real path from hidden column 3
791
#                 ret.append(self._file_store.get_value(it, 3))
792
#             it = self._file_store.iter_next(it)
793
# 
794
#         return ret
795
#     
796
#     def _toggle_commit(self, cell, path, model):
797
#         model[path][0] = not model[path][0]
798
#         return