779 lines
23 KiB
Python
779 lines
23 KiB
Python
# This file is part of Gajim.
|
|
#
|
|
# Gajim is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published
|
|
# by the Free Software Foundation; version 3 only.
|
|
#
|
|
# Gajim is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import datetime
|
|
|
|
from gi.repository import Gdk
|
|
from gi.repository import GLib
|
|
from gi.repository import Gtk
|
|
from gi.repository import GObject
|
|
|
|
from gajim.common.i18n import _
|
|
from gajim.common.i18n import Q_
|
|
from gajim.common.const import URIType
|
|
from gajim.common.helpers import open_uri
|
|
from gajim.common.helpers import parse_uri
|
|
from gajim.common.structs import URI
|
|
from gajim.gui.util import gtk_month
|
|
from gajim.gui.util import python_month
|
|
|
|
|
|
LABEL_DICT = {
|
|
'fn': _('Full Name'),
|
|
'n': _('Name'),
|
|
'bday': _('Birthday'),
|
|
'gender': _('Gender'),
|
|
'adr': Q_('?profile:Address'),
|
|
'tel': _('Phone No.'),
|
|
'email': _('Email'),
|
|
'impp': _('IM Address'),
|
|
'title': Q_('?profile:Title'),
|
|
'role': Q_('?profile:Role'),
|
|
'org': _('Organisation'),
|
|
'note': Q_('?profile:Note'),
|
|
'url': _('URL'),
|
|
'key': Q_('?profile:Public Encryption Key'),
|
|
}
|
|
|
|
|
|
FIELD_TOOLTIPS = {
|
|
'key': _('Your public key or authentication certificate')
|
|
}
|
|
|
|
|
|
ADR_FIELDS = ['street', 'ext', 'pobox', 'code', 'locality', 'region', 'country']
|
|
|
|
|
|
ADR_PLACEHOLDER_TEXT = {
|
|
'pobox': _('Post Office Box'),
|
|
'street': _('Street'),
|
|
'ext': _('Extended Address'),
|
|
'locality': _('City'),
|
|
'region': _('State'),
|
|
'code': _('Postal Code'),
|
|
'country': _('Country'),
|
|
}
|
|
|
|
|
|
DEFAULT_KWARGS = {
|
|
'fn': {'value': ''},
|
|
'bday': {'value': '', 'value_type': 'date'},
|
|
'gender': {'sex': '', 'identity': ''},
|
|
'adr': {},
|
|
'email': {'value': ''},
|
|
'impp': {'value': ''},
|
|
'tel': {'value': '', 'value_type': 'text'},
|
|
'org': {'values': []},
|
|
'title': {'value': ''},
|
|
'role': {'value': ''},
|
|
'url': {'value': ''},
|
|
'key': {'value': '', 'value_type': 'text'},
|
|
'note': {'value': ''},
|
|
}
|
|
|
|
|
|
PROPERTIES_WITH_TYPE = [
|
|
'adr',
|
|
'email',
|
|
'impp',
|
|
'tel',
|
|
'key',
|
|
]
|
|
|
|
|
|
ORDER = [
|
|
'fn',
|
|
'gender',
|
|
'bday',
|
|
'adr',
|
|
'email',
|
|
'impp',
|
|
'tel',
|
|
'org',
|
|
'title',
|
|
'role',
|
|
'url',
|
|
'key',
|
|
'note',
|
|
]
|
|
|
|
|
|
SEX_VALUES = {
|
|
'M': _('Male'),
|
|
'F': _('Female'),
|
|
'O': Q_('?Gender:Other'),
|
|
'N': Q_('?Gender:None'),
|
|
'U': _('Unknown')
|
|
}
|
|
|
|
|
|
TYPE_VALUES = {
|
|
'-': None,
|
|
'home': _('Home'),
|
|
'work': _('Work')
|
|
}
|
|
|
|
|
|
class VCardGrid(Gtk.Grid):
|
|
def __init__(self, account):
|
|
Gtk.Grid.__init__(self)
|
|
|
|
self._callbacks = {
|
|
'fn': TextEntryProperty,
|
|
'bday': DateProperty,
|
|
'gender': GenderProperty,
|
|
'adr': AdrProperty,
|
|
'tel': TextEntryProperty,
|
|
'email': TextEntryProperty,
|
|
'impp': TextEntryProperty,
|
|
'title': TextEntryProperty,
|
|
'role': TextEntryProperty,
|
|
'org': TextEntryProperty,
|
|
'url': TextEntryProperty,
|
|
'key': KeyProperty,
|
|
'note': MultiLineProperty,
|
|
}
|
|
|
|
self.set_column_spacing(12)
|
|
self.set_row_spacing(12)
|
|
self.set_no_show_all(True)
|
|
self.set_visible(True)
|
|
self.set_halign(Gtk.Align.CENTER)
|
|
|
|
self._account = account
|
|
self._row_count = 0
|
|
self._vcard = None
|
|
self._props = []
|
|
|
|
def set_editable(self, enabled):
|
|
for prop in self._props:
|
|
prop.set_editable(enabled)
|
|
|
|
def set_vcard(self, vcard):
|
|
self.clear()
|
|
self._vcard = vcard
|
|
|
|
for entry in ORDER:
|
|
for prop in vcard.get_properties():
|
|
if entry != prop.name:
|
|
continue
|
|
|
|
self.add_property(prop)
|
|
|
|
def get_vcard(self):
|
|
return self._vcard
|
|
|
|
def validate(self):
|
|
for prop in list(self._props):
|
|
base_prop = prop.get_base_property()
|
|
if base_prop.is_empty:
|
|
self.remove_property(prop)
|
|
|
|
def add_new_property(self, name):
|
|
kwargs = DEFAULT_KWARGS[name]
|
|
prop = self._vcard.add_property(name, **kwargs)
|
|
self.add_property(prop, editable=True)
|
|
|
|
def add_property(self, prop, editable=False):
|
|
prop_class = self._callbacks.get(prop.name)
|
|
if prop_class is None:
|
|
return
|
|
prop_obj = prop_class(prop, self._account)
|
|
prop_obj.set_editable(editable)
|
|
prop_obj.add_to_grid(self, self._row_count)
|
|
|
|
self._props.append(prop_obj)
|
|
self._row_count += 1
|
|
|
|
def remove_property(self, prop):
|
|
self.remove_row(prop.row_number)
|
|
self._props.remove(prop)
|
|
self._vcard.remove_property(prop.get_base_property())
|
|
|
|
def clear(self):
|
|
self._vcard = None
|
|
self._row_count = 0
|
|
for prop in list(self._props):
|
|
self.remove_row(prop.row_number)
|
|
|
|
self._props = []
|
|
|
|
def sort(self):
|
|
self.set_vcard(self._vcard)
|
|
|
|
|
|
class DescriptionLabel(Gtk.Label):
|
|
def __init__(self, value):
|
|
Gtk.Label.__init__(self, label=LABEL_DICT[value])
|
|
if value == 'adr':
|
|
self.set_valign(Gtk.Align.START)
|
|
else:
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.get_style_context().add_class('dim-label')
|
|
self.get_style_context().add_class('margin-right18')
|
|
self.set_visible(True)
|
|
self.set_xalign(1)
|
|
self.set_tooltip_text(FIELD_TOOLTIPS.get(value, ''))
|
|
|
|
|
|
class ValueLabel(Gtk.Label):
|
|
def __init__(self, prop, account):
|
|
Gtk.Label.__init__(self)
|
|
self._prop = prop
|
|
self._uri = None
|
|
self._account = account
|
|
self.set_selectable(True)
|
|
self.set_xalign(0)
|
|
self.set_max_width_chars(50)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_halign(Gtk.Align.START)
|
|
|
|
self.connect('activate-link', self._on_activate_link)
|
|
if prop.name == 'org':
|
|
self.set_value(prop.values[0] if prop.values else '')
|
|
else:
|
|
self.set_value(prop.value)
|
|
|
|
def set_value(self, value):
|
|
if self._prop.name == 'email':
|
|
self._uri = URI(type=URIType.MAIL, data=value)
|
|
self.set_markup(value)
|
|
|
|
elif self._prop.name in ('impp', 'tel'):
|
|
self._uri = parse_uri(value)
|
|
self.set_markup(value)
|
|
|
|
else:
|
|
self.set_text(value)
|
|
|
|
def set_markup(self, text):
|
|
if not text:
|
|
self.set_text('')
|
|
return
|
|
super().set_markup('<a href="{}">{}</a>'.format(
|
|
GLib.markup_escape_text(text),
|
|
GLib.markup_escape_text(text)))
|
|
|
|
def _on_activate_link(self, _label, _value):
|
|
open_uri(self._uri, self._account)
|
|
return Gdk.EVENT_STOP
|
|
|
|
|
|
class SexLabel(Gtk.Label):
|
|
def __init__(self, prop):
|
|
Gtk.Label.__init__(self)
|
|
self._prop = prop
|
|
|
|
self.set_selectable(True)
|
|
self.set_xalign(0)
|
|
self.set_max_width_chars(50)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_halign(Gtk.Align.START)
|
|
|
|
self.set_text(prop.sex)
|
|
|
|
def set_text(self, value):
|
|
if not value or value == '-':
|
|
super().set_text('')
|
|
else:
|
|
super().set_text(SEX_VALUES[value])
|
|
|
|
|
|
class IdentityLabel(Gtk.Label):
|
|
def __init__(self, prop):
|
|
Gtk.Label.__init__(self)
|
|
self._prop = prop
|
|
|
|
self.set_selectable(True)
|
|
self.set_xalign(0)
|
|
self.set_max_width_chars(50)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_halign(Gtk.Align.START)
|
|
|
|
self.set_text(prop.identity)
|
|
|
|
def set_text(self, value):
|
|
super().set_text('' if not value else value)
|
|
|
|
|
|
class ValueEntry(Gtk.Entry):
|
|
def __init__(self, prop):
|
|
Gtk.Entry.__init__(self)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_max_width_chars(50)
|
|
if prop.name == 'org':
|
|
self.set_text(prop.values[0] if prop.values else '')
|
|
else:
|
|
self.set_text(prop.value)
|
|
|
|
|
|
class AdrEntry(Gtk.Entry):
|
|
def __init__(self, prop, type_):
|
|
Gtk.Entry.__init__(self)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_max_width_chars(50)
|
|
values = getattr(prop, type_)
|
|
if not values:
|
|
value = ''
|
|
else:
|
|
value = values[0]
|
|
self.set_text(value)
|
|
self.set_placeholder_text(ADR_PLACEHOLDER_TEXT.get(type_))
|
|
|
|
|
|
class IdentityEntry(Gtk.Entry):
|
|
def __init__(self, prop):
|
|
Gtk.Entry.__init__(self)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_max_width_chars(50)
|
|
self.set_text('' if not prop.identity else prop.identity)
|
|
|
|
|
|
class AdrBox(Gtk.Box):
|
|
|
|
__gsignals__ = {
|
|
'field-changed': (
|
|
GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION,
|
|
None, # return value
|
|
(str, str) # arguments
|
|
)}
|
|
|
|
def __init__(self, prop):
|
|
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
|
|
|
for field in ADR_FIELDS:
|
|
entry = AdrEntry(prop, field)
|
|
entry.connect('notify::text', self._on_text_changed, field)
|
|
self.add(entry)
|
|
|
|
self.show_all()
|
|
|
|
def _on_text_changed(self, entry, _param, field):
|
|
self.emit('field-changed', field, entry.get_text())
|
|
|
|
|
|
class AdrLabel(Gtk.Label):
|
|
def __init__(self, prop, type_):
|
|
Gtk.Label.__init__(self)
|
|
self.set_selectable(True)
|
|
self.set_xalign(0)
|
|
self.set_max_width_chars(50)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_halign(Gtk.Align.START)
|
|
values = getattr(prop, type_)
|
|
if not values:
|
|
value = ''
|
|
else:
|
|
value = values[0]
|
|
self.set_text(value)
|
|
|
|
def set_text(self, value):
|
|
self.set_visible(bool(value))
|
|
super().set_text(value)
|
|
|
|
|
|
class AdrBoxReadOnly(Gtk.Box):
|
|
def __init__(self, prop):
|
|
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
|
|
|
self._labels = {}
|
|
|
|
for field in ADR_FIELDS:
|
|
label = AdrLabel(prop, field)
|
|
self._labels[field] = label
|
|
self.add(label)
|
|
|
|
def set_field(self, field, value):
|
|
self._labels[field].set_text(value)
|
|
|
|
|
|
class ValueTextView(Gtk.TextView):
|
|
def __init__(self, prop):
|
|
Gtk.TextView.__init__(self)
|
|
self.props.right_margin = 8
|
|
self.props.left_margin = 8
|
|
self.props.top_margin = 8
|
|
self.props.bottom_margin = 8
|
|
|
|
self.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
|
|
self.set_hexpand(True)
|
|
self.set_valign(Gtk.Align.FILL)
|
|
|
|
self._prop = prop
|
|
self.get_buffer().set_text(prop.value)
|
|
self.get_buffer().connect('notify::text', self._on_text_changed)
|
|
|
|
def get_text(self):
|
|
start_iter, end_iter = self.get_buffer().get_bounds()
|
|
return self.get_buffer().get_text(start_iter, end_iter, False)
|
|
|
|
def _on_text_changed(self, _buffer, _param):
|
|
self._prop.value = self.get_text()
|
|
|
|
|
|
class TypeComboBox(Gtk.ComboBoxText):
|
|
def __init__(self, parameters):
|
|
Gtk.ComboBoxText.__init__(self)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self._parameters = parameters
|
|
self.append('-', '-')
|
|
self.append('home', _('Home'))
|
|
self.append('work', _('Work'))
|
|
|
|
values = self._parameters.get_types()
|
|
if 'home' in values:
|
|
self.set_active_id('home')
|
|
|
|
elif 'work' in values:
|
|
self.set_active_id('work')
|
|
|
|
else:
|
|
self.set_active_id('-')
|
|
|
|
self.connect('notify::active-id', self._on_active_id_changed)
|
|
|
|
def _on_active_id_changed(self, _combobox, _param):
|
|
type_ = self.get_active_id()
|
|
if type_ == '-':
|
|
self._parameters.remove_types(['work', 'home'])
|
|
|
|
elif type_ == 'work':
|
|
self._parameters.add_types(['work'])
|
|
self._parameters.remove_types(['home'])
|
|
|
|
elif type_ == 'home':
|
|
self._parameters.add_types(['home'])
|
|
self._parameters.remove_types(['work'])
|
|
|
|
def get_text(self):
|
|
type_value = self.get_active_id()
|
|
if type_value == '-':
|
|
return ''
|
|
return self.get_active_text()
|
|
|
|
|
|
class GenderComboBox(Gtk.ComboBoxText):
|
|
def __init__(self, prop):
|
|
Gtk.ComboBoxText.__init__(self)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_halign(Gtk.Align.START)
|
|
|
|
self._prop = prop
|
|
|
|
self.append('-', '-')
|
|
for key, value in SEX_VALUES.items():
|
|
self.append(key, value)
|
|
|
|
if not prop.sex:
|
|
self.set_active_id('-')
|
|
else:
|
|
self.set_active_id(prop.sex)
|
|
|
|
|
|
class RemoveButton(Gtk.Button):
|
|
def __init__(self):
|
|
Gtk.Button.__init__(self)
|
|
self.set_valign(Gtk.Align.CENTER)
|
|
self.set_halign(Gtk.Align.START)
|
|
image = Gtk.Image.new_from_icon_name('user-trash-symbolic',
|
|
Gtk.IconSize.MENU)
|
|
self.set_image(image)
|
|
self.set_no_show_all(True)
|
|
|
|
|
|
class VCardProperty:
|
|
def __init__(self, prop):
|
|
self._prop = prop
|
|
|
|
self._second_column = []
|
|
self._third_column = []
|
|
|
|
self._desc_label = DescriptionLabel(prop.name)
|
|
self._remove_button = RemoveButton()
|
|
self._remove_button.connect('clicked', self._on_remove_clicked)
|
|
|
|
self._edit_widgets = [self._remove_button]
|
|
self._read_widgets = []
|
|
|
|
if prop.name in PROPERTIES_WITH_TYPE:
|
|
self._type_combobox = TypeComboBox(prop.parameters)
|
|
self._type_combobox.connect('notify::active-id',
|
|
self._on_type_changed)
|
|
type_ = self._type_combobox.get_active_id()
|
|
icon_name = self._get_icon_name(type_)
|
|
self._type_image = Gtk.Image.new_from_icon_name(
|
|
icon_name, Gtk.IconSize.MENU)
|
|
self._type_image.set_tooltip_text(TYPE_VALUES[type_])
|
|
|
|
if prop.name == 'adr':
|
|
self._type_image.set_valign(Gtk.Align.START)
|
|
self._type_combobox.set_valign(Gtk.Align.START)
|
|
|
|
self._edit_widgets.append(self._type_combobox)
|
|
self._read_widgets.append(self._type_image)
|
|
self._second_column.extend([self._type_combobox, self._type_image])
|
|
|
|
@staticmethod
|
|
def _get_icon_name(type_):
|
|
if type_ == 'home':
|
|
return 'feather-home'
|
|
if type_ == 'work':
|
|
return 'feather-briefcase'
|
|
return None
|
|
|
|
def _on_type_changed(self, _combobox, _param):
|
|
type_ = self._type_combobox.get_active_id()
|
|
icon_name = self._get_icon_name(type_)
|
|
self._type_image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
|
|
self._type_image.set_tooltip_text(TYPE_VALUES[type_])
|
|
|
|
def _on_remove_clicked(self, button):
|
|
button.get_parent().remove_property(self)
|
|
|
|
@property
|
|
def row_number(self):
|
|
grid = self._desc_label.get_parent()
|
|
return grid.child_get_property(self._desc_label, 'top-attach')
|
|
|
|
def get_base_property(self):
|
|
return self._prop
|
|
|
|
def set_editable(self, enabled):
|
|
for widget in self._edit_widgets:
|
|
widget.set_visible(enabled)
|
|
|
|
for widget in self._read_widgets:
|
|
widget.set_visible(not enabled)
|
|
|
|
def add_to_grid(self, grid, row_number):
|
|
# child, left, top, width, height
|
|
grid.attach(self._desc_label, 0, row_number, 1, 1)
|
|
|
|
for widget in self._second_column:
|
|
grid.attach(widget, 1, row_number, 1, 1)
|
|
|
|
for widget in self._third_column:
|
|
grid.attach(widget, 2, row_number, 1, 1)
|
|
|
|
grid.attach(self._remove_button, 3, row_number, 1, 1)
|
|
|
|
|
|
class TextEntryProperty(VCardProperty):
|
|
def __init__(self, prop, account):
|
|
VCardProperty.__init__(self, prop)
|
|
|
|
self._value_entry = ValueEntry(prop)
|
|
self._value_entry.connect('notify::text', self._on_text_changed)
|
|
|
|
self._value_label = ValueLabel(prop, account)
|
|
|
|
self._edit_widgets.append(self._value_entry)
|
|
self._read_widgets.append(self._value_label)
|
|
|
|
self._third_column = [self._value_entry, self._value_label]
|
|
|
|
def _on_text_changed(self, entry, _param):
|
|
text = entry.get_text()
|
|
if self._prop.name == 'org':
|
|
self._prop.values = [text]
|
|
else:
|
|
self._prop.value = text
|
|
self._value_label.set_value(text)
|
|
|
|
|
|
class MultiLineProperty(VCardProperty):
|
|
def __init__(self, prop, _account):
|
|
VCardProperty.__init__(self, prop)
|
|
|
|
self._edit_text_view = ValueTextView(prop)
|
|
self._edit_text_view.show()
|
|
|
|
self._edit_scrolled = Gtk.ScrolledWindow()
|
|
self._edit_scrolled.set_policy(Gtk.PolicyType.NEVER,
|
|
Gtk.PolicyType.AUTOMATIC)
|
|
self._edit_scrolled.add(self._edit_text_view)
|
|
self._edit_scrolled.set_valign(Gtk.Align.CENTER)
|
|
self._edit_scrolled.set_size_request(350, 100)
|
|
self._edit_scrolled.get_style_context().add_class('profile-scrolled')
|
|
|
|
self._read_text_view = ValueTextView(prop)
|
|
self._read_text_view.set_sensitive(False)
|
|
self._read_text_view.set_left_margin(0)
|
|
self._read_text_view.show()
|
|
|
|
self._read_scrolled = Gtk.ScrolledWindow()
|
|
self._read_scrolled.set_policy(Gtk.PolicyType.NEVER,
|
|
Gtk.PolicyType.AUTOMATIC)
|
|
self._read_scrolled.add(self._read_text_view)
|
|
self._read_scrolled.set_valign(Gtk.Align.CENTER)
|
|
self._read_scrolled.set_size_request(350, 100)
|
|
self._read_scrolled.get_style_context().add_class(
|
|
'profile-scrolled-read')
|
|
|
|
self._edit_widgets.append(self._edit_scrolled)
|
|
self._read_widgets.append(self._read_scrolled)
|
|
|
|
self._third_column = [self._edit_scrolled, self._read_scrolled]
|
|
|
|
|
|
class DateProperty(VCardProperty):
|
|
def __init__(self, prop, account):
|
|
VCardProperty.__init__(self, prop)
|
|
|
|
self._box = Gtk.Box(spacing=6)
|
|
self._value_entry = ValueEntry(prop)
|
|
self._value_entry.set_placeholder_text(_('YYYY-MM-DD'))
|
|
self._value_entry.connect('notify::text', self._on_text_changed)
|
|
|
|
self._calendar_button = Gtk.MenuButton()
|
|
image = Gtk.Image.new_from_icon_name(
|
|
'x-office-calendar-symbolic', Gtk.IconSize.BUTTON)
|
|
self._calendar_button.set_image(image)
|
|
self._calendar_button.connect(
|
|
'clicked', self._on_calendar_button_clicked)
|
|
self._box.add(self._value_entry)
|
|
self._box.add(self._calendar_button)
|
|
self._box.show_all()
|
|
|
|
self.calendar = Gtk.Calendar(year=1980, month=5, day=15)
|
|
self.calendar.set_visible(True)
|
|
self.calendar.connect(
|
|
'day-selected', self._on_calendar_day_selected)
|
|
|
|
popover = Gtk.Popover()
|
|
popover.add(self.calendar)
|
|
self._calendar_button.set_popover(popover)
|
|
|
|
self._value_label = ValueLabel(prop, account)
|
|
|
|
self._edit_widgets.append(self._box)
|
|
self._read_widgets.append(self._value_label)
|
|
|
|
self._third_column = [self._box, self._value_label]
|
|
|
|
def _on_text_changed(self, entry, _param):
|
|
text = entry.get_text()
|
|
self._prop.value = text
|
|
self._value_label.set_value(text)
|
|
|
|
def _on_calendar_button_clicked(self, _widget):
|
|
birthday = self._value_entry.get_text()
|
|
if not birthday:
|
|
return
|
|
try:
|
|
date = datetime.datetime.strptime(birthday, '%Y-%m-%d')
|
|
except ValueError:
|
|
return
|
|
month = gtk_month(date.month)
|
|
self.calendar.select_month(month, date.year)
|
|
self.calendar.select_day(date.day)
|
|
|
|
def _on_calendar_day_selected(self, _widget):
|
|
year, month, day = self.calendar.get_date() # Integers
|
|
month = python_month(month)
|
|
date_str = datetime.date(year, month, day).strftime('%Y-%m-%d')
|
|
self._value_entry.set_text(date_str)
|
|
|
|
|
|
class KeyProperty(VCardProperty):
|
|
def __init__(self, prop, _account):
|
|
VCardProperty.__init__(self, prop)
|
|
|
|
self._value_text_view = ValueTextView(prop)
|
|
self._value_text_view.show()
|
|
|
|
self._scrolled_window = Gtk.ScrolledWindow()
|
|
self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
|
|
Gtk.PolicyType.AUTOMATIC)
|
|
self._scrolled_window.add(self._value_text_view)
|
|
self._scrolled_window.set_valign(Gtk.Align.CENTER)
|
|
self._scrolled_window.set_size_request(350, 200)
|
|
self._scrolled_window.get_style_context().add_class('profile-scrolled')
|
|
|
|
self._copy_button = Gtk.Button.new_from_icon_name('edit-copy-symbolic',
|
|
Gtk.IconSize.MENU)
|
|
self._copy_button.connect('clicked', self._on_copy_clicked)
|
|
self._copy_button.set_halign(Gtk.Align.START)
|
|
self._copy_button.set_valign(Gtk.Align.CENTER)
|
|
|
|
self._edit_widgets.append(self._scrolled_window)
|
|
self._read_widgets.append(self._copy_button)
|
|
|
|
self._third_column = [self._scrolled_window, self._copy_button]
|
|
|
|
def _on_copy_clicked(self, _button):
|
|
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
|
clipboard.set_text(self._value_text_view.get_text(), -1)
|
|
|
|
|
|
class GenderProperty(VCardProperty):
|
|
def __init__(self, prop, _account):
|
|
VCardProperty.__init__(self, prop)
|
|
|
|
value_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
|
self._value_combobox = GenderComboBox(prop)
|
|
self._value_combobox.connect('notify::active-id',
|
|
self._on_active_id_changed)
|
|
self._value_combobox.show()
|
|
|
|
self._value_entry = IdentityEntry(prop)
|
|
self._value_entry.show()
|
|
self._value_entry.connect('notify::text', self._on_text_changed)
|
|
|
|
value_box.add(self._value_combobox)
|
|
value_box.add(self._value_entry)
|
|
|
|
label_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
|
self._identity_label = IdentityLabel(prop)
|
|
self._identity_label.show()
|
|
self._sex_label = SexLabel(prop)
|
|
self._sex_label.show()
|
|
|
|
label_box.add(self._sex_label)
|
|
label_box.add(self._identity_label)
|
|
|
|
self._edit_widgets.append(value_box)
|
|
self._read_widgets.append(label_box)
|
|
|
|
self._third_column = [value_box, label_box]
|
|
|
|
def _on_text_changed(self, entry, _param):
|
|
text = entry.get_text()
|
|
self._prop.identity = text
|
|
self._identity_label.set_text(text)
|
|
|
|
def _on_active_id_changed(self, combobox, _param):
|
|
sex = combobox.get_active_id()
|
|
self._prop.sex = None if sex == '-' else sex
|
|
self._sex_label.set_text(sex)
|
|
|
|
|
|
class AdrProperty(VCardProperty):
|
|
def __init__(self, prop, _account):
|
|
VCardProperty.__init__(self, prop)
|
|
|
|
self._entry_box = AdrBox(prop)
|
|
self._entry_box.connect('field-changed', self._on_field_changed)
|
|
|
|
self._read_box = AdrBoxReadOnly(prop)
|
|
|
|
self._edit_widgets.append(self._entry_box)
|
|
self._read_widgets.append(self._read_box)
|
|
|
|
self._third_column = [self._entry_box, self._read_box]
|
|
|
|
def _on_field_changed(self, _box, field, value):
|
|
setattr(self._prop, field, [value])
|
|
self._read_box.set_field(field, value)
|