/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 gtk.glade
25
import gobject
26
import pango
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
27
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
28
import bzrlib.errors as errors
93.1.10 by Alexander Belchenko
- Show file kind marker with path (i.e. directory path ends with '/')
29
from bzrlib import osutils
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
30
66.3.1 by v.ladeuil+lp at free
Fix #73737. Check empty message at commit time.
31
from dialog import error_dialog, question_dialog
93.1.12 by Alexander Belchenko
Names XML files with GUI resources obtained via olive/guifiles.py
32
from guifiles import GLADEFILENAME
93.1.6 by Alexander Belchenko
detecting name of glade file doing in separate module (olive.gladefile)
33
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
34
89 by Jelmer Vernooij
Rename OliveBranch -> BranchDialog.
35
class CommitDialog:
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
36
    """ Display Commit dialog and perform the needed actions. """
93.1.17 by Alexander Belchenko
gcommit reworked again.
37
    def __init__(self, wt, wtpath, notbranch):
93.1.7 by Alexander Belchenko
Fix gcommit bug 66937 (bzr still running after cancel/commit clicked)
38
        """ Initialize the Commit dialog.
91.1.9 by Jelmer Vernooij
Use epydoc style (for consistency with Bazaar).
39
        :param  wt:         bzr working tree object
40
        :param  wtpath:     path to working tree root
91.1.11 by Jelmer Vernooij
Cherrypick Alexanders' fix for #68127.
41
        :param  notbranch:  flag that path is not a brach
42
        :type   notbranch:  bool
93.1.7 by Alexander Belchenko
Fix gcommit bug 66937 (bzr still running after cancel/commit clicked)
43
        """
93.1.6 by Alexander Belchenko
detecting name of glade file doing in separate module (olive.gladefile)
44
        self.glade = gtk.glade.XML(GLADEFILENAME, 'window_commit', 'olive-gtk')
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
45
        
0.13.1 by Jelmer Vernooij
Remove communicator use from Commit.
46
        self.wt = wt
47
        self.wtpath = wtpath
93.1.17 by Alexander Belchenko
gcommit reworked again.
48
        self.notbranch = notbranch
93.1.7 by Alexander Belchenko
Fix gcommit bug 66937 (bzr still running after cancel/commit clicked)
49
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
50
        # Get some important widgets
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
51
        self.window = self.glade.get_widget('window_commit')
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
52
        self.checkbutton_local = self.glade.get_widget('checkbutton_commit_local')
53
        self.textview = self.glade.get_widget('textview_commit')
115 by Szilveszter Farkas (Phanatic)
Reworked Commit dialog to fix bug #73778.
54
        self.file_expander = self.glade.get_widget('expander_commit_select')
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
55
        self.file_view = self.glade.get_widget('treeview_commit_select')
115 by Szilveszter Farkas (Phanatic)
Reworked Commit dialog to fix bug #73778.
56
        self.pending_expander = self.glade.get_widget('expander_commit_pending')
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
57
        self.pending_label = self.glade.get_widget('label_commit_pending')
58
        self.pending_view = self.glade.get_widget('treeview_commit_pending')
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
59
93.1.17 by Alexander Belchenko
gcommit reworked again.
60
        if wt is None or notbranch:
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
61
            return
62
        
63
        # Set the delta
64
        self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
0.13.6 by Jelmer Vernooij
Don't pass along dialog context everywhere.
65
        self.delta = self.wt.changes_from(self.old_tree)
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
66
        
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
67
        # Get pending merges
68
        self.pending = self._pending_merges(self.wt)
69
        
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
70
        # Dictionary for signal_autoconnect
71
        dic = { "on_button_commit_commit_clicked": self.commit,
72
                "on_button_commit_cancel_clicked": self.close }
93.1.7 by Alexander Belchenko
Fix gcommit bug 66937 (bzr still running after cancel/commit clicked)
73
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
74
        # Connect the signals to the handlers
75
        self.glade.signal_autoconnect(dic)
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
76
        
77
        # Create the file list
