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

Added preferences panel

parent 7af108ab
...@@ -5,20 +5,48 @@ ...@@ -5,20 +5,48 @@
__all__ = ['AccountExtension', 'BonjourAccountExtension'] __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 import Setting, SettingsGroup, SettingsObjectExtension
from sipsimple.configuration.datatypes import AudioCodecList, MSRPConnectionModel, MSRPTransport, SIPTransportList
from sipsimple.util import user_info from sipsimple.util import user_info
from blink.configuration.datatypes import ApplicationDataPath, CustomSoundFile, DefaultPath, HTTPURL 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): class PSTNSettings(SettingsGroup):
idd_prefix = Setting(type=unicode, default=None, nillable=True) idd_prefix = Setting(type=unicode, default=None, nillable=True)
prefix = Setting(type=unicode, default=None, nillable=True) prefix = Setting(type=unicode, default=None, nillable=True)
class RTPSettingsExtension(RTPSettings): class RTPSettingsExtension(RTPSettings):
audio_codec_order = Setting(type=AudioCodecList, default=None, nillable=True)
inband_dtmf = Setting(type=bool, default=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): class ServerSettings(SettingsGroup):
...@@ -33,17 +61,28 @@ class TLSSettingsExtension(TLSSettings): ...@@ -33,17 +61,28 @@ class TLSSettingsExtension(TLSSettings):
certificate = Setting(type=ApplicationDataPath, default=None, nillable=True) certificate = Setting(type=ApplicationDataPath, default=None, nillable=True)
class XCAPSettingsExtension(XCAPSettings):
enabled = Setting(type=bool, default=True)
class AccountExtension(SettingsObjectExtension): class AccountExtension(SettingsObjectExtension):
display_name = Setting(type=str, default=user_info.fullname, nillable=True) display_name = Setting(type=str, default=user_info.fullname, nillable=True)
message_summary = MessageSummarySettingsExtension
msrp = MSRPSettingsExtension
nat_traversal = NATTraversalSettingsExtension
pstn = PSTNSettings pstn = PSTNSettings
rtp = RTPSettingsExtension rtp = RTPSettingsExtension
server = ServerSettings server = ServerSettings
sip = SIPSettingsExtension
sounds = SoundSettings sounds = SoundSettings
tls = TLSSettingsExtension tls = TLSSettingsExtension
xcap = XCAPSettingsExtension
class BonjourAccountExtension(SettingsObjectExtension): class BonjourAccountExtension(SettingsObjectExtension):
sounds = SoundSettings msrp = BonjourMSRPSettingsExtension
rtp = RTPSettingsExtension rtp = RTPSettingsExtension
sip = BonjourSIPSettings
sounds = SoundSettings
...@@ -9,15 +9,35 @@ import platform ...@@ -9,15 +9,35 @@ import platform
import sys import sys
from sipsimple.configuration import Setting, SettingsGroup, SettingsObjectExtension 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 import __version__
from blink.configuration.datatypes import ApplicationDataPath, AuthorizationToken, HTTPURL, SoundFile from blink.configuration.datatypes import ApplicationDataPath, AuthorizationToken, HTTPURL, SoundFile
from blink.resources import Resources 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): 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): class GoogleContactsSettings(SettingsGroup):
...@@ -29,9 +49,14 @@ class LogsSettingsExtension(LogsSettings): ...@@ -29,9 +49,14 @@ class LogsSettingsExtension(LogsSettings):
trace_sip = Setting(type=bool, default=False) trace_sip = Setting(type=bool, default=False)
trace_pjsip = Setting(type=bool, default=False) trace_pjsip = Setting(type=bool, default=False)
trace_msrp = 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) 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): class ServerSettings(SettingsGroup):
enrollment_url = Setting(type=HTTPURL, default="https://blink.sipthor.net/enrollment.phtml") enrollment_url = Setting(type=HTTPURL, default="https://blink.sipthor.net/enrollment.phtml")
...@@ -39,6 +64,12 @@ class ServerSettings(SettingsGroup): ...@@ -39,6 +64,12 @@ class ServerSettings(SettingsGroup):
class SoundSettings(SettingsGroup): class SoundSettings(SettingsGroup):
inbound_ringtone = Setting(type=SoundFile, default=SoundFile(Resources.get('sounds/inbound_ringtone.wav')), nillable=True) 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) 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): class TLSSettingsExtension(TLSSettings):
...@@ -46,9 +77,13 @@ class TLSSettingsExtension(TLSSettings): ...@@ -46,9 +77,13 @@ class TLSSettingsExtension(TLSSettings):
class SIPSimpleSettingsExtension(SettingsObjectExtension): class SIPSimpleSettingsExtension(SettingsObjectExtension):
answering_machine = AnsweringMachineSettings
audio = AudioSettingsExtension audio = AudioSettingsExtension
chat = ChatSettingsExtension
file_transfer = FileTransferSettingsExtension
google_contacts = GoogleContactsSettings google_contacts = GoogleContactsSettings
logs = LogsSettingsExtension logs = LogsSettingsExtension
rtp = RTPSettingsExtension
server = ServerSettings server = ServerSettings
sounds = SoundSettings sounds = SoundSettings
tls = TLSSettingsExtension tls = TLSSettingsExtension
......
...@@ -22,8 +22,9 @@ from sipsimple.configuration.settings import SIPSimpleSettings ...@@ -22,8 +22,9 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.util import limit from sipsimple.util import limit
from blink.aboutpanel import AboutPanel 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.contacts import BonjourNeighbour, Contact, ContactGroup, ContactEditorDialog, ContactModel, ContactSearchModel, GoogleContactsDialog
from blink.preferences import PreferencesWindow
from blink.sessions import SessionManager, SessionModel from blink.sessions import SessionManager, SessionModel
from blink.configuration.datatypes import InvalidToken from blink.configuration.datatypes import InvalidToken
from blink.resources import Resources from blink.resources import Resources
...@@ -88,9 +89,9 @@ class MainWindow(base_class, ui_class): ...@@ -88,9 +89,9 @@ 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.add_account_dialog = AddAccountDialog(self)
self.contact_editor_dialog = ContactEditorDialog(self.contact_model, self) self.contact_editor_dialog = ContactEditorDialog(self.contact_model, self)
self.google_contacts_dialog = GoogleContactsDialog(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) self.server_tools_window = ServerToolsWindow(self.server_tools_account_model, None)
# Signals # Signals
...@@ -135,8 +136,12 @@ class MainWindow(base_class, ui_class): ...@@ -135,8 +136,12 @@ class MainWindow(base_class, ui_class):
# Blink menu actions # Blink menu actions
self.about_action.triggered.connect(self.about_panel.show) 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.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.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.release_notes_action.triggered.connect(partial(QDesktopServices.openUrl, QUrl(u'http://icanblink.com/changelog-qt.phtml')))
self.quit_action.triggered.connect(self.close) self.quit_action.triggered.connect(self.close)
...@@ -151,6 +156,7 @@ class MainWindow(base_class, ui_class): ...@@ -151,6 +156,7 @@ class MainWindow(base_class, ui_class):
self.redial_action.triggered.connect(self._AH_RedialActionTriggered) self.redial_action.triggered.connect(self._AH_RedialActionTriggered)
# Tools menu actions # Tools menu actions
self.answering_machine_action.triggered.connect(self._AH_EnableAnsweringMachineTriggered)
self.sip_server_settings_action.triggered.connect(self._AH_SIPServerSettings) self.sip_server_settings_action.triggered.connect(self._AH_SIPServerSettings)
self.search_for_people_action.triggered.connect(self._AH_SearchForPeople) self.search_for_people_action.triggered.connect(self._AH_SearchForPeople)
self.history_on_server_action.triggered.connect(self._AH_HistoryOnServer) self.history_on_server_action.triggered.connect(self._AH_HistoryOnServer)
...@@ -204,9 +210,9 @@ class MainWindow(base_class, ui_class): ...@@ -204,9 +210,9 @@ class MainWindow(base_class, ui_class):
def closeEvent(self, event): def closeEvent(self, event):
super(MainWindow, self).closeEvent(event) super(MainWindow, self).closeEvent(event)
self.about_panel.close() self.about_panel.close()
self.add_account_dialog.close()
self.contact_editor_dialog.close() self.contact_editor_dialog.close()
self.google_contacts_dialog.close() self.google_contacts_dialog.close()
self.preferences_window.close()
self.server_tools_window.close() self.server_tools_window.close()
def set_user_icon(self, image_file_name): def set_user_icon(self, image_file_name):
...@@ -302,6 +308,21 @@ class MainWindow(base_class, ui_class): ...@@ -302,6 +308,21 @@ class MainWindow(base_class, ui_class):
settings.audio.output_device = action.data().toPyObject() settings.audio.output_device = action.data().toPyObject()
call_in_auxiliary_thread(settings.save) 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): def _AH_GoogleContactsActionTriggered(self):
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
if settings.google_contacts.authorization_token is not None: if settings.google_contacts.authorization_token is not None:
...@@ -521,6 +542,9 @@ class MainWindow(base_class, ui_class): ...@@ -521,6 +542,9 @@ class MainWindow(base_class, ui_class):
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
self.silent_action.setChecked(settings.audio.silent) self.silent_action.setChecked(settings.audio.silent)
self.silent_button.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: if settings.google_contacts.authorization_token is None:
self.google_contacts_action.setText(u'Enable Google Contacts') self.google_contacts_action.setText(u'Enable Google Contacts')
else: else:
...@@ -575,6 +599,12 @@ class MainWindow(base_class, ui_class): ...@@ -575,6 +599,12 @@ class MainWindow(base_class, ui_class):
if 'audio.alert_device' in notification.data.modified: 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 = (action for action in self.alert_devices_group.actions() if action.data().toPyObject() == settings.audio.alert_device).next()
action.setChecked(True) 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: if 'google_contacts.authorization_token' in notification.data.modified:
authorization_token = notification.sender.google_contacts.authorization_token authorization_token = notification.sender.google_contacts.authorization_token
if authorization_token is None: if authorization_token is None:
...@@ -584,10 +614,13 @@ class MainWindow(base_class, ui_class): ...@@ -584,10 +614,13 @@ class MainWindow(base_class, ui_class):
if authorization_token is InvalidToken: if authorization_token is InvalidToken:
self.google_contacts_dialog.open_for_incorrect_password() self.google_contacts_dialog.open_for_incorrect_password()
elif isinstance(notification.sender, (Account, BonjourAccount)): elif isinstance(notification.sender, (Account, BonjourAccount)):
account_manager = AccountManager()
account = notification.sender account = notification.sender
if 'enabled' in notification.data.modified: if 'enabled' in notification.data.modified:
action = (action for action in self.accounts_menu.actions() if action.data().toPyObject() is account).next() action = (action for action in self.accounts_menu.actions() if action.data().toPyObject() is account).next()
action.setChecked(account.enabled) 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): 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 = (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) 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> ...@@ -909,9 +909,6 @@ padding: 2px;</string>
<addaction name="separator"/> <addaction name="separator"/>
</widget> </widget>
<widget class="QMenu" name="quick_settings_menu"> <widget class="QMenu" name="quick_settings_menu">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title"> <property name="title">
<string>Quick &amp;settings</string> <string>Quick &amp;settings</string>
</property> </property>
...@@ -1083,9 +1080,6 @@ padding: 2px;</string> ...@@ -1083,9 +1080,6 @@ padding: 2px;</string>
</property> </property>
</action> </action>
<action name="preferences_action"> <action name="preferences_action">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>icons/configure.png</normaloff>icons/configure.png</iconset> <normaloff>icons/configure.png</normaloff>icons/configure.png</iconset>
...@@ -1140,9 +1134,6 @@ padding: 2px;</string> ...@@ -1140,9 +1134,6 @@ padding: 2px;</string>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Enable &amp;Answering Machine</string> <string>Enable &amp;Answering Machine</string>
</property> </property>
...@@ -1248,9 +1239,6 @@ padding: 2px;</string> ...@@ -1248,9 +1239,6 @@ padding: 2px;</string>
</property> </property>
</action> </action>
<action name="manage_accounts_action"> <action name="manage_accounts_action">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>&amp;Manage accounts...</string> <string>&amp;Manage accounts...</string>
</property> </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