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

  • Committer: John Arbash Meinel
  • Date: 2007-09-28 22:25:49 UTC
  • mto: (322.1.1 trunk) (330.3.3 trunk)
  • mto: This revision was merged to the branch mainline in revision 368.
  • Revision ID: john@arbash-meinel.com-20070928222549-iq71v20ax7h3x8ff
Starting to flesh out the dialog with actual windows.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
import pango
26
26
 
27
27
import os.path
 
28
import re
28
29
 
29
30
from bzrlib import errors, osutils
30
31
from bzrlib.trace import mutter
40
41
    have_dbus = False
41
42
 
42
43
 
 
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
 
43
98
class CommitDialog(gtk.Dialog):
44
 
    """ New implementation of the Commit dialog. """
45
 
    def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
46
 
        """ Initialize the Commit Dialog. """
47
 
        gtk.Dialog.__init__(self, title="Commit - Olive",
48
 
                                  parent=parent,
49
 
                                  flags=0,
50
 
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
51
 
        
52
 
        # Get arguments
53
 
        self.wt = wt
54
 
        self.wtpath = wtpath
55
 
        self.notbranch = notbranch
56
 
        self.selected = selected
57
 
        
58
 
        # Set the delta
59
 
        self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
60
 
        self.delta = self.wt.changes_from(self.old_tree)
61
 
        
62
 
        # Get pending merges
63
 
        self.pending = self._pending_merges(self.wt)
64
 
        
65
 
        # Do some preliminary checks
66
 
        self._is_checkout = False
67
 
        self._is_pending = False
68
 
        if self.wt is None and not self.notbranch:
69
 
            error_dialog(_('Directory does not have a working tree'),
70
 
                         _('Operation aborted.'))
71
 
            self.close()
72
 
            return
73
 
 
74
 
        if self.notbranch:
75
 
            error_dialog(_('Directory is not a branch'),
76
 
                         _('You can perform this action only in a branch.'))
77
 
            self.close()
78
 
            return
79
 
        else:
80
 
            if self.wt.branch.get_bound_location() is not None:
81
 
                # we have a checkout, so the local commit checkbox must appear
82
 
                self._is_checkout = True
83
 
            
84
 
            if self.pending:
85
 
                # There are pending merges, file selection not supported
86
 
                self._is_pending = True
87
 
        
88
 
        # Create the widgets
89
 
        # This is the main horizontal box, which is used to separate the commit
90
 
        # info from the diff window.
 
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()
 
111
         self.set_default_size(800, 600)
 
112
 
 
113
    def setup_params(self):
 
114
        """Setup the member variables for state."""
 
115
        self._basis_tree = self._wt.basis_tree()
 
116
        self._delta = self._wt.changes_from(self._basis_tree)
 
117
 
 
118
        self._pending = pending_revisions(self._wt)
 
119
 
 
120
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
 
121
 
 
122
    def construct(self):
 
123
        """Build up the dialog widgets."""
 
124
        # The primary pane which splits it into left and right (adjustable)
 
125
        # sections.
91
126
        self._hpane = gtk.HPaned()
92
 
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
93
 
        self._expander_files = gtk.Expander(_("File(s) to commit"))
94
 
        self._vpaned_main = gtk.VPaned()
95
 
        self._scrolledwindow_files = gtk.ScrolledWindow()
96
 
        self._scrolledwindow_message = gtk.ScrolledWindow()
 
127
 
 
128
        self._construct_left_pane()
 
129
        self._construct_right_pane()
 
130
 
 
131
        self.vbox.pack_start(self._hpane)
 
132
        self._hpane.show()
 
133
        self.set_focus(self._global_message_text_view)
 
134
 
 
135
    def _construct_left_pane(self):
 
136
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=3)
 
137
        self._construct_file_list()
 
138
        self._construct_pending_list()
 
139
 
 
140
        self._pending_box.show()
 
141
        self._hpane.pack1(self._left_pane_box, resize=False, shrink=True)
 
142
        self._left_pane_box.show()
 
143
 
 
144
    def _construct_right_pane(self):
 
145
        # TODO: I really want to make it so the diff view gets more space than
 
146
        # the global commit message, and the per-file commit message gets even
 
147
        # less. When I did it with wxGlade, I set it to 4 for diff, 2 for
 
148
        # commit, and 1 for file commit, and it looked good. But I don't seem
 
149
        # to have a way to do that with the gtk boxes... :( (Which is extra
 
150
        # weird since wx uses gtk on Linux...)
 
151
        self._right_pane_box = gtk.VBox(homogeneous=False, spacing=3)
 
152
        self._construct_diff_view()
 
153
        self._construct_file_message()
 
154
        self._construct_global_message()
 
155
 
 
156
        self._right_pane_box.show()
 
157
        self._hpane.pack2(self._right_pane_box, resize=True, shrink=True)
 
158
 
 
159
    def _construct_file_list(self):
 
160
        self._files_box = gtk.VBox()
 
161
        file_label = gtk.Label()
 
162
        file_label.set_markup(_('<b>Files</b>'))
 
163
        file_label.show()
 
164
        self._files_box.pack_start(file_label, expand=False)
 
165
 
 
166
        scroller = gtk.ScrolledWindow()
 
167
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
97
168
        self._treeview_files = gtk.TreeView()
