Exemplo n.º 1
0
class VirtualAddressEditor(CustomEditor):

    def __init__(self, 
                 parent = None, 
                 editable = True, 
                 address_type = None, 
                 address_validator = default_address_validator,
                 field_name = 'virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setObjectName( field_name )
        self._address_type = address_type
        self._address_validator = address_validator
        self.layout = QtGui.QHBoxLayout()
        self.layout.setContentsMargins( 0, 0, 0, 0)
        self.combo = QtGui.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.combo.setEnabled(editable)
        if address_type:
            self.combo.setVisible(False)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit( self )
        self.editor.setEnabled(editable)
        self.editor.set_minimum_width( 30 )
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        self.editable = editable
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtGui.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect( self.mail_click )
        self.label.hide()

        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.editor.textEdited.connect(self.editorValueChanged)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)

        self.setLayout(self.layout)
        self.checkValue(self.editor.text())

    @QtCore.pyqtSlot()
    def comboIndexChanged(self):
        self.checkValue(self.editor.text())
        self.emit_editing_finished()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.editor.setText(value[1])
            idx = camelot.types.VirtualAddress.virtual_address_types.index(self._address_type or value[0])
            self.combo.setCurrentIndex(idx)
            icon = Icon('tango/16x16/devices/printer.png').getQIcon()
# These icons don't exist any more in the new tango icon set
#            if str(self.combo.currentText()) == 'phone':
#                icon = Icon('tango/16x16/devices/phone.png').getQIcon()
            if str(self.combo.currentText()) == 'fax':
                icon = Icon('tango/16x16/devices/printer.png').getQIcon()
#            if str(self.combo.currentText()) == 'mobile':
#                icon = Icon('tango/16x16/devices/mobile.png').getQIcon()
#            if str(self.combo.currentText()) == 'im':
#                icon = Icon('tango/16x16/places/instant-messaging.png').getQIcon()
#            if str(self.combo.currentText()) == 'pager':
#                icon = Icon('tango/16x16/devices/pager.png').getQIcon()
            if str(self.combo.currentText()) == 'email':
                icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
                #self.label.setFocusPolicy(Qt.StrongFocus)
                #self.label.setAutoFillBackground(True)
                self.label.setIcon(icon)
                self.label.setEnabled( self.editable )
                self.label.show()
            else:
                self.label.hide()
                self.label.setIcon(icon)
                self.label.setEnabled(self.editable)
                self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)

#      self.update()
#      self.label.update()
#      self.layout.update()


            self.checkValue(value[1])

    def get_value(self):
        value = (unicode(self.combo.currentText()), unicode(self.editor.text()))
        return CustomEditor.get_value(self) or value

    def set_enabled(self, editable=True):
        self.combo.setEnabled(editable)
        self.editor.setEnabled(editable)
        if not editable:
            self.label.setEnabled(False)
        else:
            if self.combo.currentText() == 'email':
                self.label.setEnabled(True)

    def checkValue(self, text):
        address_type = unicode( self.combo.currentText() )
        valid, _corrected = self._address_validator( address_type, unicode( text ) )
        self.editor.set_valid( valid )

    def editorValueChanged(self, text):
        self.checkValue(text)

    @QtCore.pyqtSlot()
    def mail_click(self):
        address = self.editor.text()
        url = QtCore.QUrl()
        url.setUrl( u'mailto:%s?subject=Subject'%unicode(address) )
        QtGui.QDesktopServices.openUrl(url)

    def emit_editing_finished(self):
        self.value = []
        self.value.append(str(self.combo.currentText()))
        self.value.append(str(self.editor.text()))
        self.set_value(self.value)
        # emiting editingFinished without a value for the mechanism itself will lead to
        # integrity errors
        if self.value[1]:
            self.editingFinished.emit()

    def set_background_color(self, background_color):
        set_background_color_palette( self.editor, background_color )
            
    def set_field_attributes(self, editable = True,
                                   background_color = None,
                                   tooltip = None, **kwargs):
        self.set_enabled(editable)
        self.set_background_color(background_color)
        self.setToolTip(unicode(tooltip or ''))