78
        self._create_file_view()
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
79
        # Create the pending merges
80
        self._create_pending_merges()
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
81
    
82
    def display(self):
93.1.17 by Alexander Belchenko
gcommit reworked again.
83
        """ Display the Push dialog.
84
        @return:    True if dialog is shown.
85
        """
86
        if self.wt is None and not self.notbranch:
87
            error_dialog(_('Directory does not have a working tree'),
88
                         _('Operation aborted.'))
89
            self.close()
90
            return False
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
91
        if self.notbranch:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
92
            error_dialog(_('Directory is not a branch'),
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
93
                         _('You can perform this action only in a branch.'))
0.8.46 by Szilveszter Farkas (Phanatic)
Modified OliveDialog class interface; huge cleanups.
94
            self.close()
93.1.17 by Alexander Belchenko
gcommit reworked again.
95
            return False
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
96
        else:
0.13.1 by Jelmer Vernooij
Remove communicator use from Commit.
97
            if self.wt.branch.get_bound_location() is not None:
0.8.23 by Szilveszter Farkas (Phanatic)
Visual feedback when Olive is busy; follow bzr API changes; commit dialog update
98
                # we have a checkout, so the local commit checkbox must appear
99
                self.checkbutton_local.show()
100
            
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
101
            if self.pending:
102
                # There are pending merges, file selection not supported
115 by Szilveszter Farkas (Phanatic)
Reworked Commit dialog to fix bug #73778.
103
                self.file_expander.set_expanded(False)
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
104
                self.file_view.set_sensitive(False)
105
            else:
106
                # No pending merges
115 by Szilveszter Farkas (Phanatic)
Reworked Commit dialog to fix bug #73778.
107
                self.pending_expander.hide()
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
108
            
0.8.26 by Szilveszter Farkas (Phanatic)
Implemented Diff window; added menu.py (was missing from last commit)
109
            self.textview.modify_font(pango.FontDescription("Monospace"))
0.8.23 by Szilveszter Farkas (Phanatic)
Visual feedback when Olive is busy; follow bzr API changes; commit dialog update
110
            self.window.show()
93.1.17 by Alexander Belchenko
gcommit reworked again.
111
            return True
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
112
    
113
    def _create_file_view(self):
93.1.13 by Alexander Belchenko
Fix bug introduced by revid:bialix@ukr.net-20061025102040-90bcdbad341ee3fa
114
        self.file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
115
                                        gobject.TYPE_STRING,    # [1] path to display
116
                                        gobject.TYPE_STRING,    # [2] changes type
117
                                        gobject.TYPE_STRING)    # [3] real path
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
118
        self.file_view.set_model(self.file_store)
119
        crt = gtk.CellRendererToggle()
120
        crt.set_property("activatable", True)
121
        crt.connect("toggled", self._toggle_commit, self.file_store)
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
122
        self.file_view.append_column(gtk.TreeViewColumn(_('Commit'),
0.8.21 by Szilveszter Farkas (Phanatic)
2006-07-25 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
123
                                     crt, active=0))
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
124
        self.file_view.append_column(gtk.TreeViewColumn(_('Path'),
0.8.21 by Szilveszter Farkas (Phanatic)
2006-07-25 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
125
                                     gtk.CellRendererText(), text=1))
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
126
        self.file_view.append_column(gtk.TreeViewColumn(_('Type'),
0.8.21 by Szilveszter Farkas (Phanatic)
2006-07-25 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
127
                                     gtk.CellRendererText(), text=2))
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
128
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
129
        for path, id, kind in self.delta.added:
93.1.10 by Alexander Belchenko
- Show file kind marker with path (i.e. directory path ends with '/')
130
            marker = osutils.kind_marker(kind)
93.1.13 by Alexander Belchenko
Fix bug introduced by revid:bialix@ukr.net-20061025102040-90bcdbad341ee3fa
131
            self.file_store.append([ True, path+marker, _('added'), path ])
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
132
133
        for path, id, kind in self.delta.removed:
