doc: Document the cola.qt module
[git-cola.git] / cola / qtutils.py
blob48fab082775f06984bb11b409c75fb9dbce3d1c1
1 # Copyright (c) 2008 David Aguilar
2 """This module provides miscellaneous Qt utility functions.
3 """
4 import os
6 from PyQt4 import QtGui
7 from PyQt4 import QtCore
8 from PyQt4.QtCore import SIGNAL
10 import cola
11 from cola import core
12 from cola import utils
13 from cola import signals
14 from cola import resources
15 import cola.views.log
17 _logger = None
18 def logger():
19 global _logger
20 if not _logger:
21 _logger = cola.views.log.LogView()
22 cola.notifier().connect(signals.log_cmd, _logger.log)
23 return _logger
26 def log(status, output):
27 """Sends messages to the log window.
28 """
29 if not output:
30 return
31 cola.notifier().broadcast(signals.log_cmd, status, output)
33 def SLOT(signal, *args, **opts):
34 """
35 Returns a callback that broadcasts a message over the notifier.
37 If the caller of SLOT() provides args or opts then those are
38 used instead of the ones provided by the invoker of the callback.
40 """
41 def broadcast(*local_args, **local_opts):
42 if args or opts:
43 cola.notifier().broadcast(signal, *args, **opts)
44 else:
45 cola.notifier().broadcast(signal, *local_args, **local_opts)
46 return broadcast
49 def prompt(msg, title=None):
50 """Presents the user with an input widget and returns the input."""
51 if title is None:
52 title = msg
53 msg = tr(msg)
54 title = tr(title)
55 parent = QtGui.QApplication.instance().activeWindow()
56 result = QtGui.QInputDialog.getText(parent, msg, title)
57 return (unicode(result[0]), result[1])
59 def create_listwidget_item(text, filename):
60 """Creates a QListWidgetItem with text and the icon at filename."""
61 item = QtGui.QListWidgetItem()
62 item.setIcon(QtGui.QIcon(filename))
63 item.setText(text)
64 return item
66 _tree_icon_cache = {}
67 def create_treewidget_item(text, filename):
68 """Creates a QTreeWidgetItem with text and the icon at filename."""
69 if filename not in _tree_icon_cache:
70 _tree_icon_cache[filename] = QtGui.QIcon(filename)
71 icon = _tree_icon_cache[filename]
72 item = QtGui.QTreeWidgetItem()
73 item.setIcon(0, icon)
74 item.setText(0, text)
75 return item
77 def information(title, message=None):
78 """Launches a QMessageBox information with the
79 provided title and message."""
80 if message is None:
81 message = title
82 title = tr(title)
83 message = tr(message)
84 parent = QtGui.QApplication.instance().activeWindow()
85 QtGui.QMessageBox.information(parent, title, message)
86 # Register globally with the notifier
87 cola.notifier().connect(signals.information, information)
89 def selected_treeitem(tree_widget):
90 """Returns a(id_number, is_selected) for a QTreeWidget."""
91 id_number = None
92 selected = False
93 item = tree_widget.currentItem()
94 if item:
95 id_number = item.data(0, QtCore.Qt.UserRole).toInt()[0]
96 selected = True
97 return(id_number, selected)
99 def selected_row(list_widget):
100 """Returns a(row_number, is_selected) tuple for a QListWidget."""
101 row = list_widget.currentRow()
102 item = list_widget.item(row)
103 selected = item is not None and item.isSelected()
104 return(row, selected)
106 def selection_list(listwidget, items):
107 """Returns an array of model items that correspond to
108 the selected QListWidget indices."""
109 selected = []
110 itemcount = listwidget.count()
111 widgetitems = [ listwidget.item(idx) for idx in range(itemcount) ]
113 for item, widgetitem in zip(items, widgetitems):
114 if widgetitem.isSelected():
115 selected.append(item)
116 return selected
118 def tree_selection(treeitem, items):
119 """Returns model items that correspond to selected widget indices"""
120 itemcount = treeitem.childCount()
121 widgetitems = [ treeitem.child(idx) for idx in range(itemcount) ]
122 selected = []
123 for item, widgetitem in zip(items[:len(widgetitems)], widgetitems):
124 if widgetitem.isSelected():
125 selected.append(item)
127 return selected
129 def selected_item(list_widget, items):
130 """Returns the selected item in a QListWidget."""
131 row, selected = selected_row(list_widget)
132 if selected and row < len(items):
133 return items[row]
134 else:
135 return None
137 def open_dialog(parent, title, filename=None):
138 """Creates an Open File dialog and returns a filename."""
139 title_tr = tr(title)
140 return unicode(QtGui.QFileDialog
141 .getOpenFileName(parent, title_tr, filename))
143 def opendir_dialog(parent, title, path):
144 """Prompts for a directory path"""
146 flags = (QtGui.QFileDialog.ShowDirsOnly |
147 QtGui.QFileDialog.DontResolveSymlinks)
148 title_tr = tr(title)
149 qstr = (QtGui.QFileDialog
150 .getExistingDirectory(parent, title_tr, path, flags))
151 return unicode(qstr)
154 def save_dialog(parent, title, filename=''):
155 """Creates a Save File dialog and returns a filename."""
156 title_tr = parent.tr(title)
157 return unicode(QtGui.QFileDialog
158 .getSaveFileName(parent, title_tr, filename))
160 def icon(basename):
161 """Given a basename returns a QIcon from the corresponding cola icon."""
162 return QtGui.QIcon(resources.icon(basename))
164 def question(parent, title, message, default=True):
165 """Launches a QMessageBox question with the provided title and message.
166 Passing "default=False" will make "No" the default choice."""
167 yes = QtGui.QMessageBox.Yes
168 no = QtGui.QMessageBox.No
169 buttons = yes | no
170 if default:
171 default = yes
172 else:
173 default = no
174 title = tr(title)
175 message = tr(message)
176 result = QtGui.QMessageBox.question(parent, title, message,
177 buttons, default)
178 return result == QtGui.QMessageBox.Yes
180 def set_clipboard(text):
181 """Sets the copy/paste buffer to text."""
182 if not text:
183 return
184 clipboard = QtGui.QApplication.instance().clipboard()
185 clipboard.setText(text, QtGui.QClipboard.Clipboard)
186 clipboard.setText(text, QtGui.QClipboard.Selection)
188 def set_selected_item(widget, idx):
189 """Sets a the currently selected item to the item at index idx."""
190 if type(widget) is QtGui.QTreeWidget:
191 item = widget.topLevelItem(idx)
192 if item:
193 widget.setItemSelected(item, True)
194 widget.setCurrentItem(item)
196 def add_items(widget, items):
197 """Adds items to a widget."""
198 for item in items:
199 widget.addItem(item)
201 def set_items(widget, items):
202 """Clear the existing widget contents and set the new items."""
203 widget.clear()
204 add_items(widget, items)
206 def tr(txt):
207 """Translate a string into a local language."""
208 if type(txt) is QtCore.QString:
209 # This has already been translated; leave as-is
210 return unicode(txt)
211 return unicode(QtGui.QApplication.instance().translate('', txt))
213 def icon_file(filename, staged=False, untracked=False):
214 """Returns a file path representing a corresponding file path."""
215 if staged:
216 if os.path.exists(core.encode(filename)):
217 ifile = resources.icon('staged.png')
218 else:
219 ifile = resources.icon('removed.png')
220 elif untracked:
221 ifile = resources.icon('untracked.png')
222 else:
223 ifile = utils.file_icon(filename)
224 return ifile
226 def icon_for_file(filename, staged=False, untracked=False):
227 """Returns a QIcon for a particular file path."""
228 ifile = icon_file(filename, staged=staged, untracked=untracked)
229 return icon(ifile)
231 def create_treeitem(filename, staged=False, untracked=False, check=True):
232 """Given a filename, return a QListWidgetItem suitable
233 for adding to a QListWidget. "staged" and "untracked"
234 controls whether to use the appropriate icons."""
235 if check:
236 ifile = icon_file(filename, staged=staged, untracked=untracked)
237 else:
238 ifile = resources.icon('staged.png')
239 return create_treewidget_item(filename, ifile)
242 def update_file_icons(widget, items, staged=True,
243 untracked=False, offset=0):
244 """Populate a QListWidget with custom icon items."""
245 for idx, model_item in enumerate(items):
246 item = widget.item(idx+offset)
247 if item:
248 item.setIcon(icon_for_file(model_item, staged, untracked))
250 def set_listwidget_strings(widget, items):
251 """Sets a list widget to the strings passed in items."""
252 widget.clear()
253 add_items(widget, [ QtGui.QListWidgetItem(i) for i in items ])
255 _icon_cache = {}
256 def cached_icon(key):
257 """Maintain a cache of standard icons and return cache entries."""
258 if key not in _icon_cache:
259 style = QtGui.QApplication.instance().style()
260 _icon_cache[key] = style.standardIcon(key)
261 return _icon_cache[key]
263 def dir_icon():
264 """Return a standard icon for a directory."""
265 return cached_icon(QtGui.QStyle.SP_DirIcon)
267 def file_icon():
268 """Return a standard icon for a file."""
269 return cached_icon(QtGui.QStyle.SP_FileIcon)
271 def diff_font():
272 """Return the diff font string."""
273 qfont = QtGui.QFont()
274 font = cola.model().cola_config('fontdiff')
275 if font and qfont.fromString(font):
276 return qfont
277 family = 'Monospace'
278 if cola.utils.is_darwin():
279 family = 'Monaco'
280 qfont.setFamily(family)
281 font = unicode(qfont.toString())
282 # TODO this might not be the best place for the default
283 cola.model().set_diff_font(font)
284 return qfont
286 def set_diff_font(widget):
287 """Updates the diff font based on the configured value."""
288 qfont = diff_font()
289 block = widget.signalsBlocked()
290 widget.blockSignals(True)
291 if isinstance(widget, QtGui.QFontComboBox):
292 widget.setCurrentFont(qfont)
293 else:
294 widget.setFont(qfont)
295 widget.blockSignals(block)
298 def add_close_action(widget):
299 """Adds a Ctrl+w close action to a widget."""
300 action = QtGui.QAction(widget.tr('Close...'), widget)
301 action.setShortcut('Ctrl+w')
302 widget.addAction(action)
303 widget.connect(action, SIGNAL('triggered()'), widget.close)
306 def center_on_screen(widget):
307 """Move widget to the center of the default screen"""
308 desktop = QtGui.QApplication.instance().desktop()
309 rect = desktop.screenGeometry(QtGui.QCursor().pos())
310 cy = rect.height()/2
311 cx = rect.width()/2
312 widget.move(cx - widget.width()/2, cy - widget.height()/2)