~ubuntu-branches/ubuntu/trusty/groundcontrol/trusty

« back to all changes in this revision

Viewing changes to .pc/01_no_bzr_gtk/GroundControl/commiter.py

  • Committer: Package Import Robot
  • Author(s): Jelmer Vernooij
  • Date: 2013-10-16 23:14:19 UTC
  • Revision ID: package-import@ubuntu.com-20131016231419-5wu1km56y44qpv6l
Tags: 1.6.6-1.1
* Non-maintainer upload.
* Add 01_no_bzr_gtk: comments out dependencies on bzr-gtk, which is
  due to be removed from the archive. Closes: #722531

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Copyright 2009 Martin Owens
 
3
#
 
4
# This program is free software: you can redistribute it and/or modify
 
5
#  it under the terms of the GNU General Public License as published by
 
6
#  the Free Software Foundation, either version 3 of the License, or
 
7
#  (at your option) any later version.
 
8
#
 
9
#  This program is distributed in the hope that it will be useful,
 
10
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
#  GNU General Public License for more details.
 
13
#
 
14
#  You should have received a copy of the GNU General Public License
 
15
#  along with this program.  If not, see <http://www.gnu.org/licenses/>
 
16
#
 
17
"""App for looking at changes and confirming them."""
 
18
 
 
19
# Import standard python libs
 
20
import os
 
21
import gtk
 
22
import gtk.gdk
 
23
import logging
 
24
 
 
25
# Various required variables and locations
 
26
from GroundControl.gtkviews import (
 
27
    TreeView, Window,
 
28
    IconManager, GtkApp
 
29
)
 
30
from GroundControl.bugs import BugSelection
 
31
from bzrlib.plugins.gtk.diff import DiffView
 
32
 
 
33
change_types = (
 
34
    'added',         # (path, id, kind)
 
35
    'removed',       # (path, id, kind)
 
36
    'renamed',       # (oldpath, newpath, id, kind, text_modified, meta_mod)
 
37
    'kind_changed',  # (path, id, old_kind, new_kind)
 
38
    'modified',      # (path, id, kind, text_modified, meta_modified)
 
39
#    'unmodifed',     # (path, id, kind)
 
40
    'unversioned'    # (path, None, kind)
 
41
)
 
42
change_label = {
 
43
    'added'        : _('Added'),
 
44
    'removed'      : _('Deleted'),
 
45
    'renamed'      : _('Renamed'),
 
46
    'kind_changed' : _('Changed'),
 
47
    'modified'     : _('Edited'),
 
48
    'unversioned'  : _('New'),
 
49
    ''             : '',
 
50
}
 
51
change_icons = IconManager('chicons')
 
52
 
 
53
 
 
54
class CommitWindow(Window):
 
55
    """Select a project to import."""
 
56
    name = 'confirm'
 
57
 
 
58
    def  load(self, *args, **kwargs):
 
59
        """Load a commit window"""
 
60
        self.branch  = kwargs.pop('branch')
 
61
        self.project = kwargs.pop('project')
 
62
        self.fixing  = kwargs.pop('fixing', None)
 
63
        self.wtr     = self.branch.get_workingtree()
 
64
        self.prevt   = self.branch.get_oldtree()
 
65
        self.slist   = None
 
66
        self.child   = None
 
67
        self.result  = None
 
68
        self.comment = None
 
69
        self.newadd  = False
 
70
        self.dirs    = {}
 
71
        self.fixes   = []
 
72
        super(CommitWindow, self).load(*args, **kwargs)
 
73
        self.diffview = DiffView()
 
74
        #self.diffview.set_trees(self.prevt, self.wtr)
 
75
        self.diffview.set_trees(self.wtr, self.prevt)
 
76
        self.widget('viewchanges').add(self.diffview)
 
77
        self.slist = ChangesView(self.widget('changes'),
 
78
            selected=self.selected)
 
79
        all_changes = self.branch.get_changes()
 
80
        for atype in change_types:
 
81
            changes = getattr(all_changes, atype)
 
82
            if changes:
 
83
                for change in changes:
 
84
                    self.add_change(atype, change)
 
85
        # Remove any empty directories that aren't changing
 
86
        for iter in self.dirs.values():
 
87
            if not self.slist.get_item(iter).changes:
 
88
                self.slist.remove_item(target_iter=iter)
 
89
        # Hide the ignore new files feature if we have no new files.
 
90
        if not self.newadd:
 
91
            self.widget('ignorenew').hide()
 
92
            self.widget('ignorelabel').hide()
 
93
        # Auto add the bug we're fixing as expected
 
94
        if self.fixing:
 
95
            self.tie_bug(self.fixing)
 
96
            # If we ever add a feature to enable a user
 
97
            # To select what files should be commited, Then
 
98
            # disable it here since fixing bugs is incompatable
 
99
 
 
100
    def signals(self):
 
101
        return {
 
102
            'tie_bug' : self.get_bug,
 
103
        }
 
104
 
 
105
    def add_change(self, atype, change):
 
106
        """Return an added tree item node"""
 
107
        # Make our change item object for the list
 