93.1.10 by Alexander Belchenko
- Show file kind marker with path (i.e. directory path ends with '/')
134
            marker = osutils.kind_marker(kind)
93.1.13 by Alexander Belchenko
Fix bug introduced by revid:bialix@ukr.net-20061025102040-90bcdbad341ee3fa
135
            self.file_store.append([ True, path+marker, _('removed'), path ])
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
136
137
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
93.1.10 by Alexander Belchenko
- Show file kind marker with path (i.e. directory path ends with '/')
138
            marker = osutils.kind_marker(kind)
93.1.13 by Alexander Belchenko
Fix bug introduced by revid:bialix@ukr.net-20061025102040-90bcdbad341ee3fa
139
            if text_modified or meta_modified:
140
                changes = _('renamed and modified')
141
            else:
142
                changes = _('renamed')
93.1.10 by Alexander Belchenko
- Show file kind marker with path (i.e. directory path ends with '/')
143
            self.file_store.append([ True,
144
                                     oldpath+marker + '  =>  ' + newpath+marker,
93.1.13 by Alexander Belchenko
Fix bug introduced by revid:bialix@ukr.net-20061025102040-90bcdbad341ee3fa
145
                                     changes,
146
                                     newpath
147
                                   ])
0.8.55 by Szilveszter Farkas (Phanatic)
Gettext support added.
148
149
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
93.1.10 by Alexander Belchenko
- Show file kind marker with path (i.e. directory path ends with '/')
150
            marker = osutils.kind_marker(kind)
93.1.13 by Alexander Belchenko
Fix bug introduced by revid:bialix@ukr.net-20061025102040-90bcdbad341ee3fa
151
            self.file_store.append([ True, path+marker, _('modified'), path ])
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
152
    
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
153
    def _create_pending_merges(self):
93.1.19 by Alexander Belchenko
If there is no pending merge on commit then don't show empty pending merge part in commit dialog.
154
        if not self.pending:
155
            # hide unused pending merge part
156
            scrolled_window = self.glade.get_widget('scrolledwindow_commit_pending')
157
            parent = scrolled_window.get_parent()
158
            parent.remove(scrolled_window)
159
            parent = self.pending_label.get_parent()
160
            parent.remove(self.pending_label)
161
            return
162
        
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
163
        liststore = gtk.ListStore(gobject.TYPE_STRING,
164
                                  gobject.TYPE_STRING,
165
                                  gobject.TYPE_STRING)
166
        self.pending_view.set_model(liststore)
167
        
168
        self.pending_view.append_column(gtk.TreeViewColumn(_('Date'),
169
                                        gtk.CellRendererText(), text=0))
170
        self.pending_view.append_column(gtk.TreeViewColumn(_('Committer'),
171
                                        gtk.CellRendererText(), text=1))
172
        self.pending_view.append_column(gtk.TreeViewColumn(_('Summary'),
173
                                        gtk.CellRendererText(), text=2))
174
        
175
        for item in self.pending:
176
            liststore.append([ item['date'],
177
                               item['committer'],
178
                               item['summary'] ])
179
    
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
180
    def _get_specific_files(self):
181
        ret = []
182
        it = self.file_store.get_iter_first()
183
        while it:
184
            if self.file_store.get_value(it, 0):
93.1.13 by Alexander Belchenko
Fix bug introduced by revid:bialix@ukr.net-20061025102040-90bcdbad341ee3fa
185
                # get real path from hidden column 3
186
                ret.append(self.file_store.get_value(it, 3))
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
187
            it = self.file_store.iter_next(it)
188
189
        return ret
190
    
191
    def _toggle_commit(self, cell, path, model):
192
        model[path][0] = not model[path][0]
193
        return
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
194
    
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
195
    def _pending_merges(self, wt):
196
        """ Return a list of pending merges or None if there are none of them. """
197
        parents = wt.get_parent_ids()
198
        if len(parents) < 2:
199
            return None
200
        
201
        import re
202
        from bzrlib.osutils import format_date
203
        
204
        pending = parents[1:]
205
        branch = wt.branch
206
        last_revision = parents[0]
207
        
