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 # pylint: disable=no-member
136 self
.editor
.remote_name
.returnPressed
.connect(self
.save
)
137 self
.editor
.remote_url
.returnPressed
.connect(self
.save
)
138 self
.editor
.valid
.connect(self
.editor_valid
)
140 self
.remotes
.itemChanged
.connect(self
.remote_item_renamed
)
141 self
.remotes
.itemSelectionChanged
.connect(self
.selection_changed
)
143 self
.disable_editor()
144 self
.init_state(None, self
.resize_widget
, parent
)
145 self
.remotes
.setFocus(Qt
.OtherFocusReason
)
149 focus
= self
.focusWidget()
150 if self
.current_name
:
151 self
.activate_remote(self
.current_name
, gather_info
=False)
156 url
= self
.editor
.url
157 name
= self
.editor
.name
158 return url
!= self
.current_url
or name
!= self
.current_name
163 context
= self
.context
164 name
= self
.editor
.name
165 url
= self
.editor
.url
166 old_url
= self
.current_url
167 old_name
= self
.current_name
169 name_changed
= name
and name
!= old_name
170 url_changed
= url
and url
!= old_url
171 focus
= self
.focusWidget()
173 name_ok
= url_ok
= False
175 # Run the corresponding command
176 if name_changed
and url_changed
:
177 name_ok
, url_ok
= cmds
.do(cmds
.RemoteEdit
, context
, old_name
, name
, url
)
179 result
= cmds
.do(cmds
.RemoteRename
, context
, old_name
, name
)
182 result
= cmds
.do(cmds
.RemoteSetURL
, context
, name
, url
)
185 # Update state if the change succeeded
187 if url_changed
and url_ok
:
188 self
.current_url
= url
191 # A name change requires a refresh
192 if name_changed
and name_ok
:
193 self
.current_name
= name
195 self
.refresh(select
=False)
196 remotes
= utils
.Sequence(self
.remote_list
)
197 idx
= remotes
.index(name
)
198 self
.select_remote(idx
)
199 gather
= False # already done by select_remote()
205 def editor_valid(self
, valid
):
206 changed
= self
.changed
207 self
.reset_button
.setEnabled(changed
)
208 self
.save_button
.setEnabled(changed
and valid
)
210 def disable_editor(self
):
211 self
.save_button
.setEnabled(False)
212 self
.reset_button
.setEnabled(False)
213 self
.editor
.setEnabled(False)
214 self
.editor
.name
= ''
216 self
.info
.hint
.set_value(self
.default_hint
)
217 self
.info
.set_value('')
219 def resize_widget(self
, parent
):
220 """Set the initial size of the widget"""
221 width
, height
= qtutils
.default_size(parent
, 720, 445)
222 self
.resize(width
, height
)
224 def set_info(self
, info
):
225 self
.info
.hint
.set_value(self
.default_hint
)
226 self
.info
.set_value(info
)
228 def select_remote(self
, index
):
230 item
= self
.remotes
.item(index
)
232 item
.setSelected(True)
234 def refresh(self
, select
=True):
235 git
= self
.context
.git
236 remotes
= git
.remote()[STDOUT
].splitlines()
237 # Ignore notifications from self.remotes while mutating.
238 with qtutils
.BlockSignals(self
.remotes
):
240 self
.remotes
.addItems(remotes
)
241 self
.remote_list
= remotes
243 for idx
in range(len(remotes
)):
244 item
= self
.remotes
.item(idx
)
245 item
.setFlags(item
.flags() | Qt
.ItemIsEditable
)
248 if not self
.current_name
and remotes
:
249 # Nothing is selected; select the first item
250 self
.select_remote(0)
251 elif self
.current_name
and remotes
:
252 # Reselect the previously selected item
253 remote_seq
= utils
.Sequence(remotes
)
254 idx
= remote_seq
.index(self
.current_name
)
256 item
= self
.remotes
.item(idx
)
258 item
.setSelected(True)
261 if add_remote(self
.context
, self
):
262 self
.refresh(select
=False)
263 # Newly added remote will be last; select it
264 self
.select_remote(len(self
.remote_list
) - 1)
267 remote
= qtutils
.selected_item(self
.remotes
, self
.remote_list
)
270 cmds
.do(cmds
.RemoteRemove
, self
.context
, remote
)
271 self
.refresh(select
=False)
273 def remote_item_renamed(self
, item
):
274 idx
= self
.remotes
.row(item
)
277 if idx
>= len(self
.remote_list
):
279 old_name
= self
.remote_list
[idx
]
280 new_name
= item
.text()
281 if new_name
== old_name
:
284 item
.setText(old_name
)
286 context
= self
.context
287 ok
, status
, _
, _
= cmds
.do(cmds
.RemoteRename
, context
, old_name
, new_name
)
288 if ok
and status
== 0:
289 self
.remote_list
[idx
] = new_name
290 self
.activate_remote(new_name
)
292 item
.setText(old_name
)
294 def selection_changed(self
):
295 remote
= qtutils
.selected_item(self
.remotes
, self
.remote_list
)
297 self
.disable_editor()
299 self
.activate_remote(remote
)
301 def activate_remote(self
, name
, gather_info
=True):
302 url
= gitcmds
.remote_url(self
.context
, name
)
303 self
.current_name
= name
304 self
.current_url
= url
306 self
.editor
.setEnabled(True)
307 self
.editor
.name
= name
308 self
.editor
.url
= url
313 def gather_info(self
):
314 name
= self
.current_name
315 self
.info
.hint
.set_value(N_('Gathering info for "%s"...') % name
)
316 self
.info
.set_value('')
318 self
.info_thread
.remote
= name
319 self
.info_thread
.start()
322 def add_remote(context
, parent
, name
='', url
='', readonly_url
=False):
323 """Bring up the "Add Remote" dialog"""
324 widget
= AddRemoteDialog(context
, parent
, readonly_url
=readonly_url
)
330 cmds
.do(cmds
.RemoteAdd
, context
, widget
.name
, widget
.url
)
337 def restore_focus(focus
):
339 focus
.setFocus(Qt
.OtherFocusReason
)
340 if hasattr(focus
, 'selectAll'):
344 class RemoteInfoThread(QtCore
.QThread
):
345 result
= Signal(object)
347 def __init__(self
, context
, parent
):
348 QtCore
.QThread
.__init
__(self
, parent
)
349 self
.context
= context
356 git
= self
.context
.git
357 _
, out
, err
= git
.remote('show', '-n', remote
)
358 self
.result
.emit(out
+ err
)
361 class AddRemoteDialog(QtWidgets
.QDialog
):
362 def __init__(self
, context
, parent
, readonly_url
=False):
363 super().__init
__(parent
)
364 self
.context
= context
366 self
.setWindowModality(Qt
.WindowModal
)
368 self
.context
= context
369 self
.widget
= RemoteWidget(context
, self
, readonly_url
=readonly_url
)
370 self
.add_button
= qtutils
.create_button(
371 text
=N_('Add Remote'), icon
=icons
.ok(), enabled
=False
373 self
.close_button
= qtutils
.close_button()
375 self
._button
_layout
= qtutils
.hbox(
383 self
._layout
= qtutils
.vbox(
384 defs
.margin
, defs
.spacing
, self
.widget
, self
._button
_layout
386 self
.setLayout(self
._layout
)
388 self
.widget
.valid
.connect(self
.add_button
.setEnabled
)
389 qtutils
.connect_button(self
.add_button
, self
.accept
)
390 qtutils
.connect_button(self
.close_button
, self
.reject
)
392 def set_name(self
, value
):
393 self
.widget
.name
= value
395 def set_url(self
, value
):
396 self
.widget
.url
= value
398 name
= property(operator
.attrgetter('widget.name'), set_name
)
399 url
= property(operator
.attrgetter('widget.url'), set_url
)
404 return self
.exec_() == QtWidgets
.QDialog
.Accepted
407 def lineedit(context
, hint
):
408 """Create a HintedLineEdit with a preset minimum width"""
409 widget
= text
.HintedLineEdit(context
, hint
)
410 width
= qtutils
.text_width(widget
.font(), 'M')
411 widget
.setMinimumWidth(width
* 32)
415 class RemoteWidget(QtWidgets
.QWidget
):
417 lambda self
: get(self
.remote_name
),
418 lambda self
, value
: self
.remote_name
.set_value(value
),
421 lambda self
: get(self
.remote_url
),
422 lambda self
, value
: self
.remote_url
.set_value(value
),
426 def __init__(self
, context
, parent
, readonly_url
=False):
427 super().__init
__(parent
)
428 self
.setWindowModality(Qt
.WindowModal
)
429 self
.context
= context
430 self
.setWindowTitle(N_('Add remote'))
431 self
.remote_name
= lineedit(context
, N_('Name for the new remote'))
432 self
.remote_url
= lineedit(context
, 'git://git.example.com/repo.git')
433 self
.open_button
= qtutils
.create_button(
434 text
=N_('Browse...'), icon
=icons
.folder(), tooltip
=N_('Select repository')
437 self
.url_layout
= qtutils
.hbox(
438 defs
.no_margin
, defs
.spacing
, self
.remote_url
, self
.open_button
441 validate_remote
= completion
.RemoteValidator(self
.remote_name
)
442 self
.remote_name
.setValidator(validate_remote
)
444 self
._form
= qtutils
.form(
447 (N_('Name'), self
.remote_name
),
448 (N_('URL'), self
.url_layout
),
451 self
._layout
= qtutils
.vbox(defs
.margin
, defs
.spacing
, self
._form
)
452 self
.setLayout(self
._layout
)
454 # pylint: disable=no-member
455 self
.remote_name
.textChanged
.connect(self
.validate
)
456 self
.remote_url
.textChanged
.connect(self
.validate
)
457 qtutils
.connect_button(self
.open_button
, self
.open_repo
)
460 self
.remote_url
.setReadOnly(True)
461 self
.open_button
.setEnabled(False)
463 def validate(self
, _text
):
466 self
.valid
.emit(bool(name
) and bool(url
))
469 git
= self
.context
.git
470 repo
= qtutils
.opendir_dialog(N_('Open Git Repository'), core
.getcwd())
471 if repo
and git
.is_git_repository(repo
):