98
 
        self._vbox_message = gtk.VBox()
99
 
        self._label_message = gtk.Label(_("Commit message:"))
100
 
        self._textview_message = gtk.TextView()
101
 
        
102
 
        if self._is_pending:
103
 
            self._expander_merges = gtk.Expander(_("Pending merges"))
104
 
            self._vpaned_list = gtk.VPaned()
105
 
            self._scrolledwindow_merges = gtk.ScrolledWindow()
106
 
            self._treeview_merges = gtk.TreeView()
107
 
 
108
 
        # Set callbacks
109
 
        self._button_commit.connect('clicked', self._on_commit_clicked)
110
 
        self._treeview_files.connect('cursor-changed', self._on_treeview_files_cursor_changed)
111
 
        self._treeview_files.connect('row-activated', self._on_treeview_files_row_activated)
112
 
        
113
 
        # Set properties
114
 
        self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
115
 
                                              gtk.POLICY_AUTOMATIC)
116
 
        self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
117
 
                                                gtk.POLICY_AUTOMATIC)
118
 
        self._textview_message.modify_font(pango.FontDescription("Monospace"))
119
 
        self.set_default_size(500, 500)
120
 
        self._vpaned_main.set_position(200)
121
 
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
122
 
 
123
 
        if self._is_pending:
124
 
            self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
125
 
                                                   gtk.POLICY_AUTOMATIC)
126
 
            self._treeview_files.set_sensitive(False)
127
 
        
128
 
        # Construct the dialog
129
 
        self.action_area.pack_end(self._button_commit)
130
 
        
131
 
        self._scrolledwindow_files.add(self._treeview_files)
132
 
        self._scrolledwindow_message.add(self._textview_message)
133
 
        
134
 
        self._expander_files.add(self._scrolledwindow_files)
135
 
        
136
 
        self._vbox_message.pack_start(self._label_message, False, False)
137
 
        self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
138
 
        
139
 
        if self._is_pending:        
140
 
            self._expander_merges.add(self._scrolledwindow_merges)
141
 
            self._scrolledwindow_merges.add(self._treeview_merges)
142
 
            self._vpaned_list.add1(self._expander_files)
143
 
            self._vpaned_list.add2(self._expander_merges)
144
 
            self._vpaned_main.add1(self._vpaned_list)
145
 
        else:
146
 
            self._vpaned_main.add1(self._expander_files)
147
 
 
148
 
        self._vpaned_main.add2(self._vbox_message)
149
 
        
150
 
        self._hpane.pack1(self._vpaned_main)
151
 
        self.vbox.pack_start(self._hpane, expand=True, fill=True)
152
 
        if self._is_checkout: 
153
 
            self._check_local = gtk.CheckButton(_("_Only commit locally"),
154
 
                                                use_underline=True)
155
 
            self.vbox.pack_start(self._check_local, False, False)
156
 
            if have_dbus:
157
 
                bus = dbus.SystemBus()
158
 
                proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
159
 
                              '/org/freedesktop/NetworkManager')
160
 
                dbus_iface = dbus.Interface(
161
 
                        proxy_obj, 'org.freedesktop.NetworkManager')
162
 
                try:
163
 
                    # 3 is the enum value for STATE_CONNECTED
164
 
                    self._check_local.set_active(dbus_iface.state() != 3)
165
 
                except dbus.DBusException, e:
166
 
                    # Silently drop errors. While DBus may be 
167
 
                    # available, NetworkManager doesn't necessarily have to be
168
 
                    mutter("unable to get networkmanager state: %r" % e)
169
 
                
170
 
        # Create the file list
171
 
        self._create_file_view()
172
 
        # Create the pending merges
173
 
        self._create_pending_merges()
174
 
        self._create_diff_view()
175
 
        
176
 
        # Expand the corresponding expander
177
 
        if self._is_pending:
178
 
            self._expander_merges.set_expanded(True)
179
 
        else:
180
 
            self._expander_files.set_expanded(True)
181
 
        
182
 
        # Display dialog
183
 
        self.vbox.show_all()
184
 
        
185
 
        # Default to Commit button
186
 
        self._button_commit.grab_default()
187
 
    
188
 
    def _show_diff_view(self, treeview):
189
 
        # FIXME: the diff window freezes for some reason
190
 
        treeselection = treeview.get_selection()
191
 
        (model, iter) = treeselection.get_selected()
192
 
 
193
 
        if iter is not None:
194
 
            selected = model.get_value(iter, 3) # Get the real_path attribute
195
 
            self._diff_display.show_diff([selected])
196
 
 
197
 
    def _on_treeview_files_cursor_changed(self, treeview):
198
 
        self._show_diff_view(treeview)
199
 
        
200
 
    def _on_treeview_files_row_activated(self, treeview, path, view_column):
201
 
        self._show_diff_view(treeview)
202
 
    
203
 
    @show_bzr_error
204
 
    def _on_commit_clicked(self, button):
205
 
        """ Commit button clicked handler. """
206
 
        textbuffer = self._textview_message.get_buffer()
207
 
        start, end = textbuffer.get_bounds()
208
 
        message = textbuffer.get_text(start, end).decode('utf-8')
209
 
        
210
 
        if not self.pending:
211
 
            specific_files = self._get_specific_files()
212
 
        else:
213
 
            specific_files = None