108
        item   = ChangeItem(self.branch, atype, change)
 
109
        parent = self.get_folder(item.stem)
 
110
        # We want to track if we have any added files
 
111
        if atype == 'unversioned':
 
112
            # Don't add items that are going to be ignored anyway
 
113
            if self.wtr.is_ignored(item.path):
 
114
                return None
 
115
            self.newadd = True
 
116
        # Add item to visual tree view list
 
117
        iter1 = self.slist.add_item( item, parent )
 
118
        if item.isdir:
 
119
            self.dirs[item.stem] = iter1
 
120
        if parent:
 
121
            self.slist.expand_item(parent)
 
122
        if atype == 'unversioned' and item.isdir:
 
123
            # Also add in any sub files
 
124
            self.add_sub_files( item.stem )
 
125
 
 
126
    def get_folder(self, path):
 
127
        """Returns a folder tree item node"""
 
128
        if path == '':
 
129
            return None
 
130
        fdir = os.path.dirname(path)
 
131
        full_path = os.path.join(self.branch.path, fdir)
 
132
        if fdir and os.path.isdir(full_path):
 
133
            if not self.dirs.has_key(fdir):
 
134
                parent = self.get_folder(fdir)
 
135
                item = ChangeFolder(full_path)
 
136
                self.dirs[fdir] = self.slist.add_item( item, parent )
 
137
        return self.dirs.get(fdir, None)
 
138
 
 
139
    def add_sub_files(self, dir):
 
140
        """Add the subfiles of a directory"""
 
141
        path = self.branch.path
 
142
        full_path = os.path.join(path, dir)
 
143
        for res in os.walk(full_path):
 
144
            for sfile in (res[1]+res[2]):
 
145
                new_path = os.path.join(res[0], sfile).replace(path, '')
 
146
                self.add_change('added', (new_path, None, None))
 
147
 
 
148
    def is_valid(self):
 
149
        """Return true if inputs are correct."""
 
150
        return len(self.widget('comments').get_text()) > 2
 
151
 
 
152
    def get_bug(self, widget=None):
 
153
        """Load up the bug gui and let someone tie a bug to this report"""
 
154
        project = None
 
155
        if self.project:
 
156
            project = str(self.project.pid)
 
157
        BugSelection(project=project,
 
158
            callback=self.tie_bug,
 
159
            parent=self.window,
 
160
        )
 
161
 
 
162
    def tie_bug(self, bug=None, project=None):
 
163
        """Add the bug to our fixes list"""
 
164
        if type(bug) not in (str, unicode):
 
165
            bug = "lp:%s" % str(bug.id)
 
166
        else:
 
167
            bug = "lp:%s" % bug
 
168
        self.fixes.append(bug)
 
169
        # This button will allow us to remove the fixes.
 
170
        bugb = gtk.Button()
 
171
        bugb.set_label(bug)
 
172
        bugb.show()
 
173
        bugb.connect('clicked', self.remove_bug)
 
174
        self.widget('fixeslist').pack_start(bugb)
 
175
        self.widget('fixeslist').reorder_child(bugb, 0)
 
176
 
 
177
    def selected(self, item):
 
178
        """Show in the diff any items which we want"""
 
179
        if not item.isdir and item.type == 'modified':
 
180
            logging.debug("Showing diff for %s" % item.stem)
 
181
            self.diffview.show_diff([item.stem])
 
182
            self.widget("viewchanges").set_position(300)
 
183
            self.window.resize(800, 600)
 
184
            self.diffview.show()
 
185
        else:
 
186
            self.diffview.hide()
 
187
 
 
188
    def remove_bug(self, widget):
 
189
        """It doesn't fix this bug at all!"""
 
190
        bug = widget.get_label()
 
191
        self.fixes.remove(bug)
 
192
        widget.destroy()
 
193
 
 
194
    def get_args(self):
 
195
        """Do out commit here"""
 
196
        return {
 
197
            'message'   : self.widget('comments').get_text(),
 
198
            'ignore'    : self.widget('ignorenew').get_active(),
 
199
            'branch'    : self.branch,
 
200
            'fixes'     : self.fixes,
 
201
            'file_list' : None,
 
202
        }
 
203
 
 
204
 
 
205
class CommitChanges(GtkApp):
 
206
    """Graphical application for presenting the user with commit changes"""
 
207
    gtkfile = 'commit-branch.glade'
 
208
    windows = [ CommitWindow ]
 
209
 
 
210
 
 
211
class NodeItem(object):
 
212
    """Wrap all items in the review"""
 
213
    def __init__(self, path, atype=None):
 
214
        self._icon   = None
 
215
        self.changes = None
 
216
        self.path    = path
 
217
        self.type    = atype
 
218
        self.name    = os.path.basename(self.path)
 
219
        self.isdir   = os.path.isdir(self.path)
 
220
 
 
221
    def label(self):
 
222
        """Return the change type label"""
 
223
        return "<i>%s</i>" % self.name
 
224
 
 
225
    def icon(self):
 
226
        """Icon of the modeification"""
 
227
        if not self._icon:
 
