widgets.text: allow passing `n` to move()
[git-cola.git] / cola / qtutils.py
blob589af818ce2067ada88780ace6408a318a31195e
1 # Copyright (c) 2008 David Aguilar
2 """This module provides miscellaneous Qt utility functions.
3 """
4 from __future__ import division, absolute_import, unicode_literals
6 import mimetypes
7 import os
8 import subprocess
10 from PyQt4 import QtGui
11 from PyQt4 import QtCore
12 from PyQt4.QtCore import Qt
13 from PyQt4.QtCore import SIGNAL
15 from cola import core
16 from cola import gitcfg
17 from cola import utils
18 from cola import resources
19 from cola.decorators import memoize
20 from cola.i18n import N_
21 from cola.interaction import Interaction
22 from cola.models.prefs import FONTDIFF
23 from cola.widgets import defs
24 from cola.compat import ustr
27 KNOWN_FILE_MIME_TYPES = [
28 ('text', 'script.png'),
29 ('image', 'image.png'),
30 ('python', 'script.png'),
31 ('ruby', 'script.png'),
32 ('shell', 'script.png'),
33 ('perl', 'script.png'),
34 ('octet', 'binary.png'),
37 KNOWN_FILE_EXTENSIONS = {
38 '.java': 'script.png',
39 '.groovy': 'script.png',
40 '.cpp': 'script.png',
41 '.c': 'script.png',
42 '.h': 'script.png',
43 '.cxx': 'script.png',
47 def connect_action(action, fn):
48 """Connectc an action to a function"""
49 action.connect(action, SIGNAL('triggered()'), fn)
52 def connect_action_bool(action, fn):
53 """Connect a triggered(bool) action to a function"""
54 action.connect(action, SIGNAL('triggered(bool)'), fn)
57 def connect_button(button, fn):
58 """Connect a button to a function"""
59 button.connect(button, SIGNAL('clicked()'), fn)
62 def button_action(button, action):
63 """Make a button trigger an action"""
64 connect_button(button, action.trigger)
67 def connect_toggle(toggle, fn):
68 toggle.connect(toggle, SIGNAL('toggled(bool)'), fn)
71 def active_window():
72 return QtGui.QApplication.activeWindow()
75 def hbox(margin, spacing, *items):
76 return box(QtGui.QHBoxLayout, margin, spacing, *items)
79 def vbox(margin, spacing, *items):
80 return box(QtGui.QVBoxLayout, margin, spacing, *items)
83 STRETCH = object()
84 SKIPPED = object()
87 def box(cls, margin, spacing, *items):
88 stretch = STRETCH
89 skipped = SKIPPED
90 layout = cls()
91 layout.setMargin(margin)
92 layout.setSpacing(spacing)
94 for i in items:
95 if i is stretch:
96 layout.addStretch()
97 elif i is skipped:
98 continue
99 elif isinstance(i, QtGui.QWidget):
100 layout.addWidget(i)
101 elif isinstance(i, (QtGui.QHBoxLayout, QtGui.QVBoxLayout,
102 QtGui.QFormLayout, QtGui.QLayout)):
103 layout.addLayout(i)
104 elif isinstance(i, (int, long)):
105 layout.addSpacing(i)
107 return layout
110 def form(margin, spacing, *widgets):
111 layout = QtGui.QFormLayout()
112 layout.setMargin(margin)
113 layout.setSpacing(spacing)
114 layout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow)
116 for idx, (label, widget) in enumerate(widgets):
117 if isinstance(label, (str, ustr)):
118 layout.addRow(label, widget)
119 else:
120 layout.setWidget(idx, QtGui.QFormLayout.LabelRole, label)
121 layout.setWidget(idx, QtGui.QFormLayout.FieldRole, widget)
123 return layout
126 def grid(margin, spacing, *widgets):
127 layout = QtGui.QGridLayout()
128 layout.setMargin(defs.no_margin)
129 layout.setSpacing(defs.spacing)
131 for row in widgets:
132 item = row[0]
133 if isinstance(item, QtGui.QWidget):
134 layout.addWidget(*row)
135 elif isinstance(item, QtGui.QLayoutItem):
136 layout.addItem(*row)
138 return layout
141 def splitter(orientation, *widgets):
142 layout = QtGui.QSplitter()
143 layout.setOrientation(orientation)
144 layout.setHandleWidth(defs.handle_width)
145 layout.setChildrenCollapsible(True)
146 for idx, widget in enumerate(widgets):
147 layout.addWidget(widget)
148 layout.setStretchFactor(idx, 1)
150 return layout
152 def prompt(msg, title=None, text=''):
153 """Presents the user with an input widget and returns the input."""
154 if title is None:
155 title = msg
156 result = QtGui.QInputDialog.getText(active_window(), msg, title,
157 QtGui.QLineEdit.Normal, text)
158 return (ustr(result[0]), result[1])
161 def create_listwidget_item(text, filename):
162 """Creates a QListWidgetItem with text and the icon at filename."""
163 item = QtGui.QListWidgetItem()
164 item.setIcon(QtGui.QIcon(filename))
165 item.setText(text)
166 return item
169 class TreeWidgetItem(QtGui.QTreeWidgetItem):
171 TYPE = QtGui.QStandardItem.UserType + 101
173 def __init__(self, path, icon, deleted):
174 QtGui.QTreeWidgetItem.__init__(self)
175 self.path = path
176 self.deleted = deleted
177 self.setIcon(0, cached_icon_from_path(icon))
178 self.setText(0, path)
180 def type(self):
181 return self.TYPE
184 def paths_from_indexes(model, indexes,
185 item_type=TreeWidgetItem.TYPE,
186 item_filter=None):
187 """Return paths from a list of QStandardItemModel indexes"""
188 items = [model.itemFromIndex(i) for i in indexes]
189 return paths_from_items(items, item_type=item_type, item_filter=item_filter)
192 def paths_from_items(items,
193 item_type=TreeWidgetItem.TYPE,
194 item_filter=None):
195 """Return a list of paths from a list of items"""
196 if item_filter is None:
197 item_filter = lambda x: True
198 return [i.path for i in items
199 if i.type() == item_type and item_filter(i)]
202 @memoize
203 def cached_icon_from_path(filename):
204 return QtGui.QIcon(filename)
207 def mkicon(icon, default=None):
208 if icon is None and default is not None:
209 icon = default()
210 elif icon and isinstance(icon, (str, ustr)):
211 icon = QtGui.QIcon(icon)
212 return icon
215 def confirm(title, text, informative_text, ok_text,
216 icon=None, default=True,
217 cancel_text=None, cancel_icon=None):
218 """Confirm that an action should take place"""
219 msgbox = QtGui.QMessageBox(active_window())
220 msgbox.setWindowModality(Qt.WindowModal)
221 msgbox.setWindowTitle(title)
222 msgbox.setText(text)
223 msgbox.setInformativeText(informative_text)
225 icon = mkicon(icon, ok_icon)
226 ok = msgbox.addButton(ok_text, QtGui.QMessageBox.ActionRole)
227 ok.setIcon(icon)
229 cancel = msgbox.addButton(QtGui.QMessageBox.Cancel)
230 cancel_icon = mkicon(cancel_icon, discard_icon)
231 cancel.setIcon(cancel_icon)
232 if cancel_text:
233 cancel.setText(cancel_text)
235 if default:
236 msgbox.setDefaultButton(ok)
237 else:
238 msgbox.setDefaultButton(cancel)
239 msgbox.exec_()
240 return msgbox.clickedButton() == ok
243 class ResizeableMessageBox(QtGui.QMessageBox):
245 def __init__(self, parent):
246 QtGui.QMessageBox.__init__(self, parent)
247 self.setMouseTracking(True)
248 self.setSizeGripEnabled(True)
250 def event(self, event):
251 res = QtGui.QMessageBox.event(self, event)
252 event_type = event.type()
253 if (event_type == QtCore.QEvent.MouseMove or
254 event_type == QtCore.QEvent.MouseButtonPress):
255 maxi = QtCore.QSize(1024*4, 1024*4)
256 self.setMaximumSize(maxi)
257 text = self.findChild(QtGui.QTextEdit)
258 if text is not None:
259 expand = QtGui.QSizePolicy.Expanding
260 text.setSizePolicy(QtGui.QSizePolicy(expand, expand))
261 text.setMaximumSize(maxi)
262 return res
265 def critical(title, message=None, details=None):
266 """Show a warning with the provided title and message."""
267 if message is None:
268 message = title
269 mbox = ResizeableMessageBox(active_window())
270 mbox.setWindowTitle(title)
271 mbox.setTextFormat(Qt.PlainText)
272 mbox.setText(message)
273 mbox.setIcon(QtGui.QMessageBox.Critical)
274 mbox.setStandardButtons(QtGui.QMessageBox.Close)
275 mbox.setDefaultButton(QtGui.QMessageBox.Close)
276 if details:
277 mbox.setDetailedText(details)
278 mbox.exec_()
281 def information(title, message=None, details=None, informative_text=None):
282 """Show information with the provided title and message."""
283 if message is None:
284 message = title
285 mbox = QtGui.QMessageBox(active_window())
286 mbox.setStandardButtons(QtGui.QMessageBox.Close)
287 mbox.setDefaultButton(QtGui.QMessageBox.Close)
288 mbox.setWindowTitle(title)
289 mbox.setWindowModality(Qt.WindowModal)
290 mbox.setTextFormat(Qt.PlainText)
291 mbox.setText(message)
292 if informative_text:
293 mbox.setInformativeText(informative_text)
294 if details:
295 mbox.setDetailedText(details)
296 # Render git-cola.svg into a 1-inch wide pixmap
297 pixmap = git_icon().pixmap(96)
298 mbox.setIconPixmap(pixmap)
299 mbox.exec_()
302 def question(title, msg, default=True):
303 """Launches a QMessageBox question with the provided title and message.
304 Passing "default=False" will make "No" the default choice."""
305 yes = QtGui.QMessageBox.Yes
306 no = QtGui.QMessageBox.No
307 buttons = yes | no
308 if default:
309 default = yes
310 else:
311 default = no
312 result = (QtGui.QMessageBox
313 .question(active_window(), title, msg, buttons, default))
314 return result == QtGui.QMessageBox.Yes
317 def tree_selection(tree_item, items):
318 """Returns an array of model items that correspond to the selected
319 QTreeWidgetItem children"""
320 selected = []
321 count = min(tree_item.childCount(), len(items))
322 for idx in range(count):
323 if tree_item.child(idx).isSelected():
324 selected.append(items[idx])
326 return selected
329 def tree_selection_items(tree_item):
330 """Returns selected widget items"""
331 selected = []
332 for idx in range(tree_item.childCount()):
333 child = tree_item.child(idx)
334 if child.isSelected():
335 selected.append(child)
337 return selected
340 def selected_item(list_widget, items):
341 """Returns the model item that corresponds to the selected QListWidget
342 row."""
343 widget_items = list_widget.selectedItems()
344 if not widget_items:
345 return None
346 widget_item = widget_items[0]
347 row = list_widget.row(widget_item)
348 if row < len(items):
349 return items[row]
350 else:
351 return None
354 def selected_items(list_widget, items):
355 """Returns an array of model items that correspond to the selected
356 QListWidget rows."""
357 item_count = len(items)
358 selected = []
359 for widget_item in list_widget.selectedItems():
360 row = list_widget.row(widget_item)
361 if row < item_count:
362 selected.append(items[row])
363 return selected
366 def open_file(title, directory=None):
367 """Creates an Open File dialog and returns a filename."""
368 return ustr(QtGui.QFileDialog
369 .getOpenFileName(active_window(), title, directory))
372 def open_files(title, directory=None, filter=None):
373 """Creates an Open File dialog and returns a list of filenames."""
374 return (QtGui.QFileDialog
375 .getOpenFileNames(active_window(), title, directory, filter))
378 def opendir_dialog(title, path):
379 """Prompts for a directory path"""
381 flags = (QtGui.QFileDialog.ShowDirsOnly |
382 QtGui.QFileDialog.DontResolveSymlinks)
383 return ustr(QtGui.QFileDialog
384 .getExistingDirectory(active_window(),
385 title, path, flags))
388 def save_as(filename, title='Save As...'):
389 """Creates a Save File dialog and returns a filename."""
390 return ustr(QtGui.QFileDialog
391 .getSaveFileName(active_window(), title, filename))
394 def icon(basename):
395 """Given a basename returns a QIcon from the corresponding cola icon."""
396 return QtGui.QIcon(resources.icon(basename))
399 def copy_path(filename, absolute=True):
400 """Copy a filename to the clipboard"""
401 if filename is None:
402 return
403 if absolute:
404 filename = core.abspath(filename)
405 set_clipboard(filename)
408 def set_clipboard(text):
409 """Sets the copy/paste buffer to text."""
410 if not text:
411 return
412 clipboard = QtGui.QApplication.instance().clipboard()
413 clipboard.setText(text, QtGui.QClipboard.Clipboard)
414 clipboard.setText(text, QtGui.QClipboard.Selection)
417 def add_action_bool(widget, text, fn, checked, *shortcuts):
418 action = _add_action(widget, text, fn, connect_action_bool, *shortcuts)
419 action.setCheckable(True)
420 action.setChecked(checked)
421 return action
424 def add_action(widget, text, fn, *shortcuts):
425 return _add_action(widget, text, fn, connect_action, *shortcuts)
428 def _add_action(widget, text, fn, connect, *shortcuts):
429 action = QtGui.QAction(text, widget)
430 connect(action, fn)
431 if shortcuts:
432 action.setShortcuts(shortcuts)
433 action.setShortcutContext(Qt.WidgetWithChildrenShortcut)
434 widget.addAction(action)
435 return action
438 def set_selected_item(widget, idx):
439 """Sets a the currently selected item to the item at index idx."""
440 if type(widget) is QtGui.QTreeWidget:
441 item = widget.topLevelItem(idx)
442 if item:
443 widget.setItemSelected(item, True)
444 widget.setCurrentItem(item)
447 def add_items(widget, items):
448 """Adds items to a widget."""
449 for item in items:
450 widget.addItem(item)
453 def set_items(widget, items):
454 """Clear the existing widget contents and set the new items."""
455 widget.clear()
456 add_items(widget, items)
459 def icon_name_for_filename(filename):
460 """Returns an icon name based on the filename."""
461 mimetype = mimetypes.guess_type(filename)[0]
462 if mimetype is not None:
463 mimetype = mimetype.lower()
464 for filetype, icon_name in KNOWN_FILE_MIME_TYPES:
465 if filetype in mimetype:
466 return icon_name
467 extension = os.path.splitext(filename)[1]
468 return KNOWN_FILE_EXTENSIONS.get(extension.lower(), 'generic.png')
471 def icon_from_filename(filename):
472 icon_name = icon_name_for_filename(filename)
473 return cached_icon_from_path(resources.icon(icon_name))
476 def create_treeitem(filename, staged=False, deleted=False, untracked=False):
477 """Given a filename, return a TreeListItem suitable for adding to a
478 QListWidget. "staged", "deleted, and "untracked" control whether to use
479 the appropriate icons."""
480 if deleted:
481 icon_name = 'removed.png'
482 elif staged:
483 icon_name = 'staged-item.png'
484 elif untracked:
485 icon_name = 'untracked.png'
486 else:
487 icon_name = icon_name_for_filename(filename)
488 return TreeWidgetItem(filename, resources.icon(icon_name), deleted=deleted)
491 @memoize
492 def cached_icon(key):
493 """Maintain a cache of standard icons and return cache entries."""
494 style = QtGui.QApplication.instance().style()
495 return style.standardIcon(key)
498 def dir_icon():
499 """Return a standard icon for a directory."""
500 return cached_icon(QtGui.QStyle.SP_DirIcon)
503 def file_icon():
504 """Return a standard icon for a file."""
505 return cached_icon(QtGui.QStyle.SP_FileIcon)
508 def apply_icon():
509 """Return a standard Apply icon"""
510 return cached_icon(QtGui.QStyle.SP_DialogApplyButton)
513 def new_icon():
514 return cached_icon(QtGui.QStyle.SP_FileDialogNewFolder)
517 def save_icon():
518 """Return a standard Save icon"""
519 return cached_icon(QtGui.QStyle.SP_DialogSaveButton)
522 def ok_icon():
523 """Return a standard Ok icon"""
524 return cached_icon(QtGui.QStyle.SP_DialogOkButton)
527 def open_icon():
528 """Return a standard open directory icon"""
529 return cached_icon(QtGui.QStyle.SP_DirOpenIcon)
532 def help_icon():
533 """Return a standard open directory icon"""
534 return cached_icon(QtGui.QStyle.SP_DialogHelpButton)
537 def add_icon():
538 return theme_icon('list-add', fallback='add.svg')
541 def remove_icon():
542 return theme_icon('list-remove', fallback='remove.svg')
545 def open_file_icon():
546 return theme_icon('document-open', fallback='open.svg')
549 def options_icon():
550 """Return a standard open directory icon"""
551 return theme_icon('configure', fallback='options.svg')
554 def filter_icon():
555 """Return a filter icon"""
556 return theme_icon('view-filter.png')
559 def dir_close_icon():
560 """Return a standard closed directory icon"""
561 return cached_icon(QtGui.QStyle.SP_DirClosedIcon)
564 def titlebar_close_icon():
565 """Return a dock widget close icon"""
566 return cached_icon(QtGui.QStyle.SP_TitleBarCloseButton)
569 def titlebar_normal_icon():
570 """Return a dock widget close icon"""
571 return cached_icon(QtGui.QStyle.SP_TitleBarNormalButton)
574 def git_icon():
576 Return git-cola icon from X11 theme if it exists.
577 Else fallback to default hardcoded icon.
579 return theme_icon('git-cola.svg')
582 def reload_icon():
583 """Returna standard Refresh icon"""
584 return cached_icon(QtGui.QStyle.SP_BrowserReload)
587 def discard_icon():
588 """Return a standard Discard icon"""
589 return cached_icon(QtGui.QStyle.SP_DialogDiscardButton)
592 def close_icon():
593 """Return a standard Close icon"""
594 return cached_icon(QtGui.QStyle.SP_DialogCloseButton)
597 def add_close_action(widget):
598 """Adds close action and shortcuts to a widget."""
599 return add_action(widget, N_('Close...'),
600 widget.close, QtGui.QKeySequence.Close, 'Ctrl+Q')
603 def center_on_screen(widget):
604 """Move widget to the center of the default screen"""
605 desktop = QtGui.QApplication.instance().desktop()
606 rect = desktop.screenGeometry(QtGui.QCursor().pos())
607 cy = rect.height()//2
608 cx = rect.width()//2
609 widget.move(cx - widget.width()//2, cy - widget.height()//2)
612 def default_size(parent, width, height):
613 """Return the parent's size, or the provided defaults"""
614 if parent is not None:
615 width = parent.width()
616 height = parent.height()
617 return (width, height)
619 @memoize
620 def theme_icon(name, fallback=None):
621 """Grab an icon from the current theme with a fallback
623 Support older versions of Qt checking for fromTheme's availability.
626 if hasattr(QtGui.QIcon, 'fromTheme'):
627 base, ext = os.path.splitext(name)
628 if fallback:
629 qicon = QtGui.QIcon.fromTheme(base, icon(fallback))
630 else:
631 qicon = QtGui.QIcon.fromTheme(base)
632 if not qicon.isNull():
633 return qicon
634 return icon(fallback or name)
637 def default_monospace_font():
638 font = QtGui.QFont()
639 family = 'Monospace'
640 if utils.is_darwin():
641 family = 'Monaco'
642 font.setFamily(family)
643 return font
646 def diff_font_str():
647 font_str = gitcfg.current().get(FONTDIFF)
648 if font_str is None:
649 font = default_monospace_font()
650 font_str = ustr(font.toString())
651 return font_str
654 def diff_font():
655 font_str = diff_font_str()
656 font = QtGui.QFont()
657 font.fromString(font_str)
658 return font
661 def create_button(text='', layout=None, tooltip=None, icon=None):
662 """Create a button, set its title, and add it to the parent."""
663 button = QtGui.QPushButton()
664 button.setCursor(Qt.PointingHandCursor)
665 if text:
666 button.setText(text)
667 if icon is not None:
668 button.setIcon(icon)
669 if tooltip is not None:
670 button.setToolTip(tooltip)
671 if layout is not None:
672 layout.addWidget(button)
673 return button
676 def create_action_button(tooltip=None, icon=None):
677 button = QtGui.QPushButton()
678 button.setFixedSize(QtCore.QSize(16, 16))
679 button.setCursor(Qt.PointingHandCursor)
680 button.setFlat(True)
681 if tooltip is not None:
682 button.setToolTip(tooltip)
683 if icon is not None:
684 pixmap = icon.pixmap(QtCore.QSize(16, 16))
685 button.setIcon(QtGui.QIcon(pixmap))
686 return button
689 def hide_button_menu_indicator(button):
690 cls = type(button)
691 name = cls.__name__
692 stylesheet = """
693 %(name)s::menu-indicator {
694 image: none;
697 if name == 'QPushButton':
698 stylesheet += """
699 %(name)s {
700 border-style: none;
703 button.setStyleSheet(stylesheet % {'name': name})
706 class DockTitleBarWidget(QtGui.QWidget):
708 def __init__(self, parent, title, stretch=True):
709 QtGui.QWidget.__init__(self, parent)
710 self.label = label = QtGui.QLabel()
711 font = label.font()
712 font.setBold(True)
713 label.setFont(font)
714 label.setText(title)
716 self.setCursor(Qt.OpenHandCursor)
718 self.close_button = create_action_button(
719 tooltip=N_('Close'), icon=titlebar_close_icon())
721 self.toggle_button = create_action_button(
722 tooltip=N_('Detach'), icon=titlebar_normal_icon())
724 self.corner_layout = hbox(defs.no_margin, defs.spacing)
726 if stretch:
727 separator = STRETCH
728 else:
729 separator = SKIPPED
731 self.main_layout = hbox(defs.small_margin, defs.spacing,
732 label, separator, self.corner_layout,
733 self.toggle_button, self.close_button)
734 self.setLayout(self.main_layout)
736 connect_button(self.toggle_button, self.toggle_floating)
737 connect_button(self.close_button, self.toggle_visibility)
739 def toggle_floating(self):
740 self.parent().setFloating(not self.parent().isFloating())
741 self.update_tooltips()
743 def toggle_visibility(self):
744 self.parent().toggleViewAction().trigger()
746 def set_title(self, title):
747 self.label.setText(title)
749 def add_corner_widget(self, widget):
750 self.corner_layout.addWidget(widget)
752 def update_tooltips(self):
753 if self.parent().isFloating():
754 tooltip = N_('Attach')
755 else:
756 tooltip = N_('Detach')
757 self.toggle_button.setToolTip(tooltip)
760 def create_dock(title, parent, stretch=True):
761 """Create a dock widget and set it up accordingly."""
762 dock = QtGui.QDockWidget(parent)
763 dock.setWindowTitle(title)
764 dock.setObjectName(title)
765 titlebar = DockTitleBarWidget(dock, title, stretch=stretch)
766 dock.setTitleBarWidget(titlebar)
767 if hasattr(parent, 'dockwidgets'):
768 parent.dockwidgets.append(dock)
769 return dock
772 def create_menu(title, parent):
773 """Create a menu and set its title."""
774 qmenu = QtGui.QMenu(parent)
775 qmenu.setTitle(title)
776 return qmenu
779 def create_toolbutton(text=None, layout=None, tooltip=None, icon=None):
780 button = QtGui.QToolButton()
781 button.setAutoRaise(True)
782 button.setAutoFillBackground(True)
783 button.setCursor(Qt.PointingHandCursor)
784 if icon is not None:
785 button.setIcon(icon)
786 if text is not None:
787 button.setText(text)
788 button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
789 if tooltip is not None:
790 button.setToolTip(tooltip)
791 if layout is not None:
792 layout.addWidget(button)
793 return button
796 def mimedata_from_paths(paths):
797 """Return mimedata with a list of absolute path URLs"""
799 abspaths = [core.abspath(path) for path in paths]
800 urls = [QtCore.QUrl.fromLocalFile(path) for path in abspaths]
802 mimedata = QtCore.QMimeData()
803 mimedata.setUrls(urls)
805 # The text/x-moz-list format is always included by Qt, and doing
806 # mimedata.removeFormat('text/x-moz-url') has no effect.
807 # C.f. http://www.qtcentre.org/threads/44643-Dragging-text-uri-list-Qt-inserts-garbage
809 # gnome-terminal expects utf-16 encoded text, but other terminals,
810 # e.g. terminator, prefer utf-8, so allow cola.dragencoding
811 # to override the default.
812 paths_text = subprocess.list2cmdline(abspaths)
813 encoding = gitcfg.current().get('cola.dragencoding', 'utf-16')
814 moz_text = core.encode(paths_text, encoding=encoding)
815 mimedata.setData('text/x-moz-url', moz_text)
817 return mimedata
820 def path_mimetypes():
821 return ['text/uri-list', 'text/x-moz-url']
823 # Syntax highlighting
825 def rgba(r, g, b, a=255):
826 c = QtGui.QColor()
827 c.setRgb(r, g, b)
828 c.setAlpha(a)
829 return c
832 def RGB(args):
833 return rgba(*args)
836 def make_format(fg=None, bg=None, bold=False):
837 fmt = QtGui.QTextCharFormat()
838 if fg:
839 fmt.setForeground(fg)
840 if bg:
841 fmt.setBackground(bg)
842 if bold:
843 fmt.setFontWeight(QtGui.QFont.Bold)
844 return fmt
847 def install():
848 Interaction.critical = staticmethod(critical)
849 Interaction.confirm = staticmethod(confirm)
850 Interaction.question = staticmethod(question)
851 Interaction.information = staticmethod(information)