Exemplo n.º 2
0
class LocalFileEditor(CustomEditor):
    """Widget for browsing local files and directories"""

    browse_icon = Icon('tango/16x16/places/folder-saved-search.png')

    def __init__(self,
                 parent=None,
                 field_name='local_file',
                 directory=False,
                 save_as=False,
                 file_filter='All files (*)',
                 **kwargs):
        CustomEditor.__init__(self, parent)
        self.setObjectName(field_name)
        self._directory = directory
        self._save_as = save_as
        self._file_filter = file_filter
        self.setup_widget()

    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        browse_button = QtGui.QToolButton(self)
        browse_button.setFocusPolicy(Qt.ClickFocus)
        browse_button.setIcon(self.browse_icon.getQIcon())
        browse_button.setToolTip(_('Browse'))
        browse_button.setAutoRaise(True)
        browse_button.clicked.connect(self.browse_button_clicked)

        self.filename = DecoratedLineEdit(self)
        self.filename.editingFinished.connect(self.filename_editing_finished)
        self.setFocusProxy(self.filename)

        layout.addWidget(self.filename)
        layout.addWidget(browse_button)
        self.setLayout(layout)

    @QtCore.pyqtSlot()
    def filename_editing_finished(self):
        self.valueChanged.emit()
        self.editingFinished.emit()

    @QtCore.pyqtSlot()
    def browse_button_clicked(self):
        current_directory = os.path.dirname(self.get_value())
        if self._directory:
            value = QtGui.QFileDialog.getExistingDirectory(
                self, directory=current_directory)
        elif self._save_as:
            value = QtGui.QFileDialog.getSaveFileName(
                self, filter=self._file_filter, directory=current_directory)
        else:
            value = QtGui.QFileDialog.getOpenFileName(
                self, filter=self._file_filter, directory=current_directory)
        value = os.path.abspath(unicode(value))
        self.filename.setText(value)
        self.valueChanged.emit()
        self.editingFinished.emit()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.filename.setText(value)
        else:
            self.filename.setText('')
        self.valueChanged.emit()
        return value

    def get_value(self):
        return CustomEditor.get_value(self) or unicode(self.filename.text())

    value = QtCore.pyqtProperty(str, get_value, set_value)

    def set_field_attributes(self,
                             editable=True,
                             background_color=None,
                             tooltip=None,
                             **kwargs):
        self.setEnabled(editable)
        if self.filename:
            set_background_color_palette(self.filename, background_color)
            self.filename.setToolTip(unicode(tooltip or ''))
