PageRenderTime 45ms CodeModel.GetById 24ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/tortoisehg/hgtk/quickop.py

https://bitbucket.org/tortoisehg/hgtk/
Python | 289 lines | 254 code | 22 blank | 13 comment | 8 complexity | 109081a130fc9c480406263dab0d8eca MD5 | raw file
  1# quickop.py - TortoiseHg's dialog for quick dirstate operations
  2#
  3# Copyright 2009 Steve Borho <steve@borho.org>
  4#
  5# This software may be used and distributed according to the terms of the
  6# GNU General Public License version 2, incorporated herein by reference.
  7
  8import os
  9import gtk
 10import pango
 11
 12from mercurial import cmdutil, util
 13
 14from tortoisehg.util.i18n import _
 15from tortoisehg.util import hglib, shlib
 16
 17from tortoisehg.hgtk import gtklib, gdialog
 18
 19LABELS = { 'add': (_('Select files to add'), _('Add')),
 20           'forget': (_('Select files to forget'), _('Forget')),
 21           'revert': (_('Select files to revert'), _('Revert')),
 22           'remove': (_('Select files to remove'), _('Remove')),}
 23
 24DEFAULT_SIZE = (450, 300)
 25DEFAULT_POS = (0, 0)
 26
 27class QuickOpDialog(gdialog.GDialog):
 28    """ Dialog for performing quick dirstate operations """
 29    def __init__(self, command, pats):
 30        gdialog.GDialog.__init__(self, resizable=True)
 31        self.pats = pats
 32
 33        # Handle rm alias
 34        if command == 'rm':
 35            command = 'remove'
 36        self.command = command
 37
 38        # show minimize/maximize buttons
 39        self.realize()
 40        if self.window:
 41            self.window.set_decorations(gtk.gdk.DECOR_ALL)
 42
 43    ### Start of Overriding Section ###
 44
 45    def get_title(self, reponame):
 46        return reponame + ' - hg ' + self.command
 47
 48    def get_icon(self):
 49        return 'hg.ico'
 50
 51    def get_defsize(self):
 52        return self.defsize
 53
 54    def get_setting_name(self):
 55        return 'quickop'
 56
 57    def get_body(self, vbox):
 58        os.chdir(self.repo.root)
 59
 60        # wrap box
 61        wrapbox = gtk.VBox()
 62        wrapbox.set_border_width(5)
 63        vbox.pack_start(wrapbox, True, True)
 64        self.wrapbox = wrapbox
 65
 66        lbl = gtk.Label(LABELS[self.command][0])
 67        lbl.set_alignment(0, 0)
 68        wrapbox.pack_start(lbl, False, False)
 69
 70        def keypressed(tree, event):
 71            'Make spacebar toggle selected rows'
 72            if event.keyval != 32:
 73                return False
 74            def toggler(model, path, bufiter):
 75                model[path][0] = not model[path][0]
 76            selection = tree.get_selection()
 77            selection.selected_foreach(toggler)
 78            return True
 79
 80        # add file list treeview
 81        fm = gtk.ListStore(bool, # Checked
 82                           str,  # Path
 83                           str,  # Path-UTF8
 84                           str)  # Status
 85        self.filetree = gtk.TreeView(fm)
 86        self.filetree.connect('key-press-event', keypressed)
 87        self.filetree.set_headers_clickable(True)
 88        self.filetree.set_reorderable(False)
 89        if hasattr(self.filetree, 'set_rubber_banding'):
 90            self.filetree.set_rubber_banding(True)
 91        fontlist = hglib.getfontconfig()['fontlist']
 92        self.filetree.modify_font(pango.FontDescription(fontlist))
 93
 94        def select_toggle(cell, path):
 95            fm[path][0] = not fm[path][0]
 96
 97        # file selection checkboxes
 98        toggle_cell = gtk.CellRendererToggle()
 99        toggle_cell.connect('toggled', select_toggle)
