Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 0.8, 0.8.1, and 0.8.2

merge with crew

Changeset 9ac445927e9e

Parents ba04d623b63d

Parents 564846271733

by Adrian Buehlmann

Changes to 20 files · Browse files at 9ac445927e9e Showing diff from parent ba04d623b63d 564846271733 Diff from another changeset...

Change 1 of 1 Show Entire File hggtk/​clone.py Stacked
 
301
302
303
304
305
306
307
 
301
302
303
 
304
305
306
@@ -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):
Change 1 of 2 Show Entire File hggtk/​commit.py Stacked
 
406
407
408
409
410
411
412
 
504
505
506
507
508
509
510
 
406
407
408
 
409
410
411
 
503
504
505
 
506
507
508
@@ -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  
Change 1 of 1 Show Entire File hggtk/​hginit.py Stacked
 
164
165
166
167
168
169
170
 
164
165
166
 
167
168
169
@@ -164,7 +164,6 @@
  except:   pass   - shlib.update_thgstatus(u, dest, wait=True)   shlib.shell_notify(dest)     dialog.info_dialog(self, _('New repository created'),
Change 1 of 1 Show Entire File hggtk/​hgtk.py Stacked
 
236
237
238
239
240
241
242
 
236
237
238
 
239
240
241
@@ -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):
Change 1 of 5 Show Entire File hggtk/​status.py Stacked
 
806
807
808
809
810
811
812
 
825
826
827
828
829
830
831
 
843
844
845
846
847
848
849
 
1144
1145
1146
1147
1148
1149
1150
 
1172
1173
1174
1175
1176
1177
1178
 
806
807
808
 
809
810
811
 
824
825
826
 
827
828
829
 
841
842
843
 
844
845
846
 
1141
1142
1143
 
1144
1145
1146
 
1168
1169
1170
 
1171
1172
1173
@@ -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()  
Change 1 of 1 Show Entire File hggtk/​taskbarui.py Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@@ -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)) +'''
Change 1 of 1 Show Entire File taskbar.cmd Stacked
 
 
 
 
1
2
@@ -0,0 +1,2 @@
+@echo off +python taskbar.py
Change 1 of 1 Show Entire File taskbar.py Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
@@ -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()
Change 1 of 1 Show Entire File win32/​__init__.py Stacked
 
 
 
1
@@ -0,0 +1,1 @@
+#placeholder
 
1
2
3
 
4
5
6
7
8
 
 
 
 
 
9
10
11
 
 
 
 
12
13
14
15
16
17
18
 
 
 
 
19
20
21
22
23
24
25
26
27
 
 
 
 
 
28
29
30
31
32
33
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
36
37
38
39
40
41
 
 
 
 
 
 
 
 
 
 
 
 
 
42
43
44
45
 
46
47
48
49
50
51
52
53
54
55
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
 
 
 
 
 
 
 
 
 
 
 
 
73
74
75
76
 
 
 
 
 
 
 
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 
 
 
 
 
 
 
 
 
 
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
 
179
180
181
 
191
192
193
 
 
 
 
 
194
195
196
 
211
212
213
214
 
215
216
217
 
228
229
230
231
 
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
 
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
 
 
 
7
8
9
10
11
12
 
 
13
14
15
16
17
18
19
20
21
22
 
23
24
25
26
27
28
 
 
 
 
 
 
 
29
30
31
32
33
34
 
 
 
 
 
 
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 
 
 
 
 
 
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 
 
 
65
66
 
 
 
 
 
 
 
 
 
 
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
94
95
96
97
98
99
100
101
102
103
104
105
 
 
 
106
107
108
109
110
111
112
113
 
 
 
 
 
 
 
 
 
 
 
 
 
114
115
116
117
118
119
120
121
122
123
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
126
127
128
 
138
139
140
141
142
143
144
145
146
147
148
 
163
164
165
 
166
167
168
169
 
180
181
182
 
183
184
185
186
187
 
 
 
188
189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
191
192
 
194
195
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
198
199
200
201
202
203
204
205
206
207
@@ -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
4
5
 
6
7
8
 
481
482
483
484
485
486
487
488
489
490
491
492
493
 
 
494
495
496
 
3
4
5
6
7
8
9
 
482
483
484
 
 
 
 
 
 
 
 
 
 
485
486
487
488
489
@@ -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
18
19
 
20
21
22
 
54
55
56
57
 
58
59
60
 
64
65
66
 
 
67
68
69
 
112
113
114
115
 
 
116
117
118
 
121
122
123
124
 
125
126
127
 
17
18
19
20
21
22
23
 
55
56
57
 
58
59
60
61
 
65
66
67
68
69
70
71
72
 
115
116
117
 
118
119
120
121
122
 
125
126
127
 
128
129
130
131
@@ -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
33
34
35
 
 
36
37
38
39
 
40
 
32
33
34
 
35
36
37
38
39
 
40
41
@@ -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
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 
48
49
50
 
142
143
144
145
 
146
147
148
 
159
160
161
 
 
162
163
164
 
20
21
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24
25
26
 
118
119
120
 
121
122
123
124
 
135
136
137
138
139
140
141
142
@@ -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
15
16
 
17
18
19
 
14
15
16
17
18
19
20
@@ -14,6 +14,7 @@
  Winstat64.o \   Dirstatecache.o \   DirectoryStatus.o \ + Thgstatus.o \   QueryDirstate.o    BASE_LDFLAGS=-lole32 -lkernel32 -luser32 -lgdi32 -lshlwapi -lwininet \
 
23
24
25
 
26
27
28
 
99
100
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
103
104
 
160
161
162
163
 
164
165
166
 
193
194
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
197
198
 
23
24
25
26
27
28
29
 
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
 
184
185
186
 
187
188
189
190
 
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
@@ -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;
Change 1 of 1 Show Entire File win32/​shellext/​Thgstatus.cpp Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@@ -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; + } +}
Change 1 of 1 Show Entire File win32/​shellext/​Thgstatus.h Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@@ -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
Change 1 of 1 Show Entire File win32/​shellext/​swap.cmd Stacked
 
 
 
 
 
1
2
3
@@ -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"
Change 1 of 1 Show Entire File win32/​taskbar.py Stacked
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
@@ -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()