208
        if last_revision is not None:
209
            try:
210
                ignore = set(branch.repository.get_ancestry(last_revision))
211
            except errors.NoSuchRevision:
212
                # the last revision is a ghost : assume everything is new 
213
                # except for it
214
                ignore = set([None, last_revision])
215
        else:
216
            ignore = set([None])
217
        
218
        pm = []
219
        for merge in pending:
220
            ignore.add(merge)
221
            try:
222
                m_revision = branch.repository.get_revision(merge)
223
                
224
                rev = {}
225
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
226
                rev['summary'] = m_revision.get_summary()
227
                rev['date'] = format_date(m_revision.timestamp,
228
                                          m_revision.timezone or 0, 
229
                                          'original', date_fmt="%Y-%m-%d",
230
                                          show_offset=False)
231
                
232
                pm.append(rev)
233
                
234
                inner_merges = branch.repository.get_ancestry(merge)
235
                assert inner_merges[0] is None
236
                inner_merges.pop(0)
237
                inner_merges.reverse()
238
                for mmerge in inner_merges:
239
                    if mmerge in ignore:
240
                        continue
241
                    mm_revision = branch.repository.get_revision(mmerge)
242
                    
243
                    rev = {}
244
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
245
                    rev['summary'] = mm_revision.get_summary()
246
                    rev['date'] = format_date(mm_revision.timestamp,
247
                                              mm_revision.timezone or 0, 
248
                                              'original', date_fmt="%Y-%m-%d",
249
                                              show_offset=False)
250
                
251
                    pm.append(rev)
252
                    
253
                    ignore.add(mmerge)
254
            except errors.NoSuchRevision:
255
                print "DEBUG: NoSuchRevision:", merge
256
        
257
        return pm
258
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
259
    def commit(self, widget):
0.8.26 by Szilveszter Farkas (Phanatic)
Implemented Diff window; added menu.py (was missing from last commit)
260
        textbuffer = self.textview.get_buffer()
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
261
        start, end = textbuffer.get_bounds()
93.1.9 by Alexander Belchenko
Fix bug 67927 (non-ascii commit message saved as utf-8 string)
262
        message = textbuffer.get_text(start, end).decode('utf-8')
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
263
        
264
        checkbutton_strict = self.glade.get_widget('checkbutton_commit_strict')
265
        checkbutton_force = self.glade.get_widget('checkbutton_commit_force')
266
        
95 by Szilveszter Farkas (Phanatic)
Added pending merges to Commit dialog. Fixed bug #66091.
267
        if not self.pending:
268
            specific_files = self._get_specific_files()
269
        else:
270
            specific_files = None
66.3.1 by v.ladeuil+lp at free
Fix #73737. Check empty message at commit time.
271
272
        if message == '':
273
            response = question_dialog('Commit with an empty message ?',
274
                                       'You can describe your commit intent'
275
                                       +' in the message')
276
            if response == gtk.RESPONSE_NO:
277
                # Kindly give focus to message area
278
                self.textview.grab_focus()
279
                return
280
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
281
        try:
66.3.1 by v.ladeuil+lp at free
Fix #73737. Check empty message at commit time.
282
            self.wt.commit(message,
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
283
                           allow_pointless=checkbutton_force.get_active(),
284
                           strict=checkbutton_strict.get_active(),
0.8.23 by Szilveszter Farkas (Phanatic)
Visual feedback when Olive is busy; follow bzr API changes; commit dialog update
285
                           local=self.checkbutton_local.get_active(),
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
286
                           specific_files=specific_files)
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
287
        except errors.NotBranchError:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
288
            error_dialog(_('Directory is not a branch'),
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
289
                         _('You can perform this action only in a branch.'))
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
290
            return
291
        except errors.LocalRequiresBoundBranch:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
292
            error_dialog(_('Directory is not a checkout'),
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
293
                         _('You can perform local commit only on checkouts.'))
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
294
            return
295
        except errors.PointlessCommit:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
296
            error_dialog(_('No changes to commit'),
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
297
                         _('Try force commit if you want to commit anyway.'))
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
298
            return
