/tortoisehg/util/menuthg.py

https://bitbucket.org/tortoisehg/hgtk/ · Python · 334 lines · 257 code · 56 blank · 21 comment · 55 complexity · 2d19a5544ee22fc717f6990ee7eaff4f MD5 · raw file

  1. # menuthg.py - TortoiseHg shell extension menu
  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. import os
  8. from mercurial import hg, ui, node, error
  9. from tortoisehg.util.i18n import _ as gettext
  10. from tortoisehg.util import cachethg, paths, hglib
  11. def _(msgid):
  12. return {'id': msgid, 'str': gettext(msgid)}
  13. thgcmenu = {
  14. 'commit': { 'label': _('Commit...'),
  15. 'help': _('Commit changes in repository'),
  16. 'icon': 'menucommit.ico'},
  17. 'init': { 'label': _('Create Repository Here'),
  18. 'help': _('Create a new repository'),
  19. 'icon': 'menucreaterepos.ico'},
  20. 'clone': { 'label': _('Clone...'),
  21. 'help': _('Create clone here from source'),
  22. 'icon': 'menuclone.ico'},
  23. 'status': { 'label': _('File Status'),
  24. 'help': _('Repository status & changes'),
  25. 'icon': 'menushowchanged.ico'},
  26. 'add': { 'label': _('Add Files...'),
  27. 'help': _('Add files to version control'),
  28. 'icon': 'menuadd.ico'},
  29. 'revert': { 'label': _('Revert Files...'),
  30. 'help': _('Revert file changes'),
  31. 'icon': 'menurevert.ico'},
  32. 'forget': { 'label': _('Forget Files...'),
  33. 'help': _('Remove files from version control'),
  34. 'icon': 'menurevert.ico'},
  35. 'remove': { 'label': _('Remove Files...'),
  36. 'help': _('Remove files from version control'),
  37. 'icon': 'menudelete.ico'},
  38. 'rename': { 'label': _('Rename File'),
  39. 'help': _('Rename file or directory'),
  40. 'icon': 'general.ico'},
  41. 'workbench': { 'label': _('Workbench'),
  42. 'help': _('View change history in repository'),
  43. 'icon': 'menulog.ico'},
  44. 'log': { 'label': _('File History'),
  45. 'help': _('View change history of selected files'),
  46. 'icon': 'menulog.ico'},
  47. 'synch': { 'label': _('Synchronize'),
  48. 'help': _('Synchronize with remote repository'),
  49. 'icon': 'menusynch.ico'},
  50. 'serve': { 'label': _('Web Server'),
  51. 'help': _('Start web server for this repository'),
  52. 'icon': 'proxy.ico'},
  53. 'update': { 'label': _('Update...'),
  54. 'help': _('Update working directory'),
  55. 'icon': 'menucheckout.ico'},
  56. 'thgstatus': { 'label': _('Update Icons'),
  57. 'help': _('Update icons for this repository'),
  58. 'icon': 'refresh_overlays.ico'},
  59. 'userconf': { 'label': _('Global Settings'),
  60. 'help': _('Configure user wide settings'),
  61. 'icon': 'settings_user.ico'},
  62. 'repoconf': { 'label': _('Repository Settings'),
  63. 'help': _('Configure repository settings'),
  64. 'icon': 'settings_repo.ico'},
  65. 'shellconf': { 'label': _('Explorer Extension Settings'),
  66. 'help': _('Configure Explorer extension'),
  67. 'icon': 'settings_user.ico'},
  68. 'about': { 'label': _('About TortoiseHg'),
  69. 'help': _('Show About Dialog'),
  70. 'icon': 'menuabout.ico'},
  71. 'vdiff': { 'label': _('Visual Diff'),
  72. 'help': _('View changes using GUI diff tool'),
  73. 'icon': 'TortoiseMerge.ico'},
  74. 'hgignore': { 'label': _('Edit Ignore Filter'),
  75. 'help': _('Edit repository ignore filter'),
  76. 'icon': 'ignore.ico'},
  77. 'guess': { 'label': _('Guess Renames'),
  78. 'help': _('Detect renames and copies'),
  79. 'icon': 'detect_rename.ico'},
  80. 'grep': { 'label': _('Search History'),
  81. 'help': _('Search file revisions for patterns'),
  82. 'icon': 'menurepobrowse.ico'},
  83. 'dndsynch': { 'label': _('DnD Synchronize'),
  84. 'help': _('Synchronize with dragged repository'),
  85. 'icon': 'menusynch.ico'}}
  86. _ALWAYS_DEMOTE_ = ('about', 'userconf', 'repoconf')
  87. class TortoiseMenu(object):
  88. def __init__(self, menutext, helptext, hgcmd, icon=None, state=True):
  89. self.menutext = menutext
  90. self.helptext = helptext
  91. self.hgcmd = hgcmd
  92. self.icon = icon
  93. self.state = state
  94. def isSubmenu(self):
  95. return False
  96. def isSep(self):
  97. return False
  98. class TortoiseSubmenu(TortoiseMenu):
  99. def __init__(self, menutext, helptext, menus=[], icon=None):
  100. TortoiseMenu.__init__(self, menutext, helptext, None, icon)
  101. self.menus = menus[:]
  102. def add_menu(self, menutext, helptext, hgcmd, icon=None, state=True):
  103. self.menus.append(TortoiseMenu(menutext, helptext,
  104. hgcmd, icon, state))
  105. def add_sep(self):
  106. self.menus.append(TortoiseMenuSep())
  107. def get_menus(self):
  108. return self.menus
  109. def append(self, entry):
  110. self.menus.append(entry)
  111. def isSubmenu(self):
  112. return True
  113. class TortoiseMenuSep(object):
  114. hgcmd = '----'
  115. def isSubmenu(self):
  116. return False
  117. def isSep(self):
  118. return True
  119. class thg_menu(object):
  120. def __init__(self, ui, promoted, name = "TortoiseHg"):
  121. self.menus = [[]]
  122. self.ui = ui
  123. self.name = name
  124. self.sep = [False]
  125. self.promoted = promoted
  126. def add_menu(self, hgcmd, icon=None, state=True):
  127. if hgcmd in self.promoted:
  128. pos = 0
  129. else:
  130. pos = 1
  131. while len(self.menus) <= pos: #add Submenu
  132. self.menus.append([])
  133. self.sep.append(False)
  134. if self.sep[pos]:
  135. self.sep[pos] = False
  136. self.menus[pos].append(TortoiseMenuSep())
  137. self.menus[pos].append(TortoiseMenu(
  138. thgcmenu[hgcmd]['label']['str'],
  139. thgcmenu[hgcmd]['help']['str'], hgcmd,
  140. thgcmenu[hgcmd]['icon'], state))
  141. def add_sep(self):
  142. self.sep = [True for _s in self.sep]
  143. def get(self):
  144. menu = self.menus[0][:]
  145. for submenu in self.menus[1:]:
  146. menu.append(TortoiseSubmenu(self.name, 'Mercurial', submenu, "hg.ico"))
  147. menu.append(TortoiseMenuSep())
  148. return menu
  149. def __iter__(self):
  150. return iter(self.get())
  151. def open_repo(path):
  152. root = paths.find_root(path)
  153. if root:
  154. try:
  155. repo = hg.repository(ui.ui(), path=root)
  156. return repo
  157. except error.RepoError:
  158. pass
  159. except StandardError, e:
  160. print "error while opening repo %s:" % path
  161. print e
  162. return None
  163. class menuThg:
  164. """shell extension that adds context menu items"""
  165. def __init__(self, internal=False):
  166. self.name = "TortoiseHg"
  167. promoted = []
  168. pl = ui.ui().config('tortoisehg', 'promoteditems', 'commit,log')
  169. for item in pl.split(','):
  170. item = item.strip()
  171. if item:
  172. promoted.append(item)
  173. if internal:
  174. for item in thgcmenu.keys():
  175. promoted.append(item)
  176. for item in _ALWAYS_DEMOTE_:
  177. if item in promoted:
  178. promoted.remove(item)
  179. self.promoted = promoted
  180. def get_commands_dragdrop(self, srcfiles, destfolder):
  181. """
  182. Get a list of commands valid for the current selection.
  183. Commands are instances of TortoiseMenu, TortoiseMenuSep or TortoiseMenu
  184. """
  185. # we can only accept dropping one item
  186. if len(srcfiles) > 1:
  187. return []
  188. # open repo
  189. drag_repo = None
  190. drop_repo = None
  191. drag_path = srcfiles[0]
  192. drag_repo = open_repo(drag_path)
  193. if not drag_repo:
  194. return []
  195. if drag_repo and drag_repo.root != drag_path:
  196. return [] # dragged item must be a hg repo root directory
  197. drop_repo = open_repo(destfolder)
  198. menu = thg_menu(drag_repo.ui, self.promoted, self.name)
  199. menu.add_menu('clone')
  200. if drop_repo:
  201. menu.add_menu('dndsynch')
  202. return menu
  203. def get_norepo_commands(self, cwd, files):
  204. menu = thg_menu(ui.ui(), self.promoted, self.name)
  205. menu.add_menu('clone')
  206. menu.add_menu('init')
  207. menu.add_menu('userconf')
  208. menu.add_sep()
  209. menu.add_menu('about')
  210. menu.add_sep()
  211. return menu
  212. def get_commands(self, repo, cwd, files):
  213. """
  214. Get a list of commands valid for the current selection.
  215. Commands are instances of TortoiseMenu, TortoiseMenuSep or TortoiseMenu
  216. """
  217. states = set()
  218. onlyfiles = len(files) > 0
  219. hashgignore = False
  220. for f in files:
  221. if not os.path.isfile(f):
  222. onlyfiles = False
  223. if f.endswith('.hgignore'):
  224. hashgignore = True
  225. states.update(cachethg.get_states(f, repo))
  226. if not files:
  227. states.update(cachethg.get_states(cwd, repo))
  228. if cachethg.ROOT in states and len(states) == 1:
  229. states.add(cachethg.MODIFIED)
  230. changed = bool(states & set([cachethg.ADDED, cachethg.MODIFIED]))
  231. modified = cachethg.MODIFIED in states
  232. clean = cachethg.UNCHANGED in states
  233. tracked = changed or modified or clean
  234. new = bool(states & set([cachethg.UNKNOWN, cachethg.IGNORED]))
  235. menu = thg_menu(repo.ui, self.promoted, self.name)
  236. if changed or cachethg.UNKNOWN in states or 'qtip' in repo['.'].tags():
  237. menu.add_menu('commit')
  238. if hashgignore or new and len(states) == 1:
  239. menu.add_menu('hgignore')
  240. if changed or cachethg.UNKNOWN in states:
  241. menu.add_menu('status')
  242. # Visual Diff (any extdiff command)
  243. has_vdiff = repo.ui.config('tortoisehg', 'vdiff', 'vdiff') != ''
  244. if has_vdiff and modified:
  245. menu.add_menu('vdiff')
  246. if len(files) == 0 and cachethg.UNKNOWN in states:
  247. menu.add_menu('guess')
  248. elif len(files) == 1 and tracked: # needs ico
  249. menu.add_menu('rename')
  250. if files and new:
  251. menu.add_menu('add')
  252. if files and tracked:
  253. menu.add_menu('remove')
  254. if files and changed:
  255. menu.add_menu('revert')
  256. menu.add_sep()
  257. if tracked:
  258. menu.add_menu(files and 'log' or 'workbench')
  259. if len(files) == 0:
  260. menu.add_sep()
  261. menu.add_menu('grep')
  262. menu.add_sep()
  263. menu.add_menu('synch')
  264. menu.add_menu('serve')
  265. menu.add_sep()
  266. menu.add_menu('clone')
  267. if repo.root != cwd:
  268. menu.add_menu('init')
  269. # add common menu items
  270. menu.add_sep()
  271. menu.add_menu('userconf')
  272. if tracked:
  273. menu.add_menu('repoconf')
  274. menu.add_menu('about')
  275. menu.add_sep()
  276. return menu