214
 
 
215
 
        if message == '':
216
 
            response = question_dialog(_('Commit with an empty message?'),
217
 
                                       _('You can describe your commit intent in the message.'))
218
 
            if response == gtk.RESPONSE_NO:
219
 
                # Kindly give focus to message area
220
 
                self._textview_message.grab_focus()
221
 
                return
222
 
 
223
 
        if self._is_checkout:
224
 
            local = self._check_local.get_active()
225
 
        else:
226
 
            local = False
227
 
 
228
 
        if list(self.wt.unknowns()) != []:
229
 
            response = question_dialog(_("Commit with unknowns?"),
230
 
               _("Unknown files exist in the working tree. Commit anyway?"))
231
 
            if response == gtk.RESPONSE_NO:
232
 
                return
233
 
        
234
 
        try:
235
 
            self.wt.commit(message,
236
 
                       allow_pointless=False,
237
 
                       strict=False,
238
 
                       local=local,
239
 
                       specific_files=specific_files)
240
 
        except errors.PointlessCommit:
241
 
            response = question_dialog(_('Commit with no changes?'),
242
 
                                       _('There are no changes in the working tree.'))
243
 
            if response == gtk.RESPONSE_YES:
244
 
                self.wt.commit(message,
245
 
                               allow_pointless=True,
246
 
                               strict=False,
247
 
                               local=local,
248
 
                               specific_files=specific_files)
249
 
        self.response(gtk.RESPONSE_OK)
250
 
 
251
 
    def _pending_merges(self, wt):
252
 
        """ Return a list of pending merges or None if there are none of them. """
253
 
        parents = wt.get_parent_ids()
254
 
        if len(parents) < 2:
255
 
            return None
256
 
        
257
 
        import re
 
169
        self._treeview_files.show()
 
170
        scroller.add(self._treeview_files)
 
171
        scroller.show()
 
172
        scroller.set_shadow_type(gtk.SHADOW_IN)
 
173
        self._files_box.pack_start(scroller,
 
174
                                   expand=True, fill=True)
 
175
        self._files_box.show()
 
176
        self._left_pane_box.pack_start(self._files_box)
 
177
 
 
178
    def _construct_pending_list(self):
 
179
        # Pending information defaults to hidden, we put it all in 1 box, so
 
180
        # that we can show/hide all of them at once
 
181
        self._pending_box = gtk.VBox()
 
182
        self._pending_box.hide()
 
183
 
 
184
        # TODO: This message should be centered
 
185
        pending_message = gtk.Label()
 
186
        pending_message.set_markup(
 
187
            _('<i>Cannot select specific files when merging</i>'))
 
188
        self._pending_box.pack_start(pending_message, expand=False)
 
189
        pending_message.show()
 
190
 
 
191
        pending_label = gtk.Label()
 
192
        pending_label.set_markup(_('<b>Pending Revisions</b>'))
 
193
        self._pending_box.pack_start(pending_label, expand=False)
 
194
        pending_label.show()
 
195
 
 
196
        scroller = gtk.ScrolledWindow()
 
197
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
198
        self._treeview_pending = gtk.TreeView()
 
199
        scroller.add(self._treeview_pending)
 
200
        scroller.show()
 
201
        scroller.set_shadow_type(gtk.SHADOW_IN)
 
202
        self._pending_box.pack_start(scroller,
 
203
                                     expand=True, fill=True)
 
204
        self._treeview_pending.show()
 
205
        self._left_pane_box.pack_start(self._pending_box)
 
206
 
 
207
    def _construct_diff_view(self):
 
208
        from diff import DiffDisplay
 
209
 
 
210
        self._diff_label = gtk.Label(_('Diff for whole tree'))
 
211
        self._right_pane_box.pack_start(self._diff_label, expand=False)
 
212
        self._diff_label.show()
 
213
 
 
214
        self._diff_view = DiffDisplay()
 
215
        # self._diff_display.set_trees(self.wt, self.wt.basis_tree())
 
216
        # self._diff_display.show_diff(None)
 
217
        self._right_pane_box.pack_start(self._diff_view,
 
218
                                        expand=True, fill=True)
 
219
        self._diff_view.show()
 
220
 
 
221
    def _construct_file_message(self):
 
222
        file_message_box = gtk.VBox()
 
223
        self._file_message_text_view =  gtk.TextView()
 
224
        # There should be some way to get at the TextView's ScrolledWindow
 
225
        file_message_box.pack_start(self._file_message_text_view, expand=True, fill=True)
 
226
 
 
227
        self._file_message_text_view.modify_font(pango.FontDescription("Monospace"))
 
228
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
 
229
        self._file_message_text_view.set_accepts_tab(False)
 
230
        self._file_message_text_view.show()
 
231
 
 
232
        self._file_message_expander = gtk.Expander(_('Message for XXX'))
 
233
        self._file_message_expander.add(file_message_box)
 
234
        file_message_box.show()
 
235
        # TODO: When expanded, we want to change expand=True
 
236
        self._right_pane_box.pack_start(self._file_message_expander,
 
237
                                        expand=False, fill=True)
 
238
        self._file_message_expander.connect('notify::expanded',
 
239
                                            self._expand_file_message_callback)
 
240
        self._file_message_expander.show()
 
241
 
 
242
    def _expand_file_message_callback(self, expander, param_spec):
 
