Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 1.9, 1.9.1, and 1.9.2

sync: begin implementing a sync widget

It's like a platypus, it doesn't do much. I have grand plans for it.

Changeset a074e5d8ac31

Parent 0ffaeff50c81

by Steve Borho

Changes to 3 files · Browse files at a074e5d8ac31 Showing diff from parent 0ffaeff50c81 Diff from another changeset...

 
426
427
428
 
 
 
 
 
429
430
431
 
753
754
755
 
756
757
758
 
426
427
428
429
430
431
432
433
434
435
436
 
758
759
760
761
762
763
764
@@ -426,6 +426,11 @@
  from tortoisehg.hgqt.serve import run   qtrun(run, ui, *pats, **opts)   +def sync(ui, *pats, **opts): + """Synchronize with other repositories""" + from tortoisehg.hgqt.sync import run + qtrun(run, ui, *pats, **opts) +  def shellconfig(ui, *pats, **opts):   """explorer extension configuration editor"""   from tortoisehg.hgqt.shellconf import run @@ -753,6 +758,7 @@
  "forget": (forget, [], _('thg forget [FILE]...')),   "rename|mv|copy": (rename, [], _('thg rename SOURCE [DEST]...')),   "^serve": (serve, [], _('thg serve [OPTION]')), + "^sync": (sync, [], _('thg sync')),   "^status": (status,   [('c', 'clean', False, _('show files without changes')),   ('i', 'ignored', False, _('show ignored files'))],
 
1
2
3
 
4
5
6
7
 
 
 
8
9
10
 
 
 
11
12
13
 
14
15
16
17
18
19
20
21
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
@@ -1,22 +1,176 @@
 # sync.py - TortoiseHg's sync widget  #  # Copyright 2010 Adrian Buehlmann <adrian@cadifra.com> +# Copyright 2010 Steve Borho <steve@borho.org>  #  # This software may be used and distributed according to the terms of the  # GNU General Public License version 2 or any later version.   +import os +import re +  from PyQt4.QtCore import *  from PyQt4.QtGui import *   +from tortoisehg.hgqt.i18n import _ +from tortoisehg.hgqt import qtlib +  class SyncWidget(QWidget):   - def __init__(self, root, parent=None): + def __init__(self, root, parent=None, **opts):   QWidget.__init__(self, parent)     layout = QVBoxLayout()   layout.setContentsMargins(0, 0, 0, 0)   self.setLayout(layout)   - la = QLabel("Sync widget %s for repo '%s' " - "(widget is under construction)" % (id(self), root)) - layout.addWidget(la) + self.root = root + self.thread = None + self.tv = PathsTree(root, self) + self.refresh() + + pathsframe = QFrame() + pathsframe.setFrameStyle(QFrame.Panel|QFrame.Raised) + pathsbox = QVBoxLayout() + pathsframe.setLayout(pathsbox) + lbl = QLabel(_('<b>Configured Paths</b>')) + pathsbox.addWidget(lbl) + pathsbox.addWidget(self.tv) + layout.addWidget(pathsframe) + + if parent: + self.closeonesc = False + else: + self.setWindowTitle(_('TortoiseHg Search')) + self.resize(800, 550) + self.closeonesc = True + + + def refresh(self): + fn = os.path.join(self.root, '.hg', 'hgrc') + try: + import iniparse + # Monkypatch this regex to prevent iniparse from considering + # 'rem' as a comment + iniparse.ini.CommentLine.regex = \ + re.compile(r'^(?P<csep>[%;#])(?P<comment>.*)$') + cfg = iniparse.INIConfig(file(fn), optionxformvalue=None) + self.readonly = False + except ImportError: + from mercurial import config + cfg = config.config() + cfg.read(fn) + self.readonly = True + except Exception, e: + qtlib.WarningMsgBox(_('Unable to parse a config file'), + _('%s\nReverting to read-only mode.') % str(e), + parent=self) + self.readonly = True + cfg = {} + self.paths = {} + if 'paths' not in cfg: + return + for alias in cfg['paths']: + self.paths[ alias ] = cfg['paths'][ alias ] + tm = PathsModel(self.paths, self) + self.tv.setModel(tm) + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Escape: + if self.thread and self.thread.isRunning(): + self.thread.terminate() + # This can lockup, so stop waiting after 2sec + self.thread.wait( 2000 ) + self.finished() + self.thread = None + elif self.closeonesc: + self.close() + else: + return super(SyncWidget, self).keyPressEvent(event) + +class PathsTree(QTreeView): + def __init__(self, root, parent=None): + QTreeView.__init__(self, parent) + self.setSelectionMode(QTreeView.ExtendedSelection) + self.setContextMenuPolicy(Qt.CustomContextMenu) + self.connect(self, SIGNAL('customContextMenuRequested(const QPoint &)'), + self.customContextMenuRequested) + + def keyPressEvent(self, event): + return super(PathsTree, self).keyPressEvent(event) + + def dragObject(self): + urls = [] + for index in self.selectedRows(): + url = self.model().data(index)[1] + u = QUrl() + u.setPath(url) + urls.append(u) + if urls: + d = QDrag(self) + m = QMimeData() + m.setUrls(urls) + d.setMimeData(m) + d.start(Qt.CopyAction) + + def mousePressEvent(self, event): + self.pressPos = event.pos() + self.pressTime = QTime.currentTime() + return super(PathsTree, self).mousePressEvent(event) + + def mouseMoveEvent(self, event): + d = event.pos() - self.pressPos + if d.manhattanLength() < QApplication.startDragDistance(): + return QTreeView.mouseMoveEvent(self, event) + elapsed = self.pressTime.msecsTo(QTime.currentTime()) + if elapsed < QApplication.startDragTime(): + return super(PathsTree, self).mouseMoveEvent(event) + self.dragObject() + return super(PathsTree, self).mouseMoveEvent(event) + + def customContextMenuRequested(self, point): + point = self.mapToGlobal(point) + pass + + def selectedRows(self): + return self.selectionModel().selectedRows() + +class PathsModel(QAbstractTableModel): + def __init__(self, pathdict, parent=None): + QAbstractTableModel.__init__(self, parent) + self.headers = (_('Alias'), _('URL')) + self.rows = [] + for alias, url in pathdict.iteritems(): + self.rows.append([alias, url]) + + def rowCount(self, parent): + if parent.isValid(): + return 0 # no child + return len(self.rows) + + def columnCount(self, parent): + if parent.isValid(): + return 0 # no child + return len(self.headers) + + def data(self, index, role): + if not index.isValid(): + return QVariant() + if role == Qt.DisplayRole: + return QVariant(self.rows[index.row()][index.column()]) + return QVariant() + + def headerData(self, col, orientation, role): + if role != Qt.DisplayRole or orientation != Qt.Horizontal: + return QVariant() + else: + return QVariant(self.headers[col]) + + def flags(self, index): + flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled + return flags + + +def run(ui, *pats, **opts): + from tortoisehg.util import paths + return SyncWidget(paths.find_root(), **opts)
 
422
423
424
425
 
426
427
428
 
422
423
424
 
425
426
427
428
@@ -422,7 +422,7 @@
  sw = self.getSyncWidget(root)   if sw is None:   print "creating sync widget for %s" % root - sw = SyncWidget(root) + sw = SyncWidget(root, self)   self.syncwidgets[root] = sw   self.syncStackedWidget.addWidget(sw)   return sw