Commit 50049a12 authored by Dan Pascu's avatar Dan Pascu

Added preferences panel

parent 7af108ab
......@@ -5,20 +5,48 @@
__all__ = ['AccountExtension', 'BonjourAccountExtension']
from sipsimple.account import RTPSettings, TLSSettings
from sipsimple.account import BonjourMSRPSettings, MessageSummarySettings, MSRPSettings, NATTraversalSettings, RTPSettings, SIPSettings, TLSSettings, XCAPSettings
from sipsimple.configuration import Setting, SettingsGroup, SettingsObjectExtension
from sipsimple.configuration.datatypes import AudioCodecList, MSRPConnectionModel, MSRPTransport, SIPTransportList
from sipsimple.util import user_info
from blink.configuration.datatypes import ApplicationDataPath, CustomSoundFile, DefaultPath, HTTPURL
class BonjourMSRPSettingsExtension(BonjourMSRPSettings):
transport = Setting(type=MSRPTransport, default='tcp')
class BonjourSIPSettings(SettingsGroup):
transport_order = Setting(type=SIPTransportList, default=SIPTransportList(['tcp', 'udp', 'tls']))
class MessageSummarySettingsExtension(MessageSummarySettings):
enabled = Setting(type=bool, default=True)
class MSRPSettingsExtension(MSRPSettings):
connection_model = Setting(type=MSRPConnectionModel, default='relay')
class NATTraversalSettingsExtension(NATTraversalSettings):
use_msrp_relay_for_inbound = Setting(type=bool, default=True)
class PSTNSettings(SettingsGroup):
idd_prefix = Setting(type=unicode, default=None, nillable=True)
prefix = Setting(type=unicode, default=None, nillable=True)
class RTPSettingsExtension(RTPSettings):
audio_codec_order = Setting(type=AudioCodecList, default=None, nillable=True)
inband_dtmf = Setting(type=bool, default=True)
use_srtp_without_tls = Setting(type=bool, default=True)
class SIPSettingsExtension(SIPSettings):
always_use_my_proxy = Setting(type=bool, default=True)
register = Setting(type=bool, default=True)
class ServerSettings(SettingsGroup):
......@@ -33,17 +61,28 @@ class TLSSettingsExtension(TLSSettings):
certificate = Setting(type=ApplicationDataPath, default=None, nillable=True)
class XCAPSettingsExtension(XCAPSettings):
enabled = Setting(type=bool, default=True)
class AccountExtension(SettingsObjectExtension):
display_name = Setting(type=str, default=user_info.fullname, nillable=True)
message_summary = MessageSummarySettingsExtension
msrp = MSRPSettingsExtension
nat_traversal = NATTraversalSettingsExtension
pstn = PSTNSettings
rtp = RTPSettingsExtension
server = ServerSettings
sip = SIPSettingsExtension
sounds = SoundSettings
tls = TLSSettingsExtension
xcap = XCAPSettingsExtension
class BonjourAccountExtension(SettingsObjectExtension):
sounds = SoundSettings
msrp = BonjourMSRPSettingsExtension
rtp = RTPSettingsExtension
sip = BonjourSIPSettings
sounds = SoundSettings
......@@ -9,15 +9,35 @@ import platform
import sys
from sipsimple.configuration import Setting, SettingsGroup, SettingsObjectExtension
from sipsimple.configuration.settings import AudioSettings, LogsSettings, TLSSettings
from sipsimple.configuration.datatypes import AudioCodecList, NonNegativeInteger, PositiveInteger, Path, SampleRate
from sipsimple.configuration.settings import AudioSettings, ChatSettings, FileTransferSettings, LogsSettings, RTPSettings, TLSSettings
from blink import __version__
from blink.configuration.datatypes import ApplicationDataPath, AuthorizationToken, HTTPURL, SoundFile
from blink.resources import Resources
class AnsweringMachineSettings(SettingsGroup):
enabled = Setting(type=bool, default=False)
answer_delay = Setting(type=NonNegativeInteger, default=10)
max_recording = Setting(type=PositiveInteger, default=3)
unavailable_message = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/unavailable_message.wav')), nillable=True)
class AudioSettingsExtension(AudioSettings):
recordings_directory = Setting(type=ApplicationDataPath, default=ApplicationDataPath('recordings'), nillable=False)
recordings_directory = Setting(type=ApplicationDataPath, default=ApplicationDataPath('recordings'))
sample_rate = Setting(type=SampleRate, default=44100)
class ChatSettingsExtension(ChatSettings):
auto_accept = Setting(type=bool, default=False)
sms_replication = Setting(type=bool, default=True)
history_directory = Setting(type=ApplicationDataPath, default=ApplicationDataPath('history'))
class FileTransferSettingsExtension(FileTransferSettings):
auto_accept = Setting(type=bool, default=False)
directory = Setting(type=Path, default=None, nillable=True)
class GoogleContactsSettings(SettingsGroup):
......@@ -29,9 +49,14 @@ class LogsSettingsExtension(LogsSettings):
trace_sip = Setting(type=bool, default=False)
trace_pjsip = Setting(type=bool, default=False)
trace_msrp = Setting(type=bool, default=False)
trace_xcap = Setting(type=bool, default=False)
trace_notifications = Setting(type=bool, default=False)
class RTPSettingsExtension(RTPSettings):
audio_codec_order = Setting(type=AudioCodecList, default=AudioCodecList(('G722', 'speex', 'GSM', 'iLBC', 'PCMU', 'PCMA')))
class ServerSettings(SettingsGroup):
enrollment_url = Setting(type=HTTPURL, default="https://blink.sipthor.net/enrollment.phtml")
......@@ -39,6 +64,12 @@ class ServerSettings(SettingsGroup):
class SoundSettings(SettingsGroup):
inbound_ringtone = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/inbound_ringtone.wav')), nillable=True)
outbound_ringtone = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/outbound_ringtone.wav')), nillable=True)
message_received = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/message_received.wav')), nillable=True)
message_sent = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/message_sent.wav')), nillable=True)
file_received = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/file_received.wav')), nillable=True)
file_sent = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/file_sent.wav')), nillable=True)
play_message_alerts = Setting(type=bool, default=True)
play_file_alerts = Setting(type=bool, default=True)
class TLSSettingsExtension(TLSSettings):
......@@ -46,9 +77,13 @@ class TLSSettingsExtension(TLSSettings):
class SIPSimpleSettingsExtension(SettingsObjectExtension):
answering_machine = AnsweringMachineSettings
audio = AudioSettingsExtension
chat = ChatSettingsExtension
file_transfer = FileTransferSettingsExtension
google_contacts = GoogleContactsSettings
logs = LogsSettingsExtension
rtp = RTPSettingsExtension
server = ServerSettings
sounds = SoundSettings
tls = TLSSettingsExtension
......
......@@ -22,8 +22,9 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.util import limit
from blink.aboutpanel import AboutPanel
from blink.accounts import AccountModel, ActiveAccountModel, AddAccountDialog, ServerToolsAccountModel, ServerToolsWindow
from blink.accounts import AccountModel, ActiveAccountModel, ServerToolsAccountModel, ServerToolsWindow
from blink.contacts import BonjourNeighbour, Contact, ContactGroup, ContactEditorDialog, ContactModel, ContactSearchModel, GoogleContactsDialog
from blink.preferences import PreferencesWindow
from blink.sessions import SessionManager, SessionModel
from blink.configuration.datatypes import InvalidToken
from blink.resources import Resources
......@@ -88,9 +89,9 @@ class MainWindow(base_class, ui_class):
# Windows, dialogs and panels
self.about_panel = AboutPanel(self)
self.add_account_dialog = AddAccountDialog(self)
self.contact_editor_dialog = ContactEditorDialog(self.contact_model, self)
self.google_contacts_dialog = GoogleContactsDialog(self)
self.preferences_window = PreferencesWindow(self.account_model, None)
self.server_tools_window = ServerToolsWindow(self.server_tools_account_model, None)
# Signals
......@@ -135,8 +136,12 @@ class MainWindow(base_class, ui_class):
# Blink menu actions
self.about_action.triggered.connect(self.about_panel.show)
self.donate_action.triggered.connect(partial(QDesktopServices.openUrl, QUrl(u'http://icanblink.com/payments.phtml')))
self.add_account_action.triggered.connect(self.add_account_dialog.open_for_add)
self.add_account_action.triggered.connect(self.preferences_window.show_add_account_dialog)
self.manage_accounts_action.triggered.connect(self.preferences_window.show_for_accounts)
self.help_action.triggered.connect(partial(QDesktopServices.openUrl, QUrl(u'http://icanblink.com/help-qt.phtml')))
self.preferences_action.triggered.connect(self.preferences_window.show)
self.auto_accept_chat_action.triggered.connect(self._AH_AutoAcceptChatTriggered)
self.auto_accept_files_action.triggered.connect(self._AH_AutoAcceptFilesTriggered)
self.release_notes_action.triggered.connect(partial(QDesktopServices.openUrl, QUrl(u'http://icanblink.com/changelog-qt.phtml')))
self.quit_action.triggered.connect(self.close)
......@@ -151,6 +156,7 @@ class MainWindow(base_class, ui_class):
self.redial_action.triggered.connect(self._AH_RedialActionTriggered)
# Tools menu actions
self.answering_machine_action.triggered.connect(self._AH_EnableAnsweringMachineTriggered)
self.sip_server_settings_action.triggered.connect(self._AH_SIPServerSettings)
self.search_for_people_action.triggered.connect(self._AH_SearchForPeople)
self.history_on_server_action.triggered.connect(self._AH_HistoryOnServer)
......@@ -204,9 +210,9 @@ class MainWindow(base_class, ui_class):
def closeEvent(self, event):
super(MainWindow, self).closeEvent(event)
self.about_panel.close()
self.add_account_dialog.close()
self.contact_editor_dialog.close()
self.google_contacts_dialog.close()
self.preferences_window.close()
self.server_tools_window.close()
def set_user_icon(self, image_file_name):
......@@ -302,6 +308,21 @@ class MainWindow(base_class, ui_class):
settings.audio.output_device = action.data().toPyObject()
call_in_auxiliary_thread(settings.save)
def _AH_AutoAcceptChatTriggered(self, checked):
settings = SIPSimpleSettings()
settings.chat.auto_accept = checked
settings.save()
def _AH_AutoAcceptFilesTriggered(self, checked):
settings = SIPSimpleSettings()
settings.file_transfer.auto_accept = checked
settings.save()
def _AH_EnableAnsweringMachineTriggered(self, checked):
settings = SIPSimpleSettings()
settings.answering_machine.enabled = checked
settings.save()
def _AH_GoogleContactsActionTriggered(self):
settings = SIPSimpleSettings()
if settings.google_contacts.authorization_token is not None:
......@@ -521,6 +542,9 @@ class MainWindow(base_class, ui_class):
settings = SIPSimpleSettings()
self.silent_action.setChecked(settings.audio.silent)
self.silent_button.setChecked(settings.audio.silent)
self.answering_machine_action.setChecked(settings.answering_machine.enabled)
self.auto_accept_chat_action.setChecked(settings.chat.auto_accept)
self.auto_accept_files_action.setChecked(settings.file_transfer.auto_accept)
if settings.google_contacts.authorization_token is None:
self.google_contacts_action.setText(u'Enable Google Contacts')
else:
......@@ -575,6 +599,12 @@ class MainWindow(base_class, ui_class):
if 'audio.alert_device' in notification.data.modified:
action = (action for action in self.alert_devices_group.actions() if action.data().toPyObject() == settings.audio.alert_device).next()
action.setChecked(True)
if 'answering_machine.enabled' in notification.data.modified:
self.answering_machine_action.setChecked(settings.answering_machine.enabled)
if 'chat.auto_accept' in notification.data.modified:
self.auto_accept_chat_action.setChecked(settings.chat.auto_accept)
if 'file_transfer.auto_accept' in notification.data.modified:
self.auto_accept_files_action.setChecked(settings.file_transfer.auto_accept)
if 'google_contacts.authorization_token' in notification.data.modified:
authorization_token = notification.sender.google_contacts.authorization_token
if authorization_token is None:
......@@ -584,10 +614,13 @@ class MainWindow(base_class, ui_class):
if authorization_token is InvalidToken:
self.google_contacts_dialog.open_for_incorrect_password()
elif isinstance(notification.sender, (Account, BonjourAccount)):
account_manager = AccountManager()
account = notification.sender
if 'enabled' in notification.data.modified:
action = (action for action in self.accounts_menu.actions() if action.data().toPyObject() is account).next()
action.setChecked(account.enabled)
if 'display_name' in notification.data.modified and account is account_manager.default_account:
self.display_name.setText(account.display_name or u'')
if set(['enabled', 'message_summary.enabled', 'message_summary.voicemail_uri']).intersection(notification.data.modified):
action = (action for action in self.voicemail_menu.actions() if action.data().toPyObject() is account).next()
action.setVisible(False if account is BonjourAccount() else account.enabled and account.message_summary.enabled)
......
This diff is collapsed.
......@@ -909,9 +909,6 @@ padding: 2px;</string>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="quick_settings_menu">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string>Quick &amp;settings</string>
</property>
......@@ -1083,9 +1080,6 @@ padding: 2px;</string>
</property>
</action>
<action name="preferences_action">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>icons/configure.png</normaloff>icons/configure.png</iconset>
......@@ -1140,9 +1134,6 @@ padding: 2px;</string>
<property name="checkable">
<bool>true</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Enable &amp;Answering Machine</string>
</property>
......@@ -1248,9 +1239,6 @@ padding: 2px;</string>
</property>
</action>
<action name="manage_accounts_action">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Manage accounts...</string>
</property>
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48px"
height="48px"
id="svg2816"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="playback.svg">
<defs
id="defs2818">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective2824" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7"
inkscape:cx="23.734792"
inkscape:cy="24"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1284"
inkscape:window-height="833"
inkscape:window-x="180"
inkscape:window-y="220"
inkscape:window-maximized="0" />
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.91106749;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 11.455534,9.4555332 C 11.58353,38.544466 11.58353,38.544466 11.58353,38.544466 L 38.544468,24.190839 11.455534,9.4555332 z"
id="path2828" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48px"
height="48px"
id="svg2816"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="record48.svg">
<defs
id="defs2818">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective2824" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="23.452561"
inkscape:cy="24"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1121"
inkscape:window-height="836"
inkscape:window-x="395"
inkscape:window-y="238"
inkscape:window-maximized="0" />
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
sodipodi:type="arc"
style="fill:#f00000;fill-opacity:1;fill-rule:nonzero;stroke:#a20000;stroke-width:2.2054215;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path2826"
sodipodi:cx="9.7142859"
sodipodi:cy="12.714286"
sodipodi:rx="17.142857"
sodipodi:ry="17.142857"
d="m 26.857142,12.714286 a 17.142857,17.142857 0 1 1 -34.2857127,0 17.142857,17.142857 0 1 1 34.2857127,0 z"
transform="matrix(0.87692988,0,0,0.87692987,15.481253,12.850463)" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32px"
height="32px"
id="svg2816"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="plus.svg">
<defs
id="defs2818">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3620"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.313708"
inkscape:cx="10.038652"
inkscape:cy="15.230556"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1140"
inkscape:window-height="842"
inkscape:window-x="433"
inkscape:window-y="195"
inkscape:window-maximized="0" />
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="rect2826-0"
width="2"
height="16"
x="15"
y="-24"
transform="matrix(0,1,-1,0,0,0)" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg2816"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="plus18.svg">
<defs
id="defs2818">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 8 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="16 : 8 : 1"
inkscape:persp3d-origin="8 : 5.3333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective2836"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.627417"
inkscape:cx="8"
inkscape:cy="8"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="884"
inkscape:window-height="748"
inkscape:window-x="593"
inkscape:window-y="230"
inkscape:window-maximized="0" />
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="rect2826-0"
width="2"
height="8"
x="6"
y="-13"
transform="matrix(0,1,-1,0,0,0)" />
</g>
</svg>
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