Exemplo n.º 3
0
class Many2OneEditor(CustomEditor):
    """Widget for editing many 2 one relations"""

    arrow_down_key_pressed = QtCore.qt_signal()

    class CompletionsModel(QtCore.QAbstractListModel):
        def __init__(self, parent=None):
            QtCore.QAbstractListModel.__init__(self, parent)
            self._completions = []

        def setCompletions(self, completions):
            self._completions = completions
            self.layoutChanged.emit()

        def data(self, index, role):
            return py_to_variant(self._completions[index.row()].get(role))

        def rowCount(self, index=None):
            return len(self._completions)

        def columnCount(self, index=None):
            return 1

    def __init__(self,
                 admin=None,
                 parent=None,
                 editable=True,
                 field_name='manytoone',
                 actions=[
                     field_action.ClearObject(),
                     field_action.SelectObject(),
                     field_action.NewObject(),
                     field_action.OpenObject()
                 ],
                 **kwargs):
        """
        :param entity_admin : The Admin interface for the object on the one
        side of the relation
        """
        CustomEditor.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Preferred,
                           QtGui.QSizePolicy.Fixed)
        self.setObjectName(field_name)
        self.admin = admin
        self.new_value = None
        self._entity_representation = ''
        self.obj = None
        self._last_highlighted_entity_getter = None

        self.layout = QtWidgets.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Search input
        self.search_input = DecoratedLineEdit(self)
        self.search_input.setPlaceholderText(_('Search...'))
        self.search_input.textEdited.connect(self.textEdited)
        self.search_input.set_minimum_width(20)
        self.search_input.arrow_down_key_pressed.connect(
            self.on_arrow_down_key_pressed)
        # suppose garbage was entered, we need to refresh the content
        self.search_input.editingFinished.connect(
            self.search_input_editing_finished)
        self.setFocusProxy(self.search_input)

        # Search Completer
        self.completer = QtGui.QCompleter()
        self.completions_model = self.CompletionsModel(self.completer)
        self.completer.setModel(self.completions_model)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(
            QtGui.QCompleter.UnfilteredPopupCompletion)
        self.completer.activated[QtCore.QModelIndex].connect(
            self.completionActivated)
        self.completer.highlighted[QtCore.QModelIndex].connect(
            self.completion_highlighted)
        self.search_input.setCompleter(self.completer)

        # Setup layout
        self.layout.addWidget(self.search_input)
        self.setLayout(self.layout)
        self.add_actions(actions, self.layout)
        get_signal_handler().connect_signals(self)

    def set_field_attributes(self, **kwargs):
        super(Many2OneEditor, self).set_field_attributes(**kwargs)
        set_background_color_palette(self.search_input,
                                     kwargs.get('background_color'))
        self.search_input.setToolTip(kwargs.get('tooltip') or '')
        self.search_input.setEnabled(kwargs.get('editable', False))
        self.update_actions()

    def on_arrow_down_key_pressed(self):
        self.arrow_down_key_pressed.emit()

    def textEdited(self, text):
        self._last_highlighted_entity_getter = None
        text = six.text_type(self.search_input.text())

        def create_search_completion(text):
            return lambda: self.search_completions(text)

        post(create_search_completion(six.text_type(text)),
             self.display_search_completions)
        self.completer.complete()

    def search_completions(self, text):
        """Search for object that match text, to fill the list of completions

        :return: a list of tuples of (dict_of_object_representation, object)
        """
        search_decorator = create_entity_search_query_decorator(
            self.admin, text)
        if search_decorator:
            sresult = [
                self.admin.get_search_identifiers(e)
                for e in search_decorator(self.admin.get_query()).limit(20)
            ]
            return text, sresult
        return text, []

    def display_search_completions(self, prefix_and_completions):
        assert object_thread(self)
        prefix, completions = prefix_and_completions
        self.completions_model.setCompletions(completions)
        self.completer.setCompletionPrefix(prefix)
        self.completer.complete()

    def completionActivated(self, index):
        obj = index.data(Qt.EditRole)
        self.set_object(variant_to_py(obj))

    def completion_highlighted(self, index):
        obj = index.data(Qt.EditRole)
        self._last_highlighted_entity_getter = variant_to_py(obj)

    @QtCore.qt_slot(object, object)
    def handle_entity_update(self, sender, entity):
        if entity is self.get_value():
            self.set_object(entity, False)

    @QtCore.qt_slot(object, object)
    def handle_entity_delete(self, sender, entity):
        if entity is self.get_value():
            self.set_object(None, False)

    @QtCore.qt_slot(object, object)
    def handle_entity_create(self, sender, entity):
        if entity is self.new_value:
            self.new_value = None
            self.set_object(entity)

    def search_input_editing_finished(self):
        if self.obj is None:
            # Only try to 'guess' what the user meant when no entity is set
            # to avoid inappropriate removal of data, (eg when the user presses
            # Esc, editingfinished will be called as well, and we should not
            # overwrite the current entity set)
            if self._last_highlighted_entity_getter:
                self.set_object(self._last_highlighted_entity_getter)
            elif self.completions_model.rowCount() == 1:
                # There is only one possible option
                index = self.completions_model.index(0, 0)
                entity_getter = variant_to_py(index.data(Qt.EditRole))
                self.set_object(entity_getter)
        self.search_input.setText(self._entity_representation or u'')

    def set_value(self, value):
        """:param value: either ValueLoading, or a function that returns None
        or the entity to be shown in the editor"""
        self._last_highlighted_entity_getter = None
        self.new_value = None
        value = CustomEditor.set_value(self, value)
        self.set_object(value, propagate=False)
        self.update_actions()

    def get_value(self):
        """:return: a function that returns the selected entity or ValueLoading
        or None"""
        value = CustomEditor.get_value(self)
        if value is not None:
            return value
        return self.obj

    @QtCore.qt_slot(tuple)
    def set_instance_representation(self, representation_and_propagate):
        """Update the gui"""
        (desc, propagate) = representation_and_propagate
        self._entity_representation = desc
        self.search_input.setText(desc or u'')

        if propagate:
            self.editingFinished.emit()

    def set_object(self, obj, propagate=True):
        self.obj = obj

        def get_instance_representation(obj, propagate):
            """Get a representation of the instance"""
            if obj is not None:
                return (self.admin.get_verbose_object_name(obj), propagate)
            return (None, propagate)

        post(
            update_wrapper(
                partial(get_instance_representation, obj, propagate),
                get_instance_representation), self.set_instance_representation)

    selected_object = property(fset=set_object)