228
            base = change_icons.get_icon('file').copy()
 
229
            if self.isdir:
 
230
                base = change_icons.get_icon('folder').copy()
 
231
            overlay = change_icons.get_icon(self.type).copy()
 
232
            overlay.composite( base, 0, 0, base.props.width, base.props.height,
 
233
                0, 0, 1.0, 1.0, gtk.gdk.INTERP_BILINEAR, 128 )
 
234
            self._icon = base
 
235
        return self._icon
 
236
 
 
237
 
 
238
class ChangeItem(NodeItem):
 
239
    """Wraps any change item from TreeDelta"""
 
240
    def __init__(self, branch, atype, changes):
 
241
        self.branch   = branch
 
242
        self.selected = False
 
243
        if atype == 'renamed':
 
244
            self.oldname = changes.pop(0)
 
245
        self.stem     = changes[0]
 
246
        path = os.path.join(self.branch.path, self.stem)
 
247
        super(ChangeItem, self).__init__(path, atype)
 
248
        self.changes  = changes
 
249
        # Force the correct type for deleted elements.
 
250
        if changes[2] == 'directory':
 
251
            self.isdir = True
 
252
 
 
253
    def set_selected(self, shouldi=True):
 
254
        """Choose to confirm the changes of this file"""
 
255
        self.selected = shouldi
 
256
 
 
257
    def label(self):
 
258
        """Return the simple label for this item"""
 
259
        if self.type == 'renamed':
 
260
            return "%s => %s" % (self.oldname, self.name)
 
261
        else:
 
262
            return self.name
 
263
 
 
264
 
 
265
class ChangeFolder(NodeItem):
 
266
    """Wraps a change type"""
 
267
    def has_changes(self):
 
268
        self.changes = 'folder'
 
269
 
 
270
 
 
271
class ChangesView(TreeView):
 
272
    """Configures the view for changes."""
 
273
 
 
274
    def add_item(self, item, parent=None):
 
275
        if parent:
 
276
            folder = self.get_item(parent)
 
277
            if not folder.changes:
 
278
                folder.has_changes()
 
279
        return super(ChangesView, self).add_item(item, parent)
 
280
 
 
281
    def setup(self):
 
282
        """Setup a treeview for showing services"""
 
283
        def text_cell_func(cell_layout, renderer, model, item_iter):
 
284
            """Render the text of the services tree view"""
 
285
            item = model.get_value(item_iter, 0)
 
286
            renderer.set_property("markup", item.label())
 
287
 
 
288
        def type_cell_func(cell_layout, renderer, model, item_iter):
 
289
            """Render the kind of change"""
 
290
            item = model.get_value(item_iter, 0)
 
291
            if item.type:
 
292
                renderer.set_property("markup", change_label[item.type])
 
293
            else:
 
294
                renderer.set_property("markup", '')
 
295
 
 
296
        def icon_cell_func(column, cell, model, item_iter):
 
297
            """Reender the icons of the services tree view"""
 
298
            item = model.get_value(item_iter, 0)
 
299
            cell.set_property("pixbuf", item.icon())
 
300
            cell.set_property("visible", True)
 
301
 
 
302
        svlist = super(ChangesView, self).setup()
 
303
        column = gtk.TreeViewColumn((_("List of Changes")))
 
304
        column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
 
305
        #column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
306
        column.set_expand(True)
 
307
        # The icon
 
308
        renderer_icon = gtk.CellRendererPixbuf()
 
309
        renderer_icon.set_property("ypad", 2)
 
310
        renderer_icon.set_property("xpad", 2)
 
311
        column.pack_start(renderer_icon, False)
 
312
        column.set_cell_data_func(renderer_icon, icon_cell_func)
 
313
        # The name
 
314
        renderer = gtk.CellRendererText()
 
315
        column.pack_start(renderer, True)
 
316
        column.set_cell_data_func(renderer, text_cell_func)
 
317
        # Add the required coluns to the treeview
 
318
        svlist.append_column(column)
 
319
        # The kind of change
 
320
        column2 = gtk.TreeViewColumn(("Kind"))
 
321
        column2.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
 
322
        renderer = gtk.CellRendererText()
 
323
        column2.pack_start(renderer, True)
 
324
        column2.set_cell_data_func(renderer, type_cell_func)
 
325
        # Add the required coluns to the treeview
 
326
        svlist.append_column(column2)
 
327
 
 
328
 
 
329
class RevertWindow(Window):
 
330
    """Show a window that asks the user to confirm the revert."""
 
331
    name = 'revert'
 
332
 
 
333
    def  load(self, branch, *args, **kwargs):
 
334
        """Load a revert confirmation window"""
 
335
        self.branch  = branch
 
336
        super(RevertWindow, self).load(*args, **kwargs)
 
337
 
 
338
    def get_args(self):
 
339
        """Return the branch to a sucess response"""
 
340
        return { 'branch' : self.branch }
 
341
 
 
342
class RevertChanges(GtkApp):
 
343
    """An application to show some help to the user."""
 
344
    gtkfile = 'revert-branch.glade'
 
345
    windows = [ RevertWindow ]