243
        if expander.get_expanded():
 
244
            self._right_pane_box.set_child_packing(expander,
 
245
                expand=True, fill=True, padding=0, pack_type=gtk.PACK_START)
 
246
        else:
 
247
            self._right_pane_box.set_child_packing(expander,
 
248
                expand=False, fill=True, padding=0, pack_type=gtk.PACK_START)
 
249
 
 
250
    def _construct_global_message(self):
 
251
        self._global_message_label = gtk.Label(_('Global Commit Message'))
 
252
        self._right_pane_box.pack_start(self._global_message_label, expand=False)
 
253
        self._global_message_label.show()
 
254
 
 
255
        self._global_message_text_view =  gtk.TextView()
 
256
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
 
257
        self._right_pane_box.pack_start(self._global_message_text_view,
 
258
                                        expand=True, fill=True)
 
259
        self._file_message_text_view.set_wrap_mode(gtk.WRAP_WORD)
 
260
        self._file_message_text_view.set_accepts_tab(False)
 
261
        self._global_message_text_view.show()
 
262
 
 
263
    @staticmethod
 
264
    def _rev_to_pending_info(rev):
 
265
        """Get the information from a pending merge."""
258
266
        from bzrlib.osutils import format_date
259
 
        
260
 
        pending = parents[1:]
261
 
        branch = wt.branch
262
 
        last_revision = parents[0]
263
 
        
264
 
        if last_revision is not None:
265
 
            try:
266
 
                ignore = set(branch.repository.get_ancestry(last_revision))
267
 
            except errors.NoSuchRevision:
268
 
                # the last revision is a ghost : assume everything is new 
269
 
                # except for it
270
 
                ignore = set([None, last_revision])
271
 
        else:
272
 
            ignore = set([None])
273
 
        
274
 
        pm = []
275
 
        for merge in pending:
276
 
            ignore.add(merge)
277
 
            try:
278
 
                m_revision = branch.repository.get_revision(merge)
279
 
                
280
 
                rev = {}
281
 
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
282
 
                rev['summary'] = m_revision.get_summary()
283
 
                rev['date'] = format_date(m_revision.timestamp,
284
 
                                          m_revision.timezone or 0, 
285
 
                                          'original', date_fmt="%Y-%m-%d",
286
 
                                          show_offset=False)
287
 
                
288
 
                pm.append(rev)
289
 
                
290
 
                inner_merges = branch.repository.get_ancestry(merge)
291
 
                assert inner_merges[0] is None
292
 
                inner_merges.pop(0)
293
 
                inner_merges.reverse()
294
 
                for mmerge in inner_merges:
295
 
                    if mmerge in ignore:
296
 
                        continue
297
 
                    mm_revision = branch.repository.get_revision(mmerge)
298
 
                    
299
 
                    rev = {}
300
 
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
301
 
                    rev['summary'] = mm_revision.get_summary()
302
 
                    rev['date'] = format_date(mm_revision.timestamp,
303
 
                                              mm_revision.timezone or 0, 
304
 
                                              'original', date_fmt="%Y-%m-%d",
305
 
                                              show_offset=False)
306
 
                
307
 
                    pm.append(rev)
308
 
                    
309
 
                    ignore.add(mmerge)
310
 
            except errors.NoSuchRevision:
311
 
                print "DEBUG: NoSuchRevision:", merge
312
 
        
313
 
        return pm
314
 
 
315
 
    def _create_file_view(self):
316
 
        self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
317
 
                                         gobject.TYPE_STRING,    # [1] path to display
318
 
                                         gobject.TYPE_STRING,    # [2] changes type
319
 
                                         gobject.TYPE_STRING)    # [3] real path
320
 
        self._treeview_files.set_model(self._file_store)
321
 
        crt = gtk.CellRendererToggle()
322
 
        crt.set_property("activatable", True)
323
 
        crt.connect("toggled", self._toggle_commit, self._file_store)
324
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
325
 
                                     crt, active=0))
326
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
327
 
                                     gtk.CellRendererText(), text=1))
328
 
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
329
 
                                     gtk.CellRendererText(), text=2))
330
 
 
331
 
        for path, id, kind in self.delta.added:
332
 
            marker = osutils.kind_marker(kind)
333
 
            if self.selected is not None:
334
 
                if path == os.path.join(self.wtpath, self.selected):
335
 
                    self._file_store.append([ True, path+marker, _('added'), path ])
336
 
                else:
337
 
                    self._file_store.append([ False, path+marker, _('added'), path ])
338
 
            else:
339
 
                self._file_store.append([ True, path+marker, _('added'), path ])
340
 
 
341
 
        for path, id, kind in self.delta.removed:
342
 
            marker = osutils.kind_marker(kind)
343
 
            if self.selected is not None:
344
 
                if path == os.path.join(self.wtpath, self.selected):
345
 
                    self._file_store.append([ True, path+marker, _('removed'), path ])
346
 
                else:
347
 
                    self._file_store.append([ False, path+marker, _('removed'), path ])
348
 
            else:
349
 
                self._file_store.append([ True, path+marker, _('removed'), path ])
350
 
 
351
 
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
352
 
            marker = osutils.kind_marker(kind)
353
 
            if text_modified or meta_modified:
354
 
                changes = _('renamed and modified')
