1 """Provides widgets related to bookmarks"""
2 from __future__
import division
, absolute_import
, unicode_literals
5 from qtpy
import QtCore
6 from qtpy
import QtWidgets
7 from qtpy
.QtCore
import Qt
8 from qtpy
.QtCore
import Signal
13 from .. import hotkeys
15 from .. import qtutils
18 from ..interaction
import Interaction
19 from ..models
import prefs
20 from ..widgets
import defs
21 from ..widgets
import standard
28 def bookmark(context
, parent
):
29 return BookmarksWidget(context
, BOOKMARKS
, parent
=parent
)
32 def recent(context
, parent
):
33 return BookmarksWidget(context
, RECENT_REPOS
, parent
=parent
)
36 class BookmarksWidget(QtWidgets
.QFrame
):
37 def __init__(self
, context
, style
=BOOKMARKS
, parent
=None):
38 QtWidgets
.QFrame
.__init
__(self
, parent
)
40 self
.context
= context
42 self
.tree
= BookmarksTreeWidget(context
, style
, parent
=self
)
44 self
.add_button
= qtutils
.create_action_button(
45 tooltip
=N_('Add'), icon
=icons
.add()
48 self
.delete_button
= qtutils
.create_action_button(
49 tooltip
=N_('Delete'), icon
=icons
.remove()
52 self
.open_button
= qtutils
.create_action_button(
53 tooltip
=N_('Open'), icon
=icons
.repo()
56 self
.button_group
= utils
.Group(self
.delete_button
, self
.open_button
)
57 self
.button_group
.setEnabled(False)
59 self
.setFocusProxy(self
.tree
)
60 if style
== BOOKMARKS
:
61 self
.setToolTip(N_('Favorite repositories'))
62 elif style
== RECENT_REPOS
:
63 self
.setToolTip(N_('Recent repositories'))
64 self
.add_button
.hide()
66 self
.button_layout
= qtutils
.hbox(
74 self
.main_layout
= qtutils
.vbox(defs
.no_margin
, defs
.spacing
, self
.tree
)
75 self
.setLayout(self
.main_layout
)
77 self
.corner_widget
= QtWidgets
.QWidget(self
)
78 self
.corner_widget
.setLayout(self
.button_layout
)
79 titlebar
= parent
.titleBarWidget()
80 titlebar
.add_corner_widget(self
.corner_widget
)
82 qtutils
.connect_button(self
.add_button
, self
.tree
.add_bookmark
)
83 qtutils
.connect_button(self
.delete_button
, self
.tree
.delete_bookmark
)
84 qtutils
.connect_button(self
.open_button
, self
.tree
.open_repo
)
86 item_selection_changed
= self
.tree_item_selection_changed
87 # pylint: disable=no-member
88 self
.tree
.itemSelectionChanged
.connect(item_selection_changed
)
90 QtCore
.QTimer
.singleShot(0, self
.reload_bookmarks
)
92 def reload_bookmarks(self
):
93 # Called once after the GUI is initialized
96 def tree_item_selection_changed(self
):
97 enabled
= bool(self
.tree
.selected_item())
98 self
.button_group
.setEnabled(enabled
)
100 def connect_to(self
, other
):
101 self
.tree
.default_changed
.connect(other
.tree
.refresh
)
102 other
.tree
.default_changed
.connect(self
.tree
.refresh
)
105 def disable_rename(_path
, _name
, _new_name
):
109 # pylint: disable=too-many-ancestors
110 class BookmarksTreeWidget(standard
.TreeWidget
):
111 default_changed
= Signal()
112 worktree_changed
= Signal()
114 def __init__(self
, context
, style
, parent
=None):
115 standard
.TreeWidget
.__init
__(self
, parent
=parent
)
116 self
.context
= context
119 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
120 self
.setHeaderHidden(True)
122 # We make the items editable, but we don't want the double-click
123 # behavior to trigger editing. Make it behave like Mac OS X's Finder.
124 self
.setEditTriggers(self
.SelectedClicked
)
126 self
.open_action
= qtutils
.add_action(
127 self
, N_('Open'), self
.open_repo
, hotkeys
.OPEN
130 self
.accept_action
= qtutils
.add_action(
131 self
, N_('Accept'), self
.accept_repo
, *hotkeys
.ACCEPT
134 self
.open_new_action
= qtutils
.add_action(
135 self
, N_('Open in New Window'), self
.open_new_repo
, hotkeys
.NEW
138 self
.set_default_repo_action
= qtutils
.add_action(
139 self
, N_('Set Default Repository'), self
.set_default_repo
142 self
.clear_default_repo_action
= qtutils
.add_action(
143 self
, N_('Clear Default Repository'), self
.clear_default_repo
146 self
.rename_repo_action
= qtutils
.add_action(
147 self
, N_('Rename Repository'), self
.rename_repo
150 self
.open_default_action
= qtutils
.add_action(
151 self
, cmds
.OpenDefaultApp
.name(), self
.open_default
, hotkeys
.PRIMARY_ACTION
154 self
.launch_editor_action
= qtutils
.add_action(
155 self
, cmds
.Edit
.name(), self
.launch_editor
, hotkeys
.EDIT
158 self
.launch_terminal_action
= qtutils
.add_action(
159 self
, cmds
.LaunchTerminal
.name(), self
.launch_terminal
, hotkeys
.TERMINAL
162 self
.copy_action
= qtutils
.add_action(self
, N_('Copy'), self
.copy
, hotkeys
.COPY
)
164 self
.delete_action
= qtutils
.add_action(
165 self
, N_('Delete'), self
.delete_bookmark
168 self
.remove_missing_action
= qtutils
.add_action(
169 self
, N_('Prune Missing Entries'), self
.remove_missing
171 self
.remove_missing_action
.setToolTip(
172 N_('Remove stale entries for repositories that no longer exist')
175 # pylint: disable=no-member
176 self
.itemChanged
.connect(self
.item_changed
)
177 self
.itemSelectionChanged
.connect(self
.item_selection_changed
)
178 self
.itemDoubleClicked
.connect(self
.tree_double_clicked
)
180 self
.action_group
= utils
.Group(
182 self
.open_new_action
,
184 self
.launch_editor_action
,
185 self
.launch_terminal_action
,
186 self
.open_default_action
,
187 self
.rename_repo_action
,
190 self
.action_group
.setEnabled(False)
191 self
.set_default_repo_action
.setEnabled(False)
192 self
.clear_default_repo_action
.setEnabled(False)
195 if style
== RECENT_REPOS
:
196 self
.worktree_changed
.connect(self
.refresh
, type=Qt
.QueuedConnection
)
197 context
.model
.add_observer(
198 context
.model
.message_worktree_changed
, self
.worktree_changed
.emit
202 context
= self
.context
203 settings
= context
.settings
204 builder
= BuildItem(context
)
207 if self
.style
== BOOKMARKS
:
208 entries
= settings
.bookmarks
210 elif self
.style
== RECENT_REPOS
:
211 entries
= settings
.recent
213 items
= [builder
.get(entry
['path'], entry
['name']) for entry
in entries
]
214 if self
.style
== BOOKMARKS
and prefs
.sort_bookmarks(context
):
215 items
.sort(key
=lambda x
: x
.name
)
218 self
.addTopLevelItems(items
)
220 def contextMenuEvent(self
, event
):
221 menu
= qtutils
.create_menu(N_('Actions'), self
)
222 menu
.addAction(self
.open_action
)
223 menu
.addAction(self
.open_new_action
)
224 menu
.addAction(self
.open_default_action
)
226 menu
.addAction(self
.copy_action
)
227 menu
.addAction(self
.launch_editor_action
)
228 menu
.addAction(self
.launch_terminal_action
)
230 item
= self
.selected_item()
231 is_default
= bool(item
and item
.is_default
)
233 menu
.addAction(self
.clear_default_repo_action
)
235 menu
.addAction(self
.set_default_repo_action
)
236 menu
.addAction(self
.rename_repo_action
)
238 menu
.addAction(self
.delete_action
)
239 menu
.addAction(self
.remove_missing_action
)
240 menu
.exec_(self
.mapToGlobal(event
.pos()))
242 def item_changed(self
, item
, _index
):
243 self
.rename_entry(item
, item
.text(0))
245 def rename_entry(self
, item
, new_name
):
246 settings
= self
.context
.settings
247 if self
.style
== BOOKMARKS
:
248 rename
= settings
.rename_bookmark
249 elif self
.style
== RECENT_REPOS
:
250 rename
= settings
.rename_recent
252 rename
= disable_rename
253 if rename(item
.path
, item
.name
, new_name
):
257 item
.setText(0, item
.name
)
259 def apply_fn(self
, fn
, *args
, **kwargs
):
260 item
= self
.selected_item()
262 fn(item
, *args
, **kwargs
)
265 self
.apply_fn(lambda item
: qtutils
.set_clipboard(item
.path
))
267 def open_default(self
):
268 context
= self
.context
269 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenDefaultApp
, context
, [item
.path
]))
271 def set_default_repo(self
):
272 self
.apply_fn(self
.set_default_item
)
274 def set_default_item(self
, item
):
275 context
= self
.context
276 cmds
.do(cmds
.SetDefaultRepo
, context
, item
.path
)
278 self
.default_changed
.emit()
280 def clear_default_repo(self
):
281 self
.apply_fn(self
.clear_default_item
)
282 self
.default_changed
.emit()
284 def clear_default_item(self
, _item
):
285 context
= self
.context
286 cmds
.do(cmds
.SetDefaultRepo
, context
, None)
289 def rename_repo(self
):
290 self
.apply_fn(lambda item
: self
.editItem(item
, 0))
292 def accept_repo(self
):
293 self
.apply_fn(self
.accept_item
)
295 def accept_item(self
, item
):
296 if self
.state() & self
.EditingState
:
297 widget
= self
.itemWidget(item
, 0)
299 self
.commitData(widget
)
300 self
.closePersistentEditor(item
, 0)
305 context
= self
.context
306 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenRepo
, context
, item
.path
))
308 def open_new_repo(self
):
309 context
= self
.context
310 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenNewRepo
, context
, item
.path
))
312 def launch_editor(self
):
313 context
= self
.context
314 self
.apply_fn(lambda item
: cmds
.do(cmds
.Edit
, context
, [item
.path
]))
316 def launch_terminal(self
):
317 context
= self
.context
318 self
.apply_fn(lambda item
: cmds
.do(cmds
.LaunchTerminal
, context
, item
.path
))
320 def item_selection_changed(self
):
321 item
= self
.selected_item()
323 self
.action_group
.setEnabled(enabled
)
325 is_default
= bool(item
and item
.is_default
)
326 self
.set_default_repo_action
.setEnabled(not is_default
)
327 self
.clear_default_repo_action
.setEnabled(is_default
)
329 def tree_double_clicked(self
, item
, _column
):
330 context
= self
.context
331 cmds
.do(cmds
.OpenRepo
, context
, item
.path
)
333 def add_bookmark(self
):
334 normpath
= utils
.expandpath(core
.getcwd())
335 name
= os
.path
.basename(normpath
)
338 (N_('Path'), core
.getcwd()),
340 ok
, values
= qtutils
.prompt_n(N_('Add Favorite'), prompt
)
344 normpath
= utils
.expandpath(path
)
345 if git
.is_git_worktree(normpath
):
346 settings
= self
.context
.settings
348 settings
.add_bookmark(normpath
, name
)
352 Interaction
.critical(N_('Error'), N_('%s is not a Git repository.') % path
)
354 def delete_bookmark(self
):
355 """Removes a bookmark from the bookmarks list"""
356 item
= self
.selected_item()
357 context
= self
.context
360 if self
.style
== BOOKMARKS
:
361 cmd
= cmds
.RemoveBookmark
362 elif self
.style
== RECENT_REPOS
:
363 cmd
= cmds
.RemoveRecent
366 ok
, _
, _
, _
= cmds
.do(cmd
, context
, item
.path
, item
.name
, icon
=icons
.discard())
370 def remove_missing(self
):
371 """Remove missing entries from the favorites/recent file list"""
372 settings
= self
.context
.settings
373 if self
.style
== BOOKMARKS
:
374 settings
.remove_missing_bookmarks()
375 elif self
.style
== RECENT_REPOS
:
376 settings
.remove_missing_recent()
380 class BuildItem(object):
381 def __init__(self
, context
):
382 self
.star_icon
= icons
.star()
383 self
.folder_icon
= icons
.folder()
385 self
.default_repo
= cfg
.get('cola.defaultrepo')
387 def get(self
, path
, name
):
388 is_default
= self
.default_repo
== path
390 icon
= self
.star_icon
392 icon
= self
.folder_icon
393 return BookmarksTreeWidgetItem(path
, name
, icon
, is_default
)
396 class BookmarksTreeWidgetItem(QtWidgets
.QTreeWidgetItem
):
397 def __init__(self
, path
, name
, icon
, is_default
):
398 QtWidgets
.QTreeWidgetItem
.__init
__(self
)
401 self
.is_default
= is_default
403 self
.setIcon(0, icon
)
404 self
.setText(0, name
)
405 self
.setToolTip(0, path
)
406 self
.setFlags(self
.flags() | Qt
.ItemIsEditable
)