100        toggle_cell.set_property('activatable', True)
101
102        col = gtk.TreeViewColumn('', toggle_cell, active=0)
103        col.set_resizable(False)
104        self.filetree.append_column(col)
105
106        col = gtk.TreeViewColumn(_('status'), gtk.CellRendererText(), text=3)
107        self.filetree.append_column(col)
108
109        col = gtk.TreeViewColumn(_('path'), gtk.CellRendererText(), text=2)
110        self.filetree.append_column(col)
111
112        scroller = gtk.ScrolledWindow()
113        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
114        scroller.set_shadow_type(gtk.SHADOW_ETCHED_IN)
115        scroller.add(self.filetree)
116        wrapbox.pack_start(scroller, True, True, 6)
117
118        def toggleall(button):
119            for row in self.filetree.get_model():
120                row[0] = not row[0]
121
122        # extra box
123        self.extrabox = hbox = gtk.HBox()
124        wrapbox.pack_start(hbox, False, False)
125
126        ## toggle button
127        tb = gtk.Button(_('Toggle all selections'))
128        tb.connect('pressed', toggleall)
129        hbox.pack_start(tb, False, False)
130
131        if self.command == 'revert':
132            ## no backup checkbox
133            chk = gtk.CheckButton(_('Do not save backup files (*.orig)'))
134            hbox.pack_start(chk, False, False, 6)
135            self.nobackup = chk
136
137            ## padding
138            hbox.pack_start(gtk.Label())
139
140        types = { 'add' : 'I?',
141                  'forget' : 'MAR!C',
142                  'revert' : 'MAR!',
143                  'remove' : 'MAR!CI?',
144                }
145        filetypes = types[self.command]
146
147        try:
148            matcher = cmdutil.match(self.repo, self.pats)
149            status = self.repo.status(match=matcher,
150                                 clean='C' in filetypes,
151                                 ignored='I' in filetypes,
152                                 unknown='?' in filetypes)
153        except (EnvironmentError, util.Abort), e:
154            gdialog.Prompt(_('Unable to determine repository status'),
155                           str(e), self).run()
156            self.earlyout=True
157            self.hide()
158            return
159
160        (modified, added, removed, deleted, unknown, ignored, clean) = status
161        if 'M' in filetypes:
162            for f in modified:
163                fm.append([True, f, hglib.toutf(f), _('modified')])
164        if 'A' in filetypes:
165            for f in added:
166                fm.append([True, f, hglib.toutf(f), _('added')])
167        if 'R' in filetypes:
168            for f in removed:
169                fm.append([True, f, hglib.toutf(f), _('removed')])
170        if '!' in filetypes:
171            for f in deleted:
172                fm.append([True, f, hglib.toutf(f), _('missing')])
173        if '?' in filetypes:
174            for f in unknown:
175                fm.append([True, f, hglib.toutf(f), _('unknown')])
176        if 'I' in filetypes:
177            for f in ignored:
178                if self.command == 'remove' or f in self.pats:
179                    fm.append([True, f, hglib.toutf(f), _('ignored')])
180        if 'C' in filetypes:
181            for f in clean:
182                if self.command == 'remove' or f in self.pats:
183                    fm.append([True, f, hglib.toutf(f), _('clean')])
184
185        if not len(fm):
186            gdialog.Prompt(_('No appropriate files'),
187                           _('No files found for this operation'), self).run()
188            self.earlyout=True
189            self.hide()
190
191    def get_buttons(self):
192        return [('go', LABELS[self.command][1], gtk.RESPONSE_OK),
193                ('cancel', gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE)]
194
195    def get_default_button(self):
196        return 'go'
197
198    def get_action_map(self):
199        return {gtk.RESPONSE_OK: self.operation}
200
201    def switch_to(self, normal, working, cmd):
202        self.wrapbox.set_sensitive(normal)
203        self.buttons['go'].set_property('visible', normal)
204        self.buttons['cancel'].set_property('visible', normal)
205        if normal:
206            self.buttons['cancel'].grab_focus()
207
208    def command_done(self, returncode, useraborted, list):
209        if returncode == 0:
210            shlib.shell_notify(list)
211            self.cmd.set_result(_('Successfully'), style='ok')
212        elif useraborted:
213            self.cmd.set_result(_('Canceled'), style='error')
214        else:
215            self.cmd.set_result(_('Failed'), style='error')
216
217    def before_show(self):
218        # restore dialog state
219        if self.defmax:
220            self.maximize()
221
222        # restore dialog position
223        screen = self.get_screen()
224        w, h = screen.get_width(), screen.get_height()
225        x, y = self.defpos
226        if x >= 0 and x < w and y >= 0 and y < h:
227            self.move(x, y)
228
229    def load_settings(self):
230        self.defsize = self.settings.get_value('size', DEFAULT_SIZE)
231        self.defpos = self.settings.get_value('pos', DEFAULT_POS)
232        self.defmax = self.settings.get_value('maximize', False)
233
234    def store_settings(self):
235        state = self.window.get_state()
236        ismaximized = bool(state & gtk.gdk.WINDOW_STATE_MAXIMIZED)
237        if ismaximized or state & gtk.gdk.WINDOW_STATE_ICONIFIED:
238            self.settings.set_value('size', DEFAULT_SIZE)
239            self.settings.set_value('pos', DEFAULT_POS)
240        else:
241            rect = self.get_allocation()
242            self.settings.set_value('size', (rect.width, rect.height))
243            self.settings.set_value('pos', self.get_position())
244        self.settings.set_value('maximize', ismaximized)
245        self.settings.write()
246
247    ### End of Overriding Section ###
248
249    def operation(self):
250        fm = self.filetree.get_model()
251        deleting = self.command == 'remove'
252        list, dellist = [], []
253        for row in fm:
254            if not row[0]: continue
255            if deleting and row[3] in (_('unknown'), _('ignored')):
256                dellist.append(row[1])
257            else:
258                list.append(row[1])
259
260        if not (list or dellist):
261            gdialog.Prompt(_('No files selected'),
262                           _('No operation to perform'), self).run()
263            return
264
265        for file in dellist:
266            try:
267                os.unlink(file)
268            except EnvironmentError:
269                pass
270
271        if not list:
272            gtklib.idle_add_single_call(self.response, gtk.RESPONSE_CLOSE)
273            return
274
275        # prepare command line
276        cmdline = ['hg', self.command, '--verbose']
277        if hasattr(self, 'nobackup') and self.nobackup.get_active():
278            cmdline.append('--no-backup')
279        cmdline.append('--')
280        cmdline += list
281
282        # execute command
283        self.execute_command(cmdline, list)
284
285def run(ui, *pats, **opts):
286    pats = hglib.canonpaths(pats)
287    if opts.get('canonpats'):
288        pats = list(pats) + opts['canonpats']
289    return QuickOpDialog(opts.get('alias'), pats)