by
Changes to 20 files · Browse files at 9ac445927e9e Showing diff from parent ba04d623b63d 564846271733 Diff from another changeset...
@@ -301,7 +301,6 @@ self._close_button.grab_focus()
if dlg.return_code() == 0:
- shlib.update_thgstatus(self.ui, dest, wait=True)
shlib.shell_notify([dest])
def run(_ui, *pats, **opts):
|
@@ -406,7 +406,6 @@ _('No committable files selected'), self).run()
return
self.reload_status()
- shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
files = [self.repo.wjoin(x) for x in commit_list]
shlib.shell_notify(files)
@@ -504,7 +503,6 @@ if stat not in '?!' or self.should_addremove([file]):
self.hg_commit([file])
self.reload_status()
- shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
shlib.shell_notify([self.repo.wjoin(file)])
return True
|
@@ -164,7 +164,6 @@ except:
pass
- shlib.update_thgstatus(u, dest, wait=True)
shlib.shell_notify(dest)
dialog.info_dialog(self, _('New repository created'),
|
@@ -236,7 +236,6 @@ """add files"""
from mercurial import dispatch as _dispatch
_dispatch.dispatch(['add'] + list(pats))
- shlib.update_thgstatus(ui, paths.find_root())
shlib.shell_notify([os.getcwd()])
def thgstatus(ui, *pats, **opts):
|
@@ -806,7 +806,6 @@ commands.remove(self.ui, self.repo, *wfiles, **removeopts)
success, outtext = self._hg_call_wrapper('Remove', dohgremove)
if success:
- shlib.update_thgstatus(self.ui, self.repo.root)
self.reload_status()
@@ -825,7 +824,6 @@ commands.rename(self.ui, self.repo, *wfiles, **moveopts)
success, outtext = self._hg_call_wrapper('Move', dohgmove)
if success:
- shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
self.reload_status()
@@ -843,7 +841,6 @@ commands.copy(self.ui, self.repo, *wfiles, **cmdopts)
success, outtext = self._hg_call_wrapper('Copy', dohgcopy)
if success:
- shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
self.reload_status()
def merge_sel_changed(self, selection):
@@ -1144,7 +1141,6 @@ if not dlg or dlg.run() == gtk.RESPONSE_YES:
success, outtext = self._hg_call_wrapper('Revert', dohgrevert)
if success:
- shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
shlib.shell_notify(wfiles)
self.reload_status()
@@ -1172,7 +1168,6 @@ commands.add(self.ui, self.repo, *wfiles, **addopts)
success, outtext = self._hg_call_wrapper('Add', dohgadd)
if success:
- shlib.update_thgstatus(self.ui, self.repo.root)
shlib.shell_notify(wfiles)
self.reload_status()
|
|
@@ -0,0 +1,79 @@ + #
+# taskbarui.py - User interface for the TortoiseHg taskbar app
+#
+# Copyright (C) 2009 Steve Borho <steve@borho.org>
+#
+
+import os
+import gtk
+import gobject
+
+from thgutil.i18n import _
+from hggtk import gtklib
+
+class TaskBarUI(gtk.Window):
+ 'User interface for the TortoiseHg taskbar application'
+ def __init__(self, inputq):
+ 'Initialize the Dialog'
+ gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+ gtklib.set_tortoise_icon(self, 'hg.ico')
+ gtklib.set_tortoise_keys(self)
+
+ self.set_default_size(400, 120)
+ self.set_title(_('TortoiseHg Taskbar'))
+
+ vbox = gtk.VBox()
+ self.add(vbox)
+
+ frame = gtk.Frame(_('Event Log'))
+ frame.set_border_width(2)
+ vbox.pack_start(frame, True, True, 2)
+
+ scrolledwindow = gtk.ScrolledWindow()
+ scrolledwindow.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolledwindow.set_border_width(2)
+ textview = gtk.TextView()
+ textview.set_editable(False)
+ scrolledwindow.add(textview)
+ frame.add(scrolledwindow)
+ gobject.timeout_add(10, self.pollq, inputq, textview)
+
+ hbbox = gtk.HButtonBox()
+ hbbox.set_layout(gtk.BUTTONBOX_END)
+ vbox.pack_start(hbbox, False, False, 2)
+ close = gtk.Button(_('Close'))
+ close.connect('clicked', lambda x: self.destroy())
+
+ accelgroup = gtk.AccelGroup()
+ self.add_accel_group(accelgroup)
+ key, modifier = gtk.accelerator_parse('Escape')
+ close.add_accelerator('clicked', accelgroup, key, 0,
+ gtk.ACCEL_VISIBLE)
+ hbbox.add(close)
+
+ def pollq(self, queue, textview):
+ 'Poll the input queue'
+ buf = textview.get_buffer()
+ enditer = buf.get_end_iter()
+ while queue.qsize():
+ try:
+ msg = queue.get(0)
+ buf.insert(enditer, msg+'\n')
+ textview.scroll_to_mark(buf.get_insert(), 0)
+ except Queue.Empty:
+ pass
+ return True
+
+def run(ui, *pats, **opts):
+ return TaskBarUI(opts['queue'])
+
+'''
+import Queue
+q = Queue.Queue()
+q.put('Test1')
+q.put('Test2')
+from mercurial import ui
+from hggtk import hgtk
+hgtk.gtkrun(run(ui.ui(), queue=q))
+'''
|
|
|
|
|
@@ -0,0 +1,168 @@ + # Creates a task-bar icon. Run from Python.exe to see the
+# messages printed.
+
+from win32api import *
+from win32gui import *
+import win32ui
+import win32pipe
+import win32con
+import pywintypes
+import sys, os
+
+from mercurial import demandimport ; demandimport.enable()
+from thgutil import thread2
+from win32 import rpcserver
+
+APP_TITLE = "TortoiseHg RPC server"
+
+class MainWindow:
+ def __init__(self):
+ msg_TaskbarRestart = RegisterWindowMessage("TaskbarCreated");
+ message_map = {
+ msg_TaskbarRestart: self.OnRestart,
+ win32con.WM_DESTROY: self.OnDestroy,
+ win32con.WM_COMMAND: self.OnCommand,
+ win32con.WM_USER+20 : self.OnTaskbarNotify,
+ }
+ # Register the Window class.
+ wc = WNDCLASS()
+ hinst = wc.hInstance = GetModuleHandle(None)
+ wc.lpszClassName = "THgRpcServer"
+ wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
+ wc.hCursor = LoadCursor( 0, win32con.IDC_ARROW )
+ wc.hbrBackground = win32con.COLOR_WINDOW
+ wc.lpfnWndProc = message_map # could also specify a wndproc.
+ classAtom = RegisterClass(wc)
+ # Create the Window.
+ style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
+ self.hwnd = CreateWindow( classAtom, APP_TITLE, style, \
+ 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
+ 0, 0, hinst, None)
+ UpdateWindow(self.hwnd)
+ self.guithread = None
+ self._DoCreateIcons()
+
+ def _DoCreateIcons(self):
+ # Try and find a custom icon
+ hinst = GetModuleHandle(None)
+ from thgutil.paths import get_tortoise_icon
+ iconPathName = get_tortoise_icon("hg.ico")
+ if os.path.isfile(iconPathName):
+ icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
+ hicon = LoadImage(hinst, iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags)
+ else:
+ print "Can't find a Python icon file - using default"
+ hicon = LoadIcon(0, win32con.IDI_APPLICATION)
+
+ flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
+ nid = (self.hwnd, 0, flags, win32con.WM_USER+20, hicon, APP_TITLE)
+ try:
+ Shell_NotifyIcon(NIM_ADD, nid)
+ except error:
+ # This is common when windows is starting, and this code is hit
+ # before the taskbar has been created.
+ print "Failed to add the taskbar icon - is explorer running?"
+ # but keep running anyway - when explorer starts, we get the
+ # TaskbarCreated message.
+
+ # start namepipe server for hg status
+ self.start_pipe_server()
+
+ def OnRestart(self, hwnd, msg, wparam, lparam):
+ self._DoCreateIcons()
+
+ def OnDestroy(self, hwnd, msg, wparam, lparam):
+ nid = (self.hwnd, 0)
+ Shell_NotifyIcon(NIM_DELETE, nid)
+ PostQuitMessage(0) # Terminate the app.
+
+ def OnTaskbarNotify(self, hwnd, msg, wparam, lparam):
+ if lparam==win32con.WM_RBUTTONUP or lparam==win32con.WM_LBUTTONUP:
+ menu = CreatePopupMenu()
+ AppendMenu(menu, win32con.MF_STRING, 1023, 'Options...')
+ AppendMenu(menu, win32con.MF_SEPARATOR, 0, '')
+ AppendMenu(menu, win32con.MF_STRING, 1025, 'Exit' )
+ pos = GetCursorPos()
+ # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/menus_0hdi.asp
+ SetForegroundWindow(self.hwnd)
+ TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0], pos[1], 0, self.hwnd, None)
+ PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
+ return 1
+
+ def OnCommand(self, hwnd, msg, wparam, lparam):
+ id = LOWORD(wparam)
+ if id == 1023:
+ if not self.guithread or not self.guithread.isAlive():
+ self.launchgui()
+ else:
+ msg = "TortoiseHG options dialog already running"
+ win32ui.MessageBox(msg, 'TortoiseHG options...', win32con.MB_OK)
+ elif id == 1025:
+ self.exit_application()
+ else:
+ print "Unknown command -", id
+
+ def exit_application(self):
+ if self.stop_pipe_server():
+ DestroyWindow(self.hwnd)
+ if self.guithread and self.guithread.isAlive():
+ import gobject
+ gobject.idle_add(self.dialog.destroy)
+ print "Goodbye"
+
+ def stop_pipe_server(self):
+ print "Stopping pipe server..."
+ if not self.pipethread.isAlive():
+ return True
+
+ # Try the nice way first
+ self.svc.SvcStop()
+
+ max_try = 10
+ cnt = 1
+ while cnt <= max_try and self.pipethread.isAlive():
+ print "testing pipe [try %d] ..." % cnt
+ try:
+ self.pipethread.terminate()
+ win32pipe.CallNamedPipe(rpcserver.PIPENAME, '',
+ rpcserver.PIPEBUFSIZE, 0)
+ except:
+ pass
+ cnt += 1
+
+ if self.pipethread.isAlive():
+ print "WARNING: unable to stop server after %d trys." % max_try
+ return False
+ else:
+ return True
+
+ def launchgui(self):
+ def launch():
+ import gtk
+ from hggtk import taskbarui, hgtk
+ dlg = taskbarui.TaskBarUI(rpcserver.logq)
+ dlg.show_all()
+ dlg.connect('destroy', gtk.main_quit)
+ self.dialog = dlg
+ gtk.gdk.threads_init()
+ gtk.gdk.threads_enter()
+ gtk.main()
+ gtk.gdk.threads_leave()
+
+ self.guithread = thread2.Thread(target=launch)
+ self.guithread.start()
+
+ def start_pipe_server(self):
+ def servepipe():
+ self.svc = rpcserver.PipeServer()
+ self.svc.SvcDoRun()
+
+ self.pipethread = thread2.Thread(target=servepipe)
+ self.pipethread.start()
+
+def main():
+ w=MainWindow()
+ PumpMessages()
+
+if __name__=='__main__':
+ main()
|
|
@@ -0,0 +1,1 @@ + #placeholder
|
|
|
@@ -1,181 +1,128 @@ import os
import win32api
import win32con
+
from win32com.shell import shell, shellcon
import _winreg
-from mercurial import hg, cmdutil, util
-from mercurial import repo as _repo
-import thgutil
+
+from mercurial import ui
+
+from thgutil import paths, shlib
+
import sys
-import win32serviceutil
-import win32service
+import time
+import Queue
+import threading
+
import win32event
import win32pipe
import win32file
import pywintypes
import winerror
-PIPENAME = "\\\\.\\pipe\\PyPipeService"
+
+PIPENAME = r"\\.\pipe\TortoiseHgRpcServer-bc0c27107423-"
+PIPENAME += win32api.GetUserName()
+
PIPEBUFSIZE = 4096
-# 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
+logq = Queue.Queue(0)
+def logmsg(msg):
+ if logq.qsize() < 100:
+ ts = '[%s] ' % time.strftime('%c')
+ logq.put(ts + msg)
-# file/directory status
-UNCHANGED = "unchanged"
-ADDED = "added"
-MODIFIED = "modified"
-UNKNOWN = "unknown"
-NOT_IN_REPO = "n/a"
+def getrepos(batch):
+ roots = set()
+ notifypaths = set()
+ for path in batch:
+ r = paths.find_root(path)
+ if r is None:
+ for n in os.listdir(path):
+ r = paths.find_root(os.path.join(path, n))
+ if (r is not None):
+ roots.add(r)
+ notifypaths.add(r)
+ else:
+ roots.add(r);
+ notifypaths.add(path)
+ return roots, notifypaths
-# file status cache
-CACHE_TIMEOUT = 5000
-overlay_cache = {}
-cache_tick_count = 0
-cache_root = None
-cache_pdir = None
+def update_batch(batch):
+ '''updates thgstatus for all paths in batch'''
+ roots, notifypaths = getrepos(batch)
+ if roots:
+ _ui = ui.ui();
+ for r in sorted(roots):
+ logmsg('Updating ' + r)
+ shlib.update_thgstatus(_ui, r, wait=False)
+ shlib.shell_notify([r])
+ if notifypaths:
+ time.sleep(2)
+ shlib.shell_notify(list(notifypaths))
+ logmsg('Shell notified')
-# some misc constants
-S_OK = 0
-S_FALSE = 1
+requests = Queue.Queue(0)
-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)
+def update(args):
+ batch = []
+ r = args[0]
+ print "got update request %s (first in batch)" % r
+ batch.append(r)
+ print "wait a bit for additional requests..."
+ time.sleep(0.2)
+ deferred_requests = []
+ try:
+ while True:
+ req = requests.get_nowait()
+ s = req.split('|')
+ cmd, args = s[0], s[1:]
+ if cmd == 'update':
+ print "got update request %s" % req
+ batch.append(args[0])
+ else:
+ deferred_requests.append(req)
+ except Queue.Empty:
+ pass
+ for req in deferred_requests:
+ requests.put(req)
+ msg = "processing batch with %i update requests"
+ print msg % len(batch)
+ update_batch(batch)
-def get_hg_state(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
+def remove(args):
+ path = args[0]
+ logmsg('Removing ' + path)
+ roots, notifypaths = getrepos([path])
+ if roots:
+ for r in sorted(roots):
+ try:
+ os.remove(os.path.join(r, '.hg', 'thgstatus'))
+ except OSError:
+ pass
+ if notifypaths:
+ shlib.shell_notify(list(notifypaths))
- print "get_hg_state: path =", path
- if not path:
- return UNKNOWN
+def dispatch(req, cmd, args):
+ if cmd == 'update':
+ update(args)
+ elif cmd == 'remove':
+ remove(args)
+ else:
+ logmsg("Error: unknown request '%s'" % req)
- # 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()
+class Updater(threading.Thread):
+ def run(self):
+ while True:
+ req = requests.get()
+ s = req.split('|')
+ cmd, args = s[0], s[1:]
+ if cmd is 'terminate':
+ logmsg('Updater thread terminating')
+ return
+ dispatch(req, cmd, args)
- # 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"
- cache_pdir = pdir
- cache_root = root = thgutil.find_root(pdir)
- print "_get_state: 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 _repo.RepoError:
- # We aren't in a working tree
- print "%s: not in repo" % dir
- overlay_cache[path] = UNKNOWN
- return NOT_IN_REPO
-
- # get file status
- tc1 = win32api.GetTickCount()
-
- modified, added, removed, deleted = [], [], [], []
- unknown, ignored, clean = [], [], []
- files = []
- 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
+Updater().start()
class PipeServer:
def __init__(self):
@@ -191,6 +138,11 @@ # And create an event to be used in the OVERLAPPED object.
self.overlapped.hEvent = win32event.CreateEvent(None,0,0,None)
+ def SvcStop(self):
+ print 'PipeServer thread terminating'
+ win32event.SetEvent(self.hWaitStop)
+ requests.put('terminate')
+
def SvcDoRun(self):
# We create our named pipe.
pipeName = PIPENAME
@@ -211,7 +163,7 @@ sa)
# Loop accepting and processing connections
- while 1:
+ while True:
try:
hr = win32pipe.ConnectNamedPipe(pipeHandle, self.overlapped)
except pywintypes.error, inst:
@@ -228,35 +180,13 @@ rc = win32event.WaitForMultipleObjects(waitHandles, 0, timeout)
if rc==win32event.WAIT_OBJECT_0:
# Stop event
- break
+ return
else:
# read pipe and process request
try:
hr, data = win32file.ReadFile(pipeHandle, PIPEBUFSIZE)
- message = "Processed %d bytes: '%s'" % (len(data), data)
- print (message)
-
if not data:
raise SystemExit # signal by dispatch terminate
-
- try:
- data = data.decode('mbcs')
- except:
- pass
-
- try:
- status = get_hg_state(data)
- except SystemExit:
- raise SystemExit # interrupted by thread2.terminate()
- except:
- import traceback
- print "WARNING: something went wrong in get_hg_state()"
- print traceback.format_exc()
- status = "ERROR"
-
- win32file.WriteFile(pipeHandle, status)
-
- # And disconnect from the client.
win32pipe.DisconnectNamedPipe(pipeHandle)
except win32file.error:
# Client disconnected without sending data
@@ -264,23 +194,14 @@ # Thats OK - just get the next connection
continue
-if __name__ == '__main__':
- import sys
- if '--server' in sys.argv:
- svc = PipeServer()
- svc.SvcDoRun()
- elif '--client' in sys.argv:
- for x in sys.argv[1:]:
- if x.startswith('-'):
- continue
- path = os.path.abspath(x)
- try:
- status = win32pipe.CallNamedPipe(PIPENAME, path, PIPEBUFSIZE, 0)
- except pywintypes.error, inst:
- print "can't access named pipe '%s'" % PIPENAME
- sys.exit()
- print "%s = %s" % (path, status)
- else:
- print "usage:\n%s [--server|--client]" % sys.argv[0]
-
-
+ try:
+ requests.put(data)
+ except SystemExit:
+ raise SystemExit # interrupted by thread2.terminate()
+ except:
+ import traceback
+ print "WARNING: something went wrong in requests.put"
+ print traceback.format_exc()
+ status = "ERROR"
+ # Clean up when we exit
+ self.SvcStop()
|
@@ -3,6 +3,7 @@ #include "TortoiseUtils.h"
#include "StringUtils.h"
#include "Dirstatecache.h"
+#include "Thgstatus.h"
typedef struct {
std::string name;
@@ -481,16 +482,8 @@
if (cmd == "thgstatus")
{
- std::string hgroot = GetHgRepoRoot(cwd);
- if (hgroot.empty())
- {
- hgcmd += " --all";
- }
- else
- {
- Dirstatecache::invalidate(hgroot);
- hgcmd += " --notify .";
- }
+ Thgstatus::remove(cwd);
+ return;
}
LaunchCommand(hgcmd, cwd);
|
@@ -17,6 +17,7 @@ #include "stdafx.h"
#include "DirectoryStatus.h"
+#include "Thgstatus.h"
char DirectoryStatus::status(const std::string& relpath_) const
@@ -54,7 +55,7 @@}
-int DirectoryStatus::read(const std::string& hgroot)
+int DirectoryStatus::read(const std::string& hgroot, const std::string& cwd)
{
v_.clear();
@@ -64,6 +65,8 @@ if (!f)
{
TDEBUG_TRACE("DirectoryStatus::read: can't open '" << p << "'");
+ std::string p = (cwd.size() < hgroot.size() ? hgroot : cwd);
+ Thgstatus::update(p);
return 0;
}
@@ -112,7 +115,8 @@};
-DirectoryStatus* DirectoryStatus::get(const std::string& hgroot)
+DirectoryStatus* DirectoryStatus::get(
+ const std::string& hgroot, const std::string& cwd)
{
static CacheEntry ce;
@@ -121,7 +125,7 @@ if (ce.hgroot_ != hgroot || (tc - ce.tickcount_) > 2000)
{
ce.hgroot_.clear();
- ce.readfailed_ = (ce.ds_.read(hgroot) == 0);
+ ce.readfailed_ = (ce.ds_.read(hgroot, cwd) == 0);
ce.hgroot_ = hgroot;
ce.tickcount_ = GetTickCount();
}
|
@@ -32,9 +32,10 @@ V v_;
public:
- static DirectoryStatus* get(const std::string& hgroot);
+ static DirectoryStatus* get(
+ const std::string& hgroot, const std::string& cwd);
char status(const std::string& relpath) const;
private:
- int read(const std::string& hgroot);
+ int read(const std::string& hgroot, const std::string& cwd);
};
|
@@ -20,31 +20,7 @@ #include "Dirstatecache.h"
#include "dirstate.h"
#include "Winstat64.h"
-#include "TortoiseUtils.h"
-#include "StringUtils.h"
-
-
-void call_thgstatus(const std::string& cwd)
-{
- std::string dir = GetTHgProgRoot();
- if (dir.empty())
- {
- TDEBUG_TRACE("call_thgstatus: THG root is empty");
- return;
- }
- std::string hgcmd = dir + "\\hgtk.exe";
-
- WIN32_FIND_DATAA data;
- HANDLE hfind = FindFirstFileA(hgcmd.c_str(), &data);
- if (hfind == INVALID_HANDLE_VALUE)
- hgcmd = dir + "\\hgtk.cmd";
- else
- FindClose(hfind);
-
- hgcmd = Quote(hgcmd) + " thgstatus --notify .";
-
- LaunchCommand(hgcmd, cwd);
-}
+#include "Thgstatus.h"
std::list<Dirstatecache::E>& Dirstatecache::cache()
@@ -142,7 +118,7 @@ TDEBUG_TRACE("Dirstatecache::get: ignored (unset entries)");
if (!iter->unset)
{
- call_thgstatus(cwd);
+ Thgstatus::update(cwd);
iter->unset = true;
}
return iter->dstate;
@@ -159,6 +135,8 @@ iter->dstate_mtime = stat.mtime;
iter->dstate_size = stat.size;
+ Thgstatus::update(cwd);
+
return iter->dstate;
}
|
@@ -14,6 +14,7 @@ Winstat64.o \
Dirstatecache.o \
DirectoryStatus.o \
+ Thgstatus.o \
QueryDirstate.o
BASE_LDFLAGS=-lole32 -lkernel32 -luser32 -lgdi32 -lshlwapi -lwininet \
|
@@ -23,6 +23,7 @@ #include "Dirstatecache.h"
#include "Winstat.h"
#include "TortoiseUtils.h"
+#include "Thgstatus.h"
#include <shlwapi.h>
@@ -99,6 +100,29 @@}
+int get_relpath(
+ const std::string& hgroot,
+ const std::string& path,
+ std::string& res
+)
+{
+ size_t offset = hgroot.size();
+ if (offset == 0)
+ return 0;
+
+ if (offset > path.size())
+ return 0;
+
+ if (path[offset] == '\\')
+ offset++;
+
+ const char* relpathptr = path.c_str() + offset;
+
+ res = relpathptr;
+ return 1;
+}
+
+
int HgQueryDirstate(
const std::string& path, const char& filterStatus, char& outStatus)
{
@@ -160,7 +184,7 @@ return 0; // unknown dir -> no icon
}
- DirectoryStatus* pds = DirectoryStatus::get(cur.hgroot);
+ DirectoryStatus* pds = DirectoryStatus::get(cur.hgroot, cur.basedir);
outStatus = (pds ? pds->status(relpath) : '?');
}
else
@@ -193,6 +217,30 @@ }
outStatus = e->status(stat);
+
+ if (outStatus == 'M')
+ {
+ DirectoryStatus* dirsst =
+ DirectoryStatus::get(cur.hgroot, cur.basedir);
+ if (dirsst)
+ {
+ std::string relbase;
+ if (get_relpath(cur.hgroot, cur.basedir, relbase))
+ {
+ TDEBUG_TRACE("HgQueryDirstate: relbase = '"
+ << relbase << "'");
+
+ char basedir_status = dirsst->status(relbase);
+ TDEBUG_TRACE("HgQueryDirstate: basedir_status = "
+ << basedir_status);
+
+ if (basedir_status != 'M')
+ {
+ Thgstatus::update(cur.hgroot);
+ }
+ }
+ }
+ }
}
cur.status = outStatus;
|
|
@@ -0,0 +1,70 @@ +
+// Copyright (C) 2009 Adrian Buehlmann
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "stdafx.h"
+
+#include "Thgstatus.h"
+
+#include <vector>
+
+
+std::string GetPipeName()
+{
+ DWORD size = 260;
+ std::vector<char> buf(size);
+ if (!::GetUserNameA(&buf[0], &size))
+ return "";
+ std::string res = "\\\\.\\pipe\\TortoiseHgRpcServer-bc0c27107423-";
+ res += &buf[0];
+ return res;
+}
+
+
+int Thgstatus::SendRequest(const std::string& request)
+{
+ static const std::string pname = GetPipeName();
+
+ if (pname.empty())
+ return 0;
+
+ BOOL fSuccess;
+ DWORD cbRead;
+
+ TDEBUG_TRACE("Thgstatus::update: sending '" << request << "' to " << pname);
+
+ fSuccess = ::CallNamedPipeA(
+ pname.c_str(), (void*)request.c_str(), request.size(), 0, 0, &cbRead,
+ 200 /* ms */
+ );
+
+ DWORD err = GetLastError();
+ if (fSuccess || err == ERROR_MORE_DATA || err == ERROR_PIPE_NOT_CONNECTED)
+ {
+ return 0;
+ }
+ else if (err == ERROR_PIPE_BUSY)
+ {
+ TDEBUG_TRACE("Thgstatus::update: CallNamedPipeA failed. "
+ "ERROR_PIPE_BUSY");
+ return -1;
+ }
+ else
+ {
+ TDEBUG_TRACE("Thgstatus::update: CallNamedPipeA failed ("
+ << err << ")");
+ return -1;
+ }
+}
|
|
@@ -0,0 +1,35 @@ +
+// Copyright (C) 2009 Adrian Buehlmann
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef _THGSTATUS_H
+#define _THGSTATUS_H
+
+#include <string>
+
+class Thgstatus
+{
+ static int SendRequest(const std::string& request);
+
+public:
+ static int update(const std::string& path) {
+ return SendRequest("update|" + path);
+ }
+ static int remove(const std::string& path) {
+ return SendRequest("remove|" + path);
+ }
+};
+
+#endif
|
|
@@ -0,0 +1,3 @@ + del "C:\Program Files\TortoiseHg\ThgShell-old.dll"
+rename "C:\Program Files\TortoiseHg\ThgShell.dll" ThgShell-old.dll
+copy ThgShell.dll "C:\Program Files\TortoiseHg\ThgShell.dll"
|
|
|
@@ -1,139 +0,0 @@ - # Creates a task-bar icon. Run from Python.exe to see the
-# messages printed.
-
-import rpcserver
-import thread2
-from win32api import *
-from win32gui import *
-import win32ui
-import win32pipe
-import win32con
-import pywintypes
-import sys, os
-
-APP_TITLE = "TortoiseHg RPC server"
-
-class MainWindow:
- def __init__(self):
- msg_TaskbarRestart = RegisterWindowMessage("TaskbarCreated");
- message_map = {
- msg_TaskbarRestart: self.OnRestart,
- win32con.WM_DESTROY: self.OnDestroy,
- win32con.WM_COMMAND: self.OnCommand,
- win32con.WM_USER+20 : self.OnTaskbarNotify,
- }
- # Register the Window class.
- wc = WNDCLASS()
- hinst = wc.hInstance = GetModuleHandle(None)
- wc.lpszClassName = "THgRpcServer"
- wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
- wc.hCursor = LoadCursor( 0, win32con.IDC_ARROW )
- wc.hbrBackground = win32con.COLOR_WINDOW
- wc.lpfnWndProc = message_map # could also specify a wndproc.
- classAtom = RegisterClass(wc)
- # Create the Window.
- style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
- self.hwnd = CreateWindow( classAtom, APP_TITLE, style, \
- 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
- 0, 0, hinst, None)
- UpdateWindow(self.hwnd)
- self._DoCreateIcons()
-
- def _DoCreateIcons(self):
- # Try and find a custom icon
- hinst = GetModuleHandle(None)
- from thgutil import get_tortoise_icon
- iconPathName = get_tortoise_icon("hg.ico")
- if os.path.isfile(iconPathName):
- icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
- hicon = LoadImage(hinst, iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags)
- else:
- print "Can't find a Python icon file - using default"
- hicon = LoadIcon(0, win32con.IDI_APPLICATION)
-
- flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
- nid = (self.hwnd, 0, flags, win32con.WM_USER+20, hicon, APP_TITLE)
- try:
- Shell_NotifyIcon(NIM_ADD, nid)
- except error:
- # This is common when windows is starting, and this code is hit
- # before the taskbar has been created.
- print "Failed to add the taskbar icon - is explorer running?"
- # but keep running anyway - when explorer starts, we get the
- # TaskbarCreated message.
-
- # start namepipe server for hg status
- self.start_pipe_server()
-
- def OnRestart(self, hwnd, msg, wparam, lparam):
- self._DoCreateIcons()
-
- def OnDestroy(self, hwnd, msg, wparam, lparam):
- nid = (self.hwnd, 0)
- Shell_NotifyIcon(NIM_DELETE, nid)
- PostQuitMessage(0) # Terminate the app.
-
- def OnTaskbarNotify(self, hwnd, msg, wparam, lparam):
- if lparam==win32con.WM_RBUTTONUP or lparam==win32con.WM_LBUTTONUP:
- menu = CreatePopupMenu()
- AppendMenu(menu, win32con.MF_STRING, 1023, 'Options...')
- AppendMenu(menu, win32con.MF_SEPARATOR, 0, '')
- AppendMenu(menu, win32con.MF_STRING, 1025, 'Exit' )
- pos = GetCursorPos()
- # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/menus_0hdi.asp
- SetForegroundWindow(self.hwnd)
- TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0], pos[1], 0, self.hwnd, None)
- PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
- return 1
-
- def OnCommand(self, hwnd, msg, wparam, lparam):
- id = LOWORD(wparam)
- if id == 1023:
- # place holder for options dialog
- msg = "TortoiseHG options dialog in construction"
- win32ui.MessageBox(msg, 'TortoiseHG options...', win32con.MB_OK)
- elif id == 1025:
- self.exit_application()
- else:
- print "Unknown command -", id
-
- def exit_application(self):
- print "stopping pipe server..."
- if self.stop_pipe_server():
- DestroyWindow(self.hwnd)
- print "\n\nGoodbye"
-
- def stop_pipe_server(self):
- max_try = 10
- cnt = 1
- while cnt <= max_try and self.pipethread.isAlive():
- print "testing pipe [try %d] ..." % cnt
- try:
- self.pipethread.terminate()
- win32pipe.CallNamedPipe(rpcserver.PIPENAME, '',
- rpcserver.PIPEBUFSIZE, 0)
- except:
- pass
- cnt += 1
-
- if self.pipethread.isAlive():
- print "WARNING: unable to stop server after %d trys." % max_try
- return False
- else:
- return True
-
-
- def start_pipe_server(self):
- def servepipe():
- self.svc = rpcserver.PipeServer()
- self.svc.SvcDoRun()
-
- self.pipethread = thread2.Thread(target=servepipe)
- self.pipethread.start()
-
-def main():
- w=MainWindow()
- PumpMessages()
-
-if __name__=='__main__':
- main()
|
Loading...