Exemplo n.º 4
0
class LocalFileEditor( CustomEditor ):
    """Widget for browsing local files and directories"""

    browse_icon =  Icon( 'tango/16x16/places/folder-saved-search.png' )

    def __init__(self, 
                 parent = None, 
                 field_name = 'local_file', 
                 directory = False, 
                 save_as = False,
                 file_filter = 'All files (*)',
                 **kwargs):
        CustomEditor.__init__(self, parent)
        self.setObjectName( field_name )
        self._directory = directory
        self._save_as = save_as
        self._file_filter = file_filter
        self.setup_widget()

    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        browse_button = QtGui.QToolButton( self )
        browse_button.setFocusPolicy( Qt.ClickFocus )
        browse_button.setIcon( self.browse_icon.getQIcon() )
        browse_button.setToolTip( _('Browse') )
        browse_button.setAutoRaise( True )
        browse_button.clicked.connect( self.browse_button_clicked )

        self.filename = DecoratedLineEdit(self)
        self.filename.editingFinished.connect( self.filename_editing_finished )
        self.setFocusProxy( self.filename )
        
        layout.addWidget( self.filename )
        layout.addWidget( browse_button )
        self.setLayout( layout )

    @QtCore.pyqtSlot()
    def filename_editing_finished(self):
        self.valueChanged.emit()
        self.editingFinished.emit()

    @QtCore.pyqtSlot()
    def browse_button_clicked(self):
        current_directory = os.path.dirname( self.get_value() )
        if self._directory:
            value = QtGui.QFileDialog.getExistingDirectory( self,
                                                            directory = current_directory )
        elif self._save_as:
            value = QtGui.QFileDialog.getSaveFileName( self,
                                                       filter = self._file_filter,
                                                       directory = current_directory )
        else:
            value = QtGui.QFileDialog.getOpenFileName( self,
                                                       filter = self._file_filter,
                                                       directory = current_directory )
        value = os.path.abspath( unicode( value ) )
        self.filename.setText( value )
        self.valueChanged.emit()
        self.editingFinished.emit()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.filename.setText( value )
        else:
            self.filename.setText( '' )
        self.valueChanged.emit()
        return value

    def get_value(self):
        return CustomEditor.get_value(self) or unicode( self.filename.text() )
    
    value = QtCore.pyqtProperty( str, get_value, set_value )

    def set_field_attributes( self, 
                              editable = True,
                              background_color = None,
                              tooltip = None,
                              **kwargs):
        self.setEnabled( editable )
        if self.filename:
            set_background_color_palette( self.filename, background_color )
            self.filename.setToolTip(unicode(tooltip or ''))
Exemplo n.º 5
0
class VirtualAddressEditor(CustomEditor):
    def __init__(self,
                 parent=None,
                 editable=True,
                 address_type=None,
                 address_validator=default_address_validator,
                 field_name='virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setObjectName(field_name)
        self._address_type = address_type
        self._address_validator = address_validator
        self.layout = QtGui.QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.combo = QtGui.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.combo.setEnabled(editable)
        if address_type:
            self.combo.setVisible(False)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit(self)
        self.editor.setEnabled(editable)
        self.editor.set_minimum_width(30)
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        self.editable = editable
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtGui.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect(self.mail_click)
        self.label.hide()

        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.editor.textEdited.connect(self.editorValueChanged)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)

        self.setLayout(self.layout)
        self.checkValue(self.editor.text())

    @QtCore.pyqtSlot()
    def comboIndexChanged(self):
        self.checkValue(self.editor.text())
        self.emit_editing_finished()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.editor.setText(value[1])
            idx = camelot.types.VirtualAddress.virtual_address_types.index(
                self._address_type or value[0])
            self.combo.setCurrentIndex(idx)
            icon = Icon('tango/16x16/devices/printer.png').getQIcon()
            # These icons don't exist any more in the new tango icon set
            #            if str(self.combo.currentText()) == 'phone':
            #                icon = Icon('tango/16x16/devices/phone.png').getQIcon()
            if str(self.combo.currentText()) == 'fax':
                icon = Icon('tango/16x16/devices/printer.png').getQIcon()
#            if str(self.combo.currentText()) == 'mobile':
#                icon = Icon('tango/16x16/devices/mobile.png').getQIcon()
#            if str(self.combo.currentText()) == 'im':
#                icon = Icon('tango/16x16/places/instant-messaging.png').getQIcon()
#            if str(self.combo.currentText()) == 'pager':
#                icon = Icon('tango/16x16/devices/pager.png').getQIcon()
            if str(self.combo.currentText()) == 'email':
                icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
                #self.label.setFocusPolicy(Qt.StrongFocus)
                #self.label.setAutoFillBackground(True)
                self.label.setIcon(icon)
                self.label.setEnabled(self.editable)
                self.label.show()
            else:
                self.label.hide()
                self.label.setIcon(icon)
                self.label.setEnabled(self.editable)
                self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)

