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 self
.tree
.itemSelectionChanged
.connect(item_selection_changed
)
85 QtCore
.QTimer
.singleShot(0, self
.reload_bookmarks
)
87 def reload_bookmarks(self
):
88 # Called once after the GUI is initialized
92 def tree_item_selection_changed(self
):
93 enabled
= bool(self
.tree
.selected_item())
94 self
.button_group
.setEnabled(enabled
)
96 def connect_to(self
, other
):
97 self
.tree
.default_changed
.connect(other
.tree
.refresh
)
98 other
.tree
.default_changed
.connect(self
.tree
.refresh
)
101 def disable_rename(_path
, _name
, _new_name
):
105 class BookmarksTreeWidget(standard
.TreeWidget
):
106 default_changed
= Signal()
108 def __init__(self
, context
, style
, settings
, parent
=None):
109 standard
.TreeWidget
.__init
__(self
, parent
=parent
)
110 self
.context
= context
112 self
.settings
= settings
114 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
115 self
.setHeaderHidden(True)
117 # We make the items editable, but we don't want the double-click
118 # behavior to trigger editing. Make it behave like Mac OS X's Finder.
119 self
.setEditTriggers(self
.SelectedClicked
)
121 self
.open_action
= qtutils
.add_action(
122 self
, N_('Open'), self
.open_repo
, hotkeys
.OPEN
)
124 self
.accept_action
= qtutils
.add_action(
125 self
, N_('Accept'), self
.accept_repo
, *hotkeys
.ACCEPT
)
127 self
.open_new_action
= qtutils
.add_action(
128 self
, N_('Open in New Window'), self
.open_new_repo
, hotkeys
.NEW
)
130 self
.set_default_repo_action
= qtutils
.add_action(
131 self
, N_('Set Default Repository'), self
.set_default_repo
)
133 self
.clear_default_repo_action
= qtutils
.add_action(
134 self
, N_('Clear Default Repository'), self
.clear_default_repo
)
136 self
.rename_repo_action
= qtutils
.add_action(
137 self
, N_('Rename Repository'), self
.rename_repo
)
139 self
.open_default_action
= qtutils
.add_action(
140 self
, cmds
.OpenDefaultApp
.name(), self
.open_default
,
141 hotkeys
.PRIMARY_ACTION
)
143 self
.launch_editor_action
= qtutils
.add_action(
144 self
, cmds
.Edit
.name(), self
.launch_editor
, hotkeys
.EDIT
)
146 self
.launch_terminal_action
= qtutils
.add_action(
147 self
, cmds
.LaunchTerminal
.name(), self
.launch_terminal
,
150 self
.copy_action
= qtutils
.add_action(
151 self
, N_('Copy'), self
.copy
, hotkeys
.COPY
)
153 self
.itemChanged
.connect(self
.item_changed
)
154 self
.itemSelectionChanged
.connect(self
.item_selection_changed
)
155 self
.itemDoubleClicked
.connect(self
.tree_double_clicked
)
157 self
.action_group
= utils
.Group(self
.open_action
,
158 self
.open_new_action
,
160 self
.launch_editor_action
,
161 self
.launch_terminal_action
,
162 self
.open_default_action
,
163 self
.rename_repo_action
)
164 self
.action_group
.setEnabled(False)
165 self
.set_default_repo_action
.setEnabled(False)
166 self
.clear_default_repo_action
.setEnabled(False)
169 context
= self
.context
170 settings
= self
.settings
171 builder
= BuildItem(context
)
174 if self
.style
== BOOKMARKS
:
175 entries
= settings
.bookmarks
177 elif self
.style
== RECENT_REPOS
:
178 entries
= settings
.recent
180 items
= [builder
.get(entry
['path'], entry
['name']) for entry
in entries
]
181 if self
.style
== BOOKMARKS
and prefs
.sort_bookmarks(context
):
182 items
.sort(key
=lambda x
: x
.name
)
185 self
.addTopLevelItems(items
)
187 def contextMenuEvent(self
, event
):
188 menu
= qtutils
.create_menu(N_('Actions'), self
)
189 menu
.addAction(self
.open_action
)
190 menu
.addAction(self
.open_new_action
)
191 menu
.addAction(self
.open_default_action
)
193 menu
.addAction(self
.copy_action
)
194 menu
.addAction(self
.launch_editor_action
)
195 menu
.addAction(self
.launch_terminal_action
)
197 item
= self
.selected_item()
198 is_default
= bool(item
and item
.is_default
)
200 menu
.addAction(self
.clear_default_repo_action
)
202 menu
.addAction(self
.set_default_repo_action
)
203 menu
.addAction(self
.rename_repo_action
)
204 menu
.exec_(self
.mapToGlobal(event
.pos()))
206 def item_changed(self
, item
, _index
):
207 self
.rename_entry(item
, item
.text(0))
209 def rename_entry(self
, item
, new_name
):
210 if self
.style
== BOOKMARKS
:
211 rename
= self
.settings
.rename_bookmark
212 elif self
.style
== RECENT_REPOS
:
213 rename
= self
.settings
.rename_recent
215 rename
= disable_rename
216 if rename(item
.path
, item
.name
, new_name
):
220 item
.setText(0, item
.name
)
222 def apply_fn(self
, fn
, *args
, **kwargs
):
223 item
= self
.selected_item()
225 fn(item
, *args
, **kwargs
)
228 self
.apply_fn(lambda item
: qtutils
.set_clipboard(item
.path
))
230 def open_default(self
):
231 context
= self
.context
233 lambda item
: cmds
.do(cmds
.OpenDefaultApp
, context
, [item
.path
]))
235 def set_default_repo(self
):
236 self
.apply_fn(self
.set_default_item
)
238 def set_default_item(self
, item
):
239 context
= self
.context
240 cmds
.do(cmds
.SetDefaultRepo
, context
, item
.path
)
242 self
.default_changed
.emit()
244 def clear_default_repo(self
):
245 self
.apply_fn(self
.clear_default_item
)
246 self
.default_changed
.emit()
248 def clear_default_item(self
, _item
):
249 context
= self
.context
250 cmds
.do(cmds
.SetDefaultRepo
, context
, None)
253 def rename_repo(self
):
254 self
.apply_fn(lambda item
: self
.editItem(item
, 0))
256 def accept_repo(self
):
257 self
.apply_fn(self
.accept_item
)
259 def accept_item(self
, item
):
260 if self
.state() & self
.EditingState
:
261 widget
= self
.itemWidget(item
, 0)
263 self
.commitData(widget
)
264 self
.closePersistentEditor(item
, 0)
269 context
= self
.context
270 self
.apply_fn(lambda item
: cmds
.do(cmds
.OpenRepo
, context
, item
.path
))
272 def open_new_repo(self
):
273 context
= self
.context
275 lambda item
: cmds
.do(cmds
.OpenNewRepo
, context
, item
.path
))
277 def launch_editor(self
):
278 context
= self
.context
279 self
.apply_fn(lambda item
: cmds
.do(cmds
.Edit
, context
, [item
.path
]))
281 def launch_terminal(self
):
282 context
= self
.context
284 lambda item
: cmds
.do(cmds
.LaunchTerminal
, context
, item
.path
))
286 def item_selection_changed(self
):
287 item
= self
.selected_item()
289 self
.action_group
.setEnabled(enabled
)
291 is_default
= bool(item
and item
.is_default
)
292 self
.set_default_repo_action
.setEnabled(not is_default
)
293 self
.clear_default_repo_action
.setEnabled(is_default
)
295 def tree_double_clicked(self
, item
, _column
):
296 context
= self
.context
297 cmds
.do(cmds
.OpenRepo
, context
, item
.path
)
299 def add_bookmark(self
):
300 normpath
= utils
.expandpath(core
.getcwd())
301 name
= os
.path
.basename(normpath
)
304 (N_('Path'), core
.getcwd()),
306 ok
, values
= qtutils
.prompt_n(N_('Add Favorite'), prompt
)
310 normpath
= utils
.expandpath(path
)
311 if git
.is_git_worktree(normpath
):
312 self
.settings
.add_bookmark(normpath
, name
)
316 Interaction
.critical(
317 N_('Error'), N_('%s is not a Git repository.') % path
)
319 def delete_bookmark(self
):
320 """Removes a bookmark from the bookmarks list"""
321 item
= self
.selected_item()
322 context
= self
.context
325 if self
.style
== BOOKMARKS
:
326 cmd
= cmds
.RemoveBookmark
327 elif self
.style
== RECENT_REPOS
:
328 cmd
= cmds
.RemoveRecent
331 ok
, _
, _
, _
= cmds
.do(
332 cmd
, context
, self
.settings
, item
.path
, item
.name
,
333 icon
=icons
.discard())
338 class BuildItem(object):
340 def __init__(self
, context
):
341 self
.star_icon
= icons
.star()
342 self
.folder_icon
= icons
.folder()
344 self
.default_repo
= cfg
.get('cola.defaultrepo')
346 def get(self
, path
, name
):
347 is_default
= self
.default_repo
== path
349 icon
= self
.star_icon
351 icon
= self
.folder_icon
352 return BookmarksTreeWidgetItem(path
, name
, icon
, is_default
)
355 class BookmarksTreeWidgetItem(QtWidgets
.QTreeWidgetItem
):
357 def __init__(self
, path
, name
, icon
, is_default
):
358 QtWidgets
.QTreeWidgetItem
.__init
__(self
)
361 self
.is_default
= is_default
363 self
.setIcon(0, icon
)
364 self
.setText(0, name
)
365 self
.setToolTip(0, path
)
366 self
.setFlags(self
.flags() | Qt
.ItemIsEditable
)