1 from __future__
import division
, absolute_import
, unicode_literals
2 from functools
import partial
5 from qtpy
.QtCore
import Qt
6 from qtpy
import QtWidgets
9 from ..widgets
import standard
10 from ..qtutils
import get
12 from .. import qtutils
13 from .toolbarcmds
import COMMANDS
18 'Others::LaunchEditor',
19 'Others::RevertUnstagedEdits'
24 'File::OpenRepoNewWindow',
27 'File::RecentModified',
29 'File::ExportPatches',
37 'Actions::CherryPick',
39 'Actions::AbortMerge',
40 'Actions::ResetBranchHead',
41 'Actions::ResetWorktree',
51 'Commit::LoadCommitMessage',
52 'Commit::GetCommitMessageTemplate',
65 'Branch::DeleteRemote',
67 'Branch::BrowseCurrent',
68 'Branch::BrowseOther',
69 'Branch::VisualizeCurrent',
70 'Branch::VisualizeAll'
79 def configure(toolbar
, parent
=None):
80 """Launches the Toolbar configure dialog"""
81 view
= ToolbarView(toolbar
, parent
if parent
else qtutils
.active_window())
86 def get_toolbars(widget
):
87 return widget
.findChildren(ToolBar
)
90 def add_toolbar(context
, widget
):
91 toolbars
= get_toolbars(widget
)
92 name
= 'ToolBar%d' % (len(toolbars
) + 1)
93 toolbar
= ToolBar
.create(context
, name
)
94 widget
.addToolBar(toolbar
)
98 class ToolBarState(object):
99 """export_state() and apply_state() providers for toolbars"""
101 def __init__(self
, context
, widget
):
102 """widget must be a QMainWindow for toolBarArea(), etc."""
103 self
.context
= context
106 def apply_state(self
, toolbars
):
107 context
= self
.context
110 for data
in toolbars
:
111 toolbar
= ToolBar
.create(context
, data
['name'])
112 toolbar
.load_items(data
['items'])
113 toolbar
.set_show_icons(data
['show_icons'])
114 toolbar
.setVisible(data
['visible'])
116 toolbar_area
= decode_toolbar_area(data
['area'])
118 widget
.addToolBarBreak(toolbar_area
)
119 widget
.addToolBar(toolbar_area
, toolbar
)
121 # floating toolbars must be set after added
123 toolbar
.setWindowFlags(Qt
.Tool | Qt
.FramelessWindowHint
)
124 toolbar
.move(data
['x'], data
['y'])
125 # TODO: handle changed width when exists more than one toolbar in
128 def export_state(self
):
131 toolbars
= widget
.findChildren(ToolBar
)
133 for toolbar
in toolbars
:
134 toolbar_area
= widget
.toolBarArea(toolbar
)
135 if toolbar_area
== Qt
.NoToolBarArea
:
136 continue # filter out removed toolbars
137 items
= [x
.data() for x
in toolbar
.actions()]
140 'name': toolbar
.windowTitle(),
141 'area': encode_toolbar_area(toolbar_area
),
142 'break': widget
.toolBarBreak(toolbar
),
143 'float': toolbar
.isFloating(),
144 'x': toolbar
.pos().x(),
145 'y': toolbar
.pos().y(),
146 'width': toolbar
.width(),
147 'height': toolbar
.height(),
148 'show_icons': toolbar
.show_icons(),
149 'visible': toolbar
.isVisible(),
156 class ToolBar(QtWidgets
.QToolBar
):
157 SEPARATOR
= 'Separator'
160 def create(context
, name
):
161 return ToolBar(context
, name
, TREE_LAYOUT
, COMMANDS
)
163 def __init__(self
, context
, title
, tree_layout
, toolbar_commands
):
164 QtWidgets
.QToolBar
.__init
__(self
)
165 self
.setWindowTitle(title
)
166 self
.setObjectName(title
)
168 self
.context
= context
169 self
.tree_layout
= tree_layout
170 self
.commands
= toolbar_commands
172 def set_show_icons(self
, show_icons
):
174 self
.setToolButtonStyle(Qt
.ToolButtonIconOnly
)
176 self
.setToolButtonStyle(Qt
.ToolButtonTextOnly
)
178 def show_icons(self
):
179 return self
.toolButtonStyle() == Qt
.ToolButtonIconOnly
181 def load_items(self
, items
):
183 self
.add_action_from_data(data
)
185 def add_action_from_data(self
, data
):
186 parent
= data
['parent']
187 child
= data
['child']
189 if child
== self
.SEPARATOR
:
190 toolbar_action
= self
.addSeparator()
191 toolbar_action
.setData(data
)
194 tree_items
= self
.tree_layout
.get(parent
, [])
195 if child
in tree_items
and child
in self
.commands
:
196 command
= self
.commands
[child
]
197 title
= N_(command
['title'])
198 callback
= partial(command
['action'], self
.context
)
201 command_icon
= command
.get('icon', None)
203 icon
= getattr(icons
, command_icon
, None)
207 toolbar_action
= self
.addAction(icon
, title
, callback
)
209 toolbar_action
= self
.addAction(title
, callback
)
211 toolbar_action
.setData(data
)
213 def delete_toolbar(self
):
214 self
.parent().removeToolBar(self
)
216 def contextMenuEvent(self
, event
):
217 menu
= QtWidgets
.QMenu()
218 menu
.addAction(N_('Configure toolbar'), partial(configure
, self
))
219 menu
.addAction(N_('Delete toolbar'), self
.delete_toolbar
)
221 menu
.exec_(event
.globalPos())
224 def encode_toolbar_area(toolbar_area
):
225 """Encode a Qt::ToolBarArea as a string"""
226 if toolbar_area
== Qt
.LeftToolBarArea
:
228 elif toolbar_area
== Qt
.RightToolBarArea
:
230 elif toolbar_area
== Qt
.TopToolBarArea
:
232 elif toolbar_area
== Qt
.BottomToolBarArea
:
234 else: # fallback to "bottom"
239 def decode_toolbar_area(string
):
240 """Decode an encoded toolbar area string into a Qt::ToolBarArea"""
242 result
= Qt
.LeftToolBarArea
243 elif string
== 'right':
244 result
= Qt
.RightToolBarArea
245 elif string
== 'top':
246 result
= Qt
.TopToolBarArea
247 elif string
== 'bottom':
248 result
= Qt
.BottomToolBarArea
250 result
= Qt
.BottomToolBarArea
254 class ToolbarView(standard
.Dialog
):
255 """Provides the git-cola 'ToolBar' configure dialog"""
256 SEPARATOR_TEXT
= '----------------------------'
258 def __init__(self
, toolbar
, parent
=None):
259 standard
.Dialog
.__init
__(self
, parent
)
260 self
.setWindowTitle(N_('Configure toolbar'))
262 self
.toolbar
= toolbar
263 self
.left_list
= ToolbarTreeWidget(self
)
264 self
.right_list
= DraggableListWidget(self
)
265 self
.text_toolbar_name
= QtWidgets
.QLabel()
266 self
.text_toolbar_name
.setText(N_('Name'))
267 self
.toolbar_name
= QtWidgets
.QLineEdit()
268 self
.toolbar_name
.setText(toolbar
.windowTitle())
269 self
.add_separator
= qtutils
.create_button(N_('Add Separator'))
270 self
.remove_item
= qtutils
.create_button(N_('Remove Element'))
271 checked
= toolbar
.show_icons()
272 checkbox_text
= N_('Show icon? (if available)')
273 self
.show_icon
= qtutils
.checkbox(
274 checkbox_text
, checkbox_text
, checked
)
275 self
.apply_button
= qtutils
.ok_button(N_('Apply'))
276 self
.close_button
= qtutils
.close_button()
277 self
.close_button
.setDefault(True)
279 self
.right_actions
= qtutils
.hbox(
280 defs
.no_margin
, defs
.spacing
,
283 self
.name_layout
= qtutils
.hbox(
284 defs
.no_margin
, defs
.spacing
,
285 self
.text_toolbar_name
,
287 self
.left_layout
= qtutils
.vbox(
288 defs
.no_margin
, defs
.spacing
,
290 self
.right_layout
= qtutils
.vbox(
291 defs
.no_margin
, defs
.spacing
,
294 self
.top_layout
= qtutils
.hbox(
295 defs
.no_margin
, defs
.spacing
,
298 self
.actions_layout
= qtutils
.hbox(
299 defs
.no_margin
, defs
.spacing
,
304 self
.main_layout
= qtutils
.vbox(
305 defs
.margin
, defs
.spacing
,
309 self
.setLayout(self
.main_layout
)
311 qtutils
.connect_button(self
.add_separator
, self
.add_separator_action
)
312 qtutils
.connect_button(self
.remove_item
, self
.remove_item_action
)
313 qtutils
.connect_button(self
.apply_button
, self
.apply_action
)
314 qtutils
.connect_button(self
.close_button
, self
.accept
)
316 self
.load_right_items()
317 self
.load_left_items()
319 self
.init_size(parent
=parent
)
321 def load_right_items(self
):
322 for action
in self
.toolbar
.actions():
324 if data
['child'] == self
.toolbar
.SEPARATOR
:
325 self
.add_separator_action()
327 command
= self
.toolbar
.commands
[data
['child']]
328 title
= command
['title']
329 icon
= command
['icon']
330 self
.right_list
.add_item(title
, data
, icon
)
332 def load_left_items(self
):
334 # def current_children(actions):
336 # for action in actions:
337 # data = action.data()
338 # if data['child'] != self.toolbar.SEPARATOR:
339 # result.append(data['child'])
343 for parent
in self
.toolbar
.tree_layout
:
344 top
= self
.left_list
.insert_top(parent
)
345 # current_items = current_children(self.toolbar.actions())
346 for item
in self
.toolbar
.tree_layout
[parent
]:
347 command
= self
.toolbar
.commands
[item
]
348 child
= create_child(parent
, item
,
349 command
['title'], command
['icon'])
352 top
.sortChildren(0, Qt
.AscendingOrder
)
354 def add_separator_action(self
):
355 data
= {'parent': None, 'child': self
.toolbar
.SEPARATOR
}
356 self
.right_list
.add_separator(self
.SEPARATOR_TEXT
, data
)
358 def remove_item_action(self
):
359 items
= self
.right_list
.selectedItems()
362 self
.right_list
.takeItem(self
.right_list
.row(item
))
364 def apply_action(self
):
366 self
.toolbar
.set_show_icons(get(self
.show_icon
))
367 self
.toolbar
.setWindowTitle(self
.toolbar_name
.text())
369 for item
in self
.right_list
.get_items():
370 data
= item
.data(Qt
.UserRole
)
371 self
.toolbar
.add_action_from_data(data
)
374 class DraggableListMixin(object):
377 def __init__(self
, widget
, Base
):
381 widget
.setAcceptDrops(True)
382 widget
.setSelectionMode(widget
.SingleSelection
)
383 widget
.setDragEnabled(True)
384 widget
.setDropIndicatorShown(True)
386 def dragEnterEvent(self
, event
):
388 self
.Base
.dragEnterEvent(widget
, event
)
390 def dragMoveEvent(self
, event
):
392 self
.Base
.dragMoveEvent(widget
, event
)
394 def dragLeaveEvent(self
, event
):
396 self
.Base
.dragLeaveEvent(widget
, event
)
398 def dropEvent(self
, event
):
400 event
.setDropAction(Qt
.MoveAction
)
401 self
.Base
.dropEvent(widget
, event
)
406 items
= [base
.item(widget
, i
) for i
in range(base
.count(widget
))]
411 class DraggableListWidget(QtWidgets
.QListWidget
):
412 Mixin
= DraggableListMixin
414 def __init__(self
, parent
=None):
415 QtWidgets
.QListWidget
.__init
__(self
, parent
)
417 self
.setAcceptDrops(True)
418 self
.setSelectionMode(self
.SingleSelection
)
419 self
.setDragEnabled(True)
420 self
.setDropIndicatorShown(True)
422 self
._mixin
= self
.Mixin(self
, QtWidgets
.QListWidget
)
424 def dragEnterEvent(self
, event
):
425 return self
._mixin
.dragEnterEvent(event
)
427 def dragMoveEvent(self
, event
):
428 return self
._mixin
.dragMoveEvent(event
)
430 def dropEvent(self
, event
):
431 return self
._mixin
.dropEvent(event
)
433 def add_separator(self
, title
, data
):
434 item
= QtWidgets
.QListWidgetItem()
436 item
.setData(Qt
.UserRole
, data
)
440 def add_item(self
, title
, data
, icon_text
=None):
441 item
= QtWidgets
.QListWidgetItem()
442 item
.setText(N_(title
))
443 item
.setData(Qt
.UserRole
, data
)
445 if icon_text
is not None:
446 icon
= getattr(icons
, icon_text
, None)
452 return self
._mixin
.get_items()
455 class ToolbarTreeWidget(standard
.TreeView
):
457 def __init__(self
, parent
):
458 standard
.TreeView
.__init
__(self
, parent
)
460 self
.setDragEnabled(True)
461 self
.setDragDropMode(QtWidgets
.QAbstractItemView
.DragOnly
)
462 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
463 self
.setDropIndicatorShown(True)
464 self
.setRootIsDecorated(True)
465 self
.setHeaderHidden(True)
466 self
.setAlternatingRowColors(False)
467 self
.setSortingEnabled(False)
469 self
.setModel(QtGui
.QStandardItemModel())
471 def insert_top(self
, title
):
472 item
= create_item(title
, title
)
473 item
.setFlags(Qt
.ItemIsSelectable | Qt
.ItemIsEnabled
)
475 self
.model().insertRow(0, item
)
481 def create_child(parent
, child
, title
, icon_text
=None):
482 data
= {'parent': parent
, 'child': child
}
483 item
= create_item(title
, data
)
485 if icon_text
is not None:
486 icon
= getattr(icons
, icon_text
, None)
492 def create_item(name
, data
):
493 item
= QtGui
.QStandardItem()
495 item
.setEditable(False)
496 item
.setDragEnabled(True)
497 item
.setText(N_(name
))
498 item
.setData(data
, Qt
.UserRole
)