1 """The startup dialog is presented when no repositories can be found at startup"""
3 from qtpy
.QtCore
import Qt
4 from qtpy
import QtCore
6 from qtpy
import QtWidgets
9 from ..models
import prefs
12 from .. import display
13 from .. import guicmds
14 from .. import hotkeys
16 from .. import qtutils
18 from .. import version
21 from . import standard
28 class StartupDialog(standard
.Dialog
):
29 """Provides a GUI to Open or Clone a git repository."""
31 def __init__(self
, context
, parent
=None):
32 standard
.Dialog
.__init
__(self
, parent
)
33 self
.context
= context
34 self
.setWindowTitle(N_('git-cola'))
37 logo_pixmap
= icons
.cola().pixmap(defs
.huge_icon
, defs
.huge_icon
)
39 self
.logo_label
= QtWidgets
.QLabel()
40 self
.logo_label
.setPixmap(logo_pixmap
)
41 self
.logo_label
.setAlignment(Qt
.AlignCenter
)
43 self
.logo_text_label
= qtutils
.label(text
=version
.cola_version())
44 self
.logo_text_label
.setAlignment(Qt
.AlignCenter
)
48 self
.runtask
= context
.runtask
50 self
.runtask
= context
.runtask
= qtutils
.RunTask(parent
=self
)
52 self
.new_button
= qtutils
.create_button(text
=N_('New...'), icon
=icons
.new())
53 self
.open_button
= qtutils
.create_button(
54 text
=N_('Open Git Repository'), icon
=icons
.folder()
56 self
.clone_button
= qtutils
.create_button(
57 text
=N_('Clone...'), icon
=icons
.cola()
59 self
.close_button
= qtutils
.close_button()
61 self
.bookmarks_model
= bookmarks_model
= QtGui
.QStandardItemModel()
62 self
.items
= items
= []
64 item
= QtGui
.QStandardItem(N_('Browse...'))
65 item
.setEditable(False)
66 item
.setIcon(icons
.open_directory())
67 bookmarks_model
.appendRow(item
)
69 # The tab bar allows choosing between Folder and List mode
70 self
.tab_bar
= QtWidgets
.QTabBar()
71 self
.tab_bar
.setMovable(False)
72 self
.tab_bar
.addTab(icons
.directory(), N_('Folder'))
73 self
.tab_bar
.addTab(icons
.three_bars(), N_('List'))
75 # Bookmarks/"Favorites" and Recent are lists of {name,path: str}
76 normalize
= display
.normalize_path
77 settings
= context
.settings
78 all_repos
= get_all_repos(self
.context
, settings
)
81 builder
= BuildItem(self
.context
)
82 default_view_mode
= ICON_MODE
83 for repo
, is_bookmark
in all_repos
:
84 path
= normalize(repo
['path'])
85 name
= normalize(repo
['name'])
90 item
= builder
.get(path
, name
, default_view_mode
, is_bookmark
)
91 bookmarks_model
.appendRow(item
)
94 self
.bookmarks
= BookmarksListView(
97 self
.open_selected_bookmark
,
101 self
.tab_layout
= qtutils
.vbox(
102 defs
.no_margin
, defs
.no_spacing
, self
.tab_bar
, self
.bookmarks
105 self
.logo_layout
= qtutils
.vbox(
109 self
.logo_text_label
,
114 self
.button_layout
= qtutils
.hbox(
124 self
.main_layout
= qtutils
.grid(defs
.margin
, defs
.spacing
)
125 self
.main_layout
.addItem(self
.logo_layout
, 1, 1)
126 self
.main_layout
.addItem(self
.tab_layout
, 1, 2)
127 self
.main_layout
.addItem(self
.button_layout
, 2, 1, columnSpan
=2)
128 self
.setLayout(self
.main_layout
)
130 qtutils
.connect_button(self
.open_button
, self
.open_repo
)
131 qtutils
.connect_button(self
.clone_button
, self
.clone_repo
)
132 qtutils
.connect_button(self
.new_button
, self
.new_repo
)
133 qtutils
.connect_button(self
.close_button
, self
.reject
)
135 # pylint: disable=no-member
136 self
.tab_bar
.currentChanged
.connect(self
.tab_changed
)
138 self
.init_state(settings
, self
.resize_widget
)
139 self
.setFocusProxy(self
.bookmarks
)
140 self
.bookmarks
.setFocus()
142 # Update the list mode
143 list_mode
= context
.cfg
.get('cola.startupmode', default
='folder')
144 self
.list_mode
= list_mode
145 if list_mode
== 'list':
146 self
.tab_bar
.setCurrentIndex(1)
148 def tab_changed(self
, idx
):
149 bookmarks
= self
.bookmarks
151 mode
= QtWidgets
.QListView
.IconMode
152 icon_size
= make_size(defs
.medium_icon
)
153 grid_size
= make_size(defs
.large_icon
)
155 view_mode
= ICON_MODE
156 rename_enabled
= True
158 mode
= QtWidgets
.QListView
.ListMode
159 icon_size
= make_size(defs
.default_icon
)
160 grid_size
= QtCore
.QSize()
162 view_mode
= LIST_MODE
163 rename_enabled
= False
165 self
.bookmarks
.set_rename_enabled(rename_enabled
)
166 self
.bookmarks
.set_view_mode(view_mode
)
168 bookmarks
.setViewMode(mode
)
169 bookmarks
.setIconSize(icon_size
)
170 bookmarks
.setGridSize(grid_size
)
173 builder
= BuildItem(self
.context
)
174 for item
in self
.items
:
175 if isinstance(item
, PromptWidgetItem
):
176 item
= builder
.get(item
.path
, item
.name
, view_mode
, item
.is_bookmark
)
177 new_items
.append(item
)
179 self
.set_model(new_items
)
181 if list_mode
!= self
.list_mode
:
182 self
.list_mode
= list_mode
183 self
.context
.cfg
.set_user('cola.startupmode', list_mode
)
185 def resize_widget(self
):
186 width
, height
= qtutils
.desktop_size()
194 def find_git_repo(self
):
196 Return a path to a git repository
198 This is the entry point for external callers.
199 This method finds a git repository by allowing the
200 user to browse to one on the filesystem or by creating
201 a new one with git-clone.
206 if self
.exec_() == QtWidgets
.QDialog
.Accepted
:
211 self
.repodir
= self
.get_selected_bookmark()
213 self
.repodir
= qtutils
.opendir_dialog(
214 N_('Open Git Repository'), core
.getcwd()
219 def clone_repo(self
):
220 context
= self
.context
221 progress
= standard
.progress('', '', self
)
222 clone
.clone_repo(context
, True, progress
, self
.clone_repo_done
, False)
224 def clone_repo_done(self
, task
):
225 if task
.cmd
and task
.cmd
.status
== 0:
226 self
.repodir
= task
.destdir
229 clone
.task_finished(task
)
232 context
= self
.context
233 repodir
= guicmds
.new_repo(context
)
235 self
.repodir
= repodir
238 def open_selected_bookmark(self
):
239 selected
= self
.bookmarks
.selectedIndexes()
241 self
.open_bookmark(selected
[0])
243 def open_bookmark(self
, index
):
247 self
.repodir
= self
.bookmarks_model
.data(index
, Qt
.UserRole
)
250 if not core
.exists(self
.repodir
):
251 self
.handle_broken_repo(index
)
255 def handle_broken_repo(self
, index
):
256 settings
= self
.context
.settings
257 all_repos
= get_all_repos(self
.context
, settings
)
259 repodir
= self
.bookmarks_model
.data(index
, Qt
.UserRole
)
260 repo
= next(repo
for repo
, is_bookmark
in all_repos
if repo
['path'] == repodir
)
261 title
= N_('Repository Not Found')
262 text
= N_('%s could not be opened. Remove from bookmarks?') % repo
['path']
263 logo
= icons
.from_style(QtWidgets
.QStyle
.SP_MessageBoxWarning
)
264 if standard
.question(title
, text
, N_('Remove'), logo
=logo
):
265 self
.context
.settings
.remove_bookmark(repo
['path'], repo
['name'])
266 self
.context
.settings
.remove_recent(repo
['path'])
267 self
.context
.settings
.save()
269 item
= self
.bookmarks_model
.item(index
.row())
270 self
.items
.remove(item
)
271 self
.bookmarks_model
.removeRow(index
.row())
273 def get_selected_bookmark(self
):
274 selected
= self
.bookmarks
.selectedIndexes()
275 if selected
and selected
[0].row() != 0:
276 return self
.bookmarks_model
.data(selected
[0], Qt
.UserRole
)
279 def set_model(self
, items
):
280 bookmarks_model
= self
.bookmarks_model
281 self
.items
= new_items
= []
282 bookmarks_model
.clear()
284 item
= QtGui
.QStandardItem(N_('Browse...'))
285 item
.setEditable(False)
286 item
.setIcon(icons
.open_directory())
287 bookmarks_model
.appendRow(item
)
290 bookmarks_model
.appendRow(item
)
291 new_items
.append(item
)
294 def get_all_repos(context
, settings
):
295 """Return a sorted list of bookmarks and recent repositorties"""
296 bookmarks
= settings
.bookmarks
297 recent
= settings
.recent
298 all_repos
= [(repo
, True) for repo
in bookmarks
] + [
299 (repo
, False) for repo
in recent
301 if prefs
.sort_bookmarks(context
):
302 all_repos
.sort(key
=lambda details
: details
[0]['path'].lower())
306 class BookmarksListView(QtWidgets
.QListView
):
308 List view class implementation of QWidgets.QListView for bookmarks and recent repos.
309 Almost methods is comes from `cola/widgets/bookmarks.py`.
312 def __init__(self
, context
, model
, open_selected_repo
, set_model
, parent
=None):
313 super().__init
__(parent
)
315 self
.current_mode
= ICON_MODE
316 self
.context
= context
317 self
.open_selected_repo
= open_selected_repo
318 self
.set_model
= set_model
320 self
.setEditTriggers(self
.SelectedClicked
)
322 self
.activated
.connect(self
.open_selected_repo
)
325 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
326 self
.setViewMode(QtWidgets
.QListView
.IconMode
)
327 self
.setResizeMode(QtWidgets
.QListView
.Adjust
)
328 self
.setGridSize(make_size(defs
.large_icon
))
329 self
.setIconSize(make_size(defs
.medium_icon
))
330 self
.setDragEnabled(False)
331 self
.setWordWrap(True)
334 self
.open_action
= qtutils
.add_action(
335 self
, N_('Open'), self
.open_selected_repo
, hotkeys
.OPEN
338 self
.accept_action
= qtutils
.add_action(
339 self
, N_('Accept'), self
.accept_repo
, *hotkeys
.ACCEPT
342 self
.open_new_action
= qtutils
.add_action(
343 self
, N_('Open in New Window'), self
.open_new_repo
, hotkeys
.NEW
346 self
.set_default_repo_action
= qtutils
.add_action(
347 self
, N_('Set Default Repository'), self
.set_default_repo
350 self
.clear_default_repo_action
= qtutils
.add_action(
351 self
, N_('Clear Default Repository'), self
.clear_default_repo
354 self
.rename_repo_action
= qtutils
.add_action(
355 self
, N_('Rename Repository'), self
.rename_repo
358 self
.open_default_action
= qtutils
.add_action(
359 self
, cmds
.OpenDefaultApp
.name(), self
.open_default
, hotkeys
.PRIMARY_ACTION
362 self
.launch_editor_action
= qtutils
.add_action(
363 self
, cmds
.Edit
.name(), self
.launch_editor
, hotkeys
.EDIT
366 self
.launch_terminal_action
= qtutils
.add_action(
367 self
, cmds
.LaunchTerminal
.name(), self
.launch_terminal
, hotkeys
.TERMINAL
370 self
.copy_action
= qtutils
.add_action(self
, N_('Copy'), self
.copy
, hotkeys
.COPY
)
372 self
.delete_action
= qtutils
.add_action(self
, N_('Delete'), self
.delete_item
)
374 self
.remove_missing_action
= qtutils
.add_action(
375 self
, N_('Prune Missing Entries'), self
.remove_missing
377 self
.remove_missing_action
.setToolTip(
378 N_('Remove stale entries for repositories that no longer exist')
381 # pylint: disable=no-member
382 self
.model().itemChanged
.connect(self
.item_changed
)
384 self
.action_group
= utils
.Group(
386 self
.open_new_action
,
388 self
.launch_editor_action
,
389 self
.launch_terminal_action
,
390 self
.open_default_action
,
391 self
.rename_repo_action
,
394 self
.action_group
.setEnabled(True)
395 self
.set_default_repo_action
.setEnabled(True)
396 self
.clear_default_repo_action
.setEnabled(True)
398 def set_rename_enabled(self
, is_enabled
):
399 self
.rename_repo_action
.setEnabled(is_enabled
)
401 def set_view_mode(self
, view_mode
):
402 self
.current_mode
= view_mode
404 def selected_item(self
):
405 index
= self
.currentIndex()
406 return self
.model().itemFromIndex(index
)
409 self
.model().layoutChanged
.emit()
410 context
= self
.context
411 settings
= context
.settings
412 builder
= BuildItem(context
)
413 normalize
= display
.normalize_path
417 all_repos
= get_all_repos(self
.context
, settings
)
418 for repo
, is_bookmark
in all_repos
:
419 path
= normalize(repo
['path'])
420 name
= normalize(repo
['name'])
425 item
= builder
.get(path
, name
, self
.current_mode
, is_bookmark
)
428 self
.set_model(items
)
430 def contextMenuEvent(self
, event
):
431 """Configures prompt's context menu."""
432 item
= self
.selected_item()
434 if isinstance(item
, PromptWidgetItem
):
435 menu
= qtutils
.create_menu(N_('Actions'), self
)
436 menu
.addAction(self
.open_action
)
437 menu
.addAction(self
.open_new_action
)
438 menu
.addAction(self
.open_default_action
)
440 menu
.addAction(self
.copy_action
)
441 menu
.addAction(self
.launch_editor_action
)
442 menu
.addAction(self
.launch_terminal_action
)
444 if item
and item
.is_default
:
445 menu
.addAction(self
.clear_default_repo_action
)
447 menu
.addAction(self
.set_default_repo_action
)
448 menu
.addAction(self
.rename_repo_action
)
450 menu
.addAction(self
.delete_action
)
451 menu
.addAction(self
.remove_missing_action
)
452 menu
.exec_(self
.mapToGlobal(event
.pos()))
454 def item_changed(self
, item
):
455 self
.rename_entry(item
, item
.text())
457 def rename_entry(self
, item
, new_name
):
458 settings
= self
.context
.settings
460 rename
= settings
.rename_bookmark
462 rename
= settings
.rename_recent
464 if rename(item
.path
, item
.name
, new_name
):
468 item
.setText(item
.name
)
470 def apply_func(self
, func
, *args
, **kwargs
):
471 item
= self
.selected_item()
473 func(item
, *args
, **kwargs
)
476 self
.apply_func(lambda item
: qtutils
.set_clipboard(item
.path
))
478 def open_default(self
):
479 context
= self
.context
480 self
.apply_func(lambda item
: cmds
.do(cmds
.OpenDefaultApp
, context
, [item
.path
]))
482 def set_default_repo(self
):
483 self
.apply_func(self
.set_default_item
)
485 def set_default_item(self
, item
):
486 context
= self
.context
487 cmds
.do(cmds
.SetDefaultRepo
, context
, item
.path
)
490 def clear_default_repo(self
):
491 self
.apply_func(self
.clear_default_item
)
493 def clear_default_item(self
, _item
):
494 context
= self
.context
495 cmds
.do(cmds
.SetDefaultRepo
, context
, None)
498 def rename_repo(self
):
499 index
= self
.currentIndex()
502 def accept_repo(self
):
503 self
.apply_func(self
.accept_item
)
505 def accept_item(self
, _item
):
506 if self
.state() & self
.EditingState
:
507 current_index
= self
.currentIndex()
508 widget
= self
.indexWidget(current_index
)
510 self
.commitData(widget
)
511 self
.closePersistentEditor(current_index
)
514 self
.open_selected_repo()
516 def open_new_repo(self
):
517 context
= self
.context
518 self
.apply_func(lambda item
: cmds
.do(cmds
.OpenNewRepo
, context
, item
.path
))
520 def launch_editor(self
):
521 context
= self
.context
522 self
.apply_func(lambda item
: cmds
.do(cmds
.Edit
, context
, [item
.path
]))
524 def launch_terminal(self
):
525 context
= self
.context
526 self
.apply_func(lambda item
: cmds
.do(cmds
.LaunchTerminal
, context
, item
.path
))
528 def delete_item(self
):
529 """Remove the selected repo item
531 If the item comes from bookmarks (item.is_bookmark) then delete the item
532 from the Bookmarks list, otherwise delete it from the Recents list.
534 item
= self
.selected_item()
539 cmd
= cmds
.RemoveBookmark
541 cmd
= cmds
.RemoveRecent
542 context
= self
.context
543 ok
, _
, _
, _
= cmds
.do(cmd
, context
, item
.path
, item
.name
, icon
=icons
.discard())
547 def remove_missing(self
):
548 """Remove missing entries from the favorites/recent file list"""
549 settings
= self
.context
.settings
550 settings
.remove_missing_bookmarks()
551 settings
.remove_missing_recent()
556 def __init__(self
, context
):
557 self
.star_icon
= icons
.star()
558 self
.folder_icon
= icons
.folder()
560 self
.default_repo
= cfg
.get('cola.defaultrepo')
562 def get(self
, path
, name
, mode
, is_bookmark
):
563 is_default
= self
.default_repo
== path
565 icon
= self
.star_icon
567 icon
= self
.folder_icon
568 return PromptWidgetItem(path
, name
, mode
, icon
, is_default
, is_bookmark
)
571 class PromptWidgetItem(QtGui
.QStandardItem
):
572 def __init__(self
, path
, name
, mode
, icon
, is_default
, is_bookmark
):
573 QtGui
.QStandardItem
.__init
__(self
, icon
, name
)
577 self
.is_default
= is_default
578 self
.is_bookmark
= is_bookmark
579 editable
= mode
== ICON_MODE
581 if self
.mode
== ICON_MODE
:
582 item_text
= self
.name
584 item_text
= self
.path
586 user_role
= Qt
.UserRole
587 self
.setEditable(editable
)
588 self
.setData(path
, user_role
)
590 self
.setText(item_text
)
591 self
.setToolTip(path
)
595 """Construct a QSize from a single value"""
596 return QtCore
.QSize(size
, size
)