299
        except errors.ConflictsInTree:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
300
            error_dialog(_('Conflicts in tree'),
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
301
                         _('You need to resolve the conflicts before committing.'))
0.8.20 by Szilveszter Farkas (Phanatic)
2006-07-24 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
302
            return
303
        except errors.StrictCommitFailed:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
304
            error_dialog(_('Strict commit failed'),
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
305
                         _('There are unknown files in the working tree.\nPlease add or delete them.'))
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
306
            return
307
        except errors.BoundBranchOutOfDate, errmsg:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
308
            error_dialog(_('Bound branch is out of date'),
0.8.98 by Szilveszter Farkas (Phanatic)
Loads of fixes. Pyflakes cleanup.
309
                         _('%s') % errmsg)
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
310
            return
0.13.2 by Jelmer Vernooij
Handle unknown errors in commit.
311
        except errors.BzrError, msg:
0.13.4 by Jelmer Vernooij
Handle non-bzr unknown errors as well.
312
            error_dialog(_('Unknown bzr error'), str(msg))
313
            return
314
        except Exception, msg:
0.13.3 by Jelmer Vernooij
Start removing dialog context (it's not required).
315
            error_dialog(_('Unknown error'), str(msg))
0.13.2 by Jelmer Vernooij
Handle unknown errors in commit.
316
            return
93.1.7 by Alexander Belchenko
Fix gcommit bug 66937 (bzr still running after cancel/commit clicked)
317
93.1.17 by Alexander Belchenko
gcommit reworked again.
318
        self.close()
319
0.8.19 by Szilveszter Farkas (Phanatic)
2006-07-21 Szilveszter Farkas <Szilveszter.Farkas@gmail.com>
320
    def close(self, widget=None):
321
        self.window.destroy()
126.1.1 by Szilveszter Farkas (Phanatic)
New Commit dialog implementation (no more Glade).
322
323
class CommitDialogNew(gtk.Dialog):
324
    """ New implementation of the Commit dialog. """
325
    def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
326
        """ Initialize the Commit Dialog. """
327
        gtk.Dialog.__init__(self, title="Commit - Olive",
328
                                  parent=parent,
329
                                  flags=0,
330
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
331
        
332
        # Get arguments
333
        self.wt = wt
334
        self.wtpath = wtpath
335
        self.notbranch = notbranch
336
        self.selected = selected
337
        
338
        # Set the delta
339
        self.old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
340
        self.delta = self.wt.changes_from(self.old_tree)
341
        
342
        # Get pending merges
343
        self.pending = self._pending_merges(self.wt)
344
        
345
        # Do some preliminary checks
346
        self._is_checkout = False
347
        self._is_pending = False
348
        if self.wt is None and not self.notbranch:
349
            error_dialog(_('Directory does not have a working tree'),
350
                         _('Operation aborted.'))
351
            self.close()
352
            return
353
354
        if self.notbranch:
355
            error_dialog(_('Directory is not a branch'),
356
                         _('You can perform this action only in a branch.'))
357
            self.close()
358
            return
359
        else:
360
            if self.wt.branch.get_bound_location() is not None:
361
                # we have a checkout, so the local commit checkbox must appear
362
                self._is_checkout = True
363
            
364
            if self.pending:
365
                # There are pending merges, file selection not supported
366
                self._is_pending = True
367
        
368
        # Create the widgets
369
        self._button_commit = gtk.Button(_("Comm_it"), use_underline=True)
370
        if self._is_checkout:
371
            self._check_local = gtk.CheckButton(_("_Local only commit (works in checkouts)"),
372
                                                use_underline=True)
373
        self._check_strict = gtk.CheckButton(_("_Strict commit (fails if unknown files are present)"),
374
                                             use_underline=True)
375
        self._expander_files = gtk.Expander(_("Please select the file(s) to commit"))
376
        self._vpaned_main = gtk.VPaned()
377
        self._scrolledwindow_files = gtk.ScrolledWindow()
378
        self._scrolledwindow_message = gtk.ScrolledWindow()
379
        self._treeview_files = gtk.TreeView()
380
        self._vbox_message = gtk.VBox()
381
        self._label_message = gtk.Label(_("Please specify a commit message:"))
382
        self._textview_message = gtk.TextView()
383
        
384
        if self._is_pending:
385
            self._expander_merges = gtk.Expander(_("Pending merges"))
386
            self._vpaned_list = gtk.VPaned()
387
            self._scrolledwindow_merges = gtk.ScrolledWindow()
388
            self._treeview_merges = gtk.TreeView()
389
390
        # Set callbacks
391
        self._button_commit.connect('clicked', self._on_commit_clicked)
392
        self._treeview_files.connect('row_activated', self._on_treeview_files_row_activated)
393
        
394
        # Set properties
395
        self._scrolledwindow_files.set_policy(gtk.POLICY_AUTOMATIC,
396
                                              gtk.POLICY_AUTOMATIC)
397
        self._scrolledwindow_message.set_policy(gtk.POLICY_AUTOMATIC,
398
                                                gtk.POLICY_AUTOMATIC)
399
        self._textview_message.modify_font(pango.FontDescription("Monospace"))
400
        self.set_default_size(500, 500)
401
        self._vpaned_main.set_position(200)
402
403
        if self._is_pending:
404
            self._scrolledwindow_merges.set_policy(gtk.POLICY_AUTOMATIC,
405
                                                   gtk.POLICY_AUTOMATIC)
406
            self._treeview_files.set_sensitive(False)
407
        
408
        # Construct the dialog
409
        self.action_area.pack_end(self._button_commit)
410
        
411
        self._scrolledwindow_files.add(self._treeview_files)
412
        self._scrolledwindow_message.add(self._textview_message)
413
        
414
        self._expander_files.add(self._scrolledwindow_files)
415
        
416
        self._vbox_message.pack_start(self._label_message, False, False)
417
        self._vbox_message.pack_start(self._scrolledwindow_message, True, True)
418
        
419
        if self._is_pending:        
420
            self._expander_merges.add(self._scrolledwindow_merges)
421
            self._scrolledwindow_merges.add(self._treeview_merges)
422
            self._vpaned_list.add1(self._expander_files)
423
            self._vpaned_list.add2(self._expander_merges)
424
            self._vpaned_main.add1(self._vpaned_list)
425
        else:
426
            self._vpaned_main.add1(self._expander_files)
427
428
        self._vpaned_main.add2(self._vbox_message)
429
        
430
        self.vbox.pack_start(self._vpaned_main, True, True)
431
        if self._is_checkout:
432
            self.vbox.pack_start(self._check_local, False, False)
433
        self.vbox.pack_start(self._check_strict, False, False)
434
        
435
        # Create the file list
436
        self._create_file_view()
437
        # Create the pending merges
438
        self._create_pending_merges()
439
        
440
        # Expand the corresponding expander
441
        if self._is_pending:
442
            self._expander_merges.set_expanded(True)
443
        else:
444
            self._expander_files.set_expanded(True)
445
        
446
        # Display dialog
447
        self.vbox.show_all()
448
    
449
    def _on_treeview_files_row_activated(self, treeview, path, view_column):
450
        # FIXME: the diff window freezes for some reason
451
        treeselection = treeview.get_selection()
452
        (model, iter) = treeselection.get_selected()
453
        
454
        if iter is not None:
455
            from olive import DiffWindow
456
            
457
            _selected = model.get_value(iter, 1)
458
            
459
            diff = DiffWindow()
460
            parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
461
            diff.set_diff(self.wt.branch.nick, self.wt, parent_tree)
462
            try:
463
                diff.set_file(_selected)
464
            except errors.NoSuchFile:
465
                pass
466
            diff.show()
467
    
468
    def _on_commit_clicked(self, button):
469
        """ Commit button clicked handler. """
470
        textbuffer = self._textview_message.get_buffer()
471
        start, end = textbuffer.get_bounds()
472
        message = textbuffer.get_text(start, end).decode('utf-8')
473
        
474
        if not self.pending:
475
            specific_files = self._get_specific_files()
476
        else:
477
            specific_files = None
478
479
        if message == '':
480
            response = question_dialog(_('Commit with an empty message?'),
481
                                       _('You can describe your commit intent in the message.'))
482
            if response == gtk.RESPONSE_NO:
483
                # Kindly give focus to message area
484
                self._textview_message.grab_focus()
485
                return
486
487
        if self._is_checkout:
488
            local = self._check_local.get_active()
489
        else:
490
            local = False
491
        
492
        try:
493
            self.wt.commit(message,
494
                           allow_pointless=False,
495
                           strict=self._check_strict.get_active(),
496
                           local=local,
497
                           specific_files=specific_files)
498
        except errors.NotBranchError:
499
            error_dialog(_('Directory is not a branch'),
500
                         _('You can perform this action only in a branch.'))
501
            return
502
        except errors.LocalRequiresBoundBranch:
503
            error_dialog(_('Directory is not a checkout'),
504
                         _('You can perform local commit only on checkouts.'))
505
            return
506
        except errors.ConflictsInTree:
507
            error_dialog(_('Conflicts in tree'),
508
                         _('You need to resolve the conflicts before committing.'))
509
            return
510
        except errors.StrictCommitFailed:
511
            error_dialog(_('Strict commit failed'),
512
                         _('There are unknown files in the working tree.\nPlease add or delete them.'))
513
            return
514
        except errors.BoundBranchOutOfDate, errmsg:
515
            error_dialog(_('Bound branch is out of date'),
516
                         _('%s') % errmsg)
517
            return
518
        except errors.PointlessCommit:
519
            response = question_dialog(_('Commit with no changes?'),
520
                                       _('There are no changes in the working tree.'))
521
            if response == gtk.RESPONSE_YES:
522
                # Try to commit again
523
                try:
524
                    self.wt.commit(message,
525
                                   allow_pointless=True,
526
                                   strict=self._check_strict.get_active(),
527
                                   local=local,
528
                                   specific_files=specific_files)
529
                except errors.BzrError, msg:
530
                    error_dialog(_('Unknown bzr error'), str(msg))
531
                    return
532
                except Exception, msg:
533
                    error_dialog(_('Unknown error'), str(msg))
534
                    return
535
        except errors.BzrError, msg:
536
            error_dialog(_('Unknown bzr error'), str(msg))
537
            return
538
        except Exception, msg:
539
            error_dialog(_('Unknown error'), str(msg))
540
            return
541
        
542
        self.response(gtk.RESPONSE_OK)
543
544
    def _pending_merges(self, wt):
545
        """ Return a list of pending merges or None if there are none of them. """
546
        parents = wt.get_parent_ids()
547
        if len(parents) < 2:
548
            return None
549
        
550
        import re
551
        from bzrlib.osutils import format_date
552
        
553
        pending = parents[1:]
554
        branch = wt.branch
555
        last_revision = parents[0]
556
        
557
        if last_revision is not None:
558
            try:
559
                ignore = set(branch.repository.get_ancestry(last_revision))
560
            except errors.NoSuchRevision:
561
                # the last revision is a ghost : assume everything is new 
562
                # except for it
563
                ignore = set([None, last_revision])
564
        else:
565
            ignore = set([None])
566
        
567
        pm = []
568
        for merge in pending:
569
            ignore.add(merge)
570
            try:
571
                m_revision = branch.repository.get_revision(merge)
572
                
573
                rev = {}
574
                rev['committer'] = re.sub('<.*@.*>', '', m_revision.committer).strip(' ')
575
                rev['summary'] = m_revision.get_summary()
576
                rev['date'] = format_date(m_revision.timestamp,
577
                                          m_revision.timezone or 0, 
578
                                          'original', date_fmt="%Y-%m-%d",
579
                                          show_offset=False)
580
                
581
                pm.append(rev)
582
                
583
                inner_merges = branch.repository.get_ancestry(merge)
584
                assert inner_merges[0] is None
585
                inner_merges.pop(0)
586
                inner_merges.reverse()
587
                for mmerge in inner_merges:
588
                    if mmerge in ignore:
589
                        continue
590
                    mm_revision = branch.repository.get_revision(mmerge)
591
                    
592
                    rev = {}
593
                    rev['committer'] = re.sub('<.*@.*>', '', mm_revision.committer).strip(' ')
594
                    rev['summary'] = mm_revision.get_summary()
595
                    rev['date'] = format_date(mm_revision.timestamp,
596
                                              mm_revision.timezone or 0, 
597
                                              'original', date_fmt="%Y-%m-%d",
598
                                              show_offset=False)
599
                
600
                    pm.append(rev)
601
                    
602
                    ignore.add(mmerge)
603
            except errors.NoSuchRevision:
604
                print "DEBUG: NoSuchRevision:", merge
605
        
606
        return pm
607
608
    def _create_file_view(self):
609
        self._file_store = gtk.ListStore(gobject.TYPE_BOOLEAN,   # [0] checkbox
610
                                         gobject.TYPE_STRING,    # [1] path to display
611
                                         gobject.TYPE_STRING,    # [2] changes type
612
                                         gobject.TYPE_STRING)    # [3] real path
613
        self._treeview_files.set_model(self._file_store)
614
        crt = gtk.CellRendererToggle()
615
        crt.set_property("activatable", True)
616
        crt.connect("toggled", self._toggle_commit, self._file_store)
617
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Commit'),
618
                                     crt, active=0))