355
 
            else:
356
 
                changes = _('renamed')
357
 
            if self.selected is not None:
358
 
                if newpath == os.path.join(self.wtpath, self.selected):
359
 
                    self._file_store.append([ True,
360
 
                                              oldpath+marker + '  =>  ' + newpath+marker,
361
 
                                              changes,
362
 
                                              newpath
363
 
                                            ])
364
 
                else:
365
 
                    self._file_store.append([ False,
366
 
                                              oldpath+marker + '  =>  ' + newpath+marker,
367
 
                                              changes,
368
 
                                              newpath
369
 
                                            ])
370
 
            else:
371
 
                self._file_store.append([ True,
372
 
                                          oldpath+marker + '  =>  ' + newpath+marker,
373
 
                                          changes,
374
 
                                          newpath
375
 
                                        ])
376
 
 
377
 
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
378
 
            marker = osutils.kind_marker(kind)
379
 
            if self.selected is not None:
380
 
                if path == os.path.join(self.wtpath, self.selected):
381
 
                    self._file_store.append([ True, path+marker, _('modified'), path ])
382
 
                else:
383
 
                    self._file_store.append([ False, path+marker, _('modified'), path ])
384
 
            else:
385
 
                self._file_store.append([ True, path+marker, _('modified'), path ])
386
 
    
387
 
    def _create_pending_merges(self):
388
 
        if not self.pending:
389
 
            return
390
 
        
391
 
        liststore = gtk.ListStore(gobject.TYPE_STRING,
392
 
                                  gobject.TYPE_STRING,
393
 
                                  gobject.TYPE_STRING)
394
 
        self._treeview_merges.set_model(liststore)
395
 
        
396
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
397
 
                                            gtk.CellRendererText(), text=0))
398
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
399
 
                                            gtk.CellRendererText(), text=1))
400
 
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
401
 
                                            gtk.CellRendererText(), text=2))
402
 
        
403
 
        for item in self.pending:
404
 
            liststore.append([ item['date'],
405
 
                               item['committer'],
406
 
                               item['summary'] ])
407
 
    
408
 
 
409
 
    def _create_diff_view(self):
410
 
        from diff import DiffDisplay
411
 
 
412
 
        self._diff_display = DiffDisplay()
413
 
        self._diff_display.set_trees(self.wt, self.wt.basis_tree())
414
 
        self._diff_display.show_diff(None)
415
 
        self._diff_display.show()
416
 
        self._hpane.pack2(self._diff_display)
417
 
 
418
 
    def _get_specific_files(self):
419
 
        ret = []
420
 
        it = self._file_store.get_iter_first()
421
 
        while it:
422
 
            if self._file_store.get_value(it, 0):
423
 
                # get real path from hidden column 3
424
 
                ret.append(self._file_store.get_value(it, 3))
425
 
            it = self._file_store.iter_next(it)
426
 
 
427
 
        return ret
428
 
    
429
 
    def _toggle_commit(self, cell, path, model):
430
 
        model[path][0] = not model[path][0]
431
 
        return
 
267
 
 
268
        rev_dict = {}
 
269
        rev_dict['committer'] = re.sub('<.*@.*>', '', rev.committer).strip(' ')
 
270
        rev_dict['summary'] = rev.get_summary()
 
271
        rev_dict['date'] = format_date(rev.timestamp,
 
272
                                       rev.timezone or 0,
 
273
                                       'original', date_fmt="%Y-%m-%d",
 
274
                                       show_offset=False)
 
275
        rev_dict['revision_id'] = rev.revision_id
 
276
        return rev_dict
 
277
 
 
278
 
 
279
# class CommitDialog(gtk.Dialog):
 
280
#     """ New implementation of the Commit dialog. """
 
281
#     def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
 
282
#         """ Initialize the Commit Dialog. """
 
283
#         gtk.Dialog.__init__(self, title="Commit - Olive",
 
284
#                                   parent=parent,
 
285
#                                   flags=0,
 
286
#                                   buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
 
287
#         
 
288
#         # Get arguments
 
289
#         self.wt = wt
 
290
#         self.wtpath = wtpath
 
291
#         self.notbranch = notbranch
 
292
#         self.selected = selected
 
293
#         
 
294
#         # Set the delta
 
295
#         self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
 
296
#         self.delta = self.wt.changes_from(self.old_tree)
 
297
#         
 
298
#         # Get pending merges
 
299
#         self.pending = self._pending_merges(self.wt)
 
300
#         
 
301
#         # Do some preliminary checks
 
302
#         self._is_checkout = False
 
303
#         self._is_pending = False
 
304
#         if self.wt is None and not self.notbranch:
 
305
#             error_dialog(_('Directory does not have a working tree'),
 
306
#                          _('Operation aborted.'))
 
307
#             self.close()
 
308
#             return
 
309
 
310
#         if self.notbranch:
 
311
#             error_dialog(_('Directory is not a branch'),
 
312
#                          _('You can perform this action only in a branch.'))
 
313
#             self.close()
 
314
#             return
 
315
#         else:
 
316
#             if self.wt.branch.get_bound_location() is not None:
 
317
#                 # we have a checkout, so the local commit checkbox must appear
 
318
#                 self._is_checkout = True
 
319
#             
 
320
#             if self.pending:
 
321
#                 # There are pending merges, file selection not supported
 
322
#                 self._is_pending = True
 
323
#         
 
324
#         # Create the widgets
 
325
#         # This is the main horizontal box, which is used to separate the commit
 
326
#         # info from the diff window.
 
327
#         self._hpane = gtk.HPaned()
 
328
#         self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
 
329
#         self._expander_files = gtk.Expander(_("File(s) to commit"))
 
330
#         self._vpaned_main = gtk.VPaned()
 
331
#         self._scrolledwindow_files = gtk.ScrolledWindow()
 
332
#         self._scrolledwindow_message = gtk.ScrolledWindow()
 
333
#         self._treeview_files = gtk.TreeView()
 
334
#         self._vbox_message = gtk.VBox()
 
335
#         self._label_message = gtk.Label(_("Commit message:"))
 
336
#         self._textview_message = gtk.TextView()
 
337
#         
 
338
#         if self._is_pending:
 
339
#             self._expander_merges = gtk.Expander(_("Pending merges"))
 
340
#             self._vpaned_list = gtk.VPaned()
 
341
#             self._scrolledwindow_merges = gtk.ScrolledWindow()
 
342
#             self._treeview_merges = gtk.TreeView()
 
343
 
344
#         # Set callbacks
 
345
#         self._button_commit.connect('clicked', self._on_commit_clicked)
 
346
#         self._treeview_files.connect('cursor-changed', self._on_treeview_files_cursor_changed)
 
347
#         self._treeview_files.connect('row-activated', self._on_treeview_files_row_activated)
 
348
#         
 
349
#         # Set properties
 
350
#         self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
 
351
#                                               gtk.POLICY_AUTOMATIC)
 
352
#         self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
 
353
#                                                 gtk.POLICY_AUTOMATIC)
 
354
#         self._textview_message.modify_font(pango.FontDescription("Monospace"))
 
355
#         self.set_default_size(500, 500)
 
356
#         self._vpaned_main.set_position(200)
 
357
#         self._button_commit.set_flags(gtk.CAN_DEFAULT)
 
358
 
359
#         if self._is_pending:
 
360
#             self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
 
361
#                                                    gtk.POLICY_AUTOMATIC)
 
362
#             self._treeview_files.set_sensitive(False)
 
363
#         
 
364
#         # Construct the dialog
 
365
#         self.action_area.pack_end(self._button_commit)
 
366
#         
 
367
#         self._scrolledwindow_files.add(self._treeview_files)
 
368
#         self._scrolledwindow_message.add(self._textview_message)
 
369
#         
 
370
#         self._expander_files.add(self._scrolledwindow_files)
 
371
#         
 
372
#         self._vbox_message.pack_start(self._label_message, False, False)
 
373
#         self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
 
374
#         
 
375
#         if self._is_pending:        
 
376
#             self._expander_merges.add(self._scrolledwindow_merges)
 
377
#             self._scrolledwindow_merges.add(self._treeview_merges)
 
378
#             self._vpaned_list.add1(self._expander_files)
 
379
#             self._vpaned_list.add2(self._expander_merges)
 
380
#             self._vpaned_main.add1(self._vpaned_list)
 
381
#         else:
 
382
#             self._vpaned_main.add1(self._expander_files)
 
383
 
384
#         self._vpaned_main.add2(self._vbox_message)
 
385
#         
 
386
#         self._hpane.pack1(self._vpaned_main)
 
387
#         self.vbox.pack_start(self._hpane, expand=True, fill=True)
 
388
#         if self._is_checkout: 
 
389
#             self._check_local = gtk.CheckButton(_("_Only commit locally"),
 
390
#                                                 use_underline=True)
 
391
#             self.vbox.pack_start(self._check_local, False, False)
 
392
#             if have_dbus:
 
393
#                 bus = dbus.SystemBus()
 
394
#                 proxy_obj = bus.get_object('org.freedesktop.NetworkManager', 
 
395
#                               '/org/freedesktop/NetworkManager')
 
396
#                 dbus_iface = dbus.Interface(
 
397
#                         proxy_obj, 'org.freedesktop.NetworkManager')
 
398
#                 try:
 
399
#                     # 3 is the enum value for STATE_CONNECTED
 
400
#                     self._check_local.set_active(dbus_iface.state() != 3)
 
401
#                 except dbus.DBusException, e:
 
402
#                     # Silently drop errors. While DBus may be 
 
403
#                     # available, NetworkManager doesn't necessarily have to be
 
404
#                     mutter("unable to get networkmanager state: %r" % e)
 
405
#                 
 
406
#         # Create the file list
 
407
#         self._create_file_view()
 
408
#         # Create the pending merges
 
409
#         self._create_pending_merges()
 
410
#         self._create_diff_view()
 
411
#         
 
412
#         # Expand the corresponding expander
 
413
#         if self._is_pending:
 
414
#             self._expander_merges.set_expanded(True)
 
415
#         else:
 
416
#             self._expander_files.set_expanded(True)
 
417
#         
 
418
#         # Display dialog
 
419
#         self.vbox.show_all()
 
420
#         
 
421
#         # Default to Commit button
 
422
#         self._button_commit.grab_default()
 
423
#     
 
424
#     def _show_diff_view(self, treeview):
 
425
#         # FIXME: the diff window freezes for some reason
 
