Commit 8f5ac8a1 authored by Dan Pascu's avatar Dan Pascu

Modified code to store contacts in XCAP

parent 8a70ef47
...@@ -28,6 +28,7 @@ from gnutls.errors import GNUTLSError ...@@ -28,6 +28,7 @@ from gnutls.errors import GNUTLSError
from zope.interface import implements from zope.interface import implements
from sipsimple.account import Account, AccountManager, BonjourAccount from sipsimple.account import Account, AccountManager, BonjourAccount
from sipsimple.addressbook import Contact, Group
from sipsimple.application import SIPApplication from sipsimple.application import SIPApplication
from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.storage import FileStorage from sipsimple.storage import FileStorage
...@@ -35,6 +36,7 @@ from sipsimple.threading import run_in_twisted_thread ...@@ -35,6 +36,7 @@ from sipsimple.threading import run_in_twisted_thread
from sipsimple.threading.green import run_in_green_thread from sipsimple.threading.green import run_in_green_thread
from blink.configuration.account import AccountExtension, BonjourAccountExtension from blink.configuration.account import AccountExtension, BonjourAccountExtension
from blink.configuration.addressbook import ContactExtension, GroupExtension
from blink.configuration.datatypes import InvalidToken from blink.configuration.datatypes import InvalidToken
from blink.configuration.settings import SIPSimpleSettingsExtension from blink.configuration.settings import SIPSimpleSettingsExtension
from blink.logging import LogManager from blink.logging import LogManager
...@@ -101,6 +103,8 @@ class Blink(QApplication): ...@@ -101,6 +103,8 @@ class Blink(QApplication):
Account.register_extension(AccountExtension) Account.register_extension(AccountExtension)
BonjourAccount.register_extension(BonjourAccountExtension) BonjourAccount.register_extension(BonjourAccountExtension)
Contact.register_extension(ContactExtension)
Group.register_extension(GroupExtension)
SIPSimpleSettings.register_extension(SIPSimpleSettingsExtension) SIPSimpleSettings.register_extension(SIPSimpleSettingsExtension)
session_manager = SessionManager() session_manager = SessionManager()
session_manager.initialize(self.main_window, self.main_window.session_model) session_manager.initialize(self.main_window, self.main_window.session_model)
......
# Copyright (C) 2013 AG Projects. See LICENSE for details.
#
"""Blink addressbook settings extensions."""
__all__ = ['ContactExtension', 'GroupExtension']
from sipsimple.addressbook import ContactExtension, GroupExtension, SharedSetting
from sipsimple.configuration import Setting
from blink.configuration.datatypes import IconDescriptor
SharedSetting.set_namespace('ag-projects:blink')
class ContactExtension(ContactExtension):
#auto_answer = SharedSetting(type=bool, default=False)
default_uri = SharedSetting(type=str, nillable=True, default=None)
preferred_media = SharedSetting(type=str, default='audio')
icon = Setting(type=IconDescriptor, nillable=True, default=None)
class GroupExtension(GroupExtension):
position = Setting(type=int, nillable=True)
collapsed = Setting(type=bool, default=False)
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
"""Definitions of datatypes for use in settings extensions.""" """Definitions of datatypes for use in settings extensions."""
__all__ = ['ApplicationDataPath', 'SoundFile', 'DefaultPath', 'CustomSoundFile', 'HTTPURL', 'AuthorizationToken', 'InvalidToken'] __all__ = ['ApplicationDataPath', 'SoundFile', 'DefaultPath', 'CustomSoundFile', 'HTTPURL', 'AuthorizationToken', 'InvalidToken', 'IconDescriptor']
import os import os
import re import re
...@@ -136,3 +136,56 @@ class AuthorizationToken(str): ...@@ -136,3 +136,56 @@ class AuthorizationToken(str):
InvalidToken = AuthorizationToken() # a valid token is never empty InvalidToken = AuthorizationToken() # a valid token is never empty
class IconDescriptor(object):
def __init__(self, url, etag=None):
self.url = url
self.etag = etag
def __getstate__(self):
if self.etag is None:
return unicode(self.url)
else:
return u'%s,%s' % (self.__dict__['url'], self.etag)
def __setstate__(self, state):
try:
url, etag = state.rsplit(u',', 1)
except ValueError:
self.__init__(state)
else:
self.__init__(url, etag)
def __eq__(self, other):
if isinstance(other, IconDescriptor):
return self.url==other.url and self.etag==other.etag
return NotImplemented
def __ne__(self, other):
equal = self.__eq__(other)
return NotImplemented if equal is NotImplemented else not equal
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.url, self.etag)
def _get_url(self):
url = self.__dict__['url']
file_scheme = 'file://'
if url.startswith(file_scheme):
url = file_scheme + ApplicationData.get(url[len(file_scheme):])
return url
def _set_url(self, url):
file_scheme = 'file://'
if url.startswith(file_scheme):
filename = os.path.normpath(url[len(file_scheme):])
if filename.startswith(ApplicationData.directory+os.path.sep):
filename = filename[len(ApplicationData.directory+os.path.sep):]
url = file_scheme + filename
self.__dict__['url'] = url
url = property(_get_url, _set_url)
del _get_url, _set_url
@property
def is_local(self):
return self.__dict__['url'].startswith('file://')
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -20,7 +20,7 @@ from sipsimple.configuration.settings import SIPSimpleSettings ...@@ -20,7 +20,7 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from blink.aboutpanel import AboutPanel from blink.aboutpanel import AboutPanel
from blink.accounts import AccountModel, ActiveAccountModel, ServerToolsAccountModel, ServerToolsWindow from blink.accounts import AccountModel, ActiveAccountModel, ServerToolsAccountModel, ServerToolsWindow
from blink.contacts import BonjourNeighbour, Contact, ContactGroup, ContactEditorDialog, ContactModel, ContactSearchModel, GoogleContactsDialog from blink.contacts import BonjourNeighbour, Contact, Group, ContactEditorDialog, ContactModel, ContactSearchModel, GoogleContactsDialog
from blink.preferences import PreferencesWindow from blink.preferences import PreferencesWindow
from blink.sessions import ConferenceDialog, SessionManager, SessionModel from blink.sessions import ConferenceDialog, SessionManager, SessionModel
from blink.configuration.datatypes import InvalidToken from blink.configuration.datatypes import InvalidToken
...@@ -87,7 +87,7 @@ class MainWindow(base_class, ui_class): ...@@ -87,7 +87,7 @@ class MainWindow(base_class, ui_class):
# Windows, dialogs and panels # Windows, dialogs and panels
self.about_panel = AboutPanel(self) self.about_panel = AboutPanel(self)
self.conference_dialog = ConferenceDialog(self) self.conference_dialog = ConferenceDialog(self)
self.contact_editor_dialog = ContactEditorDialog(self.contact_model, self) self.contact_editor_dialog = ContactEditorDialog(self)
self.google_contacts_dialog = GoogleContactsDialog(self) self.google_contacts_dialog = GoogleContactsDialog(self)
self.preferences_window = PreferencesWindow(self.account_model, None) self.preferences_window = PreferencesWindow(self.account_model, None)
self.server_tools_window = ServerToolsWindow(self.server_tools_account_model, None) self.server_tools_window = ServerToolsWindow(self.server_tools_account_model, None)
...@@ -163,8 +163,6 @@ class MainWindow(base_class, ui_class): ...@@ -163,8 +163,6 @@ class MainWindow(base_class, ui_class):
self.history_on_server_action.triggered.connect(self._AH_HistoryOnServer) self.history_on_server_action.triggered.connect(self._AH_HistoryOnServer)
self.buy_pstn_access_action.triggered.connect(self._AH_PurchasePstnAccess) self.buy_pstn_access_action.triggered.connect(self._AH_PurchasePstnAccess)
self.contact_model.load()
def setupUi(self): def setupUi(self):
super(MainWindow, self).setupUi(self) super(MainWindow, self).setupUi(self)
...@@ -341,15 +339,14 @@ class MainWindow(base_class, ui_class): ...@@ -341,15 +339,14 @@ class MainWindow(base_class, ui_class):
def _SH_AddContactButtonClicked(self, clicked): def _SH_AddContactButtonClicked(self, clicked):
model = self.contact_model model = self.contact_model
selected_items = ((index.row(), model.data(index)) for index in self.contact_list.selectionModel().selectedIndexes()) groups = set()
try: for index in self.contact_list.selectionModel().selectedIndexes():
item = (item for row, item in sorted(selected_items) if type(item) in (Contact, ContactGroup)).next() item = model.data(index)
preferred_group = item if type(item) is ContactGroup else item.group if isinstance(item, Group) and not item.virtual:
except StopIteration: groups.add(item)
try: elif isinstance(item, Contact) and not item.group.virtual:
preferred_group = (group for group in model.contact_groups if type(group) is ContactGroup).next() groups.add(item.group)
except StopIteration: preferred_group = groups.pop() if len(groups)==1 else None
preferred_group = None
self.contact_editor_dialog.open_for_add(self.search_box.text(), preferred_group) self.contact_editor_dialog.open_for_add(self.search_box.text(), preferred_group)
def _SH_AudioCallButtonClicked(self): def _SH_AudioCallButtonClicked(self):
...@@ -359,7 +356,7 @@ class MainWindow(base_class, ui_class): ...@@ -359,7 +356,7 @@ class MainWindow(base_class, ui_class):
address = contact.uri or self.search_box.text() address = contact.uri or self.search_box.text()
name = contact.name or None name = contact.name or None
session_manager = SessionManager() session_manager = SessionManager()
session_manager.start_call(name, address, contact=contact, account=BonjourAccount() if isinstance(contact, BonjourNeighbour) else None) session_manager.start_call(name, address, contact=contact, account=BonjourAccount() if isinstance(contact.settings, BonjourNeighbour) else None)
def _SH_BreakConference(self): def _SH_BreakConference(self):
active_session = self.session_model.data(self.session_list.selectionModel().selectedIndexes()[0]) active_session = self.session_model.data(self.session_list.selectionModel().selectedIndexes()[0])
...@@ -370,7 +367,7 @@ class MainWindow(base_class, ui_class): ...@@ -370,7 +367,7 @@ class MainWindow(base_class, ui_class):
if not isinstance(contact, Contact): if not isinstance(contact, Contact):
return return
session_manager = SessionManager() session_manager = SessionManager()
session_manager.start_call(contact.name, contact.uri, contact=contact, account=BonjourAccount() if isinstance(contact, BonjourNeighbour) else None) session_manager.start_call(contact.name, contact.uri, contact=contact, account=BonjourAccount() if isinstance(contact.settings, BonjourNeighbour) else None)
def _SH_ContactListSelectionChanged(self, selected, deselected): def _SH_ContactListSelectionChanged(self, selected, deselected):
account_manager = AccountManager() account_manager = AccountManager()
......
# Copyright (C) 2010 AG Projects. See LICENSE for details. # Copyright (C) 2010-2013 AG Projects. See LICENSE for details.
# #
"""Provide access to Blink's resources""" """Provide access to Blink's resources"""
__all__ = ['ApplicationData', 'Resources', 'IconCache'] __all__ = ['ApplicationData', 'Resources', 'IconManager']
import cPickle as pickle
import os import os
import platform import platform
import sys import sys
from PyQt4.QtCore import Qt from PyQt4.QtCore import Qt
from PyQt4.QtGui import QPixmap from PyQt4.QtGui import QIcon, QPixmap
from application import log
from application.python.descriptor import classproperty from application.python.descriptor import classproperty
from application.python.types import Singleton from application.python.types import Singleton
from application.system import makedirs, unlink from application.system import makedirs, unlink
from collections import deque
from hashlib import sha512
class DirectoryContextManager(unicode): class DirectoryContextManager(unicode):
...@@ -78,127 +74,65 @@ class Resources(object): ...@@ -78,127 +74,65 @@ class Resources(object):
return os.path.join(cls.directory, resource or u'') return os.path.join(cls.directory, resource or u'')
class FileInfo(object): class IconManager(object):
def __init__(self, name):
self.name = name
try:
stat = os.stat(ApplicationData.get(os.path.join('images', name)))
except OSError:
self.mtime = None
self.size = None
else:
self.mtime = stat.st_mtime
self.size = stat.st_size
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return isinstance(other, FileInfo) and (self.name, self.mtime, self.size) == (other.name, other.mtime, other.size)
def __ne__(self, other):
return not self.__eq__(other)
class FileMapping(object):
def __init__(self, source, destination):
self.source = source
self.destination = destination
class IconCache(object):
__metaclass__ = Singleton __metaclass__ = Singleton
max_size = 256
def __init__(self): def __init__(self):
makedirs(ApplicationData.get('images')) self.iconmap = {}
try:
self.filemap = pickle.load(open(ApplicationData.get(os.path.join('images', '.cached_icons.map')))) def get(self, id):
except Exception:
self.filemap = {}
all_names = set('cached_icon_%04d.png' % x for x in xrange(1, 10000))
used_names = set(os.listdir(ApplicationData.get('images')))
self.available_names = deque(sorted(all_names - used_names))
def store(self, filename, pixmap=None):
if filename is None:
return None
if not os.path.isabs(filename):
return filename
if filename.startswith(ApplicationData.directory + os.path.sep):
return filename[len(ApplicationData.directory + os.path.sep):]
try: try:
file_mapping = self.filemap[filename] return self.iconmap[id]
except KeyError: except KeyError:
pass
else:
source_info = FileInfo(filename)
destination_info = FileInfo(file_mapping.destination.name)
if (source_info, destination_info) == (file_mapping.source, file_mapping.destination):
return destination_info.name
try:
destination_name = os.path.join('images', self.available_names.popleft())
except IndexError:
# No more available file names. Return original file for now
return filename
if pixmap is None:
pixmap = QPixmap() pixmap = QPixmap()
filename = ApplicationData.get(os.path.join('images', id + '.png'))
if pixmap.load(filename): if pixmap.load(filename):
pixmap = pixmap.scaled(32, 32, Qt.KeepAspectRatio, Qt.SmoothTransformation) icon = QIcon(pixmap)
makedirs(ApplicationData.get('images')) icon.filename = filename
if pixmap.save(ApplicationData.get(destination_name)): else:
source_info = FileInfo(filename) icon = None
destination_info = FileInfo(destination_name) return self.iconmap.setdefault(id, icon)
file_mapping = FileMapping(source_info, destination_info)
self.filemap[filename] = file_mapping
map_filename = ApplicationData.get(os.path.join('images', '.cached_icons.map'))
map_tempname = map_filename + '.tmp'
try:
file = open(map_tempname, 'wb')
pickle.dump(self.filemap, file)
file.close()
if sys.platform == 'win32':
unlink(map_filename)
os.rename(map_tempname, map_filename)
except Exception, e:
log.error("could not save icon cache file mappings: %s" % e)
return destination_name
else:
self.available_names.appendleft(os.path.basename(destination_name))
return filename
def store_image(self, data): def store_data(self, id, data):
if data is None: directory = ApplicationData.get('images')
return None filename = os.path.join(directory, id + '.png')
data_hash = sha512(data).hexdigest() makedirs(directory)
try: pixmap = QPixmap()
return self.filemap[data_hash].destination if data is not None and pixmap.loadFromData(data):
except KeyError: if pixmap.size().width() > self.max_size or pixmap.size().height() > self.max_size:
pass pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
try: pixmap.save(filename)
destination_name = os.path.join('images', self.available_names.popleft()) icon = QIcon(pixmap)
except IndexError: icon.filename = filename
# No more available file names. else:
return None unlink(filename)
icon = None
self.iconmap[id] = icon
return icon
def store_file(self, id, file):
directory = ApplicationData.get('images')
filename = os.path.join(directory, id + '.png')
if filename == os.path.normpath(file):
return self.iconmap.get(id, None)
makedirs(directory)
pixmap = QPixmap() pixmap = QPixmap()
if pixmap.loadFromData(data): if file is not None and pixmap.load(file):
pixmap = pixmap.scaled(32, 32, Qt.KeepAspectRatio, Qt.SmoothTransformation) if pixmap.size().width() > self.max_size or pixmap.size().height() > self.max_size:
makedirs(ApplicationData.get('images')) pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
if pixmap.save(ApplicationData.get(destination_name)): pixmap.save(filename)
file_mapping = FileMapping(data_hash, destination_name) icon = QIcon(pixmap)
self.filemap[data_hash] = file_mapping icon.filename = filename
map_filename = ApplicationData.get(os.path.join('images', '.cached_icons.map'))
map_tempname = map_filename + '.tmp'
try:
file = open(map_tempname, 'wb')
pickle.dump(self.filemap, file)
file.close()
if sys.platform == 'win32':
unlink(map_filename)
os.rename(map_tempname, map_filename)
except Exception, e:
log.error("could not save icon cache file mappings: %s" % e)
return destination_name
else: else:
self.available_names.appendleft(os.path.basename(destination_name)) unlink(filename)
return None icon = None
self.iconmap[id] = icon
return icon
def remove(self, id):
self.iconmap.pop(id, None)
unlink(ApplicationData.get(os.path.join('images', id + '.png')))
...@@ -1588,7 +1588,7 @@ class IncomingSession(QObject): ...@@ -1588,7 +1588,7 @@ class IncomingSession(QObject):
self.dialog.uri_label.setText(address) self.dialog.uri_label.setText(address)
if self.contact: if self.contact:
self.dialog.username_label.setText(contact.name or session.remote_identity.display_name or address) self.dialog.username_label.setText(contact.name or session.remote_identity.display_name or address)
self.dialog.user_icon.setPixmap(contact.icon) self.dialog.user_icon.setPixmap(contact.pixmap)
else: else:
self.dialog.username_label.setText(session.remote_identity.display_name or address) self.dialog.username_label.setText(session.remote_identity.display_name or address)
if self.audio_stream: if self.audio_stream:
...@@ -1774,7 +1774,7 @@ class SessionManager(object): ...@@ -1774,7 +1774,7 @@ class SessionManager(object):
session = Session(account) session = Session(account)
if contact is None: if contact is None:
for contact in self.main_window.contact_model.iter_contacts(): for contact in self.main_window.contact_model.iter_contacts():
if remote_uri.matches(self.normalize_number(account, contact.uri)) or any(remote_uri.matches(self.normalize_number(account, alias)) for alias in contact.sip_aliases): if any(remote_uri.matches(self.normalize_number(account, uri.uri)) for uri in contact.uris):
break break
else: else:
contact = None contact = None
...@@ -1986,18 +1986,16 @@ class SessionManager(object): ...@@ -1986,18 +1986,16 @@ class SessionManager(object):
session.reject(488) session.reject(488)
return return
session.send_ring_indication() session.send_ring_indication()
uri = session.remote_identity.uri remote_uri = session.remote_identity.uri
for contact in self.main_window.contact_model.iter_contacts(): for contact in self.main_window.contact_model.iter_contacts():
if uri.matches(self.normalize_number(session.account, contact.uri)) or any(uri.matches(self.normalize_number(session.account, alias)) for alias in contact.sip_aliases): if any(remote_uri.matches(self.normalize_number(session.account, uri.uri)) for uri in contact.uris):
break break
else: else:
matched_contacts = [] matched_contacts = []
number = uri.user.strip('0') if self.number_re.match(uri.user) else Null number = remote_uri.user.strip('0') if self.number_re.match(remote_uri.user) else Null
if len(number) > 7: if len(number) > 7:
for contact in self.main_window.contact_model.iter_contacts(): for contact in self.main_window.contact_model.iter_contacts():
if self.number_re.match(contact.uri) and self.normalize_number(session.account, contact.uri).endswith(number): if any(self.number_re.match(uri.uri) and self.normalize_number(session.account, uri.uri).endswith(number) for uri in contact.uris):
matched_contacts.append(contact)
elif any(self.number_re.match(alias) and self.normalize_number(session.account, alias).endswith(number) for alias in contact.sip_aliases):
matched_contacts.append(contact) matched_contacts.append(contact)
contact = matched_contacts[0] if len(matched_contacts)==1 else None contact = matched_contacts[0] if len(matched_contacts)==1 else None
if filetransfer_streams: if filetransfer_streams:
...@@ -2033,9 +2031,9 @@ class SessionManager(object): ...@@ -2033,9 +2031,9 @@ class SessionManager(object):
session.reject_proposal(488) session.reject_proposal(488)
return return
session.send_ring_indication() session.send_ring_indication()
uri = session.remote_identity.uri remote_uri = session.remote_identity.uri
for contact in self.main_window.contact_model.iter_contacts(): for contact in self.main_window.contact_model.iter_contacts():
if uri.matches(self.normalize_number(session.account, contact.uri)) or any(uri.matches(self.normalize_number(session.account, alias)) for alias in contact.sip_aliases): if any(remote_uri.matches(self.normalize_number(session.account, uri.uri)) for uri in contact.uris):
break break
else: else:
contact = None contact = None
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<property name="palette"> <property name="palette">
<palette> <palette>
<active> <active>
<colorrole role="Base"> <colorrole role="Window">
<brush brushstyle="SolidPattern"> <brush brushstyle="SolidPattern">
<color alpha="255"> <color alpha="255">
<red>255</red> <red>255</red>
...@@ -22,18 +22,18 @@ ...@@ -22,18 +22,18 @@
</color> </color>
</brush> </brush>
</colorrole> </colorrole>
<colorrole role="Window"> <colorrole role="AlternateBase">
<brush brushstyle="SolidPattern"> <brush brushstyle="SolidPattern">
<color alpha="255"> <color alpha="255">
<red>255</red> <red>240</red>
<green>255</green> <green>244</green>
<blue>255</blue> <blue>255</blue>
</color> </color>
</brush> </brush>
</colorrole> </colorrole>
</active> </active>
<inactive> <inactive>
<colorrole role="Base"> <colorrole role="Window">
<brush brushstyle="SolidPattern"> <brush brushstyle="SolidPattern">
<color alpha="255"> <color alpha="255">
<red>255</red> <red>255</red>
...@@ -42,18 +42,18 @@ ...@@ -42,18 +42,18 @@
</color> </color>
</brush> </brush>
</colorrole> </colorrole>
<colorrole role="Window"> <colorrole role="AlternateBase">
<brush brushstyle="SolidPattern"> <brush brushstyle="SolidPattern">
<color alpha="255"> <color alpha="255">
<red>255</red> <red>240</red>
<green>255</green> <green>244</green>
<blue>255</blue> <blue>255</blue>
</color> </color>
</brush> </brush>
</colorrole> </colorrole>
</inactive> </inactive>
<disabled> <disabled>
<colorrole role="Base"> <colorrole role="Window">
<brush brushstyle="SolidPattern"> <brush brushstyle="SolidPattern">
<color alpha="255"> <color alpha="255">
<red>255</red> <red>255</red>
...@@ -62,11 +62,11 @@ ...@@ -62,11 +62,11 @@
</color> </color>
</brush> </brush>
</colorrole> </colorrole>
<colorrole role="Window"> <colorrole role="AlternateBase">
<brush brushstyle="SolidPattern"> <brush brushstyle="SolidPattern">
<color alpha="255"> <color alpha="255">
<red>255</red> <red>240</red>
<green>255</green> <green>244</green>
<blue>255</blue> <blue>255</blue>
</color> </color>
</brush> </brush>
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="icon"> <widget class="QLabel" name="icon_label">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>36</width> <width>36</width>
...@@ -113,12 +113,12 @@ ...@@ -113,12 +113,12 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="name_uri_layout"> <layout class="QVBoxLayout" name="name_info_layout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="name"> <widget class="QLabel" name="name_label">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
...@@ -131,10 +131,7 @@ ...@@ -131,10 +131,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="uri"> <widget class="QLabel" name="info_label">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment