""" This module contains the encodings related widgets (combox, menus,...) """ import locale import logging from pyqode.core.api import ENCODINGS_MAP, convert_to_codec_key from pyqode.qt import QtCore, QtWidgets from pyqode.core.cache import Cache def _logger(): return logging.getLogger(__name__) class EncodingsComboBox(QtWidgets.QComboBox): """ This combo box display the list of user preferred encoding. The last item let you choose an additional encoding from the list of encodings define in :mod:`pyqode.core.api.encodings` using the EncodingsEditorDialog. You can also set the current encoding, it will be automatically appended if not in the user list or set as the current index. """ @property def current_encoding(self): """ Gets/Sets the current encoding """ return self._current_encoding @current_encoding.setter def current_encoding(self, value): self._current_encoding = convert_to_codec_key(value) self._refresh_items() def __init__(self, parent, default_encoding=locale.getpreferredencoding()): super(EncodingsComboBox, self).__init__(parent) self._current_encoding = default_encoding self._lock = False self._refresh_items() self.currentIndexChanged.connect(self._on_current_changed) def _on_current_changed(self, index): if self._lock: return if index == self.count() - 1: from pyqode.core.dialogs import DlgPreferredEncodingsEditor if DlgPreferredEncodingsEditor.edit_encoding(self): self._refresh_items() else: self._current_encoding = self.itemData(index, QtCore.Qt.UserRole) def _refresh_items(self): self._lock = True self.clear() for i, encoding in enumerate(sorted(Cache().preferred_encodings)): encoding = convert_to_codec_key(encoding) try: alias, lang = ENCODINGS_MAP[encoding] except KeyError: _logger().warning('KeyError with encoding: %s', encoding) else: self.addItem('%s (%s)' % (alias, lang)) self.setItemData(i, encoding, QtCore.Qt.UserRole) if encoding == self._current_encoding or \ convert_to_codec_key(alias) == self._current_encoding: self.setCurrentIndex(i) self.insertSeparator(self.count()) self.addItem('Add or remove') self._lock = False class EncodingsMenu(QtWidgets.QMenu): """ Implements a menu that show the user preferred encoding and emit reload_requested when the user changed the selected encoding. This menu is a general purpose menu that you can put anywhere in your application but you will have to manage the reload operation yourself. For an integrated context menu, prefer using :class:`pyqode.core.widgets.EncodingsContextMenu`. """ #: Signal emitted when the user triggered an action. reload_requested = QtCore.Signal(str) @property def current_encoding(self): """ Gets/Sets the current encoding """ return self._current_encoding @current_encoding.setter def current_encoding(self, value): self._current_encoding = convert_to_codec_key(value) self._refresh() def __init__(self, title='Encodings', parent=None, selected_encoding=locale.getpreferredencoding()): super(EncodingsMenu, self).__init__(parent) self.setTitle(title) self._group = QtWidgets.QActionGroup(self) self._edit_action = None self._current_encoding = '' self.current_encoding = selected_encoding def _clear_actions(self): for action in self._group.actions(): self._group.removeAction(action) self.removeAction(action) self.removeAction(self._edit_action) def _refresh(self): self._clear_actions() for i, encoding in enumerate(sorted(Cache().preferred_encodings)): encoding = convert_to_codec_key(encoding) try: alias, lang = ENCODINGS_MAP[encoding] except KeyError: _logger().debug('KeyError with encoding:', encoding) else: action = QtWidgets.QAction('%s (%s)' % (alias, lang), self) action.setData(encoding) action.setCheckable(True) if encoding == self._current_encoding or \ convert_to_codec_key(alias) == self._current_encoding: action.setChecked(True) self.addAction(action) self._group.addAction(action) self._group.triggered.connect(self._on_encoding_triggered) self.addSeparator() self._edit_action = QtWidgets.QAction('Add or remove', self) self._edit_action.triggered.connect(self._on_edit_requested) self.addAction(self._edit_action) def _on_edit_requested(self): from pyqode.core.dialogs import DlgPreferredEncodingsEditor if DlgPreferredEncodingsEditor.edit_encoding(self): self._refresh() @QtCore.Slot(QtWidgets.QAction) def _on_encoding_triggered(self, action): self.reload_requested.emit(action.data()) class EncodingsContextMenu(EncodingsMenu): """ Extends the encoding menu to be tightly coupled with a CodeEdit instance for an easier integration (automatically reload the editor file content when the encoding changed). The parent widget of the menu must be set to a CodeEdit instance. """ def __init__(self, title='Encodings', parent=None, selected_encoding=locale.getpreferredencoding()): from pyqode.core.api import CodeEdit assert isinstance(parent, CodeEdit) super(EncodingsContextMenu, self).__init__( title, parent, selected_encoding) self.reload_requested.connect(self._on_reload_requested) parent.new_text_set.connect(self._refresh) sep = QtWidgets.QAction(self.parent()) sep.setSeparator(True) self.parent().add_menu(self) self._timer = QtCore.QTimer() self._timer.setInterval(1) self._timer.timeout.connect(self._reload) self._refresh() def _refresh(self): self._current_encoding = convert_to_codec_key( self.parent().file.encoding) super(EncodingsContextMenu, self)._refresh() @QtCore.Slot(str) def _on_reload_requested(self, encoding): self._encoding = encoding self._timer.start() @QtCore.Slot() def _reload(self): self._timer.stop() try: self.parent().file.reload(self._encoding) except UnicodeDecodeError: QtWidgets.QMessageBox.warning( self.parent(), 'Decoding error', 'Failed to reload file with encoding %s' % self._encoding) else: try: from pyqode.core.panels import EncodingPanel panel = self.parent().panels.get(EncodingPanel) except KeyError: pass else: panel.cancel()