426
#         treeselection = treeview.get_selection()
 
427
#         (model, iter) = treeselection.get_selected()
 
428
 
429
#         if iter is not None:
 
430
#             selected = model.get_value(iter, 3) # Get the real_path attribute
 
431
#             self._diff_display.show_diff([selected])
 
432
 
433
#     def _on_treeview_files_cursor_changed(self, treeview):
 
434
#         self._show_diff_view(treeview)
 
435
#         
 
436
#     def _on_treeview_files_row_activated(self, treeview, path, view_column):
 
437
#         self._show_diff_view(treeview)
 
438
#     
 
439
#     @show_bzr_error
 
440
#     def _on_commit_clicked(self, button):
 
441
#         """ Commit button clicked handler. """
 
442
#         textbuffer = self._textview_message.get_buffer()
 
443
#         start, end = textbuffer.get_bounds()
 
444
#         message = textbuffer.get_text(start, end).decode('utf-8')
 
445
#         
 
446
#         if not self.pending:
 
447
#             specific_files = self._get_specific_files()
 
448
#         else:
 
449
#             specific_files = None
 
450
 
451
#         if message == '':
 
452
#             response = question_dialog(_('Commit with an empty message?'),
 
453
#                                        _('You can describe your commit intent in the message.'))
 
454
#             if response == gtk.RESPONSE_NO:
 
455
#                 # Kindly give focus to message area
 
456
#                 self._textview_message.grab_focus()
 
457
#                 return
 
458
 
459
#         if self._is_checkout:
 
460
#             local = self._check_local.get_active()
 
461
#         else:
 
462
#             local = False
 
463
 
464
#         if list(self.wt.unknowns()) != []:
 
465
#             response = question_dialog(_("Commit with unknowns?"),
 
466
#                _("Unknown files exist in the working tree. Commit anyway?"))
 
467
#             if response == gtk.RESPONSE_NO:
 
468
#                 return
 
469
#         
 
470
#         try:
 
471
#             self.wt.commit(message,
 
472
#                        allow_pointless=False,
 
473
#                        strict=False,
 
474
#                        local=local,
 
475
#                        specific_files=specific_files)
 
476
#         except errors.PointlessCommit:
 
477
#             response = question_dialog(_('Commit with no changes?'),
 
478
#                                        _('There are no changes in the working tree.'))
 
479
#             if response == gtk.RESPONSE_YES:
 
480
#                 self.wt.commit(message,
 
481
#                                allow_pointless=True,
 
482
#                                strict=False,
 
483
#                                local=local,
 
484
#                                specific_files=specific_files)
 
485
#         self.response(gtk.RESPONSE_OK)
 
486
 
487
#     def _pending_merges(self, wt):
 
488
#         """ Return a list of pending merges or None if there are none of them. """
 
489
#         parents = wt.get_parent_ids()
 
490
#         if len(parents) < 2:
 
491
#             return None
 
492
#         
 
493
#         import re
 
494
#         from bzrlib.osutils import format_date
 
495
#         
 
496
#         pending = parents[1:]
 
497
#         branch = wt.branch
 
498
#         last_revision = parents[0]
 
499
#         
 
500
#         if last_revision is not None:
 
501
#             try:
 
502
#                 ignore = set(branch.repository.get_ancestry(last_revision))
 
503
#             except errors.NoSuchRevision:
 
504
#                 # the last revision is a ghost : assume everything is new 
 
505
#                 # except for it
 
506
#                 ignore = set([None, last_revision])
 
507
#         else:
 
508
#             ignore = set([None])
 
509
#         
 
510
#         pm = []
 
511
#         for merge in pending:
 
512
#             ignore.add(merge)
 
513
#             try:
 
514
#                 m_revision = branch.repository.get_revision(merge)
 
515
#                 
 
516
#                 rev = {}
 
517
#                 rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
 
518
#                 rev['summary'] = m_revision.get_summary()
 
519
#                 rev['date'] = format_date(m_revision.timestamp,
 
520
#                                           m_revision.timezone or 0, 
 
521
#                                           'original', date_fmt="%Y-%m-%d",
 
522
#                                           show_offset=False)
 
523
#                 
 
524
#                 pm.append(rev)
 
525
#                 
 
526
#                 inner_merges = branch.repository.get_ancestry(merge)
 
527
#                 assert inner_merges[0] is None
 
528
#                 inner_merges.pop(0)
 
529
#                 inner_merges.reverse()
 
530
#                 for mmerge in inner_merges:
 
531
#                     if mmerge in ignore:
 
532
#                         continue
 
533
#                     mm_revision = branch.repository.get_revision(mmerge)
 
534
#                     
 
535
#                     rev = {}
 
536
#                     rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
 
537
#                     rev['summary'] = mm_revision.get_summary()
 
538
#                     rev['date'] = format_date(mm_revision.timestamp,
 
539
#                                               mm_revision.timezone or 0, 
 
540
#                                               'original', date_fmt="%Y-%m-%d",
 
541
#                                               show_offset=False)
 
542
#                 
 
543
#                     pm.append(rev)
 
544
#                     
 
545
#                     ignore.add(mmerge)
 
546
#             except errors.NoSuchRevision:
 
547
#                 print "DEBUG: NoSuchRevision:", merge
 
548
#         
 