619
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Path'),
620
                                     gtk.CellRendererText(), text=1))
621
        self._treeview_files.append_column(gtk.TreeViewColumn(_('Type'),
622
                                     gtk.CellRendererText(), text=2))
623
624
        for path, id, kind in self.delta.added:
625
            marker = osutils.kind_marker(kind)
626
            self._file_store.append([ True, path+marker, _('added'), path ])
627
628
        for path, id, kind in self.delta.removed:
629
            marker = osutils.kind_marker(kind)
630
            self._file_store.append([ True, path+marker, _('removed'), path ])
631
632
        for oldpath, newpath, id, kind, text_modified, meta_modified in self.delta.renamed:
633
            marker = osutils.kind_marker(kind)
634
            if text_modified or meta_modified:
635
                changes = _('renamed and modified')
636
            else:
637
                changes = _('renamed')
638
            self._file_store.append([ True,
639
                                      oldpath+marker + '  =>  ' + newpath+marker,
640
                                      changes,
641
                                      newpath
642
                                    ])
643
644
        for path, id, kind, text_modified, meta_modified in self.delta.modified:
645
            marker = osutils.kind_marker(kind)
646
            self._file_store.append([ True, path+marker, _('modified'), path ])
647
    
648
    def _create_pending_merges(self):
649
        if not self.pending:
650
            return
651
        
652
        liststore = gtk.ListStore(gobject.TYPE_STRING,
653
                                  gobject.TYPE_STRING,
654
                                  gobject.TYPE_STRING)
655
        self._treeview_merges.set_model(liststore)
656
        
657
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Date'),
658
                                            gtk.CellRendererText(), text=0))
659
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Committer'),
660
                                            gtk.CellRendererText(), text=1))
661
        self._treeview_merges.append_column(gtk.TreeViewColumn(_('Summary'),
662
                                            gtk.CellRendererText(), text=2))
663
        
664
        for item in self.pending:
665
            liststore.append([ item['date'],
666
                               item['committer'],
667
                               item['summary'] ])
668
    
669
    def _get_specific_files(self):
670
        ret = []
671
        it = self._file_store.get_iter_first()
672
        while it:
673
            if self._file_store.get_value(it, 0):
674
                # get real path from hidden column 3
675
                ret.append(self._file_store.get_value(it, 3))
676
            it = self._file_store.iter_next(it)
677
678
        return ret
679
    
680
    def _toggle_commit(self, cell, path, model):
681
        model[path][0] = not model[path][0]
682
        return