widgets.editremotes: Improve refresh behavior
[git-cola.git] / cola / widgets / editremotes.py
blobbd3dd14cf83701449dcf265f269cca69be946a06
1 from PyQt4 import QtCore
2 from PyQt4 import QtGui
3 from PyQt4.QtCore import Qt
4 from PyQt4.QtCore import SIGNAL
6 import cola.app
7 from cola import core
8 from cola import prefs
9 from cola import qtutils
10 from cola import gitcfg
11 from cola.git import git
12 from cola.widgets import defs
13 from cola.widgets import text
16 class RemoteEditor(QtGui.QDialog):
17 def __init__(self, parent):
18 super(RemoteEditor, self).__init__(parent)
20 self.setWindowTitle('Edit Remotes')
21 self.setWindowModality(Qt.WindowModal)
23 self.default_hint = (''
24 'You can add and remote remote git repositories\n'
25 'using the Add(+) and Delete(-) buttons on the left side.')
27 self.remote_list = []
28 self.remotes = QtGui.QListWidget()
29 self.remotes.setToolTip(self.tr(
30 'Remote git repositories - double-click to rename'))
32 self.info = text.HintedTextEdit(self.default_hint)
33 self.info.setReadOnly(True)
34 self.info.setFont(prefs.diff_font())
35 font = self.info.font()
36 metrics = QtGui.QFontMetrics(font)
37 width = metrics.width('_' * 72)
38 height = metrics.height() * 13
39 self.info.setMinimumWidth(width)
40 self.info.setMinimumHeight(height)
41 self.info_thread = RemoteInfoThread(self)
43 self.cfg = gitcfg.instance()
44 self.add_btn = QtGui.QToolButton()
45 self.add_btn.setIcon(qtutils.icon('add.svg'))
46 self.add_btn.setToolTip(self.tr('Add new remote git repository'))
48 self.refresh_btn = QtGui.QToolButton()
49 self.refresh_btn.setIcon(qtutils.icon('view-refresh.svg'))
50 self.refresh_btn.setToolTip(self.tr('Refresh'))
52 self.delete_btn = QtGui.QToolButton()
53 self.delete_btn.setIcon(qtutils.icon('remove.svg'))
54 self.delete_btn.setToolTip(self.tr('Delete remote'))
56 self.close_btn = QtGui.QPushButton(self.tr('Close'))
58 self._top_layout = QtGui.QSplitter()
59 self._top_layout.setOrientation(Qt.Horizontal)
60 self._top_layout.setHandleWidth(defs.handle_width)
61 self._top_layout.addWidget(self.remotes)
62 self._top_layout.addWidget(self.info)
63 width = self._top_layout.width()
64 self._top_layout.setSizes([width/4, width*3/4])
66 self._button_layout = QtGui.QHBoxLayout()
67 self._button_layout.addWidget(self.add_btn)
68 self._button_layout.addWidget(self.delete_btn)
69 self._button_layout.addWidget(self.refresh_btn)
70 self._button_layout.addStretch()
71 self._button_layout.addWidget(self.close_btn)
73 self._layout = QtGui.QVBoxLayout()
74 self._layout.setMargin(defs.margin)
75 self._layout.setSpacing(defs.spacing)
76 self._layout.addWidget(self._top_layout)
77 self._layout.addLayout(self._button_layout)
78 self.setLayout(self._layout)
80 self.refresh()
82 qtutils.connect_button(self.add_btn, self.add)
83 qtutils.connect_button(self.delete_btn, self.delete)
84 qtutils.connect_button(self.refresh_btn, self.refresh)
85 qtutils.connect_button(self.close_btn, self.close)
87 self.connect(self.info_thread, SIGNAL('info'),
88 self.info.set_value)
90 self.connect(self.remotes,
91 SIGNAL('itemChanged(QListWidgetItem*)'),
92 self.remote_renamed)
94 self.connect(self.remotes, SIGNAL('itemSelectionChanged()'),
95 self.selection_changed)
97 def refresh(self):
98 prefix = len('remote.')
99 suffix = len('.url')
100 remote_urls = self.cfg.find('remote.*.url')
101 remotes = [k[prefix:-suffix] for k in sorted(remote_urls.keys())]
102 self.remotes.clear()
103 self.remotes.addItems(remotes)
104 self.remote_list = remotes
105 self.info.set_hint(self.default_hint)
106 self.info.enable_hint(True)
107 for idx, r in enumerate(remotes):
108 item = self.remotes.item(idx)
109 item.setFlags(item.flags() | Qt.ItemIsEditable)
111 def add(self):
112 widget = AddRemoteWidget(self)
113 if not widget.add_remote():
114 return
115 name = widget.name.value()
116 url = widget.url.value()
117 status, out = git.remote('add', name, url,
118 with_status=True, with_stderr=True)
119 if status != 0:
120 qtutils.critical('Error creating remote "%s"' % name, out)
121 self.refresh()
123 def delete(self):
124 remote = qtutils.selected_item(self.remotes, self.remote_list)
125 if remote is None:
126 return
128 title = 'Delete Remote'
129 question = 'Delete remote?'
130 info = unicode(self.tr('Delete remote "%s"')) % remote
131 ok_btn = 'Delete'
132 if not qtutils.confirm(title, question, info, ok_btn):
133 return
135 status, out = git.remote('rm', remote,
136 with_status=True, with_stderr=True)
137 if status != 0:
138 qtutils.critical('Error deleting remote "%s"' % remote, out)
139 cola.model().update_status()
140 self.refresh()
142 def remote_renamed(self, item):
143 idx = self.remotes.row(item)
144 if idx < 0:
145 return
146 if idx >= len(self.remote_list):
147 return
149 old_name = self.remote_list[idx]
150 new_name = unicode(item.text())
151 if new_name == old_name:
152 return
153 if not new_name:
154 item.setText(old_name)
155 return
157 title = 'Rename Remote'
158 question = 'Rename remote?'
159 info = unicode(self.tr(
160 'Rename remote "%s" to "%s"?')) % (old_name, new_name)
161 ok_btn = 'Rename'
163 if qtutils.confirm(title, question, info, ok_btn):
164 git.remote('rename', old_name, new_name)
165 self.remote_list[idx] = new_name
166 else:
167 item.setText(old_name)
169 def selection_changed(self):
170 remote = qtutils.selected_item(self.remotes, self.remote_list)
171 if remote is None:
172 return
173 self.info.set_hint('Gathering info for "%s"...' % remote)
174 self.info.enable_hint(True)
176 self.info_thread.remote = remote
177 self.info_thread.start()
180 class RemoteInfoThread(QtCore.QThread):
181 def __init__(self, parent):
182 super(RemoteInfoThread, self).__init__(parent)
183 self.remote = None
185 def run(self):
186 remote = self.remote
187 if remote is None:
188 return
189 status, out = git.remote('show', remote,
190 with_stderr=True, with_status=True)
191 out = core.decode(out)
192 # This call takes a long time and we may have selected a
193 # different remote...
194 if remote == self.remote:
195 self.emit(SIGNAL('info'), out)
196 else:
197 self.run()
200 class AddRemoteWidget(QtGui.QDialog):
201 def __init__(self, parent):
202 super(AddRemoteWidget, self).__init__(parent)
203 self.setWindowModality(Qt.WindowModal)
205 self.add_btn = QtGui.QPushButton(self.tr('Add Remote'))
206 self.add_btn.setIcon(qtutils.apply_icon())
208 self.cancel_btn = QtGui.QPushButton(self.tr('Cancel'))
210 diff_font = prefs.diff_font()
211 metrics = QtGui.QFontMetrics(diff_font)
213 def lineedit(hint):
214 widget = text.HintedLineEdit(hint)
215 widget.setFont(diff_font)
216 widget.enable_hint(True)
217 widget.setMinimumWidth(metrics.width('_' * 32))
218 return widget
220 self.name = lineedit('Name for the new remote')
221 self.url = lineedit('git://git.example.com/repo.git')
223 self._form = QtGui.QFormLayout()
224 self._form.setMargin(defs.margin)
225 self._form.setSpacing(defs.spacing)
226 self._form.addRow(self.tr('Name'), self.name)
227 self._form.addRow(self.tr('URL'), self.url)
229 self._btn_layout = QtGui.QHBoxLayout()
230 self._btn_layout.setMargin(0)
231 self._btn_layout.setSpacing(defs.button_spacing)
232 self._btn_layout.addStretch()
233 self._btn_layout.addWidget(self.add_btn)
234 self._btn_layout.addWidget(self.cancel_btn)
236 self._layout = QtGui.QVBoxLayout()
237 self._layout.setMargin(defs.margin)
238 self._layout.setSpacing(defs.margin)
239 self._layout.addLayout(self._form)
240 self._layout.addLayout(self._btn_layout)
241 self.setLayout(self._layout)
243 self.connect(self.name, SIGNAL('textChanged(QString)'),
244 self.validate)
246 self.connect(self.url, SIGNAL('textChanged(QString)'),
247 self.validate)
249 self.add_btn.setEnabled(False)
251 qtutils.connect_button(self.add_btn, self.accept)
252 qtutils.connect_button(self.cancel_btn, self.reject)
254 def validate(self, dummy_text):
255 name = self.name.value()
256 url = self.url.value()
257 self.add_btn.setEnabled(bool(name) and bool(url))
259 def add_remote(self):
260 self.show()
261 self.raise_()
262 return self.exec_() == QtGui.QDialog.Accepted
265 def edit():
266 window = RemoteEditor(qtutils.active_window())
267 window.show()
268 window.raise_()
269 window.exec_()
270 return window
272 if __name__ == '__main__':
273 app = cola.app.ColaApplication([])
274 edit()