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 ..settings
import Settings
21 from ..widgets
import defs
22 from ..widgets
import standard
29 def bookmark(context
, parent
):
30 return BookmarksWidget(context
, BOOKMARKS
, parent
=parent
)
33 def recent(context
, parent
):
34 return BookmarksWidget(context
, RECENT_REPOS
, parent
=parent
)
37 class BookmarksWidget(QtWidgets
.QFrame
):
39 def __init__(self
, context
, style
=BOOKMARKS
, parent
=None):
40 QtWidgets
.QFrame
.__init
__(self
, parent
)
43 self
.settings
= Settings()
44 self
.tree
= BookmarksTreeWidget(
45 context
, style
, self
.settings
, parent
=self
)
47 self
.add_button
= qtutils
.create_action_button(
48 tooltip
=N_('Add'), icon
=icons
.add())
50 self
.delete_button
= qtutils
.create_action_button(
51 tooltip
=N_('Delete'), icon
=icons
.remove())
53 self
.open_button
= qtutils
.create_action_button(
54 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(defs
.no_margin
, defs
.spacing
,
67 self
.open_button
, self
.add_button
,
70 self
.main_layout
= qtutils
.vbox(defs
.no_margin
, defs
.spacing
, self
.tree
)
71 self
.setLayout(self
.main_layout
)
73 self
.corner_widget
= QtWidgets
.QWidget(self
)
74 self
.corner_widget
.setLayout(self
.button_layout
)
75 titlebar
= parent
.titleBarWidget()
76 titlebar
.add_corner_widget(self
.corner_widget
)
78 qtutils
.connect_button(self
.add_button
, self
.tree
.add_bookmark
)
79 qtutils
.connect_button(self
.delete_button
, self
.tree
.delete_bookmark
)
80 qtutils
.connect_button(self
.open_button
, self
.tree
.open_repo
)
82 item_selection_changed
= self
.tree_item_selection_changed
83 # pylint: disable=no-member
84 self
.tree
.itemSelectionChanged
.connect(item_selection_changed
)
86 QtCore
.QTimer
.singleShot(0, self
.reload_bookmarks
)
88 def reload_bookmarks(self
):
89 # Called once after the GUI is initialized
93 def tree_item_selection_changed(self
):
94 enabled
= bool(self
.tree
.selected_item())
95 self
.button_group
.setEnabled(enabled
)
97 def connect_to(self
, other
):
98 self
.tree
.default_changed
.connect(other
.tree
.refresh
)
99 other
.tree
.default_changed
.connect(self
.tree
.refresh
)
102 def disable_rename(_path
, _name
, _new_name
):
106 # pylint: disable=too-many-ancestors
107 class BookmarksTreeWidget(standard
.TreeWidget
):
108 default_changed
= Signal()
109 worktree_changed
= Signal()
111 def __init__(self
, context
, style
, settings
, parent
=None):
112 standard
.TreeWidget
.__init
__(self
, parent
=parent
)
113 self
.context
= context
115 self
.settings
= settings
117 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
118 self
.setHeaderHidden(True)
120 # We make the items editable, but we don't want the double-click
121 # behavior to trigger editing. Make it behave like Mac OS X's Finder.
122 self
.setEditTriggers(self
.SelectedClicked
)
124 self
.open_action
= qtutils
.add_action(
125 self
, N_('Open'), self
.open_repo
, hotkeys
.OPEN
)
127 self
.accept_action
= qtutils
.add_action(
128 self
, N_('Accept'), self
.accept_repo
, *hotkeys
.ACCEPT
)
130 self
.open_new_action
= qtutils
.add_action(
131 self
, N_('Open in New Window'), self
.open_new_repo
, hotkeys
.NEW
)
133 self
.set_default_repo_action
= qtutils
.add_action(
134 self
, N_('Set Default Repository'), self
.set_default_repo
)
136 self
.clear_default_repo_action
= qtutils
.add_action(
137 self
, N_('Clear Default Repository'), self
.clear_default_repo
)
139 self
.rename_repo_action
= qtutils
.add_action(
140 self
, N_('Rename Repository'), self
.rename_repo
)
142 self
.open_default_action
= qtutils
.add_action(
143 self
, cmds
.OpenDefaultApp
.name(), self
.open_default
,
144 hotkeys
.PRIMARY_ACTION
)
146 self
.launch_editor_action
= qtutils
.add_action(
147 self
, cmds
.Edit
.name(), self
.launch_editor
, hotkeys
.EDIT
)
149 self
.launch_terminal_action
= qtutils
.add_action(
150 self
, cmds
.LaunchTerminal
.name(), self
.launch_terminal
,
153 self
.copy_action
= qtutils
.add_action(
154 self
, N_('Copy'), self
.copy
, hotkeys
.COPY
)
156 self
.delete_action
= qtutils
.add_action(
157 self
, N_('Delete'), self
.delete_bookmark
)
159 # pylint: disable=no-member
160 self
.itemChanged
.connect(self
.item_changed
)
161 self
.itemSelectionChanged
.connect(self
.item_selection_changed
)
162 self
.itemDoubleClicked
.connect(self
.tree_double_clicked
)
164 self
.action_group
= utils
.Group(self
.open_action
,
165 self
.open_new_action
,
167 self
.launch_editor_action
,
168 self
.launch_terminal_action
,
169 self
.open_default_action
,
170 self
.rename_repo_action
,
172 self
.action_group
.setEnabled(False)
173 self
.set_default_repo_action
.setEnabled(False)
174 self
.clear_default_repo_action
.setEnabled(False)
177 if style
== RECENT_REPOS
:
178 self
.worktree_changed
.connect(self
.refresh
,
179 type=Qt
.QueuedConnection
)
180 context
.model
.add_observer(context
.model
.message_worktree_changed
,
181 self
.worktree_changed
.emit
)
184 context
= self
.context
185 settings
= self
.settings
186 builder
= BuildItem(context
)
189 if self
.style
== BOOKMARKS
:
190 entries
= settings
.bookmarks
192 elif self
.style
== RECENT_REPOS
:
193 settings
.reload_recent()
194 entries
= settings
.recent
196 items
= [builder
.get(entry
['path'], entry
['name']) for entry
in entries
]
197 if self
.style
== BOOKMARKS
and prefs
.sort_bookmarks(context
):
198 items
.sort(key
=lambda x
: x
.name
)
201 self
.addTopLevelItems(items
)
203 def contextMenuEvent(self
, event
):
204 menu
= qtutils
.create_menu(N_('Actions'), self
)
205 menu
.addAction(self
.open_action
)
206 menu
.addAction(self
.open_new_action
)
207 menu
.addAction(self
.open_default_action
)
209 menu
.addAction(self
.copy_action
)
210 menu
.addAction(self
.launch_editor_action
)
211 menu
.addAction(self
.launch_terminal_action
)
213 item
= self
.selected_item()
214 is_default
= bool(item
and item
.is_default
)
216 menu
.addAction(self
.clear_default_repo_action
)
218 menu
.addAction(self
.set_default_repo_action
)
219 menu
.addAction(self
.rename_repo_action
)
221 menu
.addAction(self
.delete_action
)
222 menu
.exec_(self
.mapToGlobal(event
.pos()))
224 def item_changed(self
, item
, _index
):
225 self
.rename_entry(item
, item
.text(0))
227 def rename_entry(self
, item
, new_name
):
228 if self
.style
== BOOKMARKS
:
229 rename
= self
.settings
.rename_bookmark
230 elif self
.style
== RECENT_REPOS
:
231 rename
= self
.settings
.rename_recent
233 rename
= disable_rename
234 if rename(item
.path
, item
.name
, new_name
):
238 item
.setText(0, item
.name
)
240 def apply_fn(self
, fn
, *args
, **kwargs
):
241 item
= self
.selected_item()
243 fn(item
, *args
, **kwargs
)
246 self
.apply_fn(lambda item
: qtutils
.set_clipboard(item
.path
))
248 def open_default(self
):
249 context
= self
.context
251 lambda item
: cmds
.do(cmds
.OpenDefaultApp
, context
, [item
.path
]))
253 def set_default_repo(self
):
254 self
.apply_fn(self
.set_default_item
)
256 def set_default_item(self
, item
):
257 context
= self
.context
258 cmds
.do(cmds
.SetDefaultRepo
, context
, item
.path
)
260 self
.default_changed
.emit()
262 def clear_default_repo(self
):
263 self
.apply_fn(self
.clear_default_item
)
264 self
.default_changed
.emit()
266 def clear_default_item(self
, _item
):
267 context
= self
.context
268 cmds
.do(cmds
.SetDefaultRepo
, context
, None)
271 def rename_repo(self
):
272 self
.apply_fn(lambda item
: self
.editItem(item
, 0))
274 def accept_repo(self
):
275 self
.apply_fn(self
.accept_item
)
277 def accept_item(self
, item
):
278 if self
.state() & self
.EditingState
:
279 widget
= self
.itemWidget(item
, 0)
281 self
.commitData(widget
)
282 self
.closePersistentEditor(item
, 0)
287 context
= self
.context
288 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenRepo
, context
, item
.path
))
290 def open_new_repo(self
):
291 context
= self
.context
293 lambda item
: cmds
.do(cmds
.OpenNewRepo
, context
, item
.path
))
295 def launch_editor(self
):
296 context
= self
.context
297 self
.apply_fn(lambda item
: cmds
.do(cmds
.Edit
, context
, [item
.path
]))
299 def launch_terminal(self
):
300 context
= self
.context
302 lambda item
: cmds
.do(cmds
.LaunchTerminal
, context
, item
.path
))
304 def item_selection_changed(self
):
305 item
= self
.selected_item()
307 self
.action_group
.setEnabled(enabled
)
309 is_default
= bool(item
and item
.is_default
)
310 self
.set_default_repo_action
.setEnabled(not is_default
)
311 self
.clear_default_repo_action
.setEnabled(is_default
)
313 def tree_double_clicked(self
, item
, _column
):
314 context
= self
.context
315 cmds
.do(cmds
.OpenRepo
, context
, item
.path
)
317 def add_bookmark(self
):
318 normpath
= utils
.expandpath(core
.getcwd())
319 name
= os
.path
.basename(normpath
)
322 (N_('Path'), core
.getcwd()),
324 ok
, values
= qtutils
.prompt_n(N_('Add Favorite'), prompt
)
328 normpath
= utils
.expandpath(path
)
329 if git
.is_git_worktree(normpath
):
330 self
.settings
.add_bookmark(normpath
, name
)
334 Interaction
.critical(
335 N_('Error'), N_('%s is not a Git repository.') % path
)
337 def delete_bookmark(self
):
338 """Removes a bookmark from the bookmarks list"""
339 item
= self
.selected_item()
340 context
= self
.context
343 if self
.style
== BOOKMARKS
:
344 cmd
= cmds
.RemoveBookmark
345 elif self
.style
== RECENT_REPOS
:
346 cmd
= cmds
.RemoveRecent
349 ok
, _
, _
, _
= cmds
.do(
350 cmd
, context
, self
.settings
, item
.path
, item
.name
,
351 icon
=icons
.discard())
356 class BuildItem(object):
358 def __init__(self
, context
):
359 self
.star_icon
= icons
.star()
360 self
.folder_icon
= icons
.folder()
362 self
.default_repo
= cfg
.get('cola.defaultrepo')
364 def get(self
, path
, name
):
365 is_default
= self
.default_repo
== path
367 icon
= self
.star_icon
369 icon
= self
.folder_icon
370 return BookmarksTreeWidgetItem(path
, name
, icon
, is_default
)
373 class BookmarksTreeWidgetItem(QtWidgets
.QTreeWidgetItem
):
375 def __init__(self
, path
, name
, icon
, is_default
):
376 QtWidgets
.QTreeWidgetItem
.__init
__(self
)
379 self
.is_default
= is_default
381 self
.setIcon(0, icon
)
382 self
.setText(0, name
)
383 self
.setToolTip(0, path
)
384 self
.setFlags(self
.flags() | Qt
.ItemIsEditable
)