#      self.update()
#      self.label.update()
#      self.layout.update()

            self.checkValue(value[1])

    def get_value(self):
        value = (unicode(self.combo.currentText()),
                 unicode(self.editor.text()))
        return CustomEditor.get_value(self) or value

    def set_enabled(self, editable=True):
        self.combo.setEnabled(editable)
        self.editor.setEnabled(editable)
        if not editable:
            self.label.setEnabled(False)
        else:
            if self.combo.currentText() == 'email':
                self.label.setEnabled(True)

    def checkValue(self, text):
        address_type = unicode(self.combo.currentText())
        valid, _corrected = self._address_validator(address_type,
                                                    unicode(text))
        self.editor.set_valid(valid)

    def editorValueChanged(self, text):
        self.checkValue(text)

    @QtCore.pyqtSlot()
    def mail_click(self):
        address = self.editor.text()
        url = QtCore.QUrl()
        url.setUrl(u'mailto:%s?subject=Subject' % unicode(address))
        QtGui.QDesktopServices.openUrl(url)

    def emit_editing_finished(self):
        self.value = []
        self.value.append(str(self.combo.currentText()))
        self.value.append(str(self.editor.text()))
        self.set_value(self.value)
        # emiting editingFinished without a value for the mechanism itself will lead to
        # integrity errors
        if self.value[1]:
            self.editingFinished.emit()

    def set_background_color(self, background_color):
        set_background_color_palette(self.editor, background_color)

    def set_field_attributes(self,
                             editable=True,
                             background_color=None,
                             tooltip=None,
                             **kwargs):
        self.set_enabled(editable)
        self.set_background_color(background_color)
        self.setToolTip(unicode(tooltip or ''))
Exemplo n.º 6
0
class VirtualAddressEditor(CustomEditor):
    def __init__(self,
                 parent=None,
                 address_type=None,
                 field_name='virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Preferred,
                           QtGui.QSizePolicy.Fixed)
        self.setObjectName(field_name)
        self._address_type = address_type
        self.layout = QtWidgets.QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.combo = QtWidgets.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit(self)
        self.editor.set_minimum_width(30)
        if address_type:
            self.combo.setVisible(False)
            idx = camelot.types.VirtualAddress.virtual_address_types.index(
                address_type)
            self.combo.setCurrentIndex(idx)
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtWidgets.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect(self.mail_click)
        self.label.hide()
        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)
        self.setLayout(self.layout)
        self.update_validator()

    @QtCore.qt_slot()
    def comboIndexChanged(self):
        self.update_validator()
        self.emit_editing_finished()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value is None:
            self.editor.setText('')
        else:
            self.editor.setText(value[1])
            idx = camelot.types.VirtualAddress.virtual_address_types.index(
                self._address_type or value[0])
            self.combo.setCurrentIndex(idx)
            icon = Icon('tango/16x16/devices/printer.png').getQIcon()
            if six.text_type(self.combo.currentText()) == 'fax':
                icon = Icon('tango/16x16/devices/printer.png').getQIcon()
            if six.text_type(self.combo.currentText()) == 'email':
                icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
                self.label.setIcon(icon)
                self.label.show()
            else:
                self.label.hide()
                self.label.setIcon(icon)
                self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
            self.update_validator()

    def get_value(self):
        address_value = six.text_type(self.editor.text())
        if not len(address_value):
            value = None
        else:
            value = (six.text_type(self.combo.currentText()), address_value)
        return CustomEditor.get_value(self) or value

    def set_enabled(self, editable=True):
        self.combo.setEnabled(editable)
        self.editor.setEnabled(editable)
        if not editable:
            self.label.setEnabled(False)
        else:
            if self.combo.currentText() == 'email':
                self.label.setEnabled(True)

    def update_validator(self):
        address_type = six.text_type(self.combo.currentText())
        validator = validators.get(address_type, any_character_validator)
        # change the validator instead of the regexp of the validator to inform
        # the editor it needs to update its background color
        self.editor.setValidator(validator)

    @QtCore.qt_slot()
    def mail_click(self):
        address = self.editor.text()
        url = QtCore.QUrl()
        url.setUrl(u'mailto:%s?subject=Subject' % six.text_type(address))
        QtGui.QDesktopServices.openUrl(url)

    def emit_editing_finished(self):
        self.value = []
        self.value.append(six.text_type(self.combo.currentText()))
        self.value.append(six.text_type(self.editor.text()))
        self.set_value(self.value)
        # emiting editingFinished without a value for the mechanism itself will lead to
        # integrity errors
        if self.value[1]:
            self.editingFinished.emit()

    def set_background_color(self, background_color):
        set_background_color_palette(self.editor, background_color)

    def set_field_attributes(self, **kwargs):
        super(VirtualAddressEditor, self).set_field_attributes(**kwargs)
        self.set_enabled(kwargs.get('editable', False))
        self.setToolTip(six.text_type(kwargs.get('tooltip') or ''))