widgets: do not set window modality without a parent
[git-cola.git] / cola / widgets / editremotes.py
blob1dc872eb99ef5371b7363bd75f68d79960b1357a
1 from PyQt4 import QtCore
2 from PyQt4 import QtGui
3 from PyQt4.QtCore import Qt
4 from PyQt4.QtCore import SIGNAL
6 from cola import qtutils
7 from cola.git import git
8 from cola.git import STDOUT
9 from cola.i18n import N_
10 from cola.models import main
11 from cola.widgets import defs
12 from cola.widgets import text
15 def remote_editor():
16 view= new_remote_editor(parent=qtutils.active_window())
17 view.show()
18 view.raise_()
19 return view
22 def new_remote_editor(parent=None):
23 return RemoteEditor(parent=parent)
26 class RemoteEditor(QtGui.QDialog):
27 def __init__(self, parent=None):
28 QtGui.QDialog.__init__(self, parent)
30 self.setWindowTitle(N_('Edit Remotes'))
31 if parent is not None:
32 self.setWindowModality(Qt.WindowModal)
34 self.default_hint = N_(''
35 'Add and remove remote repositories using the \n'
36 'Add(+) and Delete(-) buttons on the left-hand side.\n'
37 '\n'
38 'Remotes can be renamed by selecting one from the list\n'
39 'and pressing "enter", or by double-clicking.')
41 self.remote_list = []
42 self.remotes = QtGui.QListWidget()
43 self.remotes.setToolTip(N_(
44 'Remote git repositories - double-click to rename'))
46 self.info = text.HintedTextView(self.default_hint, self)
47 font = self.info.font()
48 metrics = QtGui.QFontMetrics(font)
49 width = metrics.width('_' * 72)
50 height = metrics.height() * 13
51 self.info.setMinimumWidth(width)
52 self.info.setMinimumHeight(height)
53 self.info_thread = RemoteInfoThread(self)
55 self.add_btn = QtGui.QToolButton()
56 self.add_btn.setIcon(qtutils.icon('add.svg'))
57 self.add_btn.setToolTip(N_('Add new remote git repository'))
59 self.refresh_btn = QtGui.QToolButton()
60 self.refresh_btn.setIcon(qtutils.icon('view-refresh.svg'))
61 self.refresh_btn.setToolTip(N_('Refresh'))
63 self.delete_btn = QtGui.QToolButton()
64 self.delete_btn.setIcon(qtutils.icon('remove.svg'))
65 self.delete_btn.setToolTip(N_('Delete remote'))
67 self.close_btn = QtGui.QPushButton(N_('Close'))
69 self._top_layout = QtGui.QSplitter()
70 self._top_layout.setOrientation(Qt.Horizontal)
71 self._top_layout.setHandleWidth(defs.handle_width)
72 self._top_layout.addWidget(self.remotes)
73 self._top_layout.addWidget(self.info)
74 width = self._top_layout.width()
75 self._top_layout.setSizes([width/4, width*3/4])
77 self._button_layout = QtGui.QHBoxLayout()
78 self._button_layout.addWidget(self.add_btn)
79 self._button_layout.addWidget(self.delete_btn)
80 self._button_layout.addWidget(self.refresh_btn)
81 self._button_layout.addStretch()
82 self._button_layout.addWidget(self.close_btn)
84 self._layout = QtGui.QVBoxLayout()
85 self._layout.setMargin(defs.margin)
86 self._layout.setSpacing(defs.spacing)
87 self._layout.addWidget(self._top_layout)
88 self._layout.addLayout(self._button_layout)
89 self.setLayout(self._layout)
91 self.refresh()
93 qtutils.connect_button(self.add_btn, self.add)
94 qtutils.connect_button(self.delete_btn, self.delete)
95 qtutils.connect_button(self.refresh_btn, self.refresh)
96 qtutils.connect_button(self.close_btn, self.close)
98 self.connect(self.info_thread, SIGNAL('info'),
99 self.info.set_value)
101 self.connect(self.remotes,
102 SIGNAL('itemChanged(QListWidgetItem*)'),
103 self.remote_renamed)
105 self.connect(self.remotes, SIGNAL('itemSelectionChanged()'),
106 self.selection_changed)
108 def refresh(self):
109 remotes = git.remote()[STDOUT].splitlines()
110 self.remotes.clear()
111 self.remotes.addItems(remotes)
112 self.remote_list = remotes
113 self.info.set_hint(self.default_hint)
114 self.info.enable_hint(True)
115 for idx, r in enumerate(remotes):
116 item = self.remotes.item(idx)
117 item.setFlags(item.flags() | Qt.ItemIsEditable)
119 def add(self):
120 widget = AddRemoteWidget(self)
121 if not widget.add_remote():
122 return
123 name = widget.name.value()
124 url = widget.url.value()
125 status, out, err = git.remote('add', name, url)
126 if status != 0:
127 qtutils.critical(N_('Error creating remote "%s"') % name,
128 out + err)
129 self.refresh()
131 def delete(self):
132 remote = qtutils.selected_item(self.remotes, self.remote_list)
133 if remote is None:
134 return
136 title = N_('Delete Remote')
137 question = N_('Delete remote?')
138 info = N_('Delete remote "%s"') % remote
139 ok_btn = N_('Delete')
140 if not qtutils.confirm(title, question, info, ok_btn):
141 return
143 status, out, err = git.remote('rm', remote)
144 if status != 0:
145 qtutils.critical(N_('Error deleting remote "%s"') % remote,
146 out + err)
147 main.model().update_status()
148 self.refresh()
150 def remote_renamed(self, item):
151 idx = self.remotes.row(item)
152 if idx < 0:
153 return
154 if idx >= len(self.remote_list):
155 return
157 old_name = self.remote_list[idx]
158 new_name = unicode(item.text())
159 if new_name == old_name:
160 return
161 if not new_name:
162 item.setText(old_name)
163 return
165 title = N_('Rename Remote')
166 question = N_('Rename remote?')
167 info = (N_('Rename remote "%(current)s" to "%(new)s"?') %
168 dict(current=old_name, new=new_name))
169 ok_btn = N_('Rename')
171 if qtutils.confirm(title, question, info, ok_btn):
172 status, out, err = git.remote('rename', old_name, new_name)
173 if status == 0:
174 self.remote_list[idx] = new_name
175 else:
176 item.setText(old_name)
178 def selection_changed(self):
179 remote = qtutils.selected_item(self.remotes, self.remote_list)
180 if remote is None:
181 return
182 self.info.set_hint(N_('Gathering info for "%s"...') % remote)
183 self.info.enable_hint(True)
185 self.info_thread.remote = remote
186 self.info_thread.start()
189 class RemoteInfoThread(QtCore.QThread):
190 def __init__(self, parent):
191 QtCore.QThread.__init__(self, parent)
192 self.remote = None
194 def run(self):
195 remote = self.remote
196 if remote is None:
197 return
198 status, out, err = git.remote('show', remote)
199 # This call takes a long time and we may have selected a
200 # different remote...
201 if remote == self.remote:
202 self.emit(SIGNAL('info'), out + err)
203 else:
204 self.run()
207 class AddRemoteWidget(QtGui.QDialog):
208 def __init__(self, parent):
209 QtGui.QDialog.__init__(self, parent)
210 self.setWindowModality(Qt.WindowModal)
212 self.add_btn = QtGui.QPushButton(N_('Add Remote'))
213 self.add_btn.setIcon(qtutils.apply_icon())
215 self.cancel_btn = QtGui.QPushButton(N_('Cancel'))
217 def lineedit(hint):
218 widget = text.HintedLineEdit(hint)
219 widget.enable_hint(True)
220 metrics = QtGui.QFontMetrics(widget.font())
221 widget.setMinimumWidth(metrics.width('_' * 32))
222 return widget
224 self.setWindowTitle(N_('Add remote'))
225 self.name = lineedit(N_('Name for the new remote'))
226 self.url = lineedit('git://git.example.com/repo.git')
228 self._form = QtGui.QFormLayout()
229 self._form.setMargin(defs.margin)
230 self._form.setSpacing(defs.spacing)
231 self._form.addRow(N_('Name'), self.name)
232 self._form.addRow(N_('URL'), self.url)
234 self._btn_layout = QtGui.QHBoxLayout()
235 self._btn_layout.setMargin(0)
236 self._btn_layout.setSpacing(defs.button_spacing)
237 self._btn_layout.addStretch()
238 self._btn_layout.addWidget(self.add_btn)
239 self._btn_layout.addWidget(self.cancel_btn)
241 self._layout = QtGui.QVBoxLayout()
242 self._layout.setMargin(defs.margin)
243 self._layout.setSpacing(defs.margin)
244 self._layout.addLayout(self._form)
245 self._layout.addLayout(self._btn_layout)
246 self.setLayout(self._layout)
248 self.connect(self.name, SIGNAL('textChanged(QString)'),
249 self.validate)
251 self.connect(self.url, SIGNAL('textChanged(QString)'),
252 self.validate)
254 self.add_btn.setEnabled(False)
256 qtutils.connect_button(self.add_btn, self.accept)
257 qtutils.connect_button(self.cancel_btn, self.reject)
259 def validate(self, dummy_text):
260 name = self.name.value()
261 url = self.url.value()
262 self.add_btn.setEnabled(bool(name) and bool(url))
264 def add_remote(self):
265 self.show()
266 self.raise_()
267 return self.exec_() == QtGui.QDialog.Accepted