by
Changes to 15 files · Browse files at 225fc4c38ac7 Showing diff from parent 090ac0b4f8ab 5a2a48e63e43 Diff from another changeset...
|
|
@@ -1,6 +1,6 @@ - # Trivial Mercurial plugin for Nautilus
+# TortoiseHg plugin for Nautilus
#
-# Copyright (C) 2007 Steve Borho
+# Copyright (C) 2007-9 Steve Borho
#
# Stolen mercilessly from nautilus-bzr, thanks guys
# Copyright (C) 2006 Jeff Bailey
@@ -9,26 +9,36 @@#
# Published under the GNU GPL
-import gconf
import gtk
import gobject
-from mercurial import hg, ui, match, util
-from mercurial.node import short
import nautilus
+
+try:
+ from mercurial import demandimport
+except ImportError:
+ # workaround to use user's local python libs
+ userlib = os.path.expanduser('~/lib/python')
+ if os.path.exists(userlib) and userlib not in sys.path:
+ sys.path.append(userlib)
+ from mercurial import demandimport
+demandimport.enable()
+
import os
import subprocess
import sys
-import tempfile
-import time
import urllib
+from mercurial import hg, ui, match, util
try:
from mercurial.error import RepoError
except ImportError:
from mercurial.repo import RepoError
+from mercurial.node import short
TORTOISEHG_PATH = '~/tools/tortoisehg-dev'
-TERMINAL_KEY = '/desktop/gnome/applications/terminal/exec'
+nofilecmds = 'about serve synch repoconfig userconfig merge unmerge'.split()
+nocachecmds = 'about serve repoconfig userconfig'.split()
+
class HgExtension(nautilus.MenuProvider,
nautilus.ColumnProvider,
@@ -38,26 +48,55 @@ def __init__(self):
self.cacherepo = None
self.cacheroot = None
- self.client = gconf.client_get_default()
- thgpath = os.environ.get('TORTOISEHG_PATH',
- os.path.expanduser(TORTOISEHG_PATH))
- os.environ['TORTOISEHG_PATH'] = thgpath
- os.environ['THG_ICON_PATH'] = os.path.join(thgpath, 'icons')
- self.hgproc = os.path.join(thgpath, 'hgproc.py')
+ self.scanStack = []
+
+ # check if nautilus-thg.py is a symlink first
+ pfile = __file__
+ if pfile.endswith('.pyc'):
+ pfile = pfile[:-1]
+ path = os.path.dirname(os.path.realpath(pfile))
+ thgpath = os.path.normpath(os.path.join(path, '..'))
+ testpath = os.path.join(thgpath, 'tortoise')
+ if os.path.isdir(testpath):
+ if thgpath not in sys.path:
+ sys.path.insert(0, thgpath)
+ else:
+ # try environment or hard-coded path
+ thgpath = os.environ.get('TORTOISEHG_PATH', TORTOISEHG_PATH)
+ thgpath = os.path.normpath(os.path.expanduser(thgpath))
+ if os.path.exists(thgpath) and thgpath not in sys.path:
+ sys.path.insert(0, thgpath)
+ # else assume tortoise is already in PYTHONPATH
+ try:
+ import tortoise.thgutil
+ import tortoise.menuthg
+ except ImportError, e:
+ # if thgutil is not found, then repository cannot be found
+ # if menuthg is not found, you have an older version in sys.path
+ print e
+ self.menu = None
+ return
+
+ self.env = os.environ
+ self.env['PYTHONPATH'] = ':'.join(sys.path)
+ self.env['TORTOISEHG_PATH'] = thgpath
+ self.env['THG_ICON_PATH'] = os.path.join(thgpath, 'icons')
+
+ self.hgproc = tortoise.thgutil.find_path('hgtk',
+ tortoise.thgutil.get_prog_root())
+ if not self.hgproc:
+ self.hgproc = tortoise.thgutil.find_path('hgtk')
self.ipath = os.path.join(thgpath, 'icons', 'tortoise')
+ self.menu = tortoise.menuthg.menuThg()
def icon(self, iname):
return os.path.join(self.ipath, iname)
def get_path_for_vfs_file(self, vfs_file):
- if vfs_file.get_uri_scheme() != 'file':
+ if vfs_file.is_gone() or vfs_file.get_uri_scheme() != 'file':
return None
return urllib.unquote(vfs_file.get_uri()[7:])
- def clear_cached_repo(self):
- self.cacheroot = None
- self.cacherepo = None
-
def get_repo_for_path(self, path):
'''
Find mercurial repository for vfs_file
@@ -81,353 +120,132 @@ self.cacheroot = None
self.cacherepo = None
return None
+ except StandardError, e:
+ print e
+ return None
- def _open_terminal_cb(self, window, vfs_file):
- path = self.get_path_for_vfs_file(vfs_file)
- if path is None:
- return
- os.chdir(path)
- terminal = self.client.get_string(TERMINAL_KEY)
- os.system('%s &' % terminal)
-
- def _about_cb(self, window, vfs_file):
- self._run_dialog('about', [vfs_file])
-
- def _add_cb(self, window, vfs_files):
- self._run_dialog('add', vfs_files)
- self.clear_cached_repo()
-
- def _clone_cb(self, window, vfs_file):
- self._run_dialog('clone', [vfs_file])
-
- def _commit_cb(self, window, vfs_files):
- self._run_dialog('commit', vfs_files)
- self.clear_cached_repo()
-
- def _datamine_cb(self, window, vfs_files):
- self._run_dialog('datamine', vfs_files)
-
- def _diff_cb(self, window, vfs_files):
- path = self.get_path_for_vfs_file(vfs_files[0])
- if path is None:
- return
- repo = self.get_repo_for_path(path)
- if repo is None:
- return
- diffcmd = repo.ui.config('tortoisehg', 'vdiff', None)
- if diffcmd is None:
- self._run_dialog('diff', vfs_files)
- else:
- cmdline = ['hg', diffcmd]
- cwd = os.path.isdir(path) and path or os.path.dirname(path)
- paths = [self.get_path_for_vfs_file(f) for f in vfs_files]
- subprocess.Popen(cmdline + paths, shell=False, cwd=cwd)
-
- def _history_cb(self, window, vfs_files):
- self._run_dialog('history', vfs_files)
- self.clear_cached_repo()
-
- def _init_cb(self, window, vfs_file):
- self._run_dialog('init', [vfs_file])
-
- def _recovery_cb(self, window, vfs_file):
- self._run_dialog('recovery', [vfs_file])
- self.clear_cached_repo()
-
- def _revert_cb(self, window, vfs_files):
- self._run_dialog('revert', vfs_files)
- self.clear_cached_repo()
-
- def _serve_cb(self, window, vfs_file):
- self._run_dialog('serve', [vfs_file], filelist=False)
-
- def _status_cb(self, window, vfs_file):
- self._run_dialog('status', [vfs_file])
-
- def _sync_cb(self, window, vfs_file):
- self._run_dialog('synch', [vfs_file], filelist=False)
- self.clear_cached_repo()
-
- def _thgconfig_repo_cb(self, window, vfs_file):
- self._run_dialog('config', [vfs_file])
-
- def _thgconfig_user_cb(self, window, vfs_file):
- self._run_dialog('config', [vfs_file], filelist=False)
-
- def _unmerge_cb(self, window, vfs_file):
- self._run_dialog('checkout', [vfs_file], filelist=False,
- extras=['--', '--clean', str(self.rev0)])
- self.clear_cached_repo()
-
- def _run_dialog(self, hgcmd, vfs_files, filelist=True, extras=[]):
+ def run_dialog(self, menuitem, hgcmd, cwd = None):
'''
hgcmd - hgproc subcommand
- vfs_files - directory, or list of selected files
- filelist - bool for whether to generate file list for hgproc
'''
- paths = [self.get_path_for_vfs_file(f) for f in vfs_files]
- if paths[0] is None:
+ if cwd: #bg
+ self.files = []
+ else:
+ cwd = self.cwd
+ repo = self.get_repo_for_path(cwd)
+
+ if hgcmd == 'vdiff':
+ diffcmd = repo.ui.config('tortoisehg', 'vdiff', None)
+ if diffcmd is None:
+ hgcmd = 'diff'
+ else:
+ cmdline = ['hg', diffcmd]
+ cmdline.extend(self.files)
+ subprocess.Popen(cmdline, shell=False, env=self.env, cwd=cwd)
+ return
+
+ cmdopts = [sys.executable, self.hgproc, hgcmd]
+
+ if hgcmd not in nofilecmds and self.files:
+ # Use stdin to pass file list (avoid shell command
+ # line limitations)
+ pipe = subprocess.PIPE
+ cmdopts += ['--listfile', '-']
+ else:
+ pipe = None
+
+ stdin = subprocess.Popen(cmdopts, cwd=cwd, stdin=pipe, env=self.env, shell=False).stdin
+
+ if pipe:
+ stdin.write('\n'.join(self.files))
+ stdin.close()
+
+ if hgcmd not in nocachecmds:
+ # Remove cached repo object, dirstate may change
+ self.cacherepo = None
+ self.cacheroot = None
+
+ def buildMenu(self, vfs_files, bg):
+ '''Build menu'''
+ self.pos = 0
+ self.files = []
+ files = []
+ for vfs_file in vfs_files:
+ f = self.get_path_for_vfs_file(vfs_file)
+ if f:
+ files.append(f)
+ if not files:
return
+ if bg or len(files) == 1 and vfs_files[0].is_directory():
+ cwd = files[0]
+ files = []
+ else:
+ cwd = os.path.dirname(files[0])
+ repo = self.get_repo_for_path(cwd)
+ if repo:
+ menus = self.menu.get_commands(repo, cwd, files)
+ if cwd == repo.root:
+ cwd_rel = ''
+ else:
+ cwd_rel = cwd[len(repo.root+os.sep):] + os.sep
+ for f in files:
+ cpath = util.canonpath(repo.root, cwd, f)
+ if cpath.startswith(cwd_rel):
+ cpath = cpath[len(cwd_rel):]
+ self.files.append(cpath)
+ else:
+ self.files.append(f)
+ else:
+ menus = self.menu.get_norepo_commands(cwd, files)
+ self.cwd = cwd
+ return self._buildMenu(menus)
- path = paths[0]
- repo = self.get_repo_for_path(path)
- cwd = os.path.isdir(path) and path or os.path.dirname(path)
-
- if repo is not None:
- root = repo.root
- else:
- root = cwd
-
- cmdopts = [sys.executable, self.hgproc]
- cmdopts += ['--root', root]
- cmdopts += ['--cwd', cwd]
- cmdopts += ['--command', hgcmd]
-
- if filelist:
- # Use temporary file to store file list (avoid shell command
- # line limitations)
- fd, tmpfile = tempfile.mkstemp(prefix="tortoisehg_filelist_")
- os.write(fd, "\n".join(paths))
- os.close(fd)
- cmdopts += ['--listfile', tmpfile, '--deletelistfile']
- cmdopts.extend(extras)
-
- subprocess.Popen(cmdopts, cwd=cwd, shell=False)
-
- # Remove cached repo object, dirstate may change
- self.cacherepo = None
- self.cacheroot = None
+ def _buildMenu(self, menus):
+ '''Build one level of a menu'''
+ items = []
+ if self.files:
+ passcwd = None
+ else: #bg
+ passcwd = self.cwd
+ for menu_info in menus:
+ idstr = 'HgNautilus::%02d' % self.pos
+ self.pos += 1
+ if menu_info.isSep():
+ # can not insert a separator till now
+ pass
+ elif menu_info.isSubmenu():
+ if hasattr(nautilus, 'Menu'):
+ item = nautilus.MenuItem(idstr, menu_info.menutext,
+ menu_info.helptext)
+ submenu = nautilus.Menu()
+ item.set_submenu(submenu)
+ for subitem in self._buildMenu(menu_info.get_menus()):
+ submenu.append_item(subitem)
+ items.append(item)
+ else: #submenu not suported
+ for subitem in self._buildMenu(menu_info.get_menus()):
+ items.append(subitem)
+ else:
+ if menu_info.state:
+ item = nautilus.MenuItem(idstr,
+ menu_info.menutext,
+ menu_info.helptext,
+ self.icon(menu_info.icon))
+ item.connect('activate', self.run_dialog, menu_info.hgcmd, passcwd)
+ items.append(item)
+ return items
def get_background_items(self, window, vfs_file):
'''Build context menu for current directory'''
- mainitem = nautilus.MenuItem('HgNautilus', 'Mercurial', '')
- submenu = nautilus.Menu()
- mainitem.set_submenu(submenu)
-
- path = self.get_path_for_vfs_file(vfs_file)
- if path is None:
- return
-
- repo = self.get_repo_for_path(path)
- if repo is None:
- ''' The name given to nautilus.MenuItem decides the
- of the menu, which is ordered alpahbetically '''
- item = nautilus.MenuItem('HgNautilus::newtree',
- 'Create New Repository',
- 'Make directory versioned',
- self.icon('menucreaterepos.ico'))
- item.connect('activate', self._init_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::clone',
- 'Create Clone',
- 'Create clone here from source',
- self.icon('menuclone.ico'))
- item.connect('activate', self._clone_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::99about',
- 'About TortoiseHg',
- 'Information about TortoiseHg installation',
- self.icon('menuabout.ico'))
- item.connect('activate', self._about_cb, vfs_file)
- submenu.append_item(item)
-
- return mainitem,
-
- if len(repo.changectx(None).parents()) > 1:
- self.rev0 = repo.changectx(None).parents()[0].rev()
- item = nautilus.MenuItem('HgNautilus::undomerge',
- 'Undo Merge',
- 'Clean checkout of original parent revision',
- self.icon('menuunmerge.ico'))
- item.connect('activate', self._unmerge_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::10commit',
- 'Commit',
- 'Commit changes',
- self.icon('menucommit.ico'))
- item.connect('activate', self._commit_cb, [vfs_file])
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::20status',
- 'Show Status',
- 'Show Repository Status',
- self.icon('menushowchanged.ico'))
- item.connect('activate', self._status_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::30diff',
- 'Visual Diff',
- 'Show Changes to Repository',
- self.icon('menudiff.ico'))
- item.connect('activate', self._diff_cb, [vfs_file])
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::40dag',
- 'Revision History',
- 'Show revision DAG',
- self.icon('menurevisiongraph.ico'))
- item.connect('activate', self._history_cb, [vfs_file])
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::50datamine',
- 'Data Mining',
- 'Search revision history',
- self.icon('menulog.ico'))
- item.connect('activate', self._datamine_cb, [vfs_file])
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::60sync',
- 'Synchronize',
- 'Sync with another repository',
- self.icon('menusynch.ico'))
- item.connect('activate', self._sync_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::70serve',
- 'Web Server',
- 'Start internal web server',
- self.icon('proxy.ico'))
- item.connect('activate', self._serve_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::75recover',
- 'Recovery',
- 'General repair and recovery of repository',
- self.icon('general.ico'))
- item.connect('activate', self._recovery_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::80repoconfig',
- 'Repository Settings',
- 'Configure Mercurial settings for this repo',
- self.icon('menusettings.ico'))
- item.connect('activate', self._thgconfig_repo_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::85userconfig',
- 'User-Global Settings',
- 'Configure global Mercurial settings',
- self.icon('menusettings.ico'))
- item.connect('activate', self._thgconfig_user_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::99about',
- 'About TortoiseHg',
- 'Information about TortoiseHg installation',
- self.icon('menuabout.ico'))
- item.connect('activate', self._about_cb, vfs_file)
- submenu.append_item(item)
-
- return mainitem,
+ if vfs_file and self.menu:
+ return self.buildMenu([vfs_file], True)
+ else:
+ self.files = []
def get_file_items(self, window, vfs_files):
- mainitem = nautilus.MenuItem('HgNautilus', 'Mercurial', '')
- submenu = nautilus.Menu()
- mainitem.set_submenu(submenu)
-
- '''Build context menu for selected files'''
- if not vfs_files:
- return None
-
- vfs_file = vfs_files[0]
- path = self.get_path_for_vfs_file(vfs_file)
- repo = self.get_repo_for_path(path)
- if repo is None:
- if not vfs_file.is_directory():
- return None
-
- # Menu for unrevisioned subdirectory
- name = vfs_files[0].get_name()
- item = nautilus.MenuItem('HgNautilus::10newtree',
- 'Make directory versioned',
- 'Create Repository in %s' % name,
- self.icon('menucreaterepos.ico'))
- item.connect('activate', self._init_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::20clone',
- 'Create clone from source',
- 'Create Clone in %s' % name,
- self.icon('menuclone.ico'))
- item.connect('activate', self._clone_cb, vfs_file)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::99about',
- 'About TortoiseHg',
- 'Information about TortoiseHg installation',
- self.icon('menuabout.ico'))
- item.connect('activate', self._about_cb, vfs_file)
- submenu.append_item(item)
-
- return mainitem,
-
- localpaths = []
- for vfs_file in vfs_files:
- path = self.get_path_for_vfs_file(vfs_file)
- if path is None:
- continue
- localpath = path[len(repo.root)+1:]
- localpaths.append(localpath)
-
- if not localpaths:
- return
- path = localpaths[0]
- cwd = os.path.isdir(path) and path or os.path.dirname(path)
- matcher = match.exact(repo.root, cwd, localpaths)
- changes = repo.dirstate.status(matcher, True, True, True)
- (lookup, modified, added, removed, deleted, unknown,
- ignored, clean) = changes
-
- # Add menu items based on states list
- if unknown:
- item = nautilus.MenuItem('HgNautilus::30add',
- 'Add Files',
- 'Add unversioned files',
- self.icon('menuadd.ico'))
- item.connect('activate', self._add_cb, vfs_files)
- submenu.append_item(item)
-
- if modified or added or removed or deleted or unknown:
- item = nautilus.MenuItem('HgNautilus::40commit',
- 'Commit Files',
- 'Commit changes',
- self.icon('menucommit.ico'))
- item.connect('activate', self._commit_cb, vfs_files)
- submenu.append_item(item)
-
- item = nautilus.MenuItem('HgNautilus::50revert',
- 'Undo Changes',
- 'Revert changes to files',
- self.icon('menurevert.ico'))
- item.connect('activate', self._revert_cb, vfs_files)
- submenu.append_item(item)
-
- if modified or clean:
- item = nautilus.MenuItem('HgNautilus::60log',
- 'File Changelog',
- 'Show file revision history',
- self.icon('menulog.ico'))
- item.connect('activate', self._history_cb, vfs_files)
- item = nautilus.MenuItem('HgNautilus::annotate',
- 'Annotate File',
- 'Annotate file at current revision',
- self.icon('menulog.ico'))
- item.connect('activate', self._datamine_cb, vfs_files)
- submenu.append_item(item)
-
-
- if modified:
- item = nautilus.MenuItem('HgNautilus::70diff',
- 'File Diffs',
- 'Show file changes',
- self.icon('menudiff.ico'))
- item.connect('activate', self._diff_cb, vfs_files)
- submenu.append_item(item)
-
- return mainitem,
+ '''Build context menu for selected files/directories'''
+ if vfs_files and self.menu:
+ return self.buildMenu(vfs_files, False)
def get_columns(self):
return nautilus.Column("HgNautilus::80hg_status",
@@ -436,64 +254,50 @@ "Version control status"),
def _get_file_status(self, repo, localpath):
- emblem = None
- status = '?'
-
- # This is not what the API is optimized for, but this appears
- # to work efficiently enough
- matcher = match.always(repo.root, localpath)
- changes = repo.dirstate.status(matcher, True, True, True)
- (lookup, modified, added, removed, deleted, unknown,
- ignored, clean) = changes
-
- if localpath in clean:
- emblem = 'default'
- status = 'clean'
- elif localpath in modified:
- emblem = 'cvs-modified'
- status = 'modified'
- elif localpath in added:
- emblem = 'cvs-aded'
- status = 'added'
- elif localpath in unknown:
- emblem = 'new'
- status = 'unrevisioned'
- elif localpath in ignored:
- status = 'ignored'
- elif localpath in deleted:
- # Should be hard to reach this state
- emblem = 'stockmail-priority-high'
- status = 'deleted'
+ from tortoise import cachethg
+ cachestate = cachethg.get_state(localpath, repo)
+ cache2state = {cachethg.UNCHANGED: ('default', 'clean'),
+ cachethg.ADDED: ('cvs-added', 'added'),
+ cachethg.MODIFIED: ('cvs-modified', 'modified'),
+ cachethg.UNKNOWN: ('new', 'unrevisioned'),
+ cachethg.IGNORED: (None, 'ignored'),
+ cachethg.NOT_IN_REPO: (None, '')}
+ emblem, status = cache2state.get(cachestate, (None, '?'))
return emblem, status
+ def update_file_info(self, file):
+ '''Queue file for emblem and hg status update'''
+ self.scanStack.append(file)
+ if len(self.scanStack) == 1:
+ gobject.idle_add(self.fileinfo_on_idle)
- def update_file_info(self, file):
- '''Return emblem and hg status for this file'''
- path = self.get_path_for_vfs_file(file)
- if path is None or file.is_directory():
- return
- repo = self.get_repo_for_path(path)
- if repo is None:
- return
- localpath = path[len(repo.root)+1:]
- emblem, status = self._get_file_status(repo, localpath)
+ def fileinfo_on_idle(self):
+ '''Update emblem and hg status for files when there is time'''
+ if not self.scanStack:
+ return False
+ vfs_file = self.scanStack.pop()
+ path = self.get_path_for_vfs_file(vfs_file)
+ if not path:
+ return True
+ emblem, status = self._get_file_status(self.cacherepo, path)
if emblem is not None:
- file.add_emblem(emblem)
- file.add_string_attribute('hg_status', status)
+ vfs_file.add_emblem(emblem)
+ vfs_file.add_string_attribute('hg_status', status)
+ return True
# property page borrowed from http://www.gnome.org/~gpoo/hg/nautilus-hg/
- def __add_row(self, table, row, label_item, label_value):
+ def __add_row(self, row, label_item, label_value):
label = gtk.Label(label_item)
label.set_use_markup(True)
label.set_alignment(1, 0)
- table.attach(label, 0, 1, row, row + 1, gtk.FILL, gtk.FILL, 0, 0)
+ self.table.attach(label, 0, 1, row, row + 1, gtk.FILL, gtk.FILL, 0, 0)
label.show()
label = gtk.Label(label_value)
label.set_use_markup(True)
label.set_alignment(0, 1)
label.show()
- table.attach(label, 1, 2, row, row + 1, gtk.FILL, 0, 0, 0)
+ self.table.attach(label, 1, 2, row, row + 1, gtk.FILL, 0, 0, 0)
def get_property_pages(self, vfs_files):
if len(vfs_files) != 1:
@@ -505,9 +309,8 @@ repo = self.get_repo_for_path(path)
if repo is None:
return
-
localpath = path[len(repo.root)+1:]
- emblem, status = self._get_file_status(repo, localpath)
+ emblem, status = self._get_file_status(repo, path)
# Get the information from Mercurial
ctx = repo.changectx(None).parents()[0]
@@ -528,22 +331,21 @@
self.property_label = gtk.Label('Mercurial')
- table = gtk.Table(7, 2, False)
- table.set_border_width(5)
- table.set_row_spacings(5)
- table.set_col_spacings(5)
+ self.table = gtk.Table(7, 2, False)
+ self.table.set_border_width(5)
+ self.table.set_row_spacings(5)
+ self.table.set_col_spacings(5)
- self.__add_row(table, 0, '<b>Status</b>:', status)
- self.__add_row(table, 1, '<b>Last-Commit-Revision</b>:', str(rev))
- self.__add_row(table, 2, '<b>Last-Commit-Description</b>:', description)
- self.__add_row(table, 3, '<b>Last-Commit-Date</b>:', date)
- self.__add_row(table, 4, '<b>Last-Commit-User</b>:', user)
+ self.__add_row(0, '<b>Status</b>:', status)
+ self.__add_row(1, '<b>Last-Commit-Revision</b>:', str(rev))
+ self.__add_row(2, '<b>Last-Commit-Description</b>:', description)
+ self.__add_row(3, '<b>Last-Commit-Date</b>:', date)
+ self.__add_row(4, '<b>Last-Commit-User</b>:', user)
if tags:
- self.__add_row(table, 5, '<b>Tags</b>:', tags)
+ self.__add_row(5, '<b>Tags</b>:', tags)
if branch != 'default':
- self.__add_row(table, 6, '<b>Branch</b>:', branch)
+ self.__add_row(6, '<b>Branch</b>:', branch)
- table.show()
-
+ self.table.show()
return nautilus.PropertyPage("MercurialPropertyPage::status",
- self.property_label, table),
+ self.property_label, self.table),
|
|
@@ -1,9 +0,0 @@ - import pygtk
-pygtk.require('2.0')
-import gtk
-
-# Default icon for apps which do not set one
-from shlib import get_tortoise_icon
-icon = get_tortoise_icon("hg.ico")
-if icon:
- gtk.window_set_default_icon_from_file(icon)
|
@@ -68,9 +68,14 @@ thg_logo = os.path.normpath(shlib.get_tortoise_icon('thg_logo_92x50.png'))
thg_icon = os.path.normpath(shlib.get_tortoise_icon('thg_logo.ico'))
prog_root = os.path.dirname(os.path.dirname(os.path.dirname(thg_icon)))
- license_file = os.path.join(prog_root, "COPYING.txt")
+ try:
+ license_file = os.path.join(prog_root, "COPYING.txt")
+ self.set_license(file(license_file).read())
+ except IOError:
+ import hgtk
+ license = hgtk.shortlicense.splitlines()[1:]
+ self.set_license('\n'.join(license))
- self.set_license(file(license_file).read())
self.set_comments("with " + lib_versions + "\n\n" + comment)
self.set_logo(gtk.gdk.pixbuf_new_from_file(thg_logo))
self.set_icon_from_file(thg_icon)
|
@@ -56,6 +56,20 @@ u.print_exc()
raise
+def get_list_from_file(filename):
+ try:
+ if filename == '-':
+ lines = [ x.replace("\n", "") for x in sys.stdin.readlines() ]
+ else:
+ fd = open(filename, "r")
+ lines = [ x.replace("\n", "") for x in fd.readlines() ]
+ fd.close()
+ os.unlink(filename)
+ return lines
+ except IOError, e:
+ sys.stderr.write(_('can not read file "%s". Ignored.\n') % filename)
+ return []
+
def _parse(ui, args):
options = {}
cmdoptions = {}
@@ -89,6 +103,11 @@ options[n] = cmdoptions[n]
del cmdoptions[n]
+ listfile = options.get('listfile')
+ if listfile:
+ del options['listfile']
+ args += get_list_from_file(listfile)
+
return (cmd, cmd and i[0] or None, args, options, cmdoptions)
def _runcatch(ui, args):
@@ -249,7 +268,7 @@def datamine(ui, *pats, **opts):
"""repository search and annotate tool"""
from hggtk.datamine import run
- opts['files'] = sys.argv[2:] or []
+ opts['files'] = pats or []
opts['cwd'] = os.getcwd()
run(**opts)
@@ -500,7 +519,7 @@ _('repository root directory or symbolic path name')),
('v', 'verbose', None, _('enable additional output')),
('h', 'help', None, _('display help and exit')),
- ('', 'debugger', None, _('start debugger')),
+ ('l', 'listfile', '', _('read file list from file')),
]
table = {
|
@@ -181,9 +181,11 @@ # Else try relative paths from hggtk, the repository layout
fdir = os.path.dirname(__file__)
paths.append(os.path.join(fdir, '..', 'icons'))
- # ... or the source installer layout
+ # ... or the unix installer layout
paths.append(os.path.join(fdir, '..', '..', '..',
- 'share', 'tortoisehg', 'icons'))
+ 'share', 'pixmaps', 'tortoisehg', 'icons'))
+ paths.append(os.path.join(fdir, '..', '..', '..', '..',
+ 'share', 'pixmaps', 'tortoisehg', 'icons'))
except NameError: # __file__ is not always available
pass
for p in paths:
|
|
@@ -1,20 +0,0 @@ - ::
-:: Win32 batch file to handle TortoiseHg external proc calls
-::
-
-@echo off
-setlocal
-
-:: Look in the registry for TortoiseHg location
-for /f "skip=2 tokens=3*" %%A in (
- '"reg query "HKEY_LOCAL_MACHINE\SOFTWARE\TortoiseHg" /ve 2> nul"' ) do set TortoisePath=%%B
-if "%TortoisePath%"=="" (goto :notfound) else (goto :hgproc)
-
-:hgproc
-python "%TortoisePath%\hgproc.py" %*
-goto end
-
-:notfound
-echo hgproc: cannot find TortoiseHg location in the registry.
-
-:end
\ No newline at end of file |
|
|
@@ -1,132 +0,0 @@ - #
-# front-end for TortoiseHg dialogs
-#
-# Copyright (C) 2007 TK Soh <teekaysoh@gmail.com>
-#
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-
-import os
-import sys
-
-from mercurial import demandimport; demandimport.enable()
-from mercurial import ui
-from tortoise.thgutil import get_prog_root
-
-# always use hg exe installed with TortoiseHg
-thgdir = get_prog_root()
-try:
- os.environ['PATH'] = os.path.pathsep.join([thgdir, os.environ['PATH']])
-except KeyError:
- os.environ['PATH'] = thgdir
-
-if not sys.stdin.isatty():
- try:
- import win32traceutil
-
- # FIXME: quick workaround traceback caused by missing "closed"
- # attribute in win32trace.
- from mercurial import ui
- def write_err(self, *args):
- for a in args:
- sys.stderr.write(str(a))
- ui.ui.write_err = write_err
- except ImportError:
- pass
- except pywintypes.error:
- pass
-
-# Map hgproc commands to dialog modules in hggtk/
-from hggtk import commit, status, addremove, tagadd, tags, history, merge
-from hggtk import diff, revisions, update, serve, clone, synch, hgcmd, about
-from hggtk import recovery, thgconfig, datamine, hginit, thgshelve, rename
-from hggtk import hgignore
-_dialogs = { 'commit' : commit, 'status' : status, 'revert' : status,
- 'add' : addremove, 'remove' : addremove, 'tag' : tagadd,
- 'tags' : tags, 'log' : history, 'history': history,
- 'diff' : diff, 'merge' : merge, 'tip' : revisions,
- 'parents': revisions, 'heads' : revisions, 'update' : update,
- 'clone' : clone, 'serve' : serve, 'synch' : synch,
- 'about' : about, 'config' : thgconfig, 'recovery': recovery,
- 'datamine': datamine, 'init' : hginit, 'shelve' : thgshelve,
- 'hgignore': hgignore, 'rename' : rename }
-
-def get_list_from_file(filename):
- fd = open(filename, "r")
- lines = [ x.replace("\n", "") for x in fd.readlines() ]
- fd.close()
- return lines
-
-def get_option(args):
- import getopt
- long_opt_list = ('command=', 'exepath=', 'listfile=', 'root=', 'cwd=',
- 'deletelistfile', 'nogui', 'detect')
- opts, args = getopt.getopt(args, "c:e:l:dR:", long_opt_list)
- # Set default options
- options = {}
- options['hgcmd'] = 'help'
- options['cwd'] = os.getcwd()
- options['files'] = []
- options['gui'] = True
- listfile = None
- delfile = False
-
- for o, a in opts:
- if o in ("-c", "--command"):
- options['hgcmd'] = a
- elif o in ("-l", "--listfile"):
- listfile = a
- elif o in ("-d", "--deletelistfile"):
- delfile = True
- elif o in ("--nogui"):
- options['gui'] = False
- elif o in ("-R", "--root"):
- options['root'] = a
- elif o in ("--cwd"):
- options['cwd'] = a
- elif o in ("--detect"):
- options['detect'] = True
-
- if listfile:
- options['files'] = get_list_from_file(listfile)
- if delfile:
- os.unlink(listfile)
-
- return (options, args)
-
-def parse(args):
- option, args = get_option(args)
-
- cmdline = ['hg', option['hgcmd']]
- if 'root' in option:
- cmdline.append('--repository')
- cmdline.append(option['root'])
- cmdline.extend(args)
- cmdline.extend(option['files'])
- option['cmdline'] = cmdline
-
- global _dialogs
- dialog = _dialogs.get(option['hgcmd'], hgcmd)
- dialog.run(**option)
-
-
-def run_trapped(args):
- try:
- dlg = parse(sys.argv[1:])
- except:
- import traceback
- from hggtk.dialog import error_dialog
- tr = traceback.format_exc()
- print tr
- error_dialog(None, "Error executing hgproc", tr)
-
-if __name__=='__main__':
- #dlg = parse(['-c', 'help', '--', '-v'])
- #dlg = parse(['-c', 'log', '--root', 'c:\hg\h1', '--', '-l1'])
- #dlg = parse(['-c', 'status', '--root', 'c:\hg\h1', ])
- #dlg = parse(['-c', 'add', '--root', 'c:\hg\h1', '--listfile', 'c:\\hg\\h1\\f1', '--notify'])
- #dlg = parse(['-c', 'rollback', '--root', 'c:\\hg\\h1'])
- print "hgproc sys.argv =", sys.argv
- dlg = run_trapped(sys.argv[1:])
|
|
@@ -0,0 +1,51 @@ + #!/usr/bin/env python
+#
+# front-end script for TortoiseHg dialogs
+#
+# Copyright (C) 2008-9 Steve Borho <steve@borho.org>
+# Copyright (C) 2008 TK Soh <teekaysoh@gmail.com>
+#
+
+shortlicense = '''\
+Copyright (C) 2009 Steve Borho <steve@borho.org>.
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+'''
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+
+from mercurial import demandimport; demandimport.enable()
+import sys
+
+if not hasattr(sys, "frozen"):
+ import os
+ # check if hggtk is a symlink first
+ pfile = __file__
+ if pfile.endswith('.pyc'):
+ pfile = pfile[:-1]
+ thgpath = os.path.dirname(os.path.realpath(pfile))
+ testpath = os.path.join(thgpath, 'hggtk')
+ if os.path.isdir(testpath):
+ if thgpath not in sys.path:
+ sys.path.insert(0, thgpath)
+ else:
+ # try environment
+ thgpath = os.environ.get('TORTOISEHG_PATH')
+ if thgpath:
+ thgpath = os.path.normpath(os.path.expanduser(thgpath))
+ if os.path.exists(thgpath) and thgpath not in sys.path:
+ sys.path.insert(0, thgpath)
+
+# else assume tortoise is already in PYTHONPATH
+try:
+ import hggtk.hgtk
+except ImportError:
+ sys.stderr.write("abort: couldn't find hggtk libraries in [%s]\n" %
+ ' '.join(sys.path))
+ sys.stderr.write("(check your install and PYTHONPATH)\n")
+ sys.exit(-1)
+
+sys.exit(hggtk.hgtk.dispatch(sys.argv[1:]))
+
|
@@ -1,19 +1,16 @@ # setup.py
# A distutils setup script to install TortoiseHg in Windows and Posix
-# environments. In Windows, it will register TortoiseHG COM server.
+# environments.
#
-# For Windows:
-# To build stand-alone package, use 'python setup.py py2exe' then use
-# InnoSetup to build the installer. By default, the installer will be
-# created as dist\Output\setup.exe.
-#
+# On Windows, this script is mostly used to build a stand-alone
+# TortoiseHg package. See installer\build.txt for details. The other
+# use is to report the current version of the TortoiseHg source.
import time
import sys
import os
from distutils.core import setup
-
def setup_windows():
# Specific definitios for Windows NT-alike installations
_scripts = []
@@ -54,8 +51,6 @@ _data_files = [(root, [os.path.join(root, file_) for file_ in files])
for root, dirs, files in os.walk('icons')]
extra['windows'] = [
- {"script":"hgproc.py",
- "icon_resources": [(1, "icons/tortoise/hg.ico")]},
{"script":"tracelog.py",
"icon_resources": [(1, "icons/tortoise/python.ico")]}
]
@@ -84,7 +79,7 @@def setup_posix():
# Specific definitios for Posix installations
_extra = {}
- _scripts = ['contrib/hgtk', 'hgproc.py']
+ _scripts = ['hgtk']
_packages = ['hggtk', 'hggtk.vis', 'hggtk.iniparse', 'tortoise']
_data_files = [(os.path.join('share/pixmaps/tortoisehg', root),
[os.path.join(root, file_) for file_ in files])
@@ -97,12 +92,10 @@
if os.name == "nt":
(scripts, packages, data_files, extra) = setup_windows()
+ desc='Windows shell extension for Mercurial VCS',
else:
(scripts, packages, data_files, extra) = setup_posix()
-
-
-# specify version string, otherwise 'hg identify' will be used:
-version = ''
+ desc='TortoiseHg dialogs for Mercurial VCS',
try:
l = os.popen('hg id -it').read().split()
@@ -111,7 +104,6 @@ version = l and l[-1] or 'unknown' # latest tag or revision number
if version.endswith('+'):
version += time.strftime('%Y%m%d')
-
except OSError:
version = "unknown"
@@ -125,7 +117,7 @@ author='TK Soh',
author_email='teekaysoh@gmail.com',
url='http://bitbucket.org/tortoisehg/stable/',
- description='Windows shell extension for Mercurial VCS',
+ description=desc,
license='GNU GPL2',
scripts=scripts,
packages=packages,
|
|
|
@@ -0,0 +1,174 @@ + import os
+from mercurial import hg, cmdutil, util, ui
+import thgutil
+import sys
+try:
+ from mercurial.error import RepoError
+except ImportError:
+ from mercurial.repo import RepoError
+
+try:
+ from win32api import GetTickCount
+ CACHE_TIMEOUT = 5000
+except ImportError:
+ from time import time as GetTickCount
+ CACHE_TIMEOUT = 5.0
+
+UNCHANGED = "unchanged"
+ADDED = "added"
+MODIFIED = "modified"
+UNKNOWN = "unknown"
+IGNORED = "ignored"
+NOT_IN_REPO = "n/a"
+ROOT = "root"
+
+# file status cache
+overlay_cache = {}
+cache_tick_count = 0
+cache_root = None
+cache_pdir = None
+
+
+def add_dirs(list):
+ dirs = set()
+ if list:
+ dirs.add('')
+ for f in list:
+ pdir = os.path.dirname(f)
+ if pdir in dirs:
+ continue
+ while pdir:
+ dirs.add(pdir)
+ pdir = os.path.dirname(pdir)
+ list.extend(dirs)
+
+
+def get_state(upath, repo=None):
+ """
+ Get the state of a given path in source control.
+ """
+ return get_states(upath, repo)[-1]
+
+
+def get_states(upath, repo=None):
+ """
+ Get the states of a given path in source control.
+ """
+ global overlay_cache, cache_tick_count
+ global cache_root, cache_pdir
+
+ #print "called: _get_state(%s)" % path
+ tc = GetTickCount()
+
+ try:
+ # handle some Asian charsets
+ path = upath.encode('mbcs')
+ except:
+ path = upath
+ # check if path is cached
+ pdir = os.path.dirname(path)
+ if cache_pdir == pdir and overlay_cache:
+ if tc - cache_tick_count < CACHE_TIMEOUT:
+ status = overlay_cache.get(path)
+ if not status:
+ if os.path.isdir(os.path.join(path, '.hg')):
+ add(path, ROOT)
+ status = ROOT,
+ else:
+ status = overlay_cache.get(pdir, [NOT_IN_REPO])
+ print "%s: %s (cached)" % (path, status)
+ return status
+ else:
+ print "Timed out!! ",
+ overlay_cache.clear()
+ cache_tick_count = GetTickCount()
+ # path is a drive
+ if path.endswith(":\\"):
+ add(path, NOT_IN_REPO)
+ return [NOT_IN_REPO]
+ # open repo
+ if cache_pdir == pdir:
+ root = cache_root
+ else:
+ print "find new root ",
+ root = thgutil.find_root(path)
+ if root == path:
+ add(path, ROOT)
+ return [ROOT]
+ cache_root = root
+ cache_pdir = pdir
+
+ print "_get_state: root = ", root
+ if root is None:
+ print "_get_state: not in repo"
+ overlay_cache = {None: None}
+ cache_tick_count = GetTickCount()
+ return [NOT_IN_REPO]
+ hgdir = os.path.join(root, '.hg', '')
+ if pdir == hgdir[:-1] or pdir.startswith(hgdir):
+ add(pdir, NOT_IN_REPO)
+ return [NOT_IN_REPO]
+ try:
+ tc1 = GetTickCount()
+ if not repo or (repo.root != root and repo.root != os.path.realpath(root)):
+ repo = hg.repository(ui.ui(), path=root)
+ print "hg.repository() took %g ticks" % (GetTickCount() - tc1)
+ # check if to display overlay icons in this repo
+ overlayopt = repo.ui.config('tortoisehg', 'overlayicons', ' ').lower()
+ print "%s: repo overlayicons = " % path, overlayopt
+ if overlayopt == 'localdisk':
+ overlayopt = bool(thgutil.netdrive_status(path))
+ if not overlayopt or overlayopt in 'false off no'.split():
+ print "%s: overlayicons disabled" % path
+ overlay_cache = {None: None}
+ cache_tick_count = GetTickCount()
+ return [NOT_IN_REPO]
+ except RepoError:
+ # We aren't in a working tree
+ print "%s: not in repo" % pdir
+ add(pdir, IGNORED)
+ return [IGNORED]
+ except StandardError, e:
+ print "error while handling %s:" % pdir
+ print e
+ add(pdir, UNKNOWN)
+ return [UNKNOWN]
+ # get file status
+ tc1 = GetTickCount()
+
+ try:
+ matcher = cmdutil.match(repo, [pdir])
+ repostate = repo.status(match=matcher, ignored=True,
+ clean=True, unknown=True)
+ except util.Abort, inst:
+ print "abort: %s" % inst
+ print "treat as unknown : %s" % path
+ return [UNKNOWN]
+
+ print "status() took %g ticks" % (GetTickCount() - tc1)
+ modified, added, removed, deleted, unknown, ignored, clean = repostate
+ # cached file info
+ tc = GetTickCount()
+ overlay_cache = {}
+ for grp, st in (
+ (ignored, IGNORED),
+ (unknown, UNKNOWN),
+ (clean, UNCHANGED),
+ (added, ADDED),
+ (removed, MODIFIED),
+ (deleted, MODIFIED),
+ (modified, MODIFIED)):
+ add_dirs(grp)
+ for f in grp:
+ fpath = os.path.join(root, os.path.normpath(f))
+ add(fpath, st)
+ add(root, ROOT)
+ status = overlay_cache.get(path, [UNKNOWN])
+ print "\n%s: %s" % (path, status)
+ cache_tick_count = GetTickCount()
+ return status
+
+
+def add(path, state):
+ c = overlay_cache.setdefault(path, [])
+ c.append(state)
|
|
|
@@ -15,8 +15,9 @@ import win32gui_struct
import win32api
import _winreg
-from mercurial import hg
+from mercurial import hg, util
from thgutil import *
+import menuthg
try:
from mercurial.error import RepoError
@@ -35,70 +36,6 @@S_OK = 0
S_FALSE = 1
-class TortoiseMenu(object):
- def __init__(self, menutext, helptext, handler, icon=None, state=True):
- self.menutext = menutext
- self.helptext = helptext
- self.handler = handler
- self.state = state
- self.icon = icon
-
-class TortoiseSubmenu(object):
- def __init__(self, menutext, menus=[], icon=None):
- self.menutext = menutext
- self.menus = menus[:]
- self.icon = icon
-
- def add_menu(self, menutext, helptext, handler, icon=None, state=True):
- self.menus.append(TortoiseMenu(menutext, helptext, handler, icon, state))
-
- def add_sep(self):
- self.menus.append(TortoiseMenuSep())
-
- def get_menus(self):
- return self.menus
-
-class TortoiseMenuSep(object):
- def __init__(self):
- pass
-
-def open_repo(path):
- root = find_root(path)
- if root:
- try:
- repo = hg.repository(ui.ui(), path=root)
- return repo
- except RepoError:
- pass
-
- return None
-
-def open_dialog(cmd, cmdopts='', cwd=None, root=None, filelist=[], gui=True):
- app_path = find_path("hgproc", get_prog_root(), '.EXE;.BAT')
-
- if filelist:
- fd, tmpfile = tempfile.mkstemp(prefix="tortoisehg_filelist_")
- os.write(fd, "\n".join(filelist))
- os.close(fd)
-
- # start gpopen
- gpopts = "--command %s" % cmd
- if root:
- gpopts += " --root %s" % shellquote(root)
- if filelist:
- gpopts += " --listfile %s --deletelistfile" % (shellquote(tmpfile))
- if cwd:
- gpopts += " --cwd %s" % shellquote(cwd)
- if not gui:
- gpopts += " --nogui"
-
- cmdline = '%s %s -- %s' % (shellquote(app_path), gpopts, cmdopts)
-
- try:
- run_program(cmdline)
- except win32api.error, details:
- win32ui.MessageBox("Error executing command - %s" % (details), "gpopen")
-
def get_clone_repo_name(dir, repo_name):
dest_clone = os.path.join(dir, repo_name)
if os.path.exists(dest_clone):
@@ -110,19 +47,8 @@ i += 1
return dest_clone
-def run_program(cmdline):
- print "run_program: %s" % (cmdline)
-
- import subprocess
- pop = subprocess.Popen(cmdline,
- shell=False,
- creationflags=win32con.CREATE_NO_WINDOW,
- stderr=subprocess.STDOUT,
- stdout=subprocess.PIPE,
- stdin=subprocess.PIPE)
-
"""Windows shell extension that adds context menu items to Mercurial repository"""
-class ContextMenuExtension:
+class ContextMenuExtension(menuthg.menuThg):
_reg_progid_ = "Mercurial.ShellExtension.ContextMenu"
_reg_desc_ = "Mercurial Shell Extension"
_reg_clsid_ = "{EEE9936B-73ED-4D45-80C9-AF918354F885}"
@@ -154,28 +80,29 @@ ]
def __init__(self):
- self._folder = None
- self._filenames = []
- self._handlers = {}
+ self.folder = None
+ self.fnames = []
+ self.menuitems = {}
+ menuthg.menuThg.__init__(self)
def Initialize(self, folder, dataobj, hkey):
if folder:
- self._folder = shell.SHGetPathFromIDList(folder)
+ self.folder = shell.SHGetPathFromIDList(folder)
if dataobj:
format_etc = win32con.CF_HDROP, None, 1, -1, pythoncom.TYMED_HGLOBAL
sm = dataobj.GetData(format_etc)
num_files = shell.DragQueryFile(sm.data_handle, -1)
for i in range(num_files):
- self._filenames.append(shell.DragQueryFile(sm.data_handle, i))
+ self.fnames.append(shell.DragQueryFile(sm.data_handle, i))
def _create_menu(self, parent, menus, pos, idCmd, idCmdFirst):
for menu_info in menus:
- if type(menu_info) == TortoiseMenuSep:
+ if menu_info.isSep():
win32gui.InsertMenu(parent, pos,
win32con.MF_BYPOSITION|win32con.MF_SEPARATOR,
idCmdFirst + idCmd, None)
- elif type(menu_info) == TortoiseSubmenu:
+ elif menu_info.isSubmenu():
submenu = win32gui.CreatePopupMenu()
idCmd = self._create_menu(submenu, menu_info.get_menus(), 0,
idCmd, idCmdFirst)
@@ -192,8 +119,8 @@
item, _ = win32gui_struct.PackMENUITEMINFO(**opt)
win32gui.InsertMenuItem(parent, pos, True, item)
- self._handlers[idCmd] = ("", lambda x,y: 0)
- elif type(menu_info) == TortoiseMenu:
+ self.menuitems[idCmd] = ("", "")
+ else:
fstate = win32con.MF_BYCOMMAND
if menu_info.state is False:
fstate |= win32con.MF_GRAYED
@@ -211,7 +138,7 @@
item, _ = win32gui_struct.PackMENUITEMINFO(**opt)
win32gui.InsertMenuItem(parent, pos, True, item)
- self._handlers[idCmd] = (menu_info.helptext, menu_info.handler)
+ self.menuitems[idCmd] = (menu_info.helptext, menu_info.hgcmd)
idCmd += 1
pos += 1
return idCmd
@@ -220,246 +147,57 @@ if uFlags & shellcon.CMF_DEFAULTONLY:
return 0
- thgmenu = [] # hg menus
+ thgmenu = []
# a brutal hack to detect if we are the first menu to go on to the
# context menu. If we are not the first, then add a menu separator
# The number '30000' is just a guess based on my observation
print "idCmdFirst = ", idCmdFirst
if idCmdFirst >= 30000:
- thgmenu.append(TortoiseMenuSep())
-
+ thgmenu.append(menuthg.TortoiseMenuSep())
# As we are a context menu handler, we can ignore verbs.
- self._handlers = {}
- if self._folder and self._filenames:
+
+ if self.folder:
+ cwd = self.folder
+ elif self.fnames:
+ f = self.fnames[0]
+ if len(self.fnames) == 1 and os.path.isdir(f):
+ cwd = f
+ self.fnames = []
+ else:
+ cwd = os.path.dirname(f)
+
+ self.menuitems = {}
+ if self.folder and self.fnames:
# get menus with drag-n-drop support
- commands = self._get_commands_dragdrop()
+ thgmenu += self.get_commands_dragdrop(self.fnames, self.folder)
+ repo = menuthg.open_repo(self.folder)
else:
- # add regularly used commit menu to main context menu
- rpath = self._folder or self._filenames[0]
- if open_repo(rpath):
- thgmenu.append(TortoiseMenu(_("HG Commit..."),
- _("Commit changes in repository"),
- self._commit, icon="menucommit.ico"))
-
- # get other menus for hg submenu
- commands = self._get_commands()
-
- # add common menu items
- commands.append(TortoiseMenuSep())
- commands.append(TortoiseMenu(_("About"),
- _("About TortoiseHg"),
- self._about, icon="menuabout.ico"))
-
- if commands:
- # create submenus with Hg commands
- thgmenu.append(TortoiseSubmenu("TortoiseHG", commands,
- icon="hg.ico"))
- thgmenu.append(TortoiseMenuSep())
-
- idCmd = self._create_menu(hMenu, thgmenu, indexMenu, 0,
- idCmdFirst)
-
- # Return total number of menus & submenus we've added
- return idCmd
- else:
- # no applicable Hg actions
- return 0
-
- def _get_commands_dragdrop(self):
- """
- Get a list of commands valid for the current selection.
-
- Each command is a tuple containing (display text, handler).
- """
-
- print "_get_commands_dragdrop() on %s" % ", ".join(self._filenames)
-
- # we can only accept dropping one item
- if len(self._filenames) > 1:
- return []
-
- # open repo
- drag_repo = None
- drop_repo = None
-
- print "drag = %s" % self._filenames[0]
- print "drop = %s" % self._folder
-
- drag_path = self._filenames[0]
- drag_repo = open_repo(drag_path)
- if not drag_repo:
- return []
- if drag_repo and drag_repo.root != drag_path:
- return [] # dragged item must be a hg repo root directory
-
- drop_repo = open_repo(self._folder)
-
- result = []
- result.append(TortoiseMenu(_("Create Clone"),
- _("Create clone here from source"),
- self._clone_here, icon="menuclone.ico"))
-
- if drop_repo:
- result.append(TortoiseMenu(_("Synchronize"),
- _("Synchronize with dragged repository"),
- self._synch_here, icon="menusynch.ico"))
- return result
-
- def _get_commands(self):
- """
- Get a list of commands valid for the current selection.
-
- Each command is a tuple containing (display text, handler).
- """
-
- print "_get_commands() on %s" % ", ".join(self._filenames)
-
- # open repo
- result = []
- rpath = self._folder or self._filenames[0]
- repo = open_repo(rpath)
- if repo is None:
- result.append(TortoiseMenu(_("Clone a Repository"),
- _("clone a repository"),
- self._clone, icon="menuclone.ico"))
- result.append(TortoiseMenu(_("Create Repository Here"),
- _("create a new repository in this directory"),
- self._init, icon="menucreaterepos.ico",
- state=os.path.isdir(rpath)))
- else:
- for f in self._filenames:
- if f.endswith('.hgignore'):
- result.append(TortoiseMenu(_("Edit Ignore Filter"),
- _("Edit repository ignore filter"),
- self._hgignore, icon="ignore.ico"))
- break
-
- result.append(TortoiseMenu(_("View File Status"),
- _("Repository status"),
- self._status, icon="menushowchanged.ico"))
- result.append(TortoiseMenu(_("Shelve Changes"),
- _("Shelve or unshelve repository changes"),
- self._shelve, icon="shelve.ico"))
-
- # Visual Diff (any extdiff command)
- has_vdiff = repo.ui.config('tortoisehg', 'vdiff', '') != ''
- result.append(TortoiseMenu(_("Visual Diff"),
- _("View changes using GUI diff tool"),
- self._vdiff, icon="TortoiseMerge.ico",
- state=has_vdiff))
-
- if len(self._filenames) == 0:
- result.append(TortoiseMenu(_("Guess Renames"),
- _("Detect renames and copies"),
- self._guess_rename, icon="detect_rename.ico"))
- elif len(self._filenames) == 1:
- result.append(TortoiseMenu(_("Rename File"),
- _("Rename file or directory"),
- self._rename, icon="general.ico")) # needs ico
-
- result.append(TortoiseMenu(_("Add Files"),
- _("Add files to Hg repository"),
- self._add, icon="menuadd.ico"))
- result.append(TortoiseMenu(_("Remove Files"),
- _("Remove selected files on the next commit"),
- self._remove, icon="menudelete.ico"))
- result.append(TortoiseMenu(_("Undo Changes"),
- _("Revert selected files"),
- self._revert, icon="menurevert.ico"))
-
- # we can only annotate file but not directories
- annotatible = len(self._filenames) > 0
- for f in self._filenames:
- if not os.path.isfile(f):
- annotatible = False
- break
- result.append(TortoiseMenu(_("Annotate Files"),
- _("show changeset information per file line"),
- self._annotate, icon="menublame.ico",
- state=annotatible))
-
- result.append(TortoiseMenuSep())
- result.append(TortoiseMenu(_("Update To Revision"),
- _("update working directory"),
- self._update, icon="menucheckout.ico"))
-
- can_merge = len(repo.heads()) > 1 and \
- len(repo.changectx(None).parents()) < 2
- result.append(TortoiseMenu(_("Merge Revisions"),
- _("merge working directory with another revision"),
- self._merge, icon="menumerge.ico",
- state=can_merge))
-
- in_merge = len(repo.changectx(None).parents()) > 1
- result.append(TortoiseMenu(_("Undo Merge"),
- _("Undo merge by updating to revision"),
- self._merge, icon="menuunmerge.ico",
- state=in_merge))
-
- result.append(TortoiseMenuSep())
-
- result.append(TortoiseMenu(_("View Changelog"),
- _("View revision history"),
- self._history, icon="menulog.ico"))
-
- result.append(TortoiseMenu(_("Search Repository"),
- _("Search revisions of files for a text pattern"),
- self._grep, icon="menurepobrowse.ico"))
-
- if repo.ui.config('tortoisehg', 'view'):
- result.append(TortoiseMenu(_("Revision Graph"),
- _("View history with DAG graph"),
- self._view, icon="menurevisiongraph.ico"))
-
- result.append(TortoiseMenuSep())
-
- result.append(TortoiseMenu(_("Synchronize..."),
- _("Synchronize with remote repository"),
- self._synch, icon="menusynch.ico"))
- result.append(TortoiseMenu(_("Recovery..."),
- _("General repair and recovery of repository"),
- self._recovery, icon="general.ico"))
- result.append(TortoiseMenu(_("Web Server"),
- _("start web server for this repository"),
- self._serve, icon="proxy.ico"))
-
- result.append(TortoiseMenuSep())
- result.append(TortoiseMenu(_("Create Clone"),
- _("Clone a repository here"),
- self._clone, icon="menuclone.ico"))
- can_init = repo.root != rpath and os.path.isdir(rpath)
- result.append(TortoiseMenu(_("Create Repository Here"),
- _("create a new repository in this directory"),
- self._init, icon="menucreaterepos.ico",
- state=can_init))
-
- # config setttings menu
- result.append(TortoiseMenuSep())
- optmenu = TortoiseSubmenu(_("Settings"),icon="menusettings.ico")
- optmenu.add_menu(_("Global"),
- _("Configure user wide settings"),
- self._config_user, icon="settings_user.ico")
- if repo:
- optmenu.add_menu(_("Repository"),
- _("Configure settings local to this repository"),
- self._config_repo, icon="settings_repo.ico")
- result.append(optmenu)
-
- return result
+ # get menus for hg menu
+ repo = menuthg.open_repo(cwd)
+ if repo:
+ thgmenu += self.get_commands(repo, cwd, self.fnames)
+ else:
+ thgmenu += self.get_norepo_commands(cwd, self.fnames)
+
+ self.cwd = cwd
+ self.repo = repo
+ idCmd = self._create_menu(hMenu, thgmenu, indexMenu, 0, idCmdFirst)
+ # Return total number of menus & submenus we've added
+ return idCmd
def InvokeCommand(self, ci):
mask, hwnd, verb, params, dir, nShow, hotkey, hicon = ci
if verb >> 16:
# This is a textual verb invocation... not supported.
return S_FALSE
- if verb not in self._handlers:
+ if verb not in self.menuitems:
raise Exception("Unsupported command id %i!" % verb)
- self._handlers[verb][1](hwnd)
+ self.run_dialog(self.menuitems[verb][1])
def GetCommandString(self, cmd, uFlags):
if uFlags & shellcon.GCS_VALIDATEA or uFlags & shellcon.GCS_VALIDATEW:
- if cmd in self._handlers:
+ if cmd in self.menuitems:
return S_OK
return S_FALSE
if uFlags & shellcon.GCS_VERBA or uFlags & shellcon.GCS_VERBW:
@@ -467,222 +205,42 @@ if uFlags & shellcon.GCS_HELPTEXTA or uFlags & shellcon.GCS_HELPTEXTW:
# The win32com.shell implementation encodes the resultant
# string into the correct encoding depending on the flags.
- return self._handlers[cmd][0]
+ return self.menuitems[cmd][0]
return S_FALSE
- def _commit(self, parent_window):
- self._run_dialog('commit')
-
- def _config_user(self, parent_window):
- self._run_dialog('config', noargs=True)
-
- def _config_repo(self, parent_window):
- self._run_dialog('config')
-
- def _vdiff(self, parent_window):
- '''[tortoisehg] vdiff = <any extdiff command>'''
- diff = ui.ui().config('tortoisehg', 'vdiff', None)
- if not diff:
- msg = "You must configure tortoisehg.vdiff in your Mercurial.ini"
- title = "Visual Diff Not Configured"
- win32ui.MessageBox(msg, title, win32con.MB_OK|win32con.MB_ICONERROR)
- return
- targets = self._filenames or [self._folder]
- root = find_root(targets[0])
- open_dialog(diff, root=root, filelist=targets, gui=False)
-
- def _view(self, parent_window):
- '''[tortoisehg] view = [hgk | hgview]'''
- view = ui.ui().config('tortoisehg', 'view', '')
- if not view:
- msg = "You must configure tortoisehg.view in your Mercurial.ini"
- title = "Revision Graph Tool Not Configured"
- win32ui.MessageBox(msg, title, win32con.MB_OK|win32con.MB_ICONERROR)
- return
-
- targets = self._filenames or [self._folder]
- root = find_root(targets[0])
- if view == 'hgview':
- hgviewpath = find_path('hgview')
- cmd = "%s --repository=%s" % \
- (shellquote(hgviewpath), shellquote(root))
- if len(self._filenames) == 1:
- cmd += " --file=%s" % shellquote(self._filenames[0])
- run_program(cmd)
- else:
- if view == 'hgk':
- open_dialog('view', root=root, gui=False)
- else:
- msg = "Revision graph viewer %s not recognized" % view
- title = "Unknown history tool"
- win32ui.MessageBox(msg, title, win32con.MB_OK|win32con.MB_ICONERROR)
-
- def _history(self, parent_window):
- self._log(parent_window)
-
- def _clone_here(self, parent_window):
- src = self._filenames[0]
- dest = self._folder
- repo_name = os.path.basename(src)
- dest_clone = get_clone_repo_name(dest, repo_name)
- cmdopts = "--verbose"
- repos = [src, dest_clone]
- open_dialog('clone', cmdopts, cwd=dest, filelist=repos)
-
- def _push_here(self, parent_window):
- src = self._filenames[0]
- dest = self._folder
- msg = "Push changes from %s into %s?" % (src, dest)
- title = "Mercurial: push"
- rv = win32ui.MessageBox(msg, title, win32con.MB_OKCANCEL)
- if rv == 2:
- return
-
- cmdopts = "--verbose"
- open_dialog('push', cmdopts, root=src, filelist=[dest])
-
- def _pull_here(self, parent_window):
- src = self._filenames[0]
- dest = self._folder
- msg = "Pull changes from %s?" % (src)
- title = "Mercurial: pull"
- rv = win32ui.MessageBox(msg, title, win32con.MB_OKCANCEL)
- if rv == 2:
- return
-
- cmdopts = "--verbose"
- open_dialog('pull', cmdopts, root=src, filelist=[dest])
-
- def _incoming_here(self, parent_window):
- src = self._filenames[0]
- dest = self._folder
- cmdopts = "--verbose"
- open_dialog('incoming', cmdopts, root=src, filelist=[dest])
-
- def _outgoing_here(self, parent_window):
- src = self._filenames[0]
- dest = self._folder
- cmdopts = "--verbose"
- open_dialog('outgoing', cmdopts, root=src, filelist=[dest])
-
- def _init(self, parent_window):
- self._run_dialog('init')
-
- def _shelve(self, parent_window):
- self._run_dialog('shelve')
-
- def _hgignore(self, parent_window):
- self._run_dialog('hgignore')
-
- def _rename(self, parent_window):
- src = self._filenames[0]
- if self._folder:
- cwd = self._folder
- elif self._filenames:
- f = self._filenames[0]
- cwd = os.path.isdir(f) and f or os.path.dirname(f)
- cmdopts = "--verbose"
- open_dialog('rename', cmdopts, cwd=cwd, filelist=[src])
-
- def _guess_rename(self, parent_window):
- self._run_dialog('rename --detect')
-
- def _status(self, parent_window):
- self._run_dialog('status')
-
- def _clone(self, parent_window):
- self._run_dialog('clone', True)
-
- def _synch(self, parent_window):
- self._run_dialog('synch', True)
-
- def _synch_here(self, parent_window):
- self._run_dialog('synch', False)
-
- def _pull(self, parent_window):
- self._run_dialog('pull', True)
-
- def _push(self, parent_window):
- self._run_dialog('push', True)
-
- def _incoming(self, parent_window):
- self._run_dialog('incoming', True)
-
- def _outgoing(self, parent_window):
- self._run_dialog('outgoing', True)
-
- def _serve(self, parent_window):
- self._run_dialog('serve', noargs=True)
-
- def _add(self, parent_window):
- self._run_dialog('add', modal=True)
-
- def _remove(self, parent_window):
- self._run_dialog('remove')
-
- def _revert(self, parent_window):
- self._run_dialog('status')
-
- def _tip(self, parent_window):
- self._run_dialog('tip', True)
-
- def _parents(self, parent_window):
- self._run_dialog('parents', True)
-
- def _heads(self, parent_window):
- self._run_dialog('heads', True)
-
- def _log(self, parent_window):
- self._run_dialog('log', verbose=False)
-
- def _show_tags(self, parent_window):
- self._run_dialog('tags', True, verbose=False)
-
- def _add_tag(self, parent_window):
- self._run_dialog('tag', True, verbose=False)
-
- def _diff(self, parent_window):
- self._run_dialog('diff')
-
- def _merge(self, parent_window):
- self._run_dialog('merge', noargs=True)
-
- def _recovery(self, parent_window):
- self._run_dialog('recovery')
-
- def _update(self, parent_window):
- self._run_dialog('update', noargs=True)
-
- def _grep(self, parent_window):
- # open datamine dialog with no file brings up a search tab
- self._run_dialog('datamine', noargs=True)
-
- def _annotate(self, parent_window):
- # open datamine dialog with files brings up the annotate
- # tabs for each file
- self._run_dialog('datamine')
-
- def _run_dialog(self, hgcmd, noargs=False, verbose=True, modal=False):
- if self._folder:
- cwd = self._folder
- elif self._filenames:
- f = self._filenames[0]
- cwd = os.path.isdir(f) and f or os.path.dirname(f)
- else:
- win32ui.MessageBox("Can't get cwd!", 'Hg ERROR',
- win32con.MB_OK|win32con.MB_ICONERROR)
- return
-
- targets = self._filenames or [self._folder]
- root = find_root(targets[0])
- filelist = []
- if noargs == False:
- filelist = targets
- cmdopts = "%s" % (verbose and "--verbose" or "")
- open_dialog(hgcmd, cmdopts, cwd=cwd, root=root, filelist=filelist)
-
- def _help(self, parent_window):
- open_dialog('help', '--verbose')
-
- def _about(self, parent_window):
- open_dialog('about')
+ def run_dialog(self, hgcmd):
+ cwd = self.cwd
+ if self.repo:
+ # Convert filenames to be relative to cwd
+ files = []
+ cwd_rel = cwd[len(repo.root+os.sep):]
+ for f in self.fnames:
+ cpath = util.canonpath(self.repo.root, cwd, f)
+ if cpath.startswith(cwd_rel):
+ cpath = cpath[len(cwd_rel):]
+ files.append(cpath)
+ else:
+ files.append(f)
+ self.fnames = files
+ gpopts = " %s" % hgcmd
+ if self.fnames:
+ fd, tmpfile = tempfile.mkstemp(prefix="tortoisehg_filelist_")
+ os.write(fd, "\n".join(self.fnames))
+ os.close(fd)
+ gpopts += " --listfile %s" % (shellquote(tmpfile))
+ app_path = find_path("hgtk", get_prog_root(), '.EXE;.BAT')
+ if not app_path:
+ app_path = find_path("hgtk", None, '.EXE;.BAT')
+ cmdline = shellquote(app_path) + gpopts
+ try:
+ import subprocess
+ pop = subprocess.Popen(cmdline,
+ shell=False,
+ cwd=cwd,
+ creationflags=win32con.CREATE_NO_WINDOW,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE)
+ except win32api.error, details:
+ win32ui.MessageBox("Error executing command - %s" % (details),
+ "gpopen")
|
|
@@ -25,19 +25,7 @@ sys.stderr.write(str(a))
ui.ui.write_err = write_err
-# file/directory status
-UNCHANGED = "unchanged"
-ADDED = "added"
-MODIFIED = "modified"
-UNKNOWN = "unknown"
-NOT_IN_REPO = "n/a"
-
-# file status cache
-CACHE_TIMEOUT = 5000
-overlay_cache = {}
-cache_tick_count = 0
-cache_root = None
-cache_pdir = None
+import cachethg
cache_lock = threading.Semaphore()
@@ -45,16 +33,6 @@S_OK = 0
S_FALSE = 1
-def add_dirs(list):
- dirs = set()
- for f in list:
- dir = os.path.dirname(f)
- if dir in dirs:
- continue
- while dir:
- dirs.add(dir)
- dir = os.path.dirname(dir)
- list.extend(dirs)
class IconOverlayExtension(object):
"""
@@ -83,129 +61,12 @@ def GetPriority(self):
return 0
- def _get_state(self, upath):
- """
- Get the state of a given path in source control.
- """
- global overlay_cache, cache_tick_count
- global cache_root, cache_pdir
-
- #print "called: _get_state(%s)" % path
- tc = win32api.GetTickCount()
-
- try:
- # handle some Asian charsets
- path = upath.encode('mbcs')
- except:
- path = upath
-
- # check if path is cached
- pdir = os.path.dirname(path)
- if cache_pdir == pdir and overlay_cache:
- if tc - cache_tick_count < CACHE_TIMEOUT:
- try:
- status = overlay_cache[path]
- except:
- status = UNKNOWN
- print "%s: %s (cached)" % (path, status)
- return status
- else:
- print "Timed out!!"
- overlay_cache.clear()
-
- # path is a drive
- if path.endswith(":\\"):
- overlay_cache[path] = UNKNOWN
- return NOT_IN_REPO
-
- # open repo
- if cache_pdir == pdir:
- root = cache_root
- else:
- print "find new root from", path, "was", cache_pdir
- cache_pdir = pdir
- cache_root = root = thgutil.find_root(pdir)
- print "_get_state: new root = ", root
- if root is None:
- print "_get_state: not in repo"
- overlay_cache = {None : None}
- cache_tick_count = win32api.GetTickCount()
- return NOT_IN_REPO
-
- try:
- tc1 = win32api.GetTickCount()
- repo = hg.repository(ui.ui(), path=root)
- print "hg.repository() took %d ticks" % (win32api.GetTickCount() - tc1)
-
- # check if to display overlay icons in this repo
- global_opts = ui.ui().configlist('tortoisehg', 'overlayicons', [])
- repo_opts = repo.ui.configlist('tortoisehg', 'overlayicons', [])
-
- # print "%s: global overlayicons = " % path, global_opts
- # print "%s: repo overlayicons = " % path, repo_opts
- is_netdrive = thgutil.netdrive_status(path) is not None
- if (is_netdrive and 'localdisks' in global_opts) \
- or 'False' in repo_opts:
- print "%s: overlayicons disabled" % path
- overlay_cache = {None : None}
- cache_tick_count = win32api.GetTickCount()
- return NOT_IN_REPO
- except RepoError:
- # We aren't in a working tree
- print "%s: not in repo" % pdir
- overlay_cache[path] = UNKNOWN
- return NOT_IN_REPO
-
- # get file status
- tc1 = win32api.GetTickCount()
-
- modified, added, removed, deleted = [], [], [], []
- unknown, ignored, clean = [], [], []
- try:
- matcher = cmdutil.match(repo, [pdir])
- modified, added, removed, deleted, unknown, ignored, clean = \
- repo.status(match=matcher, ignored=True,
- clean=True, unknown=True)
-
- # add directory status to list
- for grp in (clean,modified,added,removed,deleted,ignored,unknown):
- add_dirs(grp)
- except util.Abort, inst:
- print "abort: %s" % inst
- print "treat as unknown : %s" % path
- return UNKNOWN
-
- print "status() took %d ticks" % (win32api.GetTickCount() - tc1)
-
- # cached file info
- tc = win32api.GetTickCount()
- overlay_cache = {}
- for grp, st in (
- (ignored, UNKNOWN),
- (unknown, UNKNOWN),
- (clean, UNCHANGED),
- (added, ADDED),
- (removed, MODIFIED),
- (deleted, MODIFIED),
- (modified, MODIFIED)):
- for f in grp:
- fpath = os.path.join(repo.root, os.path.normpath(f))
- overlay_cache[fpath] = st
-
- if path in overlay_cache:
- status = overlay_cache[path]
- else:
- status = overlay_cache[path] = UNKNOWN
- print "%s: %s" % (path, status)
- cache_tick_count = win32api.GetTickCount()
- return status
-
def IsMemberOf(self, path, attrib):
global cache_lock
try:
cache_lock.acquire()
tc = win32api.GetTickCount()
- if self._get_state(path) == self.state:
+ if cachethg.get_state(path) == self.state:
return S_OK
return S_FALSE
finally:
@@ -240,6 +101,6 @@ globals()[classname] = cls
_overlay_classes = []
-make_icon_overlay("Changed", "Modified", MODIFIED, "{4D0F33E1-654C-4A1B-9BE8-E47A98752BAB}")
-make_icon_overlay("Unchanged", "Normal", UNCHANGED, "{4D0F33E2-654C-4A1B-9BE8-E47A98752BAB}")
-make_icon_overlay("Added", "Added", ADDED, "{4D0F33E3-654C-4A1B-9BE8-E47A98752BAB}")
+make_icon_overlay("Changed", "Modified", cachethg.MODIFIED, "{4D0F33E1-654C-4A1B-9BE8-E47A98752BAB}")
+make_icon_overlay("Unchanged", "Normal", cachethg.UNCHANGED, "{4D0F33E2-654C-4A1B-9BE8-E47A98752BAB}")
+make_icon_overlay("Added", "Added", cachethg.ADDED, "{4D0F33E3-654C-4A1B-9BE8-E47A98752BAB}")
|
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
@@ -173,5 +173,12 @@ path = os.environ.get('TORTOISEHG_PATH', defpath)
return os.path.isdir(path) and path or os.path.dirname(path)
+ def netdrive_status(drive):
+ """
+ return True if a network drive is accessible (connected, ...),
+ or None if <drive> is not a network drive
+ """
+ return None
+
def icon_to_bitmap(iconPathName):
pass
|
Loading...