1 """Provides widgets related to bookmarks"""
2 from __future__
import absolute_import
, division
, print_function
, 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()
113 def __init__(self
, context
, style
, parent
=None):
114 standard
.TreeWidget
.__init
__(self
, parent
=parent
)
115 self
.context
= context
118 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
119 self
.setHeaderHidden(True)
121 # We make the items editable, but we don't want the double-click
122 # behavior to trigger editing. Make it behave like Mac OS X's Finder.
123 self
.setEditTriggers(self
.SelectedClicked
)
125 self
.open_action
= qtutils
.add_action(
126 self
, N_('Open'), self
.open_repo
, hotkeys
.OPEN
129 self
.accept_action
= qtutils
.add_action(
130 self
, N_('Accept'), self
.accept_repo
, *hotkeys
.ACCEPT
133 self
.open_new_action
= qtutils
.add_action(
134 self
, N_('Open in New Window'), self
.open_new_repo
, hotkeys
.NEW
137 self
.set_default_repo_action
= qtutils
.add_action(
138 self
, N_('Set Default Repository'), self
.set_default_repo
141 self
.clear_default_repo_action
= qtutils
.add_action(
142 self
, N_('Clear Default Repository'), self
.clear_default_repo
145 self
.rename_repo_action
= qtutils
.add_action(
146 self
, N_('Rename Repository'), self
.rename_repo
149 self
.open_default_action
= qtutils
.add_action(
150 self
, cmds
.OpenDefaultApp
.name(), self
.open_default
, hotkeys
.PRIMARY_ACTION
153 self
.launch_editor_action
= qtutils
.add_action(
154 self
, cmds
.Edit
.name(), self
.launch_editor
, hotkeys
.EDIT
157 self
.launch_terminal_action
= qtutils
.add_action(
158 self
, cmds
.LaunchTerminal
.name(), self
.launch_terminal
, hotkeys
.TERMINAL
161 self
.copy_action
= qtutils
.add_action(self
, N_('Copy'), self
.copy
, hotkeys
.COPY
)
163 self
.delete_action
= qtutils
.add_action(
164 self
, N_('Delete'), self
.delete_bookmark
167 self
.remove_missing_action
= qtutils
.add_action(
168 self
, N_('Prune Missing Entries'), self
.remove_missing
170 self
.remove_missing_action
.setToolTip(
171 N_('Remove stale entries for repositories that no longer exist')
174 # pylint: disable=no-member
175 self
.itemChanged
.connect(self
.item_changed
)
176 self
.itemSelectionChanged
.connect(self
.item_selection_changed
)
177 self
.itemDoubleClicked
.connect(self
.tree_double_clicked
)
179 self
.action_group
= utils
.Group(
181 self
.open_new_action
,
183 self
.launch_editor_action
,
184 self
.launch_terminal_action
,
185 self
.open_default_action
,
186 self
.rename_repo_action
,
189 self
.action_group
.setEnabled(False)
190 self
.set_default_repo_action
.setEnabled(False)
191 self
.clear_default_repo_action
.setEnabled(False)
194 if style
== RECENT_REPOS
:
195 context
.model
.worktree_changed
.connect(
196 self
.refresh
, type=Qt
.QueuedConnection
200 context
= self
.context
201 settings
= context
.settings
202 builder
= BuildItem(context
)
205 if self
.style
== BOOKMARKS
:
206 entries
= settings
.bookmarks
208 elif self
.style
== RECENT_REPOS
:
209 entries
= settings
.recent
211 items
= [builder
.get(entry
['path'], entry
['name']) for entry
in entries
]
212 if self
.style
== BOOKMARKS
and prefs
.sort_bookmarks(context
):
213 items
.sort(key
=lambda x
: x
.name
)
216 self
.addTopLevelItems(items
)
218 def contextMenuEvent(self
, event
):
219 menu
= qtutils
.create_menu(N_('Actions'), self
)
220 menu
.addAction(self
.open_action
)
221 menu
.addAction(self
.open_new_action
)
222 menu
.addAction(self
.open_default_action
)
224 menu
.addAction(self
.copy_action
)
225 menu
.addAction(self
.launch_editor_action
)
226 menu
.addAction(self
.launch_terminal_action
)
228 item
= self
.selected_item()
229 is_default
= bool(item
and item
.is_default
)
231 menu
.addAction(self
.clear_default_repo_action
)
233 menu
.addAction(self
.set_default_repo_action
)
234 menu
.addAction(self
.rename_repo_action
)
236 menu
.addAction(self
.delete_action
)
237 menu
.addAction(self
.remove_missing_action
)
238 menu
.exec_(self
.mapToGlobal(event
.pos()))
240 def item_changed(self
, item
, _index
):
241 self
.rename_entry(item
, item
.text(0))
243 def rename_entry(self
, item
, new_name
):
244 settings
= self
.context
.settings
245 if self
.style
== BOOKMARKS
:
246 rename
= settings
.rename_bookmark
247 elif self
.style
== RECENT_REPOS
:
248 rename
= settings
.rename_recent
250 rename
= disable_rename
251 if rename(item
.path
, item
.name
, new_name
):
255 item
.setText(0, item
.name
)
257 def apply_fn(self
, fn
, *args
, **kwargs
):
258 item
= self
.selected_item()
260 fn(item
, *args
, **kwargs
)
263 self
.apply_fn(lambda item
: qtutils
.set_clipboard(item
.path
))
265 def open_default(self
):
266 context
= self
.context
267 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenDefaultApp
, context
, [item
.path
]))
269 def set_default_repo(self
):
270 self
.apply_fn(self
.set_default_item
)
272 def set_default_item(self
, item
):
273 context
= self
.context
274 cmds
.do(cmds
.SetDefaultRepo
, context
, item
.path
)
276 self
.default_changed
.emit()
278 def clear_default_repo(self
):
279 self
.apply_fn(self
.clear_default_item
)
280 self
.default_changed
.emit()
282 def clear_default_item(self
, _item
):
283 context
= self
.context
284 cmds
.do(cmds
.SetDefaultRepo
, context
, None)
287 def rename_repo(self
):
288 self
.apply_fn(lambda item
: self
.editItem(item
, 0))
290 def accept_repo(self
):
291 self
.apply_fn(self
.accept_item
)
293 def accept_item(self
, item
):
294 if self
.state() & self
.EditingState
:
295 widget
= self
.itemWidget(item
, 0)
297 self
.commitData(widget
)
298 self
.closePersistentEditor(item
, 0)
303 context
= self
.context
304 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenRepo
, context
, item
.path
))
306 def open_new_repo(self
):
307 context
= self
.context
308 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenNewRepo
, context
, item
.path
))
310 def launch_editor(self
):
311 context
= self
.context
312 self
.apply_fn(lambda item
: cmds
.do(cmds
.Edit
, context
, [item
.path
]))
314 def launch_terminal(self
):
315 context
= self
.context
316 self
.apply_fn(lambda item
: cmds
.do(cmds
.LaunchTerminal
, context
, item
.path
))
318 def item_selection_changed(self
):
319 item
= self
.selected_item()
321 self
.action_group
.setEnabled(enabled
)
323 is_default
= bool(item
and item
.is_default
)
324 self
.set_default_repo_action
.setEnabled(not is_default
)
325 self
.clear_default_repo_action
.setEnabled(is_default
)
327 def tree_double_clicked(self
, item
, _column
):
328 context
= self
.context
329 cmds
.do(cmds
.OpenRepo
, context
, item
.path
)
331 def add_bookmark(self
):
332 normpath
= utils
.expandpath(core
.getcwd())
333 name
= os
.path
.basename(normpath
)
336 (N_('Path'), core
.getcwd()),
338 ok
, values
= qtutils
.prompt_n(N_('Add Favorite'), prompt
)
342 normpath
= utils
.expandpath(path
)
343 if git
.is_git_worktree(normpath
):
344 settings
= self
.context
.settings
346 settings
.add_bookmark(normpath
, name
)
350 Interaction
.critical(N_('Error'), N_('%s is not a Git repository.') % path
)
352 def delete_bookmark(self
):
353 """Removes a bookmark from the bookmarks list"""
354 item
= self
.selected_item()
355 context
= self
.context
358 if self
.style
== BOOKMARKS
:
359 cmd
= cmds
.RemoveBookmark
360 elif self
.style
== RECENT_REPOS
:
361 cmd
= cmds
.RemoveRecent
364 ok
, _
, _
, _
= cmds
.do(cmd
, context
, item
.path
, item
.name
, icon
=icons
.discard())
368 def remove_missing(self
):
369 """Remove missing entries from the favorites/recent file list"""
370 settings
= self
.context
.settings
371 if self
.style
== BOOKMARKS
:
372 settings
.remove_missing_bookmarks()
373 elif self
.style
== RECENT_REPOS
:
374 settings
.remove_missing_recent()
378 class BuildItem(object):
379 def __init__(self
, context
):
380 self
.star_icon
= icons
.star()
381 self
.folder_icon
= icons
.folder()
383 self
.default_repo
= cfg
.get('cola.defaultrepo')
385 def get(self
, path
, name
):
386 is_default
= self
.default_repo
== path
388 icon
= self
.star_icon
390 icon
= self
.folder_icon
391 return BookmarksTreeWidgetItem(path
, name
, icon
, is_default
)
394 class BookmarksTreeWidgetItem(QtWidgets
.QTreeWidgetItem
):
395 def __init__(self
, path
, name
, icon
, is_default
):
396 QtWidgets
.QTreeWidgetItem
.__init
__(self
)
399 self
.is_default
= is_default
401 self
.setIcon(0, icon
)
402 self
.setText(0, name
)
403 self
.setToolTip(0, path
)
404 self
.setFlags(self
.flags() | Qt
.ItemIsEditable
)