549
#         return pm
 
550
 
551
#     def _create_file_view(self):
 
552
#         self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
 
553
#                                          gobject.TYPE_STRING,    # [1] path to display
 
554
#                                          gobject.TYPE_STRING,    # [2] changes type
 
555
#                                          gobject.TYPE_STRING)    # [3] real path
 
556
#         self._treeview_files.set_model(self._file_store)
 
557
#         crt = gtk.CellRendererToggle()
 
558
#         crt.set_property("activatable", True)
 
559
#         crt.connect("toggled", self._toggle_commit, self._file_store)
 
560
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
 
561
#                                      crt, active=0))
 
562
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
 
563
#                                      gtk.CellRendererText(), text=1))
 
564
#         self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
 
565
#                                      gtk.CellRendererText(), text=2))
 
566
 
567
#         for path, id, kind in self.delta.added:
 
568
#             marker = osutils.kind_marker(kind)
 
569
#             if self.selected is not None:
 
570
#                 if path == os.path.join(self.wtpath, self.selected):
 
571
#                     self._file_store.append([ True, path+marker, _('added'), path ])
 
572
#                 else:
 
573
#                     self._file_store.append([ False, path+marker, _('added'), path ])
 
574
#             else:
 
575
#                 self._file_store.append([ True, path+marker, _('added'), path ])
 
576
 
577
#         for path, id, kind in self.delta.removed:
 
578
#             marker = osutils.kind_marker(kind)
 
579
#             if self.selected is not None:
 
580
#                 if path == os.path.join(self.wtpath, self.selected):
 
581
#                     self._file_store.append([ True, path+marker, _('removed'), path ])
 
582
#                 else:
 
583
#                     self._file_store.append([ False, path+marker, _('removed'), path ])
 
584
#             else:
 
585
#                 self._file_store.append([ True, path+marker, _('removed'), path ])
 
586
 
587
#         for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
 
588
#             marker = osutils.kind_marker(kind)
 
589
#             if text_modified or meta_modified:
 
590
#                 changes = _('renamed and modified')
 
591
#             else:
 
592
#                 changes = _('renamed')
 
593
#             if self.selected is not None:
 
594
#                 if newpath == os.path.join(self.wtpath, self.selected):
 
595
#                     self._file_store.append([ True,
 
596
#                                               oldpath+marker + '  =>  ' + newpath+marker,
 
597
#                                               changes,
 
598
#                                               newpath
 
599
#                                             ])
 
600
#                 else:
 
601
#                     self._file_store.append([ False,
 
602
#                                               oldpath+marker + '  =>  ' + newpath+marker,
 
603
#                                               changes,
 
604
#                                               newpath
 
605
#                                             ])
 
606
#             else:
 
607
#                 self._file_store.append([ True,
 
608
#                                           oldpath+marker + '  =>  ' + newpath+marker,
 
609
#                                           changes,
 
610
#                                           newpath
 
611
#                                         ])
 
612
 
613
#         for path, id, kind, text_modified, meta_modified in self.delta.modified:
 
614
#             marker = osutils.kind_marker(kind)
 
615
#             if self.selected is not None:
 
616
#                 if path == os.path.join(self.wtpath, self.selected):
 
617
#                     self._file_store.append([ True, path+marker, _('modified'), path ])
 
618
#                 else:
 
619
#                     self._file_store.append([ False, path+marker, _('modified'), path ])
 
620
#             else:
 
621
#                 self._file_store.append([ True, path+marker, _('modified'), path ])
 
622
#     
 
623
#     def _create_pending_merges(self):
 
624
#         if not self.pending:
 
625
#             return
 
626
#         
 
627
#         liststore = gtk.ListStore(gobject.TYPE_STRING,
 
628
#                                   gobject.TYPE_STRING,
 
629
#                                   gobject.TYPE_STRING)
 
630
#         self._treeview_merges.set_model(liststore)
 
631
#         
 
632
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
 
633
#                                             gtk.CellRendererText(), text=0))
 
634
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
 
635
#                                             gtk.CellRendererText(), text=1))
 
636
#         self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
 
637
#                                             gtk.CellRendererText(), text=2))
 
638
#         
 
639
#         for item in self.pending:
 
640
#             liststore.append([ item['date'],
 
641
#                                item['committer'],
 
642
#                                item['summary'] ])
 
643
#     
 
644
 
645
#     def _create_diff_view(self):
 
646
#         from diff import DiffDisplay
 
647
 
648
#         self._diff_display = DiffDisplay()
 
649
#         self._diff_display.set_trees(self.wt, self.wt.basis_tree())
 
650
#         self._diff_display.show_diff(None)
 
651
#         self._diff_display.show()
 
652
#         self._hpane.pack2(self._diff_display)
 
653
 
654
#     def _get_specific_files(self):
 
655
#         ret = []
 
656
#         it = self._file_store.get_iter_first()
 
657
#         while it:
 
658
#             if self._file_store.get_value(it, 0):
 
659
#                 # get real path from hidden column 3
 
660
#                 ret.append(self._file_store.get_value(it, 3))
 
661
#             it = self._file_store.iter_next(it)
 
662
 
663
#         return ret
 
664
#     
 
665
#     def _toggle_commit(self, cell, path, model):
 
666
#         model[path][0] = not model[path][0]
 
667
#         return