Commit 1752d1af authored by Dan Pascu's avatar Dan Pascu

Added chat preferences

parent 2ca9570e
...@@ -24,7 +24,7 @@ from zope.interface import implements ...@@ -24,7 +24,7 @@ from zope.interface import implements
from sipsimple.account import AccountManager from sipsimple.account import AccountManager
from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.configuration.settings import SIPSimpleSettings
from blink.configuration.datatypes import FileURL from blink.configuration.datatypes import FileURL, GraphTimeScale
from blink.configuration.settings import BlinkSettings from blink.configuration.settings import BlinkSettings
from blink.contacts import URIUtils from blink.contacts import URIUtils
from blink.resources import IconManager, Resources from blink.resources import IconManager, Resources
...@@ -476,24 +476,22 @@ ui_class, base_class = uic.loadUiType(Resources.get('chat_widget.ui')) ...@@ -476,24 +476,22 @@ ui_class, base_class = uic.loadUiType(Resources.get('chat_widget.ui'))
class ChatWidget(base_class, ui_class): class ChatWidget(base_class, ui_class):
default_user_icon = IconDescriptor(Resources.get('icons/default-avatar.png')) default_user_icon = IconDescriptor(Resources.get('icons/default-avatar.png'))
chat_template = open(Resources.get('chat/template.html')).read()
def __init__(self, session, parent=None): def __init__(self, session, parent=None):
super(ChatWidget, self).__init__(parent) super(ChatWidget, self).__init__(parent)
with Resources.directory: with Resources.directory:
self.setupUi(self) self.setupUi(self)
self.session = session blink_settings = BlinkSettings()
self.style = ChatMessageStyle('Stockholm') self.style = ChatMessageStyle(blink_settings.chat_window.style)
self.style_variant = self.style.default_variant self.style_variant = blink_settings.chat_window.style_variant or self.style.default_variant
#self.style_variant = 'Blue - Red' self.user_icons_css_class = 'show-icons' if blink_settings.chat_window.show_user_icons else 'hide-icons'
#self.style = ChatMessageStyle('Smooth Operator') self.chat_view.setChatFont(blink_settings.chat_window.font or self.style.font_family, blink_settings.chat_window.font_size or self.style.font_size)
#self.style_variant = self.style.default_variant self.chat_view.setHtml(self.chat_template.format(base_url=FileURL(self.style.path)+'/', style_url=self.style_variant+'.style'))
#self.style_variant = 'Classic'
#self.style_variant = 'Time-Icon'
chat_template = open(Resources.get('chat/template.html')).read()
self.chat_view.setChatFont(self.style.font_family, self.style.font_size)
self.chat_view.setHtml(chat_template.format(base_url=FileURL(self.style.path)+'/', style_url=self.style_variant+'.style'))
self.chat_element = self.chat_view.page().mainFrame().findFirstElement('#chat') self.chat_element = self.chat_view.page().mainFrame().findFirstElement('#chat')
self.composing_timer = QTimer() self.composing_timer = QTimer()
self.last_message = None self.last_message = None
self.session = session
# connect to signals # connect to signals
self.chat_input.textChanged.connect(self._SH_ChatInputTextChanged) self.chat_input.textChanged.connect(self._SH_ChatInputTextChanged)
self.chat_input.textEntered.connect(self._SH_ChatInputTextEntered) self.chat_input.textEntered.connect(self._SH_ChatInputTextEntered)
...@@ -505,10 +503,10 @@ class ChatWidget(base_class, ui_class): ...@@ -505,10 +503,10 @@ class ChatWidget(base_class, ui_class):
insertion_point = self.chat_element.findFirst('#insert') insertion_point = self.chat_element.findFirst('#insert')
if message.is_related_to(self.last_message): if message.is_related_to(self.last_message):
message.consecutive = True message.consecutive = True
insertion_point.replace(message.to_html(self.style, user_icons='show-icons')) insertion_point.replace(message.to_html(self.style, user_icons=self.user_icons_css_class))
else: else:
insertion_point.removeFromDocument() insertion_point.removeFromDocument()
self.chat_element.appendInside(message.to_html(self.style, user_icons='show-icons')) self.chat_element.appendInside(message.to_html(self.style, user_icons=self.user_icons_css_class))
self.last_message = message self.last_message = message
def _align_chat(self, scroll=False): def _align_chat(self, scroll=False):
...@@ -657,6 +655,10 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -657,6 +655,10 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.session_widget.installEventFilter(self) self.session_widget.installEventFilter(self)
self.state_label.installEventFilter(self) self.state_label.installEventFilter(self)
self.latency_graph.installEventFilter(self)
self.packet_loss_graph.installEventFilter(self)
self.traffic_graph.installEventFilter(self)
self.mute_button.clicked.connect(self._SH_MuteButtonClicked) self.mute_button.clicked.connect(self._SH_MuteButtonClicked)
self.hold_button.clicked.connect(self._SH_HoldButtonClicked) self.hold_button.clicked.connect(self._SH_HoldButtonClicked)
self.record_button.clicked.connect(self._SH_RecordButtonClicked) self.record_button.clicked.connect(self._SH_RecordButtonClicked)
...@@ -708,9 +710,6 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -708,9 +710,6 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.lock_grey_icon = QIcon(Resources.get('icons/lock-grey-12.svg')) self.lock_grey_icon = QIcon(Resources.get('icons/lock-grey-12.svg'))
self.lock_green_icon = QIcon(Resources.get('icons/lock-green-12.svg')) self.lock_green_icon = QIcon(Resources.get('icons/lock-green-12.svg'))
# re-apply the stylesheet for self.session_info_container_widget to account for all its subwidget role properties that were set after it
self.info_panel_container_widget.setStyleSheet(self.info_panel_container_widget.styleSheet())
# fix the SVG icons as the generated code loads them as pixmaps, losing their ability to scale -Dan # fix the SVG icons as the generated code loads them as pixmaps, losing their ability to scale -Dan
def svg_icon(filename_off, filename_on): def svg_icon(filename_off, filename_on):
icon = QIcon() icon = QIcon()
...@@ -764,18 +763,16 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -764,18 +763,16 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.traffic_graph.add_graph(self.incoming_traffic_graph) self.traffic_graph.add_graph(self.incoming_traffic_graph)
self.traffic_graph.add_graph(self.outgoing_traffic_graph) self.traffic_graph.add_graph(self.outgoing_traffic_graph)
self.info_panel_files_button.hide() self.dummy_tab = None # will be replaced by a dummy ChatWidget during SIPApplicationDidStart (creating a ChatWidget needs access to settings)
self.info_panel_participants_button.hide()
self.participants_panel_files_button.hide()
self.tab_widget.clear() # remove the tab(s) added in designer self.tab_widget.clear() # remove the tab(s) added in designer
self.tab_widget.tabBar().hide() self.tab_widget.tabBar().hide()
self.dummy_tab = ChatWidget(None, self.tab_widget)
self.dummy_tab.setDisabled(True)
self.tab_widget.addTab(self.dummy_tab, "Dummy")
self.tab_widget.setCurrentWidget(self.dummy_tab)
self.session_list.hide() self.session_list.hide()
self.info_panel_files_button.hide()
self.info_panel_participants_button.hide()
self.participants_panel_files_button.hide()
self.new_messages_button.hide() self.new_messages_button.hide()
self.hold_button.hide() self.hold_button.hide()
self.record_button.hide() self.record_button.hide()
...@@ -996,6 +993,15 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -996,6 +993,15 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
watched.event(event) watched.event(event)
self.drawSessionWidgetIndicators() self.drawSessionWidgetIndicators()
return True return True
elif watched in (self.latency_graph, self.packet_loss_graph, self.traffic_graph):
if event_type == QEvent.Wheel and event.modifiers() == Qt.ControlModifier:
settings = BlinkSettings()
if event.delta() > 0 and settings.chat_window.session_info.graph_time_scale > GraphTimeScale.min_value:
settings.chat_window.session_info.graph_time_scale -= 1
settings.save()
elif event.delta() < 0 and settings.chat_window.session_info.graph_time_scale < GraphTimeScale.max_value:
settings.chat_window.session_info.graph_time_scale += 1
settings.save()
return False return False
def drawSessionWidgetIndicators(self): def drawSessionWidgetIndicators(self):
...@@ -1065,9 +1071,33 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -1065,9 +1071,33 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
handler = getattr(self, '_NH_%s' % notification.name, Null) handler = getattr(self, '_NH_%s' % notification.name, Null)
handler(notification) handler(notification)
def _NH_SIPApplicationDidStart(self, notification): # this should not run in the gui thread -Dan def _NH_SIPApplicationDidStart(self, notification):
notification.center.add_observer(self, name='CFGSettingsObjectDidChange') notification.center.add_observer(self, name='CFGSettingsObjectDidChange')
blink_settings = BlinkSettings()
if blink_settings.chat_window.session_info.alternate_style:
title_role = 'alt-title'
value_role = 'alt-value'
else:
title_role = 'title'
value_role = 'value'
for label in (attr for name, attr in vars(self).iteritems() if name.endswith('_title_label') and attr.property('role') is not None):
label.setProperty('role', title_role)
for label in (attr for name, attr in vars(self).iteritems() if name.endswith('_value_label') or name.endswith('_value_widget') and attr.property('role') is not None):
label.setProperty('role', value_role)
self.info_panel_container_widget.setStyleSheet(self.info_panel_container_widget.styleSheet())
self.latency_graph.horizontalPixelsPerUnit = blink_settings.chat_window.session_info.graph_time_scale
self.packet_loss_graph.horizontalPixelsPerUnit = blink_settings.chat_window.session_info.graph_time_scale
self.traffic_graph.horizontalPixelsPerUnit = blink_settings.chat_window.session_info.graph_time_scale
self.latency_graph.update()
self.packet_loss_graph.update()
self.traffic_graph.update()
self.dummy_tab = ChatWidget(None, self.tab_widget)
self.dummy_tab.setDisabled(True)
self.tab_widget.addTab(self.dummy_tab, "Dummy")
self.tab_widget.setCurrentWidget(self.dummy_tab)
def _NH_CFGSettingsObjectDidChange(self, notification): def _NH_CFGSettingsObjectDidChange(self, notification):
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
blink_settings = BlinkSettings() blink_settings = BlinkSettings()
...@@ -1077,6 +1107,27 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -1077,6 +1107,27 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
elif notification.sender is blink_settings: elif notification.sender is blink_settings:
if 'presence.icon' in notification.data.modified: if 'presence.icon' in notification.data.modified:
QWebSettings.clearMemoryCaches() QWebSettings.clearMemoryCaches()
if 'chat_window.session_info.alternate_style' in notification.data.modified:
if blink_settings.chat_window.session_info.alternate_style:
title_role = 'alt-title'
value_role = 'alt-value'
else:
title_role = 'title'
value_role = 'value'
for label in (attr for name, attr in vars(self).iteritems() if name.endswith('_title_label') and attr.property('role') is not None):
label.setProperty('role', title_role)
for label in (attr for name, attr in vars(self).iteritems() if name.endswith('_value_label') or name.endswith('_value_widget') and attr.property('role') is not None):
label.setProperty('role', value_role)
self.info_panel_container_widget.setStyleSheet(self.info_panel_container_widget.styleSheet())
if 'chat_window.session_info.bytes_per_second' in notification.data.modified:
self.traffic_graph.update()
if 'chat_window.session_info.graph_time_scale' in notification.data.modified:
self.latency_graph.horizontalPixelsPerUnit = blink_settings.chat_window.session_info.graph_time_scale
self.packet_loss_graph.horizontalPixelsPerUnit = blink_settings.chat_window.session_info.graph_time_scale
self.traffic_graph.horizontalPixelsPerUnit = blink_settings.chat_window.session_info.graph_time_scale
self.latency_graph.update()
self.packet_loss_graph.update()
self.traffic_graph.update()
def _NH_BlinkSessionNewIncoming(self, notification): def _NH_BlinkSessionNewIncoming(self, notification):
if 'chat' in notification.sender.streams.types: if 'chat' in notification.sender.streams.types:
...@@ -1263,8 +1314,11 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin): ...@@ -1263,8 +1314,11 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.packet_loss_label.setText(u'Packet Loss: %.1f%%, max=%.1f%%' % (max(self.audio_packet_loss_graph.last_value, self.video_packet_loss_graph.last_value), self.packet_loss_graph.max_value)) self.packet_loss_label.setText(u'Packet Loss: %.1f%%, max=%.1f%%' % (max(self.audio_packet_loss_graph.last_value, self.video_packet_loss_graph.last_value), self.packet_loss_graph.max_value))
def _SH_TrafficGraphUpdated(self): def _SH_TrafficGraphUpdated(self):
#incoming_traffic = TrafficNormalizer.normalize(self.incoming_traffic_graph.last_value) blink_settings = BlinkSettings()
#outgoing_traffic = TrafficNormalizer.normalize(self.outgoing_traffic_graph.last_value) if blink_settings.chat_window.session_info.bytes_per_second:
incoming_traffic = TrafficNormalizer.normalize(self.incoming_traffic_graph.last_value)
outgoing_traffic = TrafficNormalizer.normalize(self.outgoing_traffic_graph.last_value)
else:
incoming_traffic = TrafficNormalizer.normalize(self.incoming_traffic_graph.last_value*8, bits_per_second=True) incoming_traffic = TrafficNormalizer.normalize(self.incoming_traffic_graph.last_value*8, bits_per_second=True)
outgoing_traffic = TrafficNormalizer.normalize(self.outgoing_traffic_graph.last_value*8, bits_per_second=True) outgoing_traffic = TrafficNormalizer.normalize(self.outgoing_traffic_graph.last_value*8, bits_per_second=True)
self.traffic_label.setText(u"""<p>Traffic: <span style="color: #d70000;">\u2193</span> %s <span style="color: #0064d7;">\u2191</span> %s</p>""" % (incoming_traffic, outgoing_traffic)) self.traffic_label.setText(u"""<p>Traffic: <span style="color: #d70000;">\u2193</span> %s <span style="color: #0064d7;">\u2191</span> %s</p>""" % (incoming_traffic, outgoing_traffic))
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
"""Definitions for datatypes used in configuration extensions.""" """Definitions for datatypes used in configuration extensions."""
__all__ = ['ApplicationDataPath', 'DefaultPath', 'SoundFile', 'CustomSoundFile', 'HTTPURL', 'FileURL', 'AuthorizationToken', 'InvalidToken', 'IconDescriptor', 'PresenceState', 'PresenceStateList'] __all__ = ['ApplicationDataPath', 'DefaultPath', 'SoundFile', 'CustomSoundFile', 'HTTPURL', 'FileURL', 'AuthorizationToken', 'InvalidToken',
'IconDescriptor', 'PresenceState', 'PresenceStateList', 'GraphTimeScale']
import os import os
import re import re
...@@ -223,3 +224,14 @@ class PresenceStateList(List): ...@@ -223,3 +224,14 @@ class PresenceStateList(List):
type = PresenceState type = PresenceState
class GraphTimeScale(int):
min_value = 2
max_value = 4
def __new__(cls, value):
value = int(value)
if not (cls.min_value <= value <= cls.max_value):
raise ValueError("expected an integer number between %d and %d, found %d" % (cls.min_value, cls.max_value, value))
return value
...@@ -14,7 +14,7 @@ from sipsimple.configuration.datatypes import AudioCodecList, NonNegativeInteger ...@@ -14,7 +14,7 @@ from sipsimple.configuration.datatypes import AudioCodecList, NonNegativeInteger
from sipsimple.configuration.settings import AudioSettings, ChatSettings, EchoCancellerSettings, FileTransferSettings, LogsSettings, RTPSettings, TLSSettings from sipsimple.configuration.settings import AudioSettings, ChatSettings, EchoCancellerSettings, FileTransferSettings, LogsSettings, RTPSettings, TLSSettings
from blink import __version__ from blink import __version__
from blink.configuration.datatypes import ApplicationDataPath, AuthorizationToken, HTTPURL, IconDescriptor, SoundFile, PresenceState, PresenceStateList from blink.configuration.datatypes import ApplicationDataPath, AuthorizationToken, GraphTimeScale, HTTPURL, IconDescriptor, SoundFile, PresenceState, PresenceStateList
from blink.resources import Resources from blink.resources import Resources
...@@ -98,6 +98,22 @@ class SIPSimpleSettingsExtension(SettingsObjectExtension): ...@@ -98,6 +98,22 @@ class SIPSimpleSettingsExtension(SettingsObjectExtension):
user_agent = Setting(type=str, default='Blink %s (%s)' % (__version__, platform.system() if sys.platform!='darwin' else 'MacOSX Qt')) user_agent = Setting(type=str, default='Blink %s (%s)' % (__version__, platform.system() if sys.platform!='darwin' else 'MacOSX Qt'))
class SessionInfoSettings(SettingsGroup):
alternate_style = Setting(type=bool, default=False)
bytes_per_second = Setting(type=bool, default=False)
graph_time_scale = Setting(type=GraphTimeScale, default=3)
class ChatWindowSettings(SettingsGroup):
session_info = SessionInfoSettings
style = Setting(type=str, default='Stockholm')
style_variant = Setting(type=str, default=None, nillable=True)
show_user_icons = Setting(type=bool, default=True)
font = Setting(type=str, default=None, nillable=True)
font_size = Setting(type=int, default=None, nillable=True)
class BlinkPresenceSettings(SettingsGroup): class BlinkPresenceSettings(SettingsGroup):
current_state = Setting(type=PresenceState, default=PresenceState('Available')) current_state = Setting(type=PresenceState, default=PresenceState('Available'))
state_history = Setting(type=PresenceStateList, default=PresenceStateList()) state_history = Setting(type=PresenceStateList, default=PresenceStateList())
...@@ -108,5 +124,6 @@ class BlinkPresenceSettings(SettingsGroup): ...@@ -108,5 +124,6 @@ class BlinkPresenceSettings(SettingsGroup):
class BlinkSettings(SettingsObject): class BlinkSettings(SettingsObject):
__id__ = 'BlinkSettings' __id__ = 'BlinkSettings'
chat_window = ChatWindowSettings
presence = BlinkPresenceSettings presence = BlinkPresenceSettings
...@@ -8,7 +8,7 @@ import urlparse ...@@ -8,7 +8,7 @@ import urlparse
from PyQt4 import uic from PyQt4 import uic
from PyQt4.QtCore import Qt, QRegExp from PyQt4.QtCore import Qt, QRegExp
from PyQt4.QtGui import QActionGroup, QButtonGroup, QFileDialog, QListView, QListWidgetItem, QMessageBox, QRegExpValidator, QSpinBox, QStyle, QStyleOptionComboBox, QValidator from PyQt4.QtGui import QActionGroup, QButtonGroup, QFileDialog, QFont, QListView, QListWidgetItem, QMessageBox, QRegExpValidator, QSpinBox, QStyle, QStyleOptionComboBox, QValidator
from application import log from application import log
from application.notification import IObserver, NotificationCenter from application.notification import IObserver, NotificationCenter
...@@ -25,6 +25,9 @@ from sipsimple.configuration.settings import SIPSimpleSettings ...@@ -25,6 +25,9 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.threading import run_in_thread from sipsimple.threading import run_in_thread
from blink.accounts import AddAccountDialog from blink.accounts import AddAccountDialog
from blink.chatwindow import ChatMessageStyle, ChatStyleError, ChatMessage, ChatEvent, ChatSender
from blink.configuration.datatypes import FileURL
from blink.configuration.settings import BlinkSettings
from blink.resources import ApplicationData, Resources from blink.resources import ApplicationData, Resources
from blink.logging import LogManager from blink.logging import LogManager
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread
...@@ -257,8 +260,23 @@ class PreferencesWindow(base_class, ui_class): ...@@ -257,8 +260,23 @@ class PreferencesWindow(base_class, ui_class):
self.max_recording.valueChanged[int].connect(self._SH_MaxRecordingValueChanged) self.max_recording.valueChanged[int].connect(self._SH_MaxRecordingValueChanged)
# Chat and SMS # Chat and SMS
self.style_view.sizeChanged.connect(self._SH_StyleViewSizeChanged)
self.style_view.page().mainFrame().contentsSizeChanged.connect(self._SH_StyleViewFrameContentsSizeChanged)
self.style_button.activated[int].connect(self._SH_StyleButtonActivated)
self.style_variant_button.activated[str].connect(self._SH_StyleVariantButtonActivated)
self.style_show_icons_button.clicked.connect(self._SH_StyleShowIconsButtonClicked)
self.style_font_button.currentIndexChanged[str].connect(self._SH_StyleFontButtonCurrentIndexChanged)
self.style_font_size.valueChanged[int].connect(self._SH_StyleFontSizeValueChanged)
self.style_default_font_button.clicked.connect(self._SH_StyleDefaultFontButtonClicked)
self.auto_accept_chat_button.clicked.connect(self._SH_AutoAcceptChatButtonClicked) self.auto_accept_chat_button.clicked.connect(self._SH_AutoAcceptChatButtonClicked)
self.sms_replication_button.clicked.connect(self._SH_SMSReplicationButtonClicked) self.sms_replication_button.clicked.connect(self._SH_SMSReplicationButtonClicked)
self.chat_message_alert_button.clicked.connect(self._SH_ChatMessageAlertButtonClicked)
self.session_info_style_button.clicked.connect(self._SH_SessionInfoStyleButtonClicked)
self.traffic_units_button.clicked.connect(self._SH_TrafficUnitsButtonClicked)
# File transfer # File transfer
self.download_directory_editor.locationCleared.connect(self._SH_DownloadDirectoryEditorLocationCleared) self.download_directory_editor.locationCleared.connect(self._SH_DownloadDirectoryEditorLocationCleared)
...@@ -266,7 +284,6 @@ class PreferencesWindow(base_class, ui_class): ...@@ -266,7 +284,6 @@ class PreferencesWindow(base_class, ui_class):
# Alerts # Alerts
self.silence_alerts_button.clicked.connect(self._SH_SilenceAlertsButtonClicked) self.silence_alerts_button.clicked.connect(self._SH_SilenceAlertsButtonClicked)
self.message_alerts_button.clicked.connect(self._SH_MessageAlertsButtonClicked)
self.file_alerts_button.clicked.connect(self._SH_FileAlertsButtonClicked) self.file_alerts_button.clicked.connect(self._SH_FileAlertsButtonClicked)
# File logging # File logging
...@@ -305,8 +322,23 @@ class PreferencesWindow(base_class, ui_class): ...@@ -305,8 +322,23 @@ class PreferencesWindow(base_class, ui_class):
self.tail_length_slider.hide() self.tail_length_slider.hide()
self.tail_length_value_label.hide() self.tail_length_value_label.hide()
# Hide the answering machine section for now as it's not implemented -Dan # Hide the controls for the features that are not yet implemented -Dan
self.answering_machine_group_box.hide() self.answering_machine_group_box.hide()
self.sms_replication_button.hide()
self.style_view.template = open(Resources.get('chat/template.html')).read()
self.style_button.clear()
self.style_variant_button.clear()
styles_path = Resources.get('chat/styles')
for style_name in os.listdir(styles_path):
try:
style = ChatMessageStyle(style_name)
except ChatStyleError:
pass
else:
self.style_button.addItem(style_name, style)
self.section_group = QActionGroup(self) self.section_group = QActionGroup(self)
self.section_group.setExclusive(True) self.section_group.setExclusive(True)
...@@ -495,6 +527,7 @@ class PreferencesWindow(base_class, ui_class): ...@@ -495,6 +527,7 @@ class PreferencesWindow(base_class, ui_class):
def load_settings(self): def load_settings(self):
"""Load settings from configuration into the UI controls""" """Load settings from configuration into the UI controls"""
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
blink_settings = BlinkSettings()
# Audio devices # Audio devices
self.load_audio_devices() self.load_audio_devices()
...@@ -522,15 +555,44 @@ class PreferencesWindow(base_class, ui_class): ...@@ -522,15 +555,44 @@ class PreferencesWindow(base_class, ui_class):
# TODO: load unavailable message -Dan # TODO: load unavailable message -Dan
# Chat and SMS settings # Chat and SMS settings
style_index = self.style_button.findText(blink_settings.chat_window.style)
if style_index == -1:
style_index = 0
blink_settings.chat_window.style = self.style_button.itemText(style_index)
blink_settings.chat_window.style_variant = None
blink_settings.save()
style = self.style_button.itemData(style_index)
self.style_button.setCurrentIndex(style_index)
self.style_variant_button.clear()
for variant in style.variants:
self.style_variant_button.addItem(variant)
variant_index = self.style_variant_button.findText(blink_settings.chat_window.style_variant or style.default_variant)
if variant_index == -1:
variant_index = self.style_variant_button.findText(style.default_variant)
blink_settings.chat_window.style_variant = None
blink_settings.save()
self.style_variant_button.setCurrentIndex(variant_index)
self.style_show_icons_button.setChecked(blink_settings.chat_window.show_user_icons)
self.update_chat_preview()
with blocked_qt_signals(self.style_font_button):
self.style_font_button.setCurrentFont(QFont(blink_settings.chat_window.font or style.font_family))
with blocked_qt_signals(self.style_font_size):
self.style_font_size.setValue(blink_settings.chat_window.font_size or style.font_size)
self.style_default_font_button.setEnabled(blink_settings.chat_window.font is not None or blink_settings.chat_window.font_size is not None)
self.auto_accept_chat_button.setChecked(settings.chat.auto_accept) self.auto_accept_chat_button.setChecked(settings.chat.auto_accept)
self.sms_replication_button.setChecked(settings.chat.sms_replication) self.sms_replication_button.setChecked(settings.chat.sms_replication)
self.chat_message_alert_button.setChecked(settings.sounds.play_message_alerts)
self.session_info_style_button.setChecked(blink_settings.chat_window.session_info.alternate_style)
self.traffic_units_button.setChecked(blink_settings.chat_window.session_info.bytes_per_second)
# File transfer settings # File transfer settings
self.download_directory_editor.setText(settings.file_transfer.directory or u'') self.download_directory_editor.setText(settings.file_transfer.directory or u'')
# Alert settings # Alert settings
self.silence_alerts_button.setChecked(settings.audio.silent) self.silence_alerts_button.setChecked(settings.audio.silent)
self.message_alerts_button.setChecked(settings.sounds.play_message_alerts)
self.file_alerts_button.setChecked(settings.sounds.play_file_alerts) self.file_alerts_button.setChecked(settings.sounds.play_file_alerts)
# File logging settings # File logging settings
...@@ -658,6 +720,45 @@ class PreferencesWindow(base_class, ui_class): ...@@ -658,6 +720,45 @@ class PreferencesWindow(base_class, ui_class):
self.account_tls_cert_file_editor.setText(account.tls.certificate or u'') self.account_tls_cert_file_editor.setText(account.tls.certificate or u'')
self.account_tls_verify_server_button.setChecked(account.tls.verify_server) self.account_tls_verify_server_button.setChecked(account.tls.verify_server)
def update_chat_preview(self):
blink_settings = BlinkSettings()
style = self.style_button.itemData(self.style_button.currentIndex())
style_variant = self.style_variant_button.itemText(self.style_variant_button.currentIndex())
user_icons = 'show-icons' if blink_settings.chat_window.show_user_icons else 'hide-icons'
self.style_view.setChatFont(blink_settings.chat_window.font or style.font_family, blink_settings.chat_window.font_size or style.font_size)
self.style_view.setHtml(self.style_view.template.format(base_url=FileURL(style.path)+'/', style_url=style_variant+'.style'))
chat_element = self.style_view.page().mainFrame().findFirstElement('#chat')
chat_element.last_message = None
def add_message(message):
insertion_point = chat_element.findFirst('#insert')
if message.is_related_to(chat_element.last_message):
message.consecutive = True
insertion_point.replace(message.to_html(style, user_icons=user_icons))
else:
insertion_point.removeFromDocument()
chat_element.appendInside(message.to_html(style, user_icons=user_icons))
chat_element.last_message = message
ruby = ChatSender("Ruby", 'ruby@example.com', Resources.get('icons/avatar-ruby.png'))
nate = ChatSender("Nate", 'nate@example.net', Resources.get('icons/avatar-nate.png'))
messages = [ChatMessage("Andrew stepped into the room cautiously. The air was stale as if the place has not been visited in years and he had an acute feeling of being watched. "
"Was this the place he was looking for, the place holding the answers he looked for so long? He was hopeful but felt uneasy about it.", ruby, 'incoming'),
ChatMessage("Hey Ruby. Is this from the new book you're working on? Looks like it will be another interesting story to read :)", nate, 'outgoing'),
ChatMessage("Yeah. But I'm kind of lacking inspiration right now and the book needs to be finished in a month :(", ruby, 'incoming'),
ChatMessage("I think you put too much pressure on yourself. What about we get out for a bit? Watch a movie, chat about everyday events for a bit...", nate, 'outgoing'),
ChatMessage("It could help you take your mind off of things and relax. We can meet at the usual spot in an hour if you want.", nate, 'outgoing'),
ChatMessage("You may be right. Maybe that's what I need indeed. See you there.", ruby, 'incoming'),
ChatEvent("Ruby has left the conversation")]
for message in messages:
add_message(message)
del chat_element.last_message
def show(self): def show(self):
selection_model = self.account_list.selectionModel() selection_model = self.account_list.selectionModel()
if not selection_model.selectedIndexes(): if not selection_model.selectedIndexes():
...@@ -718,6 +819,21 @@ class PreferencesWindow(base_class, ui_class): ...@@ -718,6 +819,21 @@ class PreferencesWindow(base_class, ui_class):
idd_prefix = self.idd_prefix_button.currentText() idd_prefix = self.idd_prefix_button.currentText()
self.pstn_example_transformed_label.setText(u"%s%s442079460000" % ('' if prefix=='None' else prefix, idd_prefix)) self.pstn_example_transformed_label.setText(u"%s%s442079460000" % ('' if prefix=='None' else prefix, idd_prefix))
def _align_style_preview(self, scroll=False):
chat_element = self.style_view.page().mainFrame().findFirstElement('#chat')
widget_height = self.style_view.size().height()
content_height = chat_element.geometry().height()
if widget_height > content_height:
chat_element.setStyleProperty('position', 'relative')
chat_element.setStyleProperty('top', '%dpx' % (widget_height-content_height))
else:
chat_element.setStyleProperty('position', 'static')
chat_element.setStyleProperty('top', None)
frame = self.style_view.page().mainFrame()
if scroll or frame.scrollBarMaximum(Qt.Vertical) - frame.scrollBarValue(Qt.Vertical) <= widget_height*0.2:
frame = self.style_view.page().mainFrame()
frame.setScrollBarValue(Qt.Vertical, frame.scrollBarMaximum(Qt.Vertical))
# Signal handlers # Signal handlers
# #
def _SH_ToolbarActionTriggered(self, action): def _SH_ToolbarActionTriggered(self, action):
...@@ -1098,6 +1214,58 @@ class PreferencesWindow(base_class, ui_class): ...@@ -1098,6 +1214,58 @@ class PreferencesWindow(base_class, ui_class):
settings.save() settings.save()
# Chat and SMS signal handlers # Chat and SMS signal handlers
def _SH_StyleViewSizeChanged(self):
self._align_style_preview(scroll=True)
def _SH_StyleViewFrameContentsSizeChanged(self, size):
self._align_style_preview(scroll=True)
def _SH_StyleButtonActivated(self, index):
style = self.style_button.itemData(index)
settings = BlinkSettings()
if style.name != settings.chat_window.style:
self.style_variant_button.clear()
for variant in style.variants:
self.style_variant_button.addItem(variant)
self.style_variant_button.setCurrentIndex(self.style_variant_button.findText(style.default_variant))
settings.chat_window.style = style.name
settings.chat_window.style_variant = None
settings.save()
def _SH_StyleVariantButtonActivated(self, style_variant):
style = self.style_button.itemData(self.style_button.currentIndex())
settings = BlinkSettings()
current_variant = settings.chat_window.style_variant or style.default_variant
if style_variant != current_variant:
settings.chat_window.style_variant = style_variant
settings.save()
def _SH_StyleShowIconsButtonClicked(self, checked):
settings = BlinkSettings()
settings.chat_window.show_user_icons = checked
settings.save()
def _SH_StyleFontButtonCurrentIndexChanged(self, font):
settings = BlinkSettings()
settings.chat_window.font = font
settings.save()
def _SH_StyleFontSizeValueChanged(self, size):
settings = BlinkSettings()
settings.chat_window.font_size = size
settings.save()
def _SH_StyleDefaultFontButtonClicked(self, checked):
settings = BlinkSettings()
settings.chat_window.font = DefaultValue
settings.chat_window.font_size = DefaultValue
settings.save()
style = self.style_button.itemData(self.style_button.currentIndex())
with blocked_qt_signals(self.style_font_button):
self.style_font_button.setCurrentFont(QFont(style.font_family))
with blocked_qt_signals(self.style_font_size):
self.style_font_size.setValue(style.font_size)
def _SH_AutoAcceptChatButtonClicked(self, checked): def _SH_AutoAcceptChatButtonClicked(self, checked):
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
settings.chat.auto_accept = checked settings.chat.auto_accept = checked
...@@ -1108,6 +1276,21 @@ class PreferencesWindow(base_class, ui_class): ...@@ -1108,6 +1276,21 @@ class PreferencesWindow(base_class, ui_class):
settings.chat.sms_replication = checked settings.chat.sms_replication = checked
settings.save() settings.save()
def _SH_ChatMessageAlertButtonClicked(self, checked):
settings = SIPSimpleSettings()
settings.sounds.play_message_alerts = checked
settings.save()
def _SH_SessionInfoStyleButtonClicked(self, checked):
settings = BlinkSettings()
settings.chat_window.session_info.alternate_style = checked
settings.save()
def _SH_TrafficUnitsButtonClicked(self, checked):
settings = BlinkSettings()
settings.chat_window.session_info.bytes_per_second = checked
settings.save()
# File transfer signal handlers # File transfer signal handlers
def _SH_DownloadDirectoryEditorLocationCleared(self): def _SH_DownloadDirectoryEditorLocationCleared(self):
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
...@@ -1132,11 +1315,6 @@ class PreferencesWindow(base_class, ui_class): ...@@ -1132,11 +1315,6 @@ class PreferencesWindow(base_class, ui_class):
settings.audio.silent = checked settings.audio.silent = checked
settings.save() settings.save()
def _SH_MessageAlertsButtonClicked(self, checked):
settings = SIPSimpleSettings()
settings.sounds.play_message_alerts = checked
settings.save()
def _SH_FileAlertsButtonClicked(self, checked): def _SH_FileAlertsButtonClicked(self, checked):
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
settings.sounds.play_file_alerts = checked settings.sounds.play_file_alerts = checked
...@@ -1299,7 +1477,14 @@ class PreferencesWindow(base_class, ui_class): ...@@ -1299,7 +1477,14 @@ class PreferencesWindow(base_class, ui_class):
def _NH_CFGSettingsObjectDidChange(self, notification): def _NH_CFGSettingsObjectDidChange(self, notification):
settings = SIPSimpleSettings() settings = SIPSimpleSettings()
if notification.sender is settings: blink_settings = BlinkSettings()
if notification.sender is blink_settings:
if {'chat_window.style', 'chat_window.style_variant', 'chat_window.show_user_icons'}.intersection(notification.data.modified):
self.update_chat_preview()
if {'chat_window.font', 'chat_window.font_size'}.intersection(notification.data.modified):
self.update_chat_preview()
self.style_default_font_button.setEnabled(blink_settings.chat_window.font is not None or blink_settings.chat_window.font_size is not None)
elif notification.sender is settings:
if 'audio.silent' in notification.data.modified: if 'audio.silent' in notification.data.modified:
self.silence_alerts_button.setChecked(settings.audio.silent) self.silence_alerts_button.setChecked(settings.audio.silent)
if 'audio.alert_device' in notification.data.modified: if 'audio.alert_device' in notification.data.modified:
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
<item row="0" column="0"> <item row="0" column="0">
<widget class="QStackedWidget" name="pages"> <widget class="QStackedWidget" name="pages">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="accounts_page"> <widget class="QWidget" name="accounts_page">
<layout class="QGridLayout" name="accounts_layout"> <layout class="QGridLayout" name="accounts_layout">
...@@ -1908,16 +1908,152 @@ ...@@ -1908,16 +1908,152 @@
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="chat_page"> <widget class="QWidget" name="chat_page">
<layout class="QVBoxLayout" name="chat_layout"> <layout class="QGridLayout" name="chat_layout" rowstretch="1,0" rowminimumheight="0,150">
<property name="margin"> <property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="messages_group_box">
<property name="title">
<string>Messages</string>
</property>
<layout class="QGridLayout" name="messages_layout">
<item row="1" column="1">
<widget class="QComboBox" name="style_button">
<item> <item>
<widget class="QGroupBox" name="chat_group_box"> <property name="text">
<string>Smooth Operator</string>
</property>
</item>
<item>
<property name="text">
<string>Stockholm</string>
</property>
</item>
</widget>
</item>
<item row="1" column="3">
<spacer name="messages_spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="style_variant_button">
<property name="maxVisibleItems">
<number>20</number>
</property>
<item>
<property name="text">
<string>Blue - Grey</string>
</property>
</item>
</widget>
</item>
<item row="1" column="6">
<widget class="QSpinBox" name="style_font_size">
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
</property>
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>9</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="value">
<number>11</number>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QFontComboBox" name="style_font_button"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="style_label">
<property name="text">
<string>Style:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="8">
<widget class="ChatWebView" name="style_view">
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
<item row="1" column="7">
<widget class="QPushButton" name="style_default_font_button">
<property name="text">
<string>Default</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="style_font_label">
<property name="text">
<string>Font:</string>
</property>
</widget>
</item>
<item row="2" column="3" colspan="5">
<widget class="QLabel" name="style_note_label">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Style changes take effect for new sessions</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="style_show_icons_button">
<property name="text">
<string>Show user icons</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="style_variant_label">
<property name="text">
<string>Variant:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="chat_sessions_group_box">
<property name="title"> <property name="title">
<string>Chat and SMS Settings</string> <string>Chat Sessions</string>
</property> </property>
<layout class="QVBoxLayout" name="chat_group_box_layout"> <layout class="QVBoxLayout" name="chat_sessions_group_box_layout">
<item> <item>
<widget class="QCheckBox" name="auto_accept_chat_button"> <widget class="QCheckBox" name="auto_accept_chat_button">
<property name="text"> <property name="text">
...@@ -1933,14 +2069,57 @@ ...@@ -1933,14 +2069,57 @@
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="chat_spacer"> <widget class="QCheckBox" name="chat_message_alert_button">
<property name="text">
<string>Play a sound when a chat message is received</string>
</property>
</widget>
</item>
<item>
<spacer name="chat_sessions_spacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>40</height> <height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="chat_window_group_box">
<property name="title">
<string>Chat Window</string>
</property>
<layout class="QVBoxLayout" name="chat_window_group_box_layout">
<item>
<widget class="QCheckBox" name="session_info_style_button">
<property name="text">
<string>Use alternate style for the session information panel</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="traffic_units_button">
<property name="text">
<string>Show traffic in bytes per second</string>
</property>
</widget>
</item>
<item>
<spacer name="chat_window_spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size> </size>
</property> </property>
</spacer> </spacer>
...@@ -2017,28 +2196,7 @@ ...@@ -2017,28 +2196,7 @@
<string>Alert Settings</string> <string>Alert Settings</string>
</property> </property>
<layout class="QGridLayout" name="alerts_group_box_layout"> <layout class="QGridLayout" name="alerts_group_box_layout">
<item row="0" column="0">
<widget class="QCheckBox" name="silence_alerts_button">
<property name="text">
<string>Silence all alerts (incoming ringtones, text message notifications and file transfer notifications)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="message_alerts_button">
<property name="text">
<string>Play a sound when a chat or SMS message is sent or received</string>
</property>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="file_alerts_button">
<property name="text">
<string>Play a sound when a file transfer finishes</string>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="alerts_spacer"> <spacer name="alerts_spacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
...@@ -2051,6 +2209,20 @@ ...@@ -2051,6 +2209,20 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="1" column="0">
<widget class="QCheckBox" name="file_alerts_button">
<property name="text">
<string>Play a sound when a file transfer finishes</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="silence_alerts_button">
<property name="text">
<string>Silence all alerts (incoming ringtones, text message notifications and file transfer notifications)</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
...@@ -2740,6 +2912,16 @@ ...@@ -2740,6 +2912,16 @@
</action> </action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>QWebView</class>
<extends>QWidget</extends>
<header>QtWebKit/QWebView</header>
</customwidget>
<customwidget>
<class>ChatWebView</class>
<extends>QWebView</extends>
<header>blink.chatwindow</header>
</customwidget>
<customwidget> <customwidget>
<class>LineEdit</class> <class>LineEdit</class>
<extends>QLineEdit</extends> <extends>QLineEdit</extends>
...@@ -2770,12 +2952,12 @@ ...@@ -2770,12 +2952,12 @@
<slot>setNum(int)</slot> <slot>setNum(int)</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>228</x> <x>147</x>
<y>229</y> <y>245</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>364</x> <x>382</x>
<y>238</y> <y>245</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
......
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