by
Changes to 8 files · Browse files at 19713f6b9977 Showing diff from parent 49a7f14a6975 e1d1d0df457a Diff from another changeset...
|
@@ -13,7 +13,7 @@ import binascii
from mercurial import patch, util, error
-from mercurial.node import short, hex
+from mercurial.node import hex
from tortoisehg.util.i18n import _
from tortoisehg.util import hglib, paths
@@ -171,7 +171,7 @@ def __str__(self):
node = self.node()
if node:
- return short(node)
+ return node[:12]
return ''
def __int__(self):
@@ -191,6 +191,7 @@ def tags(self): return ()
def parents(self): return self._parents
def children(self): return ()
+ def extra(self): return {}
class SummaryInfo(object):
@@ -212,7 +213,9 @@ if item == 'rev':
revnum = self.get_data('revnum', *args)
revid = self.get_data('revid', *args)
- return (revnum, revid)
+ if revid:
+ return (revnum, revid)
+ return None
elif item == 'revnum':
return ctx.rev()
elif item == 'revid':
@@ -254,7 +257,7 @@ if dblist and value in [hglib.toutf(b.strip()) \
for b in dblist.split(',')]:
return None
- return value
+ return None
elif item == 'rawtags':
value = [hglib.toutf(tag) for tag in ctx.tags()]
if len(value) == 0:
@@ -269,7 +272,7 @@ value = [tag for tag in value if tag not in htags]
if len(value) == 0:
return None
- return value
+ return None
elif item == 'transplant':
extra = ctx.extra()
try:
@@ -321,7 +324,9 @@ if item == 'rev':
revnum, revid = value
revid = gtklib.markup(revid, **mono)
- return '%s (%s)' % (revnum, revid)
+ if revnum is not None and revid is not None:
+ return '%s (%s)' % (revnum, revid)
+ return '%s' % revid
elif item in ('revid', 'transplant'):
return gtklib.markup(value, **mono)
elif item == 'revnum':
|
|
|
@@ -0,0 +1,801 @@ + # cslist.py - embeddable changeset/patch list component
+#
+# Copyright 2009 Yuki KODAMA <endflow.net@gmail.com>
+#
+# 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 gobject
+import urllib
+
+from mercurial import hg, ui
+
+from tortoisehg.util.i18n import _
+from tortoisehg.util import hglib, paths
+
+from tortoisehg.hgtk import csinfo, gtklib
+
+CSL_DND_ITEM = 1024
+CSL_DND_URI_LIST = 1025
+
+class ChangesetList(gtk.Frame):
+
+ __gsignals__ = {
+ 'list-updated': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (object, # number of all items or None
+ object, # number of selections or None
+ object, # number of showings or None
+ bool)), # whether cslist is updating
+ 'files-dropped': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (object, # list of dropped files
+ str)) # raw string data
+ }
+
+ def __init__(self):
+ gtk.Frame.__init__(self)
+ self.set_shadow_type(gtk.SHADOW_IN)
+
+ # member variables
+ self.curitems = None
+ self.currepo = None
+ self.showitems = None
+ self.chkmap = {}
+ self.limit = 20
+ self.curfactory = None
+
+ self.timeout_queue = []
+ self.sel_enable = False
+ self.dnd_enable = False
+
+ # dnd variables
+ self.itemmap = {}
+ self.hlsep = None
+ self.dnd_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 1, 1)
+ self.scroll_timer = None
+
+ # base box
+ basebox = gtk.VBox()
+ self.add(basebox)
+
+ ## status box
+ self.statusbox = statusbox = gtk.HBox()
+ basebox.pack_start(statusbox)
+ basebox.pack_start(gtk.HSeparator(), False, False, 2)
+
+ # copy form thgstrip.py
+ def createlabel():
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ label.set_size_request(-1, 24)
+ label.size_request()
+ return label
+
+ ### status label
+ self.statuslabel = createlabel()
+ statusbox.pack_start(self.statuslabel, False, False, 2)
+
+ ### show all button
+ self.allbtn = gtk.Button(_('Show all')) # add later
+
+ ### list option
+ self.compactopt = gtk.CheckButton(_('Use compact view'))
+ statusbox.pack_end(self.compactopt, False, False, 2)
+
+ ## item list
+ scroll = gtk.ScrolledWindow()
+ basebox.add(scroll)
+ self.scroll = scroll
+ scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scroll.set_size_request(400, 180)
+ scroll.size_request()
+ self.csbox = gtk.VBox()
+ self.csevent = csevent = gtk.EventBox()
+ csevent.add(self.csbox)
+ csevent.add_events(gtk.gdk.BUTTON_PRESS_MASK |
+ gtk.gdk.BUTTON_RELEASE_MASK)
+ scroll.add_with_viewport(csevent)
+ scroll.child.set_shadow_type(gtk.SHADOW_NONE)
+ self.csbox.set_border_width(4)
+
+ # signal handlers
+ self.allbtn.connect('clicked', lambda b: self.update(self.curitems, \
+ self.currepo, limit=False, queue=False, keep=True))
+ self.compactopt.connect('toggled', lambda b: self.update( \
+ self.curitems, self.currepo, queue=False, keep=True))
+
+ # dnd setup
+ self.dnd_targets = [('thg-dnd', gtk.TARGET_SAME_WIDGET, CSL_DND_ITEM)]
+ targets = self.dnd_targets + [('text/uri-list', 0, CSL_DND_URI_LIST)]
+ csevent.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_DROP,
+ targets, gtk.gdk.ACTION_MOVE)
+ csevent.connect('drag-begin', self.dnd_begin)
+ csevent.connect('drag-end', self.dnd_end)
+ csevent.connect('drag-motion', self.dnd_motion)
+ csevent.connect('drag-leave', self.dnd_leave)
+ csevent.connect('drag-data-received', self.dnd_received)
+ csevent.connect('drag-data-get', self.dnd_get)
+ csevent.connect('button-press-event', self.button_press)
+
+ # csetinfo
+ def data_func(widget, item, ctx):
+ if item in ('item', 'item_l'):
+ if not isinstance(ctx, csinfo.patchctx):
+ return True # dummy
+ revid = widget.get_data('revid')
+ if not revid:
+ return widget.target
+ filename = os.path.basename(widget.target)
+ return filename, revid
+ raise csinfo.UnknownItem(item)
+ def label_func(widget, item):
+ if item in ('item', 'item_l'):
+ if not isinstance(widget.ctx, csinfo.patchctx):
+ return _('Revision:')
+ return _('Patch:')
+ raise csinfo.UnknownItem(item)
+ def markup_func(widget, item, value):
+ if item in ('item', 'item_l'):
+ if not isinstance(widget.ctx, csinfo.patchctx):
+ if item == 'item':
+ return widget.get_markup('rev')
+ return widget.get_markup('revnum')
+ mono = dict(face='monospace', size='9000')
+ if isinstance(value, basestring):
+ return gtklib.markup(value, **mono)
+ filename = gtklib.markup(value[0])
+ revid = gtklib.markup(value[1], **mono)
+ if item == 'item':
+ return '%s (%s)' % (filename, revid)
+ return filename
+ raise csinfo.UnknownItem(item)
+ self.custom = csinfo.custom(data=data_func, label=label_func,
+ markup=markup_func)
+ self.lstyle = csinfo.labelstyle(selectable=True,
+ contents=('%(item_l)s:', ' %(branch)s',
+ ' %(tags)s', ' %(summary)s'))
+ self.pstyle = csinfo.panelstyle(selectable=True,
+ contents=('item',) + csinfo.PANEL_DEFAULT[1:])
+
+ # prepare to show
+ gtklib.idle_add_single_call(self.after_init)
+
+ ### public functions ###
+
+ def update(self, items=None, repo=None, limit=True, queue=False, **kargs):
+ """
+ Update the item list.
+
+ Public arguments:
+ items: List of revision numbers and/or patch file path.
+ You can pass mixed list. The order will be respected.
+ If omitted, previous items will be used to show them.
+ Default: None.
+ repo: Repository used to get changeset information.
+ If omitted, previous repo will be used to show them.
+ Default: None.
+ limit: If True, some of items will be shown. Default: True.
+ queue: If True, the update request will be queued to prevent
+ frequent updatings. In some cases, this option will help
+ to improve UI response. Default: False.
+
+ Internal argument:
+ keep: If True, it keeps previous selection states and 'limit' value
+ after refreshing. Note that if you use 'limit' and this options
+ at the same time, 'limit' value is used against previous value.
+ Default: False.
+
+ return: True if the item list was updated successfully,
+ False if it wasn't updated.
+ """
+ # check parameters
+ if not items or not repo:
+ self.clear()
+ return False
+ elif queue:
+ def timeout(eid, items, repo):
+ if self.timeout_queue and self.timeout_queue[-1] == eid[0]:
+ self.timeout_queue = []
+ self.update(items, repo, limit, False)
+ return False # don't repeat
+ eid = [None]
+ eid[0] = gobject.timeout_add(650, timeout, eid, items, repo)
+ self.timeout_queue.append(eid[0])
+ return False
+
+ # determine whether to keep previous 'limit' state
+ if kargs.get('keep', False) and self.has_limit() is False:
+ limit = False
+
+ # initialize variables
+ self.curitems = items
+ self.currepo = repo
+ self.itemmap = {}
+
+ if self.sel_enable and not kargs.get('keep', False):
+ self.chkmap = {}
+ for item in items:
+ self.chkmap[item] = True
+
+ # determine items to show
+ numtotal = len(items)
+ if limit and self.limit < numtotal:
+ toshow, lastitem = items[:self.limit-1], items[-1]
+ else:
+ toshow, lastitem = items, None
+ numshow = len(toshow) + (lastitem and 1 or 0)
+ self.showitems = toshow + (lastitem and [lastitem] or [])
+
+ # prepare to update item list
+ self.curfactory = csinfo.factory(repo, self.custom, withupdate=True)
+
+ def add_sep():
+ sep = self.create_sep()
+ self.csbox.pack_start(sep, False, False)
+
+ # clear item list
+ self.csbox.foreach(lambda c: c.parent.remove(c))
+
+ # update item list
+ def proc():
+ # add csinfo widgets
+ for index, r in enumerate(toshow):
+ self.add_csinfo(r)
+ if lastitem:
+ self.add_snip()
+ self.add_csinfo(lastitem)
+ add_sep()
+ self.csbox.show_all()
+
+ # show/hide separators
+ self.update_seps()
+
+ # update status
+ self.update_status()
+
+ if numshow < 80:
+ proc()
+ else:
+ self.update_status(updating=True)
+ gtklib.idle_add_single_call(proc)
+
+ return True
+
+ def clear(self):
+ """ Clear the item list """
+ self.csbox.foreach(lambda c: c.parent.remove(c))
+ self.curitems = None
+ self.update_status()
+
+ def get_items(self, sel=False):
+ """
+ Return a list of items or tuples contained 2 values:
+ 'item' (String) and 'selection state' (Boolean).
+ If cslist lists no items, it returns an empty list.
+
+ sel: If True, it returns a list of tuples. Default: False.
+ """
+ items = self.curitems
+ if items:
+ if not sel:
+ return items
+ return [(item, self.chkmap[item]) for item in items]
+ return []
+
+ def get_list_limit(self):
+ """ Return number of items to limit to display """
+ return self.limit
+
+ def set_list_limit(self, limit):
+ """
+ Set number of items to limit to display.
+
+ limit: Integer, must be more than 3. Default: 20.
+ """
+ if limit < 3:
+ limit = 3
+ self.limit = limit
+
+ def get_dnd_enable(self):
+ """ Return whether drag and drop feature is enabled """
+ return self.dnd_enable
+
+ def set_dnd_enable(self, enable):
+ """
+ Set whether drag and drop feature is enabled.
+
+ enable: Boolean, if True, drag and drop feature will be enabled.
+ Default: False.
+ """
+ self.dnd_enable = enable
+
+ def get_checkbox_enable(self):
+ """ Return whether the selection feature is enabled """
+ return self.sel_enable
+
+ def set_checkbox_enable(self, enable):
+ """
+ Set whether the selection feature is enabled.
+ When it's enabled, checboxes will be placed at the left of
+ csinfo widgets.
+
+ enable: Boolean, if True, the selection feature will be enabled.
+ Default: False.
+ """
+ self.sel_enable = enable
+
+ def get_compact_view(self):
+ """ Return whether the compact view is enabled """
+ return self.compactopt.get_active()
+
+ def set_compact_view(self, compact):
+ """
+ Set whether the compact view is enabled.
+
+ enable: Boolean, if True, the compact view will be enabled.
+ Default: False.
+ """
+ self.compactopt.set_active(compact)
+
+ def has_limit(self):
+ """
+ Return whether the item list shows all items.
+ If the item list has no items, it will return None.
+ """
+ if self.curitems:
+ num = len(self.curitems)
+ return self.limit < num and len(self.showitems) != num
+ return None
+
+ ### internal functions ###
+
+ def after_init(self):
+ self.statusbox.pack_start(self.allbtn, False, False, 4)
+
+ # prepare for auto-scrolling while DnD
+ SIZE = 20
+ alloc = self.scroll.child.allocation
+ self.areas = {}
+ def add(name, arg):
+ region = gtk.gdk.region_rectangle(arg)
+ self.areas[name] = (region, gtk.gdk.Rectangle(*arg))
+ add('top', (0, 0, alloc.width, SIZE))
+ add('right', (alloc.width - SIZE, 0, SIZE, alloc.height))
+ add('bottom', (0, alloc.height - SIZE, alloc.width, SIZE))
+ add('left', (0, 0, SIZE, alloc.height))
+ add('center', (SIZE, SIZE, alloc.width - 2 * SIZE,
+ alloc.height - 2 * SIZE))
+
+ def update_status(self, updating=False):
+ numshow = numsel = numtotal = all = None
+ if self.curitems is None:
+ button = False
+ text = _('No items to display')
+ else:
+ # prepare data
+ numshow, numtotal = len(self.showitems), len(self.curitems)
+ data = dict(count=numshow, total=numtotal)
+ if self.sel_enable:
+ items = self.get_items(sel=True)
+ numsel = len([item for item, sel in items if sel])
+ data['sel'] = numsel
+ all = data['count'] == data['total']
+ button = not all
+ # generate status text
+ if updating:
+ text = _('Updating...')
+ elif self.sel_enable:
+ if all:
+ text = _('Selecting %(sel)d of %(total)d, displaying '
+ 'all items') % data
+ else:
+ text = _('Selecting %(sel)d, displaying %(count)d of '
+ '%(total)d items') % data
+ else:
+ if all:
+ text = _('Displaying all items')
+ else:
+ text = _('Displaying %(count)d of %(total)d items') % data
+ self.statuslabel.set_text(text)
+ self.allbtn.set_property('visible', button)
+ self.emit('list-updated', numtotal, numsel, numshow, updating)
+
+ def setup_dnd(self, restart=False):
+ if not restart and self.scroll_timer is None:
+ self.scroll_timer = gobject.timeout_add(25, self.scroll_timeout)
+
+ def teardown_dnd(self, pause=False):
+ first = self.get_sep(0)
+ if first:
+ first.set_visible(False)
+ last = self.get_sep(-1)
+ if last:
+ last.set_visible(False)
+ if self.hlsep:
+ self.hlsep.drag_unhighlight()
+ self.hlsep = None
+ if not pause and self.scroll_timer:
+ gobject.source_remove(self.scroll_timer)
+ self.scroll_timer = None
+
+ def get_item_pos(self, y, detail=False):
+ pos = None
+ items = self.curitems
+ num = len(items)
+ numshow = len(self.showitems)
+ first = self.itemmap[items[0]]
+ beforesnip = self.itemmap[items[numshow - 2]]
+ snip = self.has_limit() and self.itemmap['snip'] or None
+ last = self.itemmap[items[-1]]
+ def calc_ratio(geom):
+ return (y - geom['y']) / float(geom['height'])
+ if y < first['y']:
+ start, end = -1, 0
+ elif last['bottom'] < y:
+ start, end = num - 1, num
+ elif snip and beforesnip['bottom'] < y and y < last['y']:
+ ratio = calc_ratio(snip)
+ if ratio < 0.5:
+ start, end = numshow - 2, numshow - 1
+ else:
+ start, end = num - 2, num - 1
+ else:
+ # calc item showitems pos (binary search)
+ def mid(start, end):
+ return (start + end) / 2
+ start, end = 0, numshow - 1
+ pos = mid(start, end)
+ while start < end:
+ data = self.itemmap[self.showitems[pos]]
+ if y < data['y']:
+ end = pos - 1
+ elif data['bottom'] < y:
+ start = pos + 1
+ else:
+ break
+ pos = mid(start, end)
+ # translate to curitems pos
+ pos = self.trans_to_cur(pos)
+ # calc detailed pos if need
+ if detail:
+ data = self.itemmap[items[pos]]
+ ratio = calc_ratio(data)
+ if ratio < 0.5:
+ start, end = pos - 1, pos
+ else:
+ start, end = pos, pos + 1
+ if detail:
+ return pos, start, end
+ return pos
+
+ def get_sep(self, pos):
+ """
+ pos: Number, the position of separator you need.
+ If -1 or list length, indicates the last separator.
+ """
+ # invalid position
+ if pos < -1:
+ return None
+ def get_last():
+ children = self.csbox.get_children()
+ return children[-1]
+ # -1
+ if pos == -1:
+ return get_last()
+ # limiting case
+ if self.has_limit():
+ # snip box separator
+ if pos == self.limit - 1:
+ return self.itemmap['snip']['sep']
+ # list length (+ snip box)
+ if pos == self.limit + 1:
+ return get_last()
+ # separators after snip box
+ if self.limit - 1 < pos:
+ return self.itemmap[self.showitems[pos-1]]['sep']
+ # list length
+ elif pos == len(self.showitems):
+ return get_last()
+ # others
+ return self.itemmap[self.showitems[pos]]['sep']
+
+ def get_sep_by_y(self, y):
+ pos, start, end = self.get_item_pos(y, detail=True)
+ sep_pos = self.trans_to_show(end)
+ if self.has_limit() and len(self.curitems) - 1 <= end:
+ sep_pos += 1
+ return self.get_sep(sep_pos)
+
+ def update_seps(self):
+ """ Update visibility of all separators """
+ compact = self.get_compact_view()
+ for item in self.showitems[1:]:
+ sep = self.itemmap[item]['sep']
+ sep.set_visible(not compact)
+ if self.has_limit():
+ self.itemmap['snip']['sep'].set_visible(False)
+ self.itemmap[self.showitems[-1]]['sep'].set_visible(False)
+ self.get_sep(0).set_visible(False)
+ self.get_sep(-1).set_visible(False)
+
+ def reorder_item(self, pos, insert):
+ """
+ pos: Number, the position of item to move. This must be curitems
+ index, not showitems index.
+ insert: Number, the new position to insert target item.
+ If list length, indicates the end of the list.
+ This must be curitems index, not showitems index.
+ """
+ # reject unneeded reordering
+ if pos == insert or pos + 1 == insert:
+ return
+
+ # reorder target csinfo
+ if self.has_limit() and self.limit - 1 <= pos:
+ item = self.curitems[pos]
+ if insert < self.limit - 1:
+ # move target csinfo to insert pos
+ target = self.itemmap[item]['widget']
+ self.csbox.reorder_child(target, insert)
+
+ # remove csinfo to be snipped
+ item = self.showitems[-2]
+ self.remove_csinfo(item)
+ else:
+ # remove target csinfo
+ self.remove_csinfo(item)
+
+ # insert csinfo before the last separator
+ item = self.curitems[-2]
+ info = self.add_csinfo(item)
+ info.show_all()
+ numc = len(self.csbox.get_children())
+ self.csbox.reorder_child(info, numc - 2)
+
+ elif self.has_limit() and self.limit - 1 < insert:
+ if self.trans_to_show(insert) < self.limit:
+ # remove target csinfo
+ rm_item = self.curitems[pos]
+ else:
+ # move target csinfo to the end of VBox
+ item = self.curitems[pos]
+ target = self.itemmap[item]['widget']
+ numc = len(self.csbox.get_children())
+ self.csbox.reorder_child(target, numc - 2)
+
+ # remove last csinfo
+ rm_item = self.showitems[-1]
+
+ # remove it
+ self.remove_csinfo(rm_item)
+
+ # insert csinfo before snip box
+ item = self.curitems[self.limit - 1]
+ info = self.add_csinfo(item)
+ info.show_all()
+ self.csbox.reorder_child(info, self.limit - 2)
+ else:
+ info = self.itemmap[self.showitems[pos]]['widget']
+ if insert < pos:
+ self.csbox.reorder_child(info, insert)
+ else:
+ self.csbox.reorder_child(info, insert - 1)
+
+ # reorder curitems
+ item = self.curitems[pos]
+ items = self.curitems[:pos] + self.curitems[pos+1:]
+ if insert < pos:
+ items.insert(insert, item)
+ else:
+ items.insert(insert - 1, item)
+ self.curitems = items
+
+ # reorder showitems
+ if self.has_limit():
+ self.showitems = items[:self.limit-1] + [items[-1]]
+ else:
+ self.showitems = items
+
+ # show/hide separators
+ self.update_seps()
+
+ # just emit 'list-updated' signal
+ self.update_status()
+
+ def trans_to_show(self, index):
+ """ Translate from curitems index to showitems index """
+ numrest = len(self.curitems) - self.limit
+ if self.has_limit() and numrest <= index:
+ return index - numrest
+ return index
+
+ def trans_to_cur(self, index):
+ """ Translate from showitems index to curitems index """
+ if self.has_limit() and self.limit - 1 <= index:
+ return index + len(self.curitems) - self.limit
+ return index
+
+ def create_sep(self):
+ return FixedHSeparator()
+
+ def add_csinfo(self, item):
+ wrapbox = gtk.VBox()
+ sep = self.create_sep()
+ wrapbox.pack_start(sep, False, False)
+ style = self.get_compact_view() and self.lstyle or self.pstyle
+ if self.dnd_enable:
+ style['selectable'] = False
+ info = self.curfactory(item, style)
+ if self.sel_enable:
+ check = gtk.CheckButton()
+ check.set_active(self.chkmap[item])
+ check.connect('toggled', self.check_toggled, item)
+ align = gtk.Alignment(0.5, 0)
+ align.add(check)
+ hbox = gtk.HBox()
+ hbox.pack_start(align, False, False)
+ hbox.pack_start(info, False, False)
+ info = hbox
+ wrapbox.pack_start(info, False, False)
+ self.csbox.pack_start(wrapbox, False, False)
+ self.itemmap[item] = {'widget': wrapbox,
+ 'info': info,
+ 'sep': sep}
+ return wrapbox
+
+ def remove_csinfo(self, item):
+ info = self.itemmap[item]['widget']
+ self.csbox.remove(info)
+ del self.itemmap[item]
+
+ def add_snip(self):
+ wrapbox = gtk.VBox()
+ sep = self.create_sep()
+ wrapbox.pack_start(sep, False, False)
+ snipbox = gtk.HBox()
+ wrapbox.pack_start(snipbox, False, False)
+ spacer = gtk.Label()
+ snipbox.pack_start(spacer, False, False)
+ spacer.set_width_chars(24)
+ sniplbl = gtk.Label()
+ snipbox.pack_start(sniplbl, False, False)
+ sniplbl.set_markup('<span size="large" weight="heavy"'
+ ' font_family="monospace">...</span>')
+ sniplbl.set_angle(90)
+ snipbox.pack_start(gtk.Label())
+ self.csbox.pack_start(wrapbox, False, False, 2)
+ self.itemmap['snip'] = {'widget': wrapbox,
+ 'snip': snipbox,
+ 'sep': sep}
+ return wrapbox
+
+ ### signal handlers ###
+
+ def check_toggled(self, button, item):
+ self.chkmap[item] = button.get_active()
+ self.update_status()
+
+ def dnd_begin(self, widget, context):
+ self.setup_dnd()
+ context.set_icon_pixbuf(self.dnd_pb, 0, 0)
+
+ def dnd_end(self, widget, context):
+ self.teardown_dnd()
+
+ def dnd_motion(self, widget, context, x, y, event_time):
+ if self.item_drag is not None:
+ num = len(self.curitems)
+ if not self.hlsep:
+ self.setup_dnd(restart=True)
+ # highlight separator
+ sep = self.get_sep_by_y(y)
+ first = self.get_sep(0)
+ first.set_visible(first == sep)
+ last = self.get_sep(-1)
+ last.set_visible(last == sep)
+ if self.hlsep != sep:
+ if self.hlsep:
+ self.hlsep.drag_unhighlight()
+ sep.drag_highlight()
+ self.hlsep = sep
+
+ def dnd_leave(self, widget, context, event_time):
+ self.teardown_dnd(pause=True)
+
+ def dnd_received(self, widget, context, x, y, sel, target_type, *args):
+ if target_type == CSL_DND_ITEM:
+ items = self.curitems
+ pos, start, end = self.get_item_pos(y, detail=True)
+ self.reorder_item(self.item_drag, end)
+ elif target_type == CSL_DND_URI_LIST:
+ files = []
+ for line in sel.data.rstrip('\x00').splitlines():
+ if line.startswith('file:'):
+ path = os.path.normpath(urllib.url2pathname(line[5:]))
+ files.append(path)
+ if files:
+ self.emit('files-dropped', files, sel.data)
+
+ def dnd_get(self, widget, context, sel, target_type, event_time):
+ pos = self.item_drag
+ if target_type == CSL_DND_ITEM and pos is not None:
+ sel.set(sel.target, 8, str(self.curitems[pos]))
+
+ def button_press(self, widget, event):
+ items = self.curitems
+ if not self.dnd_enable or not items or len(items) <= 1:
+ return
+ if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:
+ # gather geometry data for dnd
+ items = self.showitems + (self.has_limit() and ['snip'] or [])
+ for item in items:
+ data = self.itemmap[item]
+ alloc = data['widget'].allocation
+ data.update(y=alloc.y, height=alloc.height)
+ data['bottom'] = alloc.y + alloc.height
+ # get pressed csinfo widget based on pointer position
+ pos = self.get_item_pos(event.y)
+ if pos is not None:
+ self.item_drag = pos
+ # start dnd
+ self.csevent.drag_begin(self.dnd_targets,
+ gtk.gdk.ACTION_MOVE, 1, event)
+
+ def scroll_timeout(self):
+ x, y = self.scroll.get_pointer()
+ if not self.areas['center'][0].point_in(x, y):
+ def hscroll(left=False, fast=False):
+ amount = 2
+ if left: amount *= -1
+ if fast: amount *= 3
+ hadj = self.scroll.get_hadjustment()
+ hadj.set_value(hadj.get_value() + amount)
+ def vscroll(up=False, fast=False):
+ amount = 2
+ if up: amount *= -1
+ if fast: amount *= 3
+ vadj = self.scroll.get_vadjustment()
+ vadj.set_value(vadj.get_value() + amount)
+ top, topr = self.areas['top']
+ bottom, bottomr = self.areas['bottom']
+ if y < topr.y:
+ vscroll(up=True, fast=True)
+ elif top.point_in(x, y):
+ vscroll(up=True)
+ elif (bottomr.y + bottomr.height) < y:
+ vscroll(fast=True)
+ elif bottom.point_in(x, y):
+ vscroll()
+ left, leftr = self.areas['left']
+ right, rightr = self.areas['right']
+ if x < leftr.x:
+ hscroll(left=True, fast=True)
+ elif left.point_in(x, y):
+ hscroll(left=True)
+ elif (rightr.x + rightr.width) < x:
+ hscroll(fast=True)
+ elif right.point_in(x, y):
+ hscroll()
+ return True # repeat
+
+class FixedHSeparator(gtk.VBox):
+
+ def __init__(self, visible=True):
+ gtk.VBox.__init__(self)
+ self.set_size_request(-1, 2)
+
+ self.visible = visible
+
+ self.sep = gtk.HSeparator()
+ self.pack_start(self.sep, False, False)
+ self.sep.set_no_show_all(not visible)
+
+ def set_visible(self, visible):
+ if self.visible != visible:
+ self.visible = visible
+ self.sep.set_no_show_all(False)
+ self.sep.set_property('visible', visible)
+ self.sep.set_no_show_all(not visible)
|
@@ -150,7 +150,7 @@ that isn't available"""
def __init__(self, initial = None, title = _('Save File'),
filter = ((_('All files'), '*.*'),), filterindex = 1,
- filename = '', open=False):
+ filename = '', open=False, multi=False):
if initial is None:
initial = os.path.expanduser("~")
self.initial = initial
@@ -159,6 +159,7 @@ self.filter = filter
self.filterindex = filterindex
self.open = open
+ self.multi = multi
def run(self):
"""run the file dialog, either return a file name, or False if
@@ -185,8 +186,11 @@ f = ''
for name, mask in self.filter:
f += '\0'.join([name, mask,''])
+ flags = win32con.OFN_EXPLORER
+ if self.multi:
+ flags |= win32con.OFN_ALLOWMULTISELECT
opts = dict(InitialDir=self.initial,
- Flags=win32con.OFN_EXPLORER,
+ Flags=flags,
File=self.filename,
DefExt=None,
Title=hglib.fromutf(self.title),
@@ -194,11 +198,10 @@ CustomFilter=None,
FilterIndex=self.filterindex)
if self.open:
- fname, _, _ = win32gui.GetOpenFileNameW(**opts)
+ ret = win32gui.GetOpenFileNameW(**opts)
else:
- fname, _, _ = win32gui.GetSaveFileNameW(**opts)
- if fname:
- fname = os.path.abspath(fname)
+ ret = win32gui.GetSaveFileNameW(**opts)
+ fname = ret[0]
except pywintypes.error:
pass
os.chdir(cwd)
@@ -213,6 +216,14 @@ fname = False
if q.qsize():
fname = q.get(0)
+ if fname and self.multi and fname.find('\x00') != -1:
+ splitted = fname.split('\x00')
+ dir, fnames = splitted[0], splitted[1:]
+ fname = []
+ for fn in fnames:
+ path = os.path.abspath(os.path.join(dir, fn))
+ if os.path.exists(path):
+ fname.append(hglib.toutf(path))
return fname
def runCompatible(self):
@@ -227,6 +238,8 @@ dlg = gtk.FileChooserDialog(self.title, None, action, buttons)
dlg.set_default_response(gtk.RESPONSE_OK)
dlg.set_current_folder(self.initial)
+ if self.multi:
+ dlg.set_select_multiple(True)
if not self.open:
dlg.set_current_name(self.filename)
for name, pattern in self.filter:
@@ -235,7 +248,10 @@ fi.add_pattern(pattern)
dlg.add_filter(fi)
if dlg.run() == gtk.RESPONSE_OK:
- result = dlg.get_filename();
+ if self.multi:
+ result = dlg.get_filenames()
+ else:
+ result = dlg.get_filename()
else:
result = False
dlg.destroy()
|
@@ -25,7 +25,7 @@ from tortoisehg.hgtk import gdialog, gtklib, hgcmd, gorev, thgstrip
from tortoisehg.hgtk import backout, status, hgemail, tagadd, update, merge
from tortoisehg.hgtk import archive, changeset, thgconfig, thgmq, histdetails
-from tortoisehg.hgtk import statusbar, bookmark
+from tortoisehg.hgtk import statusbar, bookmark, thgimport
def create_menu(label, callback=None):
menuitem = gtk.MenuItem(label, True)
@@ -304,9 +304,6 @@ dict(text=_('Stop'), name='stop', sensitive=False,
func=self.stop_clicked, icon=gtk.STOCK_STOP),
dict(text='----'),
- dict(text=_('Add Bundle...'), name='add-bundle',
- sensitive=not bool(self.bfile),
- func=self.add_bundle_clicked, icon=gtk.STOCK_ADD),
dict(text=_('Accept Bundle'), name='accept',
sensitive=bool(self.bfile),
func=self.apply_clicked, icon=gtk.STOCK_APPLY),
@@ -314,6 +311,12 @@ sensitive=bool(self.bfile),
func=self.reject_clicked, icon=gtk.STOCK_DIALOG_ERROR),
dict(text='----'),
+ dict(text=_('Import...'), name='import',
+ func=self.import_clicked, icon='menuimport.ico'),
+ dict(text=_('Add Bundle...'), name='add-bundle',
+ sensitive=not bool(self.bfile),
+ func=self.add_bundle_clicked, icon=gtk.STOCK_ADD),
+ dict(text='----'),
dict(text=_('Use proxy server'), name='use-proxy-server',
ascheck=True, func=toggle_proxy),
dict(text=_('Force push'), ascheck=True, func=toggle_force),
@@ -1595,6 +1598,17 @@ def stop_clicked(self, toolbutton):
self.runner.stop()
+ def import_clicked(self, toolbutton):
+ oldlen = len(self.repo)
+ def import_completed():
+ self.repo.invalidate()
+ self.changeview.clear()
+ if oldlen < len(self.repo):
+ self.reload_log()
+ dialog = thgimport.ImportDialog(self.repo)
+ dialog.set_notify_func(import_completed)
+ self.show_dialog(dialog)
+
def update_urllist(self):
urllist = self.urlcombo.get_model()
urllist.clear()
|
|
|
@@ -0,0 +1,283 @@ + # thgimport.py - TortoiseHg's dialog for (q)importing patches
+#
+# Copyright 2009 Yuki KODAMA <endflow.net@gmail.com>
+#
+# 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 gobject
+
+from mercurial import hg, ui
+
+from tortoisehg.util.i18n import _
+from tortoisehg.util import hglib, paths
+
+from tortoisehg.hgtk import hgcmd, gtklib, gdialog, cslist
+
+MODE_NORMAL = 'normal'
+MODE_WORKING = 'working'
+
+DEST_ID = 0
+DEST_LABEL = 1
+
+class ImportDialog(gtk.Dialog):
+ """ Dialog to import patches """
+
+ def __init__(self, repo=None):
+ """ Initialize the Dialog """
+ gtk.Dialog.__init__(self)
+ gtklib.set_tortoise_icon(self, 'menuimport.ico')
+ gtklib.set_tortoise_keys(self)
+ self.set_resizable(False)
+ self.set_has_separator(False)
+
+ # buttons
+ self.importbtn = self.add_button(_('Import'), gtk.RESPONSE_OK)
+ self.closebtn = self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
+
+ if not repo:
+ try:
+ repo = hg.repository(ui.ui(), path=paths.find_root())
+ except hglib.RepoError:
+ gtklib.idle_add_single_call(self.destroy)
+ return
+ self.repo = repo
+ self.set_title(_('Import - %s') % hglib.get_reponame(repo))
+ self.done = False
+
+ # layout table
+ self.table = table = gtklib.LayoutTable()
+ self.vbox.pack_start(table, True, True, 2)
+
+ ## source path entry & browse button
+ self.source_entry = gtk.Entry()
+ self.files_btn = gtk.Button(_('Files...'))
+ self.dir_btn = gtk.Button(_('Directory...'))
+ table.add_row(_('Source:'), self.source_entry, 1,
+ self.files_btn, self.dir_btn, expand=0)
+
+ # copy form thgstrip.py
+ def createlabel():
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ label.set_size_request(-1, 24)
+ label.size_request()
+ return label
+
+ ## info label
+ self.infolbl = createlabel()
+ self.infobox = gtk.HBox()
+ self.infobox.pack_start(self.infolbl, False, False)
+ table.add_row(_('Preview:'), self.infobox, padding=False)
+
+ ## dest combo
+ self.dest_model = gtk.ListStore(gobject.TYPE_STRING, # dest id
+ gobject.TYPE_STRING) # dest label
+ for row in {'repo': _('Repository'),
+ 'mq': _('Patch Queue')}.items():
+ self.dest_model.append(row)
+ self.dest_combo = gtk.ComboBox(self.dest_model)
+ cell = gtk.CellRendererText()
+ self.dest_combo.pack_start(cell, True)
+ self.dest_combo.add_attribute(cell, 'text', DEST_LABEL)
+ self.dest_combo.set_active(0)
+
+ ## patch preview
+ self.cslist = cslist.ChangesetList()
+ table.add_row(None, self.cslist, padding=False)
+ self.cslist.set_dnd_enable(True)
+ self.cslist.set_checkbox_enable(True)
+
+ # signal handlers
+ self.connect('response', self.dialog_response)
+ self.files_btn.connect('clicked', self.files_clicked)
+ self.dir_btn.connect('clicked', self.dir_clicked)
+ self.cslist.connect('list-updated', self.list_updated)
+ self.cslist.connect('files-dropped', self.files_dropped)
+ self.source_entry.connect('changed', lambda e: self.preview(queue=True))
+
+ # prepare to show
+ self.cslist.clear()
+ gtklib.idle_add_single_call(self.after_init)
+
+ def after_init(self):
+ # dest combo
+ self.dest_combo.show_all()
+ self.dest_combo.hide()
+ self.infobox.pack_start(self.dest_combo, False, False, 6)
+
+ # CmdWidget
+ self.cmd = hgcmd.CmdWidget()
+ self.cmd.show_all()
+ self.cmd.hide()
+ self.vbox.pack_start(self.cmd, True, True, 6)
+
+ # abort button
+ self.abortbtn = self.add_button(_('Abort'), gtk.RESPONSE_CANCEL)
+ self.abortbtn.hide()
+
+ def files_clicked(self, button):
+ initdir = self.get_initial_dir()
+ result = gtklib.NativeSaveFileDialogWrapper(title=_('Select Patches'),
+ initial=initdir, open=True, multi=True).run()
+ if result and result != initdir:
+ if not isinstance(result, basestring):
+ result = os.pathsep.join(result)
+ self.source_entry.set_text(result)
+ self.preview()
+
+ def dir_clicked(self, button):
+ initdir = self.get_initial_dir()
+ result = gtklib.NativeFolderSelectDialog(
+ title=_('Select Directory contains patches:'),
+ initial=initdir).run()
+ if result and result != initdir:
+ self.source_entry.set_text(result)
+ self.preview()
+
+ def list_updated(self, cslist, total, sel, *args):
+ self.update_status(sel)
+
+ def files_dropped(self, cslist, files, *args):
+ src = self.source_entry.get_text()
+ if src:
+ files = [src] + files
+ self.source_entry.set_text(os.pathsep.join(files))
+ self.preview()
+
+ def dialog_response(self, dialog, response_id):
+ def abort():
+ self.cmd.stop()
+ self.cmd.show_log()
+ self.switch_to(MODE_NORMAL, cmd=False)
+ # Import button
+ if response_id == gtk.RESPONSE_OK:
+ self.doimport()
+ # Close button or dialog closing by the user
+ elif response_id in (gtk.RESPONSE_CLOSE, gtk.RESPONSE_DELETE_EVENT):
+ if self.cmd.is_alive():
+ ret = gdialog.Confirm(_('Confirm Abort'), [], self,
+ _('Do you want to abort?')).run()
+ if ret == gtk.RESPONSE_YES:
+ abort()
+ elif len(self.cslist.get_items()) != 0 and not self.done:
+ ret = gdialog.Confirm(_('Confirm Close'), [], self,
+ _('Do you want to close?')).run()
+ if ret == gtk.RESPONSE_YES:
+ self.destroy()
+ return # close dialog
+ else:
+ self.destroy()
+ return # close dialog
+ # Abort button
+ elif response_id == gtk.RESPONSE_CANCEL:
+ abort()
+ else:
+ raise _('unexpected response id: %s') % response_id
+
+ self.run() # don't close dialog
+
+ def get_initial_dir(self):
+ src = self.source_entry.get_text()
+ if src and os.path.exists(src):
+ if os.path.isdir(src):
+ return src
+ parent = os.path.dirname(src)
+ if parent and os.path.exists(parent):
+ return parent
+ return None
+
+ def update_status(self, count):
+ if count:
+ info = _('<span weight="bold">%s patches</span> will'
+ ' be imported to the') % count
+ else:
+ info = '<span weight="bold" foreground="#880000">%s</span>' \
+ % _('Nothing to import')
+ self.infolbl.set_markup(info)
+ self.dest_combo.set_property('visible', bool(count))
+ self.importbtn.set_sensitive(bool(count))
+
+ def get_filepaths(self):
+ src = self.source_entry.get_text()
+ if not src:
+ return []
+ files = []
+ for path in src.split(os.pathsep):
+ path = path.strip('\r\n\t ')
+ if not os.path.exists(path) or path in files:
+ continue
+ if os.path.isdir(path):
+ entries = os.listdir(path)
+ for entry in entries:
+ file = os.path.join(path, entry)
+ if os.path.isfile(file):
+ files.append(file)
+ elif os.path.isfile(path):
+ files.append(path)
+ return files
+
+ def get_dest(self):
+ iter = self.dest_combo.get_active_iter()
+ return self.dest_model.get(iter, DEST_ID)[0]
+
+ def preview(self, queue=False):
+ files = self.get_filepaths()
+ if files:
+ self.cslist.update(files, self.repo, queue=queue)
+ else:
+ self.cslist.clear()
+
+ def set_notify_func(self, func, *args, **kargs):
+ self.notify_func = func
+ self.notify_args = args
+ self.notify_kargs = kargs
+
+ def switch_to(self, mode, cmd=True):
+ if mode == MODE_NORMAL:
+ normal = True
+ self.closebtn.grab_focus()
+ elif mode == MODE_WORKING:
+ normal = False
+ self.abortbtn.grab_focus()
+ else:
+ raise _('unknown mode name: %s') % mode
+ working = not normal
+
+ self.table.set_sensitive(normal)
+ self.importbtn.set_property('visible', normal)
+ self.closebtn.set_property('visible', normal)
+ if cmd:
+ self.cmd.set_property('visible', working)
+ self.abortbtn.set_property('visible', working)
+
+ def doimport(self):
+ items = self.cslist.get_items(sel=True)
+ files = [file for file, sel in items if sel]
+ if not files:
+ return
+
+ if 'repo' == self.get_dest():
+ cmd = 'import'
+ else:
+ cmd = 'qimport'
+ cmdline = ['hg', cmd, '--verbose']
+ cmdline.extend(files)
+
+ def cmd_done(returncode, useraborted):
+ self.done = True
+ self.switch_to(MODE_NORMAL, cmd=False)
+ if hasattr(self, 'notify_func'):
+ self.notify_func(*self.notify_args, **self.notify_kargs)
+ if returncode == 0:
+ if not self.cmd.is_show_log():
+ self.response(gtk.RESPONSE_CLOSE)
+ self.cmd.set_result(_('Imported successfully'), style='ok')
+ elif useraborted:
+ self.cmd.set_result(_('Canceled importing'), style='error')
+ else:
+ self.cmd.set_result(_('Failed to import'), style='error')
+ self.switch_to(MODE_WORKING)
+ self.cmd.execute(cmdline, cmd_done)
|
@@ -497,18 +497,20 @@ return self.mqloaded and os.path.isdir(self.repo.mq.path)
def has_patch(self):
- """ return True if MQ has applicable patch """
- if self.mqloaded:
- return len(self.repo.mq.series) > 0
- return False
+ """ return True if MQ has applicable patches """
+ return bool(self.get_num_patches())
def has_applied(self):
""" return True if MQ has applied patches """
+ return bool(self.get_num_applied())
+
+ def get_num_patches(self):
+ """ return the number of patches in patch queue """
if self.mqloaded:
- return len(self.repo.mq.applied) > 0
- return False
+ return len(self.repo.mq.series)
+ return 0
- def number_applied(self):
+ def get_num_applied(self):
""" return the number of applied patches """
if self.mqloaded:
return len(self.repo.mq.applied)
@@ -629,7 +631,7 @@ is_qtip = self.is_qtip(row[MQ_NAME])
is_qparent = row[MQ_INDEX] == INDEX_QPARENT
is_applied = row[MQ_STATUS] == 'A'
- is_next = row[MQ_INDEX] == self.number_applied()
+ is_next = row[MQ_INDEX] == self.get_num_applied()
if is_operable and not is_qtip and (not is_qparent or has_applied):
append(_('_Goto'), self.goto_activated)
|
|
@@ -17,7 +17,7 @@ from tortoisehg.util.i18n import _
from tortoisehg.util import hglib, paths
-from tortoisehg.hgtk import csinfo, hgcmd, gtklib, gdialog
+from tortoisehg.hgtk import csinfo, hgcmd, gtklib, gdialog, cslist
MODE_NORMAL = 'normal'
MODE_WORKING = 'working'
@@ -33,7 +33,6 @@ self.set_resizable(False)
self.set_has_separator(False)
self.connect('response', self.dialog_response)
- self.timeout_queue = []
# buttons
self.stripbtn = self.add_button(_('Strip'), gtk.RESPONSE_OK)
@@ -52,8 +51,6 @@ elif rev is None:
rev = 'tip'
rev = str(rev)
- self.currev = None
- self.curnum = None
# layout table
self.table = table = gtklib.LayoutTable()
@@ -92,40 +89,9 @@ self.resultlbl = createlabel()
table.add_row(_('Preview:'), self.resultlbl, padding=False)
- ## preview box
- pframe = gtk.Frame()
- table.add_row(None, pframe, padding=False)
- pframe.set_shadow_type(gtk.SHADOW_IN)
- pbox = gtk.VBox()
- pframe.add(pbox)
-
- ### preview status box
- self.pstatbox = gtk.HBox()
- pbox.pack_start(self.pstatbox)
- pbox.pack_start(gtk.HSeparator(), False, False, 2)
-
- #### status label
- self.pstatlbl = createlabel()
- self.pstatbox.pack_start(self.pstatlbl, False, False, 2)
-
- #### show all button
- self.allbtn = gtk.Button(_('Show all')) # add later
-
- #### preview option
- self.compactopt = gtk.CheckButton(_('Use compact view'))
- self.pstatbox.pack_end(self.compactopt, False, False, 2)
- self.compactopt.connect('toggled', self.compact_toggled)
-
- ### changeset view
- rview = gtk.ScrolledWindow()
- pbox.add(rview)
- rview.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- rview.set_size_request(400, 180)
- rview.size_request()
- self.resultbox = gtk.VBox()
- rview.add_with_viewport(self.resultbox)
- rview.child.set_shadow_type(gtk.SHADOW_NONE)
- self.resultbox.set_border_width(4)
+ ## changeset list
+ self.cslist = cslist.ChangesetList()
+ table.add_row(None, self.cslist, padding=False)
## options
self.expander = gtk.Expander(_('Options:'))
@@ -138,13 +104,7 @@
# signal handlers
self.revcombo.connect('changed', lambda c: self.preview(queue=True))
- self.allbtn.connect('clicked', lambda b: self.preview(limit=False))
-
- # csetinfo factory
- self.factory = csinfo.factory(repo, withupdate=True)
- self.lstyle = csinfo.labelstyle(contents=('%(revnum)s:',
- ' %(branch)s', ' %(tags)s', ' %(summary)s'))
- self.pstyle = csinfo.panelstyle()
+ self.cslist.connect('list-updated', self.preview_updated)
# prepare to show
self.preview()
@@ -152,9 +112,6 @@ gtklib.idle_add_single_call(self.after_init)
def after_init(self):
- # add 'Show all' button
- self.pstatbox.pack_start(self.allbtn, False, False, 4)
-
# backup types (foldable)
self.butable = gtklib.LayoutTable()
self.vbox.pack_start(self.butable, True, True)
@@ -188,53 +145,11 @@ self.notify_kargs = kargs
def preview(self, limit=True, queue=False, force=False):
- def clear_preview():
- self.resultbox.foreach(lambda c: c.parent.remove(c))
- def update_info(num=None):
- if num is None:
- info = '<span weight="bold" foreground="#880000">%s</span>' \
- % _('Unknown revision!')
- else:
- info = _('<span weight="bold">%s changesets</span> will'
- ' be stripped') % num
- self.resultlbl.set_markup(info)
- def update_stat(show=None, total=None):
- if show is None or total is None:
- all = False
- stat = _('No changesets to display')
- else:
- all = show == total
- if all:
- stat = _('Displaying all changesets')
- else:
- stat = _('Displaying %(count)d of %(total)d changesets') \
- % dict(count=show, total=total)
- self.pstatlbl.set_text(stat)
- return all
-
# check revision
rev = self.get_rev()
if rev is None:
- clear_preview()
- update_info()
- update_stat()
- self.timeout_queue = []
- self.currev = None
+ self.cslist.clear()
return
- elif not force and limit and self.currev == rev: # is already shown?
- update_info(self.curnum)
- return
- elif queue: # queueing if need
- def timeout(eid):
- if self.timeout_queue and self.timeout_queue[-1] == eid[0]:
- self.preview()
- self.timeout_queue = []
- return False # don't repeat
- event_id = [None]
- event_id[0] = gobject.timeout_add(600, timeout, event_id)
- self.timeout_queue.append(event_id[0])
- return
- self.currev = rev
# enumerate all descendants
# borrowed from strip() in 'mercurial/repair.py'
@@ -244,54 +159,9 @@ parents = cl.parentrevs(r)
if parents[0] in tostrip or parents[1] in tostrip:
tostrip.append(r)
- self.curnum = numtotal = len(tostrip)
- LIM = 100
- compactview = self.compactopt.get_active()
- style = compactview and self.lstyle or self.pstyle
- def add_csinfo(revnum):
- info = self.factory(revnum, style)
- if info.parent:
- info.parent.remove(info)
- self.resultbox.pack_start(info, False, False, 2)
- def add_sep():
- if not compactview:
- self.resultbox.pack_start(gtk.HSeparator(), False, False)
- def add_snip():
- snipbox = gtk.HBox()
- self.resultbox.pack_start(snipbox, False, False, 4)
- spacer = gtk.Label()
- snipbox.pack_start(spacer, False, False)
- spacer.set_width_chars(24)
- sniplbl = gtk.Label()
- snipbox.pack_start(sniplbl, False, False)
- sniplbl.set_markup('<span size="large" weight="heavy"'
- ' font_family="monospace">...</span>')
- sniplbl.set_angle(90)
- snipbox.pack_start(gtk.Label())
-
- # update changeset preview
- if limit and numtotal > LIM:
- toshow, lastrev = tostrip[:LIM-1], tostrip[LIM-1:][-1]
- else:
- toshow, lastrev = tostrip, None
- numshow = len(toshow) + (lastrev and 1 or 0)
- clear_preview()
- for r in toshow:
- add_csinfo(r)
- if not r == toshow[-1]: # no need to append to the last
- add_sep()
- if lastrev:
- add_snip()
- add_csinfo(lastrev)
- self.resultbox.show_all()
-
- # update info label
- update_info(numtotal)
-
- # update preview status & button
- all = update_stat(numshow, numtotal)
- self.allbtn.set_property('visible', not all)
+ # update preview
+ self.cslist.update(tostrip, self.repo, limit, queue)
def dialog_response(self, dialog, response_id):
def abort():
@@ -325,8 +195,14 @@ else:
self.butable.hide()
- def compact_toggled(self, checkbtn):
- self.preview(force=True)
+ def preview_updated(self, cslist, total, *args):
+ if total is None:
+ info = '<span weight="bold" foreground="#880000">%s</span>' \
+ % _('Unknown revision!')
+ else:
+ info = _('<span weight="bold">%s changesets</span> will'
+ ' be stripped') % total
+ self.resultlbl.set_markup(info)
def get_rev(self):
""" Return integer revision number or None """
|
Loading...