3 from qtpy
import QtCore
4 from qtpy
import QtWidgets
5 from qtpy
.QtCore
import Qt
6 from qtpy
.QtCore
import Signal
8 from ..git
import STDOUT
10 from ..qtutils
import get
13 from .. import gitcmds
15 from .. import qtutils
17 from . import completion
19 from . import standard
23 def editor(context
, run
=True):
24 view
= RemoteEditor(context
, parent
=qtutils
.active_window())
31 class RemoteEditor(standard
.Dialog
):
32 def __init__(self
, context
, parent
=None):
33 standard
.Dialog
.__init
__(self
, parent
)
34 self
.setWindowTitle(N_('Edit Remotes'))
35 if parent
is not None:
36 self
.setWindowModality(Qt
.WindowModal
)
38 self
.context
= context
39 self
.current_name
= ''
42 hint
= N_('Edit remotes by selecting them from the list')
43 self
.default_hint
= hint
46 self
.remotes
= QtWidgets
.QListWidget()
47 self
.remotes
.setToolTip(N_('Remote git repositories - double-click to rename'))
49 self
.editor
= RemoteWidget(context
, self
)
51 self
.save_button
= qtutils
.create_button(
52 text
=N_('Save'), icon
=icons
.save(), default
=True
55 self
.reset_button
= qtutils
.create_button(
56 text
=N_('Reset'), icon
=icons
.discard()
60 'Add and remove remote repositories using the \n'
61 'Add(+) and Delete(-) buttons on the left-hand side.\n'
63 'Remotes can be renamed by selecting one from the list\n'
64 'and pressing "enter", or by double-clicking.'
66 hint
= self
.default_hint
67 self
.info
= text
.VimHintedPlainTextEdit(context
, hint
, parent
=self
)
68 self
.info
.setToolTip(tooltip
)
70 text_width
, text_height
= qtutils
.text_size(self
.info
.font(), 'M')
71 width
= text_width
* 42
72 height
= text_height
* 13
73 self
.info
.setMinimumWidth(width
)
74 self
.info
.setMinimumHeight(height
)
75 self
.info_thread
= RemoteInfoThread(context
, self
)
78 tooltip
= N_('Add new remote git repository')
79 self
.add_button
= qtutils
.create_toolbutton(icon
=icon
, tooltip
=tooltip
)
81 self
.refresh_button
= qtutils
.create_toolbutton(
82 icon
=icons
.sync(), tooltip
=N_('Refresh')
84 self
.delete_button
= qtutils
.create_toolbutton(
85 icon
=icons
.remove(), tooltip
=N_('Delete remote')
87 self
.close_button
= qtutils
.close_button()
89 self
._edit
_button
_layout
= qtutils
.vbox(
90 defs
.no_margin
, defs
.spacing
, self
.save_button
, self
.reset_button
93 self
._edit
_layout
= qtutils
.hbox(
94 defs
.no_margin
, defs
.spacing
, self
.editor
, self
._edit
_button
_layout
97 self
._display
_layout
= qtutils
.vbox(
98 defs
.no_margin
, defs
.spacing
, self
._edit
_layout
, self
.info
100 self
._display
_widget
= QtWidgets
.QWidget(self
)
101 self
._display
_widget
.setLayout(self
._display
_layout
)
103 self
._top
_layout
= qtutils
.splitter(
104 Qt
.Horizontal
, self
.remotes
, self
._display
_widget
106 width
= self
._top
_layout
.width()
107 self
._top
_layout
.setSizes([width
// 4, width
* 3 // 4])
109 self
._button
_layout
= qtutils
.hbox(
119 self
._layout
= qtutils
.vbox(
120 defs
.margin
, defs
.spacing
, self
._top
_layout
, self
._button
_layout
122 self
.setLayout(self
._layout
)
124 qtutils
.connect_button(self
.add_button
, self
.add
)
125 qtutils
.connect_button(self
.delete_button
, self
.delete
)
126 qtutils
.connect_button(self
.refresh_button
, self
.refresh
)
127 qtutils
.connect_button(self
.close_button
, self
.accept
)
128 qtutils
.connect_button(self
.save_button
, self
.save
)
129 qtutils
.connect_button(self
.reset_button
, self
.reset
)
130 qtutils
.add_close_action(self
)
132 thread
= self
.info_thread
133 thread
.result
.connect(self
.set_info
, type=Qt
.QueuedConnection
)
135 self
.editor
.remote_name
.returnPressed
.connect(self
.save
)
136 self
.editor
.remote_url
.returnPressed
.connect(self
.save
)
137 self
.editor
.valid
.connect(self
.editor_valid
)
139 self
.remotes
.itemChanged
.connect(self
.remote_item_renamed
)
140 self
.remotes
.itemSelectionChanged
.connect(self
.selection_changed
)
142 self
.disable_editor()
143 self
.init_state(None, self
.resize_widget
, parent
)
144 self
.remotes
.setFocus(Qt
.OtherFocusReason
)
148 focus
= self
.focusWidget()
149 if self
.current_name
:
150 self
.activate_remote(self
.current_name
, gather_info
=False)
155 url
= self
.editor
.url
156 name
= self
.editor
.name
157 return url
!= self
.current_url
or name
!= self
.current_name
162 context
= self
.context
163 name
= self
.editor
.name
164 url
= self
.editor
.url
165 old_url
= self
.current_url
166 old_name
= self
.current_name
168 name_changed
= name
and name
!= old_name
169 url_changed
= url
and url
!= old_url
170 focus
= self
.focusWidget()
172 name_ok
= url_ok
= False
174 # Run the corresponding command
175 if name_changed
and url_changed
:
176 name_ok
, url_ok
= cmds
.do(cmds
.RemoteEdit
, context
, old_name
, name
, url
)
178 result
= cmds
.do(cmds
.RemoteRename
, context
, old_name
, name
)
181 result
= cmds
.do(cmds
.RemoteSetURL
, context
, name
, url
)
184 # Update state if the change succeeded
186 if url_changed
and url_ok
:
187 self
.current_url
= url
190 # A name change requires a refresh
191 if name_changed
and name_ok
:
192 self
.current_name
= name
194 self
.refresh(select
=False)
195 remotes
= utils
.Sequence(self
.remote_list
)
196 idx
= remotes
.index(name
)
197 self
.select_remote(idx
)
198 gather
= False # already done by select_remote()
204 def editor_valid(self
, valid
):
205 changed
= self
.changed
206 self
.reset_button
.setEnabled(changed
)
207 self
.save_button
.setEnabled(changed
and valid
)
209 def disable_editor(self
):
210 self
.save_button
.setEnabled(False)
211 self
.reset_button
.setEnabled(False)
212 self
.editor
.setEnabled(False)
213 self
.editor
.name
= ''
215 self
.info
.hint
.set_value(self
.default_hint
)
216 self
.info
.set_value('')
218 def resize_widget(self
, parent
):
219 """Set the initial size of the widget"""
220 width
, height
= qtutils
.default_size(parent
, 720, 445)
221 self
.resize(width
, height
)
223 def set_info(self
, info
):
224 self
.info
.hint
.set_value(self
.default_hint
)
225 self
.info
.set_value(info
)
227 def select_remote(self
, index
):
229 item
= self
.remotes
.item(index
)
231 item
.setSelected(True)
233 def refresh(self
, select
=True):
234 git
= self
.context
.git
235 remotes
= git
.remote()[STDOUT
].splitlines()
236 # Ignore notifications from self.remotes while mutating.
237 with qtutils
.BlockSignals(self
.remotes
):
239 self
.remotes
.addItems(remotes
)
240 self
.remote_list
= remotes
242 for idx
in range(len(remotes
)):
243 item
= self
.remotes
.item(idx
)
244 item
.setFlags(item
.flags() | Qt
.ItemIsEditable
)
247 if not self
.current_name
and remotes
:
248 # Nothing is selected; select the first item
249 self
.select_remote(0)
250 elif self
.current_name
and remotes
:
251 # Reselect the previously selected item
252 remote_seq
= utils
.Sequence(remotes
)
253 idx
= remote_seq
.index(self
.current_name
)
255 item
= self
.remotes
.item(idx
)
257 item
.setSelected(True)
260 if add_remote(self
.context
, self
):
261 self
.refresh(select
=False)
262 # Newly added remote will be last; select it
263 self
.select_remote(len(self
.remote_list
) - 1)
266 remote
= qtutils
.selected_item(self
.remotes
, self
.remote_list
)
269 cmds
.do(cmds
.RemoteRemove
, self
.context
, remote
)
270 self
.refresh(select
=False)
272 def remote_item_renamed(self
, item
):
273 idx
= self
.remotes
.row(item
)
276 if idx
>= len(self
.remote_list
):
278 old_name
= self
.remote_list
[idx
]
279 new_name
= item
.text()
280 if new_name
== old_name
:
283 item
.setText(old_name
)
285 context
= self
.context
286 ok
, status
, _
, _
= cmds
.do(cmds
.RemoteRename
, context
, old_name
, new_name
)
287 if ok
and status
== 0:
288 self
.remote_list
[idx
] = new_name
289 self
.activate_remote(new_name
)
291 item
.setText(old_name
)
293 def selection_changed(self
):
294 remote
= qtutils
.selected_item(self
.remotes
, self
.remote_list
)
296 self
.disable_editor()
298 self
.activate_remote(remote
)
300 def activate_remote(self
, name
, gather_info
=True):
301 url
= gitcmds
.remote_url(self
.context
, name
)
302 self
.current_name
= name
303 self
.current_url
= url
305 self
.editor
.setEnabled(True)
306 self
.editor
.name
= name
307 self
.editor
.url
= url
312 def gather_info(self
):
313 name
= self
.current_name
314 self
.info
.hint
.set_value(N_('Gathering info for "%s"...') % name
)
315 self
.info
.set_value('')
317 self
.info_thread
.remote
= name
318 self
.info_thread
.start()
321 def add_remote(context
, parent
, name
='', url
='', readonly_url
=False):
322 """Bring up the "Add Remote" dialog"""
323 widget
= AddRemoteDialog(context
, parent
, readonly_url
=readonly_url
)
329 cmds
.do(cmds
.RemoteAdd
, context
, widget
.name
, widget
.url
)
336 def restore_focus(focus
):
338 focus
.setFocus(Qt
.OtherFocusReason
)
339 if hasattr(focus
, 'selectAll'):
343 class RemoteInfoThread(QtCore
.QThread
):
344 result
= Signal(object)
346 def __init__(self
, context
, parent
):
347 QtCore
.QThread
.__init
__(self
, parent
)
348 self
.context
= context
355 git
= self
.context
.git
356 _
, out
, err
= git
.remote('show', '-n', remote
)
357 self
.result
.emit(out
+ err
)
360 class AddRemoteDialog(QtWidgets
.QDialog
):
361 def __init__(self
, context
, parent
, readonly_url
=False):
362 super().__init
__(parent
)
363 self
.context
= context
365 self
.setWindowModality(Qt
.WindowModal
)
367 self
.context
= context
368 self
.widget
= RemoteWidget(context
, self
, readonly_url
=readonly_url
)
369 self
.add_button
= qtutils
.create_button(
370 text
=N_('Add Remote'), icon
=icons
.ok(), enabled
=False
372 self
.close_button
= qtutils
.close_button()
374 self
._button
_layout
= qtutils
.hbox(
382 self
._layout
= qtutils
.vbox(
383 defs
.margin
, defs
.spacing
, self
.widget
, self
._button
_layout
385 self
.setLayout(self
._layout
)
387 self
.widget
.valid
.connect(self
.add_button
.setEnabled
)
388 qtutils
.connect_button(self
.add_button
, self
.accept
)
389 qtutils
.connect_button(self
.close_button
, self
.reject
)
391 def set_name(self
, value
):
392 self
.widget
.name
= value
394 def set_url(self
, value
):
395 self
.widget
.url
= value
397 name
= property(operator
.attrgetter('widget.name'), set_name
)
398 url
= property(operator
.attrgetter('widget.url'), set_url
)
403 return self
.exec_() == QtWidgets
.QDialog
.Accepted
406 def lineedit(context
, hint
):
407 """Create a HintedLineEdit with a preset minimum width"""
408 widget
= text
.HintedLineEdit(context
, hint
)
409 width
= qtutils
.text_width(widget
.font(), 'M')
410 widget
.setMinimumWidth(width
* 32)
414 class RemoteWidget(QtWidgets
.QWidget
):
416 lambda self
: get(self
.remote_name
),
417 lambda self
, value
: self
.remote_name
.set_value(value
),
420 lambda self
: get(self
.remote_url
),
421 lambda self
, value
: self
.remote_url
.set_value(value
),
425 def __init__(self
, context
, parent
, readonly_url
=False):
426 super().__init
__(parent
)
427 self
.setWindowModality(Qt
.WindowModal
)
428 self
.context
= context
429 self
.setWindowTitle(N_('Add remote'))
430 self
.remote_name
= lineedit(context
, N_('Name for the new remote'))
431 self
.remote_url
= lineedit(context
, 'git://git.example.com/repo.git')
432 self
.open_button
= qtutils
.create_button(
433 text
=N_('Browse...'), icon
=icons
.folder(), tooltip
=N_('Select repository')
436 self
.url_layout
= qtutils
.hbox(
437 defs
.no_margin
, defs
.spacing
, self
.remote_url
, self
.open_button
440 validate_remote
= completion
.RemoteValidator(self
.remote_name
)
441 self
.remote_name
.setValidator(validate_remote
)
443 self
._form
= qtutils
.form(
446 (N_('Name'), self
.remote_name
),
447 (N_('URL'), self
.url_layout
),
450 self
._layout
= qtutils
.vbox(defs
.margin
, defs
.spacing
, self
._form
)
451 self
.setLayout(self
._layout
)
453 self
.remote_name
.textChanged
.connect(self
.validate
)
454 self
.remote_url
.textChanged
.connect(self
.validate
)
455 qtutils
.connect_button(self
.open_button
, self
.open_repo
)
458 self
.remote_url
.setReadOnly(True)
459 self
.open_button
.setEnabled(False)
461 def validate(self
, _text
):
464 self
.valid
.emit(bool(name
) and bool(url
))
467 git
= self
.context
.git
468 repo
= qtutils
.opendir_dialog(N_('Open Git Repository'), core
.getcwd())
469 if repo
and git
.is_git_repository(repo
):