/tortoisehg/hgtk/quickop.py
https://bitbucket.org/tortoisehg/hgtk/ · Python · 289 lines · 216 code · 51 blank · 22 comment · 53 complexity · 109081a130fc9c480406263dab0d8eca MD5 · raw file
- # quickop.py - TortoiseHg's dialog for quick dirstate operations
- #
- # Copyright 2009 Steve Borho <steve@borho.org>
- #
- # This software may be used and distributed according to the terms of the
- # GNU General Public License version 2, incorporated herein by reference.
- import os
- import gtk
- import pango
- from mercurial import cmdutil, util
- from tortoisehg.util.i18n import _
- from tortoisehg.util import hglib, shlib
- from tortoisehg.hgtk import gtklib, gdialog
- LABELS = { 'add': (_('Select files to add'), _('Add')),
- 'forget': (_('Select files to forget'), _('Forget')),
- 'revert': (_('Select files to revert'), _('Revert')),
- 'remove': (_('Select files to remove'), _('Remove')),}
- DEFAULT_SIZE = (450, 300)
- DEFAULT_POS = (0, 0)
- class QuickOpDialog(gdialog.GDialog):
- """ Dialog for performing quick dirstate operations """
- def __init__(self, command, pats):
- gdialog.GDialog.__init__(self, resizable=True)
- self.pats = pats
- # Handle rm alias
- if command == 'rm':
- command = 'remove'
- self.command = command
- # show minimize/maximize buttons
- self.realize()
- if self.window:
- self.window.set_decorations(gtk.gdk.DECOR_ALL)
- ### Start of Overriding Section ###
- def get_title(self, reponame):
- return reponame + ' - hg ' + self.command
- def get_icon(self):
- return 'hg.ico'
- def get_defsize(self):
- return self.defsize
- def get_setting_name(self):
- return 'quickop'
- def get_body(self, vbox):
- os.chdir(self.repo.root)
- # wrap box
- wrapbox = gtk.VBox()
- wrapbox.set_border_width(5)
- vbox.pack_start(wrapbox, True, True)
- self.wrapbox = wrapbox
- lbl = gtk.Label(LABELS[self.command][0])
- lbl.set_alignment(0, 0)
- wrapbox.pack_start(lbl, False, False)
- def keypressed(tree, event):
- 'Make spacebar toggle selected rows'
- if event.keyval != 32:
- return False
- def toggler(model, path, bufiter):
- model[path][0] = not model[path][0]
- selection = tree.get_selection()
- selection.selected_foreach(toggler)
- return True
- # add file list treeview
- fm = gtk.ListStore(bool, # Checked
- str, # Path
- str, # Path-UTF8
- str) # Status
- self.filetree = gtk.TreeView(fm)
- self.filetree.connect('key-press-event', keypressed)
- self.filetree.set_headers_clickable(True)
- self.filetree.set_reorderable(False)
- if hasattr(self.filetree, 'set_rubber_banding'):
- self.filetree.set_rubber_banding(True)
- fontlist = hglib.getfontconfig()['fontlist']
- self.filetree.modify_font(pango.FontDescription(fontlist))
- def select_toggle(cell, path):
- fm[path][0] = not fm[path][0]
- # file selection checkboxes
- toggle_cell = gtk.CellRendererToggle()
- toggle_cell.connect('toggled', select_toggle)
- toggle_cell.set_property('activatable', True)
- col = gtk.TreeViewColumn('', toggle_cell, active=0)
- col.set_resizable(False)
- self.filetree.append_column(col)
- col = gtk.TreeViewColumn(_('status'), gtk.CellRendererText(), text=3)
- self.filetree.append_column(col)
- col = gtk.TreeViewColumn(_('path'), gtk.CellRendererText(), text=2)
- self.filetree.append_column(col)
- scroller = gtk.ScrolledWindow()
- scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scroller.set_shadow_type(gtk.SHADOW_ETCHED_IN)
- scroller.add(self.filetree)
- wrapbox.pack_start(scroller, True, True, 6)
- def toggleall(button):
- for row in self.filetree.get_model():
- row[0] = not row[0]
- # extra box
- self.extrabox = hbox = gtk.HBox()
- wrapbox.pack_start(hbox, False, False)
- ## toggle button
- tb = gtk.Button(_('Toggle all selections'))
- tb.connect('pressed', toggleall)
- hbox.pack_start(tb, False, False)
- if self.command == 'revert':
- ## no backup checkbox
- chk = gtk.CheckButton(_('Do not save backup files (*.orig)'))
- hbox.pack_start(chk, False, False, 6)
- self.nobackup = chk
- ## padding
- hbox.pack_start(gtk.Label())
- types = { 'add' : 'I?',
- 'forget' : 'MAR!C',
- 'revert' : 'MAR!',
- 'remove' : 'MAR!CI?',
- }
- filetypes = types[self.command]
- try:
- matcher = cmdutil.match(self.repo, self.pats)
- status = self.repo.status(match=matcher,
- clean='C' in filetypes,
- ignored='I' in filetypes,
- unknown='?' in filetypes)
- except (EnvironmentError, util.Abort), e:
- gdialog.Prompt(_('Unable to determine repository status'),
- str(e), self).run()
- self.earlyout=True
- self.hide()
- return
- (modified, added, removed, deleted, unknown, ignored, clean) = status
- if 'M' in filetypes:
- for f in modified:
- fm.append([True, f, hglib.toutf(f), _('modified')])
- if 'A' in filetypes:
- for f in added:
- fm.append([True, f, hglib.toutf(f), _('added')])
- if 'R' in filetypes:
- for f in removed:
- fm.append([True, f, hglib.toutf(f), _('removed')])
- if '!' in filetypes:
- for f in deleted:
- fm.append([True, f, hglib.toutf(f), _('missing')])
- if '?' in filetypes:
- for f in unknown:
- fm.append([True, f, hglib.toutf(f), _('unknown')])
- if 'I' in filetypes:
- for f in ignored:
- if self.command == 'remove' or f in self.pats:
- fm.append([True, f, hglib.toutf(f), _('ignored')])
- if 'C' in filetypes:
- for f in clean:
- if self.command == 'remove' or f in self.pats:
- fm.append([True, f, hglib.toutf(f), _('clean')])
- if not len(fm):
- gdialog.Prompt(_('No appropriate files'),
- _('No files found for this operation'), self).run()
- self.earlyout=True
- self.hide()
- def get_buttons(self):
- return [('go', LABELS[self.command][1], gtk.RESPONSE_OK),
- ('cancel', gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE)]
- def get_default_button(self):
- return 'go'
- def get_action_map(self):
- return {gtk.RESPONSE_OK: self.operation}
- def switch_to(self, normal, working, cmd):
- self.wrapbox.set_sensitive(normal)
- self.buttons['go'].set_property('visible', normal)
- self.buttons['cancel'].set_property('visible', normal)
- if normal:
- self.buttons['cancel'].grab_focus()
- def command_done(self, returncode, useraborted, list):
- if returncode == 0:
- shlib.shell_notify(list)
- self.cmd.set_result(_('Successfully'), style='ok')
- elif useraborted:
- self.cmd.set_result(_('Canceled'), style='error')
- else:
- self.cmd.set_result(_('Failed'), style='error')
- def before_show(self):
- # restore dialog state
- if self.defmax:
- self.maximize()
- # restore dialog position
- screen = self.get_screen()
- w, h = screen.get_width(), screen.get_height()
- x, y = self.defpos
- if x >= 0 and x < w and y >= 0 and y < h:
- self.move(x, y)
- def load_settings(self):
- self.defsize = self.settings.get_value('size', DEFAULT_SIZE)
- self.defpos = self.settings.get_value('pos', DEFAULT_POS)
- self.defmax = self.settings.get_value('maximize', False)
- def store_settings(self):
- state = self.window.get_state()
- ismaximized = bool(state & gtk.gdk.WINDOW_STATE_MAXIMIZED)
- if ismaximized or state & gtk.gdk.WINDOW_STATE_ICONIFIED:
- self.settings.set_value('size', DEFAULT_SIZE)
- self.settings.set_value('pos', DEFAULT_POS)
- else:
- rect = self.get_allocation()
- self.settings.set_value('size', (rect.width, rect.height))
- self.settings.set_value('pos', self.get_position())
- self.settings.set_value('maximize', ismaximized)
- self.settings.write()
- ### End of Overriding Section ###
- def operation(self):
- fm = self.filetree.get_model()
- deleting = self.command == 'remove'
- list, dellist = [], []
- for row in fm:
- if not row[0]: continue
- if deleting and row[3] in (_('unknown'), _('ignored')):
- dellist.append(row[1])
- else:
- list.append(row[1])
- if not (list or dellist):
- gdialog.Prompt(_('No files selected'),
- _('No operation to perform'), self).run()
- return
- for file in dellist:
- try:
- os.unlink(file)
- except EnvironmentError:
- pass
- if not list:
- gtklib.idle_add_single_call(self.response, gtk.RESPONSE_CLOSE)
- return
- # prepare command line
- cmdline = ['hg', self.command, '--verbose']
- if hasattr(self, 'nobackup') and self.nobackup.get_active():
- cmdline.append('--no-backup')
- cmdline.append('--')
- cmdline += list
- # execute command
- self.execute_command(cmdline, list)
- def run(ui, *pats, **opts):
- pats = hglib.canonpaths(pats)
- if opts.get('canonpats'):
- pats = list(pats) + opts['canonpats']
- return QuickOpDialog(opts.get('alias'), pats)