Commit e6145b72 authored by Dan Pascu's avatar Dan Pascu

Modernized code and improved PEP-8 compliance

parent 8c6e300f
#
# Boring file regular expresions
# Boring file regular expressions
#
~$
......
__all__ = ['Blink']
__version__ = '1.4.2'
__date__ = 'December 4th 2015'
import os
import sys
import sip
......@@ -42,6 +38,7 @@ try:
from blink import branding
except ImportError:
branding = Null
from blink.chatwindow import ChatWindow
from blink.configuration.account import AccountExtension, BonjourAccountExtension
from blink.configuration.addressbook import ContactExtension, GroupExtension
......@@ -56,6 +53,9 @@ from blink.update import UpdateManager
from blink.util import QSingleton, run_in_gui_thread
__all__ = ['Blink']
if hasattr(sys, 'frozen'):
output = sys.stdout
makedirs(ApplicationData.get('logs'))
......@@ -293,7 +293,7 @@ class Blink(QApplication):
self.main_window.show()
settings = SIPSimpleSettings()
accounts = AccountManager().get_accounts()
if not accounts or (self.first_run and accounts==[BonjourAccount()]):
if not accounts or (self.first_run and accounts == [BonjourAccount()]):
self.main_window.preferences_window.show_create_account_dialog()
if settings.google_contacts.authorization_token is InvalidToken:
self.main_window.google_contacts_dialog.open_for_incorrect_password()
......
__all__ = ['AboutPanel']
from PyQt4 import uic
from blink import __date__, __version__
......@@ -8,6 +6,9 @@ from blink.resources import Resources
from blink.util import QSingleton
__all__ = ['AboutPanel']
credits_text = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
......@@ -32,6 +33,7 @@ credits_text = """
ui_class, base_class = uic.loadUiType(Resources.get('about_panel.ui'))
class AboutPanel(base_class, ui_class):
__metaclass__ = QSingleton
......@@ -43,16 +45,16 @@ class AboutPanel(base_class, ui_class):
self.version.setText(u'Version %s\n%s' % (__version__, __date__))
credits_width = self.credits_text.fontMetrics().width("NLNET Foundation" + "http://sipsimpleclient.org") + 40
credits_width = self.credits_text.fontMetrics().width("NLnet Foundation" + "http://sipsimpleclient.org") + 40
self.credits_text.setFixedWidth(credits_width)
self.credits_text.document().documentLayout().documentSizeChanged.connect(self._credits_size_changed)
self.credits_text.setHtml(credits_text)
def _credits_size_changed(self, size):
self.credits_text.document().documentLayout().documentSizeChanged.disconnect(self._credits_size_changed)
self.setFixedSize(self.minimumSize().width(), self.minimumSize().width()*1.40) # set a fixed aspect ratio
row_height = self.credits_text.fontMetrics().height() + 2 # +2 for cellspacing
max_credits_height = 8*row_height + 2 + 14 # allow for maximum 8 rows; +2 for cellspacing and +14 for top/bottom margins
self.setFixedSize(self.minimumSize().width(), self.minimumSize().width()*1.40) # set a fixed aspect ratio
row_height = self.credits_text.fontMetrics().height() + 2 # +2 for cellspacing
max_credits_height = 8*row_height + 2 + 14 # allow for maximum 8 rows; +2 for cellspacing and +14 for top/bottom margins
if self.credits_text.height() > max_credits_height:
self.setFixedHeight(self.height() - (self.credits_text.height() - max_credits_height))
......
__all__ = ['AccountModel', 'ActiveAccountModel', 'AccountSelector', 'AddAccountDialog', 'ServerToolsAccountModel', 'ServerToolsWindow']
import os
import re
import sys
......@@ -10,9 +8,9 @@ from collections import defaultdict
from PyQt4 import uic
from PyQt4.QtCore import Qt, QAbstractListModel, QModelIndex, QUrl
from PyQt4.QtGui import QAction, QButtonGroup, QComboBox, QIcon, QMenu, QMovie, QSortFilterProxyModel
from PyQt4.QtGui import QAction, QButtonGroup, QComboBox, QIcon, QMenu, QMovie, QSortFilterProxyModel
from PyQt4.QtNetwork import QNetworkAccessManager
from PyQt4.QtWebKit import QWebView
from PyQt4.QtWebKit import QWebView
import cjson
from application.notification import IObserver, NotificationCenter
......@@ -31,17 +29,23 @@ from blink.widgets.labels import Status
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread
__all__ = ['AccountModel', 'ActiveAccountModel', 'AccountSelector', 'AddAccountDialog', 'ServerToolsAccountModel', 'ServerToolsWindow']
class IconDescriptor(object):
def __init__(self, filename):
self.filename = filename
self.icon = None
def __get__(self, obj, objtype):
def __get__(self, instance, owner):
if self.icon is None:
self.icon = QIcon(self.filename)
self.icon.filename = self.filename
return self.icon
def __set__(self, obj, value):
raise AttributeError("attribute cannot be set")
def __delete__(self, obj):
raise AttributeError("attribute cannot be deleted")
......@@ -222,6 +226,7 @@ class AccountSelector(QComboBox):
ui_class, base_class = uic.loadUiType(Resources.get('add_account.ui'))
class AddAccountDialog(base_class, ui_class):
__metaclass__ = QSingleton
......@@ -241,9 +246,9 @@ class AddAccountDialog(base_class, ui_class):
font.setFamily("Sans Serif")
self.title_label.setFont(font)
font_metrics = self.create_status_label.fontMetrics()
self.create_status_label.setMinimumHeight(font_metrics.height() + 2*(font_metrics.height() + font_metrics.leading())) # reserve space for 3 lines
self.create_status_label.setMinimumHeight(font_metrics.height() + 2*(font_metrics.height() + font_metrics.leading())) # reserve space for 3 lines
font_metrics = self.email_note_label.fontMetrics()
self.email_note_label.setMinimumWidth(font_metrics.width(u'The E-mail address is used when sending voicemail')) # hack to make text justification look nice everywhere
self.email_note_label.setMinimumWidth(font_metrics.width(u'The E-mail address is used when sending voicemail')) # hack to make text justification look nice everywhere
self.add_account_button.setChecked(True)
self.panel_view.setCurrentWidget(self.add_account_panel)
self.new_password_editor.textChanged.connect(self._SH_PasswordTextChanged)
......@@ -259,7 +264,7 @@ class AddAccountDialog(base_class, ui_class):
self.email_address_editor.statusChanged.connect(self._SH_ValidityStatusChanged)
self.display_name_editor.regexp = re.compile('^.*$')
self.name_editor.regexp = re.compile('^.+$')
self.username_editor.regexp = re.compile('^\w(?<=[^0_])[\w.-]{4,31}(?<=[^_.-])$', re.IGNORECASE) # in order to enable unicode characters add re.UNICODE to flags
self.username_editor.regexp = re.compile('^\w(?<=[^0_])[\w.-]{4,31}(?<=[^_.-])$', re.IGNORECASE) # in order to enable unicode characters add re.UNICODE to flags
self.sip_address_editor.regexp = re.compile('^[^@\s]+@[^@\s]+$')
self.password_editor.regexp = re.compile('^.*$')
self.new_password_editor.regexp = re.compile('^.{8,}$')
......@@ -587,6 +592,7 @@ class ServerToolsWebView(QWebView):
ui_class, base_class = uic.loadUiType(Resources.get('server_tools.ui'))
class ServerToolsWindow(base_class, ui_class):
__metaclass__ = QSingleton
......@@ -599,7 +605,7 @@ class ServerToolsWindow(base_class, ui_class):
self.spinner_label.hide()
self.progress_bar.hide()
while self.tab_widget.count():
self.tab_widget.removeTab(0) # remove the tab(s) added in designer
self.tab_widget.removeTab(0) # remove the tab(s) added in designer
self.tab_widget.tabBar().hide()
self.account_button.setMenu(QMenu(self.account_button))
self.setWindowTitle('Blink Server Tools')
......@@ -629,7 +635,7 @@ class ServerToolsWindow(base_class, ui_class):
self.spinner_label.show()
self.spinner_movie.start()
self.progress_bar.setValue(0)
#self.progress_bar.show()
# self.progress_bar.show()
def _SH_WebViewLoadFinished(self, load_ok):
self.spinner_movie.stop()
......
......@@ -112,7 +112,7 @@ class Link(object):
class OrderedSet(MutableSet):
def __init__(self, iterable=None):
self.__hardroot = Link() # sentinel node for doubly linked list
self.__hardroot = Link() # sentinel node for doubly linked list
self.__root = root = proxy(self.__hardroot)
root.prev = root.next = root
self.__map = {}
......@@ -171,10 +171,10 @@ class ChatContentBooleanOption(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, objtype):
if obj is None:
def __get__(self, instance, owner):
if instance is None:
return self
return self.name in obj.__cssclasses__
return self.name in instance.__cssclasses__
def __set__(self, obj, value):
if value:
......@@ -188,6 +188,7 @@ class ChatContentBooleanOption(object):
class AnyValue: __metaclass__ = MarkerType
class ChatContentStringAttribute(object):
"""A string attribute that is also added as a css class"""
......@@ -195,11 +196,11 @@ class ChatContentStringAttribute(object):
self.name = name
self.allowed_values = allowed_values
def __get__(self, obj, objtype):
if obj is None:
def __get__(self, instance, owner):
if instance is None:
return self
try:
return obj.__dict__[self.name]
return instance.__dict__[self.name]
except KeyError:
raise AttributeError("'{}' attribute is not set".format(self.name))
......@@ -221,12 +222,12 @@ class ChatContent(object):
__cssclasses__ = ()
continuation_interval = timedelta(0, 5*60) # 5 minutes
continuation_interval = timedelta(0, 5*60) # 5 minutes
history = ChatContentBooleanOption('history')
focus = ChatContentBooleanOption('focus')
consecutive = ChatContentBooleanOption('consecutive')
mention = ChatContentBooleanOption('mention') # keep it here? or keep it at all? -Dan
mention = ChatContentBooleanOption('mention') # keep it here? or keep it at all? -Dan
def __init__(self, message, history=False, focus=False):
self.__cssclasses__ = OrderedSet(self.__class__.__cssclasses__)
......@@ -338,10 +339,10 @@ class ChatWebPage(QWebPage):
super(ChatWebPage, self).__init__(parent)
self.setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
self.linkClicked.connect(self._SH_LinkClicked)
#self.downloadRequested.connect(self._SH_DownloadRequested)
#self.setForwardUnsupportedContent(True)
#self.unsupportedContent.connect(self._SH_UnsupportedContent)
#allowed_actions = {QWebPage.InspectElement, QWebPage.CopyLinkToClipboard, QWebPage.CopyImageToClipboard, QWebPage.CopyImageUrlToClipboard}
# self.downloadRequested.connect(self._SH_DownloadRequested)
# self.setForwardUnsupportedContent(True)
# self.unsupportedContent.connect(self._SH_UnsupportedContent)
# allowed_actions = {QWebPage.InspectElement, QWebPage.CopyLinkToClipboard, QWebPage.CopyImageToClipboard, QWebPage.CopyImageUrlToClipboard}
disable_actions = {QWebPage.OpenLink, QWebPage.OpenLinkInNewWindow, QWebPage.DownloadLinkToDisk, QWebPage.OpenImageInNewWindow, QWebPage.DownloadImageToDisk,
QWebPage.Back, QWebPage.Forward, QWebPage.Stop, QWebPage.Reload}
for action in (self.action(action) for action in disable_actions):
......@@ -360,10 +361,10 @@ class ChatWebPage(QWebPage):
def _SH_LinkClicked(self, url):
QDesktopServices.openUrl(url)
#def _SH_DownloadRequested(self, request):
# def _SH_DownloadRequested(self, request):
# print "-- download requested", request.url().toString()
#def _SH_UnsupportedContent(self, reply):
# def _SH_UnsupportedContent(self, reply):
# print "-- unsupported", reply.url().toString()
......@@ -377,7 +378,7 @@ class ChatWebView(QWebView):
self.setPalette(palette)
self.setPage(ChatWebPage(self))
self.setAttribute(Qt.WA_OpaquePaintEvent, False)
self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # temporary for debugging -Dan
self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # temporary for debugging -Dan
def contextMenuEvent(self, event):
menu = self.page().createStandardContextMenu()
......@@ -558,7 +559,7 @@ class IconDescriptor(object):
self.filename = filename
self.icon = None
def __get__(self, obj, objtype):
def __get__(self, instance, owner):
if self.icon is None:
self.icon = QIcon(self.filename)
self.icon.filename = self.filename
......@@ -701,10 +702,10 @@ class ChatWidget(base_class, ui_class):
self.session.chat_stream.send_message(content, content_type, recipients, courtesy_recipients, subject, timestamp, required, additional_headers)
def _align_chat(self, scroll=False):
#frame_height = self.chat_view.page().mainFrame().contentsSize().height()
# frame_height = self.chat_view.page().mainFrame().contentsSize().height()
widget_height = self.chat_view.size().height()
content_height = self.chat_element.geometry().height()
#print widget_height, frame_height, content_height
# print widget_height, frame_height, content_height
if widget_height > content_height:
self.chat_element.setStyleProperty('position', 'relative')
self.chat_element.setStyleProperty('top', '%dpx' % (widget_height-content_height))
......@@ -713,10 +714,10 @@ class ChatWidget(base_class, ui_class):
self.chat_element.setStyleProperty('top', None)
frame = self.chat_view.page().mainFrame()
if scroll or frame.scrollBarMaximum(Qt.Vertical) - frame.scrollBarValue(Qt.Vertical) <= widget_height*0.2:
#print "scroll requested or scrollbar is closer than %dpx to the bottom" % (widget_height*0.2)
#self._print_scrollbar_position()
# print "scroll requested or scrollbar is closer than %dpx to the bottom" % (widget_height*0.2)
# self._print_scrollbar_position()
self._scroll_to_bottom()
#self._print_scrollbar_position()
# self._print_scrollbar_position()
def _scroll_to_bottom(self):
frame = self.chat_view.page().mainFrame()
......@@ -773,7 +774,7 @@ class ChatWidget(base_class, ui_class):
try:
self.send_message(image.thumbnail.data, content_type=image.thumbnail.type)
except Exception, e:
self.add_message(ChatStatus("Error sending image '%s': %s" % (os.path.basename(image.filename), e))) # decide what type to use here. -Dan
self.add_message(ChatStatus("Error sending image '%s': %s" % (os.path.basename(image.filename), e))) # decide what type to use here. -Dan
else:
content = u'''<a href="{}"><img src="data:{};base64,{}" class="scaled-to-fit" /></a>'''.format(image.fileurl, image.thumbnail.type, image.thumbnail.data.encode('base64').rstrip())
sender = ChatSender(blink_session.account.display_name, blink_session.account.id, self.user_icon.filename)
......@@ -788,7 +789,7 @@ class ChatWidget(base_class, ui_class):
try:
self.send_message(match.group('data').decode('base64'), content_type=match.group('type'))
except Exception, e:
self.add_message(ChatStatus('Error sending image: %s' % e)) # decide what type to use here. -Dan
self.add_message(ChatStatus('Error sending image: %s' % e)) # decide what type to use here. -Dan
else:
account = self.session.blink_session.account
content = u'''<img src="{}" class="scaled-to-fit" />'''.format(text)
......@@ -801,11 +802,11 @@ class ChatWidget(base_class, ui_class):
self.chat_input.setHtml(user_text)
def _SH_ChatViewSizeChanged(self):
#print "chat view size changed"
# print "chat view size changed"
self._align_chat(scroll=True)
def _SH_ChatViewFrameContentsSizeChanged(self, size):
#print "frame contents size changed to %r (current=%r)" % (size, self.chat_view.page().mainFrame().contentsSize())
# print "frame contents size changed to %r (current=%r)" % (size, self.chat_view.page().mainFrame().contentsSize())
self._align_chat(scroll=True)
def _SH_ChatInputTextChanged(self):
......@@ -832,7 +833,7 @@ class ChatWidget(base_class, ui_class):
try:
self.send_message(text, content_type='text/html')
except Exception, e:
self.add_message(ChatStatus('Error sending message: %s' % e)) # decide what type to use here. -Dan
self.add_message(ChatStatus('Error sending message: %s' % e)) # decide what type to use here. -Dan
else:
account = self.session.blink_session.account
content = HtmlProcessor.autolink(text)
......@@ -882,6 +883,7 @@ class VideoToolButton(QToolButton):
ui_class, base_class = uic.loadUiType(Resources.get('video_widget.ui'))
class VideoWidget(VideoSurface, ui_class):
implements(IObserver)
......@@ -922,7 +924,7 @@ class VideoWidget(VideoSurface, ui_class):
self.no_flicker_widget = QLabel()
self.no_flicker_widget.setWindowFlags(Qt.FramelessWindowHint)
#self.no_flicker_widget.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
# self.no_flicker_widget.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.camera_preview = VideoSurface(self, framerate=10)
self.camera_preview.interactive = True
......@@ -935,7 +937,7 @@ class VideoWidget(VideoSurface, ui_class):
self.detach_animation = QPropertyAnimation(self, 'geometry')
self.detach_animation.setDuration(200)
#self.detach_animation.setEasingCurve(QEasingCurve.Linear)
# self.detach_animation.setEasingCurve(QEasingCurve.Linear)
self.preview_animation = QPropertyAnimation(self.camera_preview, 'geometry')
self.preview_animation.setDuration(500)
......@@ -1031,7 +1033,7 @@ class VideoWidget(VideoSurface, ui_class):
super(VideoWidget, self).mousePressEvent(event)
if self._interaction.active:
for button in self.active_tool_buttons:
button.show() # show or hide the toolbuttons while we move/resize? -Dan
button.show() # show or hide the tool buttons while we move/resize? -Dan
self.idle_timer.stop()
def mouseReleaseEvent(self, event):
......@@ -1251,7 +1253,7 @@ class VideoWidget(VideoSurface, ui_class):
self.preview_animation.start()
def _NH_VideoDeviceDidChangeCamera(self, notification):
#self.camera_preview.producer = SIPApplication.video_device.producer
# self.camera_preview.producer = SIPApplication.video_device.producer
self.camera_preview.producer = notification.data.new_camera
def _NH_CFGSettingsObjectDidChange(self, notification):
......@@ -1275,19 +1277,19 @@ class VideoWidget(VideoSurface, ui_class):
geometry = self.rect().translated(self.mapToGlobal(QPoint(0, 0)))
self.setParent(None)
self.setGeometry(geometry)
self.show() # without this, showFullScreen below doesn't work properly
self.show() # without this, showFullScreen below doesn't work properly
self.detach_button.active = False
self.mute_button.active = True
self.hold_button.active = True
self.close_button.active = True
self.showFullScreen()
self.fullscreen_button.hide() # it seems the leave event after the button is pressed doesn't register and starting the idle timer here doesn't work well either -Dan
self.fullscreen_button.hide() # it seems the leave event after the button is pressed doesn't register and starting the idle timer here doesn't work well either -Dan
self.fullscreen_button.show()
else:
if not self.detach_button.isChecked():
self.setGeometry(self.geometryHint(self.parent_widget)) # force a geometry change before reparenting, else we will get a change from (-1, -1) to the parent geometry hint
self.setParent(self.parent_widget) # this is probably because since it unmaps when it's reparented, the geometry change won't appear from fullscreen
self.setGeometry(self.geometryHint()) # to the new size, since we changed the geometry after returning from fullscreen, while invisible
self.setGeometry(self.geometryHint(self.parent_widget)) # force a geometry change before re-parenting, else we will get a change from (-1, -1) to the parent geometry hint
self.setParent(self.parent_widget) # this is probably because since it unmaps when it's re-parented, the geometry change won't appear from fullscreen
self.setGeometry(self.geometryHint()) # to the new size, since we changed the geometry after returning from fullscreen, while invisible
self.mute_button.active = False
self.hold_button.active = False
self.close_button.active = False
......@@ -1335,7 +1337,7 @@ class VideoWidget(VideoSurface, ui_class):
self.detach_animation.setDirection(QPropertyAnimation.Backward)
self.detach_animation.setEasingCurve(QEasingCurve.InQuad)
self.detach_animation.setStartValue(final_geometry) # start and end are reversed because we go backwards
self.detach_animation.setStartValue(final_geometry) # start and end are reversed because we go backwards
self.detach_animation.setEndValue(start_geometry)
self.detach_animation.start()
self.fullscreen_button.setChecked(False)
......@@ -1378,20 +1380,20 @@ class VideoWidget(VideoSurface, ui_class):
self.no_flicker_widget.setGeometry(self.geometry())
self.no_flicker_widget.show()
self.no_flicker_widget.raise_()
#self.no_flicker_widget.repaint()
#self.repaint()
# self.no_flicker_widget.repaint()
# self.repaint()
self.setParent(self.parent_widget)
self.setGeometry(self.geometryHint())
self.show() # solve the flicker -Dan
#self.repaint()
#self.no_flicker_widget.lower()
# self.repaint()
# self.no_flicker_widget.lower()
self.no_flicker_widget.hide()
#self.window().show()
# self.window().show()
self.mute_button.active = False
self.hold_button.active = False
self.close_button.active = False
else:
self.detach_button.hide() # it seems the leave event after the button is pressed doesn't register and starting the idle timer here doesn't work well either -Dan
self.detach_button.hide() # it seems the leave event after the button is pressed doesn't register and starting the idle timer here doesn't work well either -Dan
self.detach_button.show()
self.mute_button.active = True
self.hold_button.active = True
......@@ -1431,6 +1433,7 @@ class NoSessionsLabel(QLabel):
ui_class, base_class = uic.loadUiType(Resources.get('chat_window.ui'))
class ChatWindow(base_class, ui_class, ColorHelperMixin):
implements(IObserver)
......@@ -1504,7 +1507,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
notification_center.add_observer(self, name='MediaStreamDidEnd')
notification_center.add_observer(self, name='MediaStreamWillEnd')
#self.splitter.splitterMoved.connect(self._SH_SplitterMoved) # check this and decide on what size to have in the window (see Notes) -Dan
# self.splitter.splitterMoved.connect(self._SH_SplitterMoved) # check this and decide on what size to have in the window (see Notes) -Dan
def _SH_SplitterMoved(self, pos, index):
print "-- splitter:", pos, index, self.splitter.sizes()
......@@ -1580,9 +1583,9 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.control_button.actions.end_screen_sharing = QAction("End screen sharing", self, triggered=self._AH_EndScreenSharing)
self.control_button.actions.main_window = QAction("Main Window", self, triggered=self._AH_MainWindow, shortcut='Ctrl+B', shortcutContext=Qt.ApplicationShortcut)
self.addAction(self.control_button.actions.main_window) # make this active even when it's not in the contol_button's menu
self.addAction(self.control_button.actions.main_window) # make this active even when it's not in the control_button's menu
self.slide_direction = self.session_details.RightToLeft # decide if we slide from one direction only -Dan
self.slide_direction = self.session_details.RightToLeft # decide if we slide from one direction only -Dan
self.slide_direction = self.session_details.Automatic
self.session_details.animationDuration = 300
self.session_details.animationEasingCurve = QEasingCurve.OutCirc
......@@ -1604,8 +1607,8 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.traffic_graph.add_graph(self.incoming_traffic_graph)
self.traffic_graph.add_graph(self.outgoing_traffic_graph)
self.dummy_tab = None # will be replaced by a dummy ChatWidget during SIPApplicationDidStart (creating a ChatWidget needs access to settings)
self.tab_widget.clear() # remove the tab(s) added in designer
self.dummy_tab = None # will be replaced by a dummy ChatWidget during SIPApplicationDidStart (creating a ChatWidget needs access to settings)
self.tab_widget.clear() # remove the tab(s) added in designer
self.tab_widget.tabBar().hide()
self.session_list.hide()
......@@ -1652,7 +1655,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
if new_session is not None:
notification_center.add_observer(self, sender=new_session)
notification_center.add_observer(self, sender=new_session.blink_session)
self._update_widgets_for_session() # clean this up -Dan (too many functions called in 3 different places: on selection changed, here and on notifications handlers)
self._update_widgets_for_session() # clean this up -Dan (too many functions called in 3 different places: on selection changed, here and on notifications handlers)
self._update_control_menu()
self._update_panel_buttons()
self._update_session_info_panel(elements={'session', 'media', 'statistics', 'status'}, update_visibility=True)
......@@ -1689,7 +1692,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
menu.hide()
blink_session = self.selected_session.blink_session
state = blink_session.state
if state=='connecting/*' and blink_session.direction=='outgoing' or state=='connected/sent_proposal':
if state=='connecting/*' and blink_session.direction == 'outgoing' or state == 'connected/sent_proposal':
self.control_button.setMenu(None)
self.control_button.setIcon(self.cancel_icon)
elif state == 'connected/received_proposal':
......@@ -1894,7 +1897,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.screen_encryption_label.setToolTip(u'Media is encrypted using TLS')
self.screen_connection_label.setVisible(screen_info.remote_address is not None)
self.screen_encryption_label.setVisible(screen_info.remote_address is not None and screen_info.transport=='tls')
self.screen_encryption_label.setVisible(screen_info.remote_address is not None and screen_info.transport == 'tls')
if 'statistics' in elements:
self.duration_value_label.value = session_info.duration
......@@ -1955,7 +1958,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self._EH_CloseSession()
else:
self._EH_ShowSessions()
elif event_type == QEvent.Paint: # and self.session_widget.hovered:
elif event_type == QEvent.Paint: # and self.session_widget.hovered:
watched.event(event)
self.drawSessionWidgetIndicators()
return True
......@@ -2140,7 +2143,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
position = model.sessions.index(notification.sender.items.chat)
selection_model = self.session_list.selectionModel()
selection_model.select(model.index(position), selection_model.ClearAndSelect)
self.session_list.scrollTo(model.index(position), QListView.EnsureVisible) # or PositionAtCenter
self.session_list.scrollTo(model.index(position), QListView.EnsureVisible) # or PositionAtCenter
if notification.sender.streams.types.intersection(self.__streamtypes__):
self.show()
......@@ -2149,7 +2152,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
position = model.sessions.index(notification.sender.items.chat)
selection_model = self.session_list.selectionModel()
selection_model.select(model.index(position), selection_model.ClearAndSelect)
self.session_list.scrollTo(model.index(position), QListView.EnsureVisible) # or PositionAtCenter
self.session_list.scrollTo(model.index(position), QListView.EnsureVisible) # or PositionAtCenter
if notification.sender.stream_descriptions.types.intersection(self.__streamtypes__):
self.show()
......@@ -2206,7 +2209,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
if message.content_type.startswith('image/'):
content = u'''<img src="data:{};base64,{}" class="scaled-to-fit" />'''.format(message.content_type, message.content.encode('base64').rstrip())
elif message.content_type.startswith('text/'):
content = HtmlProcessor.autolink(message.content if message.content_type=='text/html' else QTextDocument(message.content).toHtml())
content = HtmlProcessor.autolink(message.content if message.content_type == 'text/html' else QTextDocument(message.content).toHtml())
else:
return
......@@ -2221,7 +2224,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
else:
sender = ChatSender(message.sender.display_name or session.name, uri, session.icon.filename)
is_status_message = any(h.name=='Message-Type' and h.value=='status' and h.namespace=='urn:ag-projects:xml:ns:cpim' for h in message.additional_headers)
is_status_message = any(h.name == 'Message-Type' and h.value == 'status' and h.namespace == 'urn:ag-projects:xml:ns:cpim' for h in message.additional_headers)
if is_status_message:
session.chat_widget.add_message(ChatStatus(content))
else:
......@@ -2283,7 +2286,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
session = notification.sender.blink_session.items.chat
if session is None:
return
#session.chat_widget.add_message(ChatStatus('Connecting...')) # disable it until we can replace it in the DOM -Dan
# session.chat_widget.add_message(ChatStatus('Connecting...')) # disable it until we can replace it in the DOM -Dan
def _NH_MediaStreamDidNotInitialize(self, notification):
if notification.sender.type != 'chat':
......@@ -2413,12 +2416,12 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
def _SH_SessionModelSessionAboutToBeRemoved(self, session):
# choose another one to select (a chat only or ended session if available, else one with audio but keep audio on hold? or select nothing and display the dummy tab?)
#selection_model = self.session_list.selectionModel()
#selection_model.clearSelection()
# selection_model = self.session_list.selectionModel()
# selection_model.clearSelection()
pass
def _SH_SessionListSelectionChanged(self, selected, deselected):
#print "-- chat selection changed %s -> %s" % ([x.row() for x in deselected.indexes()], [x.row() for x in selected.indexes()])
# print "-- chat selection changed %s -> %s" % ([x.row() for x in deselected.indexes()], [x.row() for x in selected.indexes()])
self.selected_session = selected[0].topLeft().data(Qt.UserRole) if selected else None
if self.selected_session is not None:
self.tab_widget.setCurrentWidget(self.selected_session.chat_widget) # why do we switch the tab here, but do everything else in the selected_session property setter? -Dan
......@@ -2556,7 +2559,7 @@ del ui_class, base_class
#
class HtmlProcessor(object):
_autolink_re = [#re.compile(r"(?P<body>https?://(?:[^:@]+(?::[^@]*)?@)?(?P<host>[a-z0-9.-]+)(?::\d*)?(?:/[\w/%!$@#*&='~():;,.+-]*(?:\?[\w%!$@*&='~():;,.+-]*)?)?)", re.I|re.U),
_autolink_re = [ # re.compile(r"(?P<body>https?://(?:[^:@]+(?::[^@]*)?@)?(?P<host>[a-z0-9.-]+)(?::\d*)?(?:/[\w/%!$@#*&='~():;,.+-]*(?:\?[\w%!$@*&='~():;,.+-]*)?)?)", re.I|re.U),
re.compile(r"""
(?P<body>
https?://(?:[^:@]+(?::[^@]*)?@)?(?P<host>[a-z0-9.-]+)(?::\d*)? # scheme :// [ user [ : password ] @ ] host [ : port ]
......
......@@ -176,7 +176,7 @@ class IconDescriptor(object):
def __eq__(self, other):
if isinstance(other, IconDescriptor):
return self.url==other.url and self.etag==other.etag
return self.url == other.url and self.etag == other.etag
return NotImplemented
def __ne__(self, other):
......@@ -208,7 +208,7 @@ class PresenceState(object):
def __eq__(self, other):
if isinstance(other, PresenceState):
return self.state==other.state and self.note==other.note
return self.state == other.state and self.note == other.note
return NotImplemented
def __ne__(self, other):
......
......@@ -83,7 +83,7 @@ class SIPSimpleSettingsExtension(SettingsObjectExtension):
sounds = SoundSettings
tls = TLSSettingsExtension
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):
......
__all__ = ['Group', 'Contact', 'BonjourNeighbour', 'GoogleContact', 'ContactModel', 'ContactSearchModel', 'ContactListView', 'ContactSearchListView', 'ContactEditorDialog', 'GoogleContactsDialog', 'URIUtils']
import cPickle as pickle
import os
import re
......@@ -56,6 +54,10 @@ from blink.google.gdata.contacts.service import ContactsQuery
from blink.google.gdata.gauth import ClientLoginToken
__all__ = ['Group', 'Contact', 'BonjourNeighbour', 'GoogleContact', 'ContactModel', 'ContactSearchModel', 'ContactListView', 'ContactSearchListView',
'ContactEditorDialog', 'GoogleContactsDialog', 'URIUtils']
class VirtualGroupManager(object):
__metaclass__ = Singleton
implements(IObserver)
......@@ -170,14 +172,19 @@ class VirtualGroup(SettingsState):
class AllContactsList(object):
def __init__(self):
self.manager = addressbook.AddressbookManager()
def __iter__(self):
return iter(self.manager.get_contacts())
def __getitem__(self, id):
return self.manager.get_contact(id)
def __contains__(self, id):
return self.manager.has_contact(id)
def __len__(self):
return len(self.manager.get_contacts())
__hash__ = None
......@@ -271,26 +278,36 @@ class BonjourNeighbourURI(object):
class BonjourNeighbourURIList(object):
def __init__(self, uris):
self._uri_map = OrderedDict((uri.id, uri) for uri in uris)
def __getitem__(self, id):
return self._uri_map[id]
def __contains__(self, id):
return id in self._uri_map
def __iter__(self):
return iter(self._uri_map.values())
def __len__(self):
return len(self._uri_map)
__hash__ = None
def get(self, key, default=None):
return self._uri_map.get(key, default)
def add(self, uri):
self._uri_map[uri.id] = uri
def pop(self, id, *args):
return self._uri_map.pop(id, *args)
def remove(self, uri):
self._uri_map.pop(uri.id, None)
@property
def default(self):
return sorted(self, key=lambda item: 0 if item.uri.transport=='tls' else 1 if item.uri.transport=='tcp' else 2)[0] if self._uri_map else None
return sorted(self, key=lambda item: 0 if item.uri.transport == 'tls' else 1 if item.uri.transport == 'tcp' else 2)[0] if self._uri_map else None
class BonjourPresence(object):
......@@ -314,19 +331,27 @@ class BonjourNeighbour(object):
class BonjourNeighboursList(object):
def __init__(self):
self._contact_map = {}
def __getitem__(self, id):
return self._contact_map[id]
def __contains__(self, id):
return id in self._contact_map
def __iter__(self):
return iter(self._contact_map.values())
def __len__(self):
return len(self._contact_map)
__hash__ = None
def add(self, contact):
self._contact_map[contact.id] = contact
def pop(self, id, *args):
return self._contact_map.pop(id, *args)
def remove(self, contact):
return self._contact_map.pop(contact.id, None)
......@@ -439,11 +464,11 @@ class GoogleContactURI(object):
@classmethod
def from_number(cls, number):
return cls(re.sub('[\s()-]', '', number.text), cls._get_label(number), number.primary=='true')
return cls(re.sub('[\s()-]', '', number.text), cls._get_label(number), number.primary == 'true')
@classmethod
def from_email(cls, email):
return cls(re.sub('^sips?:', '', email.address), cls._get_label(email), False) # for now do not let email addresses become default URIs -Dan
return cls(re.sub('^sips?:', '', email.address), cls._get_label(email), False) # for now do not let email addresses become default URIs -Dan
@staticmethod
def _get_label(entry):
......@@ -456,23 +481,33 @@ class GoogleContactURI(object):
class GoogleContactURIList(object):
def __init__(self, uris):
self._uri_map = OrderedDict((uri.id, uri) for uri in uris)
def __getitem__(self, id):
return self._uri_map[id]
def __contains__(self, id):
return id in self._uri_map
def __iter__(self):
return iter(self._uri_map.values())
def __len__(self):
return len(self._uri_map)
__hash__ = None
def get(self, key, default=None):
return self._uri_map.get(key, default)
def add(self, uri):
self._uri_map[uri.id] = uri
def pop(self, id, *args):
return self._uri_map.pop(id, *args)
def remove(self, uri):
self._uri_map.pop(uri.id, None)
@property
def default(self):
try:
......@@ -500,27 +535,35 @@ class GoogleContact(object):
self.preferred_media = PreferredMedia('audio')
def __reduce__(self):
return (self.__class__, (self.id, self.name, self.company, self.icon, self.uris))
return self.__class__, (self.id, self.name, self.company, self.icon, self.uris)
class GoogleContactsList(object):
def __init__(self):
self._contact_map = {}
self.timestamp = None
def __getitem__(self, id):
return self._contact_map[id]
def __contains__(self, id):
return id in self._contact_map
def __iter__(self):
return iter(self._contact_map.values())
def __len__(self):
return len(self._contact_map)
__hash__ = None
def __setstate__(self, state):
self.__dict__.update(state)
self._contact_map # accessing this will fail if the pickle was created by an older version of the code, thus invalidating the unpickling
self._contact_map # accessing this will fail if the pickle was created by an older version of the code, thus invalidating the unpickling
def add(self, contact):
self._contact_map[contact.id] = contact
def pop(self, id, *args):
return self._contact_map.pop(id, *args)
......@@ -654,7 +697,7 @@ class GoogleContactsManager(object):
self.client.auth_token = ClientLoginToken(settings.google_contacts.authorization_token)
try:
group_id = next(entry.id.text for entry in self.client.get_groups().entry if entry.title.text=='System Group: My Contacts')
group_id = next(entry.id.text for entry in self.client.get_groups().entry if entry.title.text == 'System Group: My Contacts')
if self.need_sync:
query = ContactsQuery(feed=self.client.get_feed_uri(kind='contacts'), group=group_id, params={})
feed = self.client.get_feed(query.ToUri(), desired_class=ContactsFeed)
......@@ -838,23 +881,33 @@ class DummyContactURI(object):
class DummyContactURIList(object):
def __init__(self, uris):
self._uri_map = OrderedDict((uri.id, uri) for uri in uris)
def __getitem__(self, id):
return self._uri_map[id]
def __contains__(self, id):
return id in self._uri_map
def __iter__(self):
return iter(self._uri_map.values())
def __len__(self):
return len(self._uri_map)
__hash__ = None
def get(self, key, default=None):
return self._uri_map.get(key, default)
def add(self, uri):
self._uri_map[uri.id] = uri
def pop(self, id, *args):
return self._uri_map.pop(id, *args)
def remove(self, uri):
self._uri_map.pop(uri.id, None)
@property
def default(self):
try:
......@@ -877,7 +930,7 @@ class DummyContact(object):
self.preferred_media = PreferredMedia('audio')
def __reduce__(self):
return (self.__class__, (self.name, self.uris))
return self.__class__, (self.name, self.uris)
class Group(object):
......@@ -903,7 +956,7 @@ class Group(object):
return "%s(%r)" % (self.__class__.__name__, self.settings)
def __getstate__(self):
return (self.settings.id, dict(widget=Null, saved_state=self.saved_state, reference_group=self.reference_group))
return self.settings.id, dict(widget=Null, saved_state=self.saved_state, reference_group=self.reference_group)
def __setstate__(self, state):
group_id, state = state
......@@ -990,14 +1043,17 @@ class ContactIconDescriptor(object):
def __init__(self, filename):
self.filename = filename
self.icon = None
def __get__(self, obj, objtype):
def __get__(self, instance, owner):
if self.icon is None:
self.icon = QIcon(self.filename)
self.icon.filename = self.filename
return self.icon
def __set__(self, obj, value):
def __set__(self, instance, value):
raise AttributeError("attribute cannot be set")
def __delete__(self, obj):
def __delete__(self, instance):
raise AttributeError("attribute cannot be deleted")
......@@ -1046,7 +1102,7 @@ class Contact(object):
return '%s(%r, %r)' % (self.__class__.__name__, self.settings, self.group)
def __getstate__(self):
return (self.settings.id, dict(group=self.group))
return self.settings.id, dict(group=self.group)
def __setstate__(self, state):
contact_id, state = state
......@@ -1058,7 +1114,7 @@ class Contact(object):
group = BonjourNeighboursGroup()
else:
group = None
self.settings = group.contacts[contact_id] # problem if group is None -Dan
self.settings = group.contacts[contact_id] # problem if group is None -Dan
self.__dict__.update(state)
def __unicode__(self):
......@@ -1172,7 +1228,7 @@ class Contact(object):
handler(notification)
def _NH_AddressbookContactDidChange(self, notification):
if set(['icon', 'alternate_icon']).intersection(notification.data.modified):
if {'icon', 'alternate_icon'}.intersection(notification.data.modified):
self.__dict__.pop('icon', None)
self.__dict__.pop('pixmap', None)
notification.center.post_notification('BlinkContactDidChange', sender=self)
......@@ -1206,7 +1262,7 @@ class ContactDetail(object):
return '%s(%r)' % (self.__class__.__name__, self.settings)
def __getstate__(self):
return (self.settings.id, {})
return self.settings.id, {}
def __setstate__(self, state):
contact_id, state = state
......@@ -1218,7 +1274,7 @@ class ContactDetail(object):
group = BonjourNeighboursGroup()
else:
group = None
self.settings = group.contacts[contact_id] # problem if group is None -Dan
self.settings = group.contacts[contact_id] # problem if group is None -Dan
self.__dict__.update(state)
def __unicode__(self):
......@@ -1368,7 +1424,7 @@ class ContactURI(object):
else:
uri_id = None
state_dict = dict(uri=self.uri)
return (self.contact.id, uri_id, state_dict)
return self.contact.id, uri_id, state_dict
def __setstate__(self, state):
contact_id, uri_id, state = state
......@@ -1380,7 +1436,7 @@ class ContactURI(object):
group = BonjourNeighboursGroup()
else:
group = None
self.contact = group.contacts[contact_id] # problem if group is None -Dan
self.contact = group.contacts[contact_id] # problem if group is None -Dan
if uri_id is not None:
self.uri = self.contact.uris[uri_id]
self.__dict__.update(state)
......@@ -1414,6 +1470,7 @@ class GoogleAuthorizationData(object):
ui_class, base_class = uic.loadUiType(Resources.get('google_contacts_dialog.ui'))
class GoogleContactsDialog(base_class, ui_class):
__metaclass__ = QSingleton
......@@ -1556,6 +1613,7 @@ del ui_class, base_class
ui_class, base_class = uic.loadUiType(Resources.get('contact.ui'))
class ContactWidget(base_class, ui_class):
def __init__(self, parent=None):
super(ContactWidget, self).__init__(parent)
......@@ -1589,6 +1647,7 @@ del ui_class, base_class
ui_class, base_class = uic.loadUiType(Resources.get('contact_group.ui'))
class GroupWidget(base_class, ui_class):
def __init__(self, parent=None):
super(GroupWidget, self).__init__(parent)
......@@ -1624,7 +1683,7 @@ class GroupWidget(base_class, ui_class):
return
self.__dict__['selected'] = value
self.name_label.setStyleSheet("color: #ffffff; font-weight: bold;" if value else "color: #000000;")
#self.name_label.setForegroundRole(QPalette.BrightText if value else QPalette.WindowText)
# self.name_label.setForegroundRole(QPalette.BrightText if value else QPalette.WindowText)
self.update()
selected = property(_get_selected, _set_selected)
......@@ -1646,7 +1705,7 @@ class GroupWidget(base_class, ui_class):
self._start_editing()
def _start_editing(self):
#self.name_editor.setText(self.name_label.text())
# self.name_editor.setText(self.name_label.text())
self.name_editor.selectAll()
self.name_view.setCurrentWidget(self.editor_widget)
self.name_editor.setFocus()
......@@ -1726,7 +1785,7 @@ class GroupWidget(base_class, ui_class):
def event(self, event):
if type(event) is QKeyEvent and self.editing:
return True # do not propagate keyboard events while editing
return True # do not propagate keyboard events while editing
elif type(event) is QMouseEvent and event.type() == QEvent.MouseButtonDblClick and event.button() == Qt.LeftButton:
self._start_editing()
return super(GroupWidget, self).event(event)
......@@ -1776,7 +1835,7 @@ class ContactDelegate(QStyledItemDelegate, ColorHelperMixin):
item = index.data(Qt.UserRole)
if isinstance(item, Group):
item.widget = GroupWidget(parent)
item.widget.collapse_button.toggled.connect(partial(self._update_list_view, item)) # the partial still creates a memory cycle -Dan
item.widget.collapse_button.toggled.connect(partial(self._update_list_view, item)) # the partial still creates a memory cycle -Dan
return item.widget
else:
return None
......@@ -1784,7 +1843,7 @@ class ContactDelegate(QStyledItemDelegate, ColorHelperMixin):
def editorEvent(self, event, model, option, index):
arrow_rect = QRect(0, 0, 14, option.rect.height())
arrow_rect.moveTopRight(option.rect.topRight())
if event.type()==QEvent.MouseButtonRelease and event.button()==Qt.LeftButton and event.modifiers()==Qt.NoModifier and arrow_rect.contains(event.pos()):
if event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton and event.modifiers() == Qt.NoModifier and arrow_rect.contains(event.pos()):
model.contact_list.detail_model.contact = index.data(Qt.UserRole).settings
detail_view = model.contact_list.detail_view
detail_view.animation.setDirection(QPropertyAnimation.Forward)
......@@ -1841,8 +1900,8 @@ class ContactDelegate(QStyledItemDelegate, ColorHelperMixin):
gradient.setColorAt(1.0, self.color_with_alpha(base_contrast_color, 0.8*255))
contrast_color = QBrush(gradient)
else:
#foreground_color = option.palette.color(QPalette.Normal, QPalette.WindowText)
#background_color = option.palette.color(QPalette.Window)
# foreground_color = option.palette.color(QPalette.Normal, QPalette.WindowText)
# background_color = option.palette.color(QPalette.Window)
foreground_color = widget.palette().color(QPalette.Normal, widget.foregroundRole())
background_color = widget.palette().color(widget.backgroundRole())
contrast_color = self.calc_light_color(background_color)
......@@ -1909,7 +1968,7 @@ class ContactDetailDelegate(QStyledItemDelegate, ColorHelperMixin):
def editorEvent(self, event, model, option, index):
arrow_rect = QRect(0, 0, 14, option.rect.height())
arrow_rect.moveTopRight(option.rect.topRight())
if index.row()==0 and event.type()==QEvent.MouseButtonRelease and event.button()==Qt.LeftButton and event.modifiers()==Qt.NoModifier and arrow_rect.contains(event.pos()):
if index.row() == 0 and event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton and event.modifiers() == Qt.NoModifier and arrow_rect.contains(event.pos()):
detail_view = self.parent()
detail_view.animation.setDirection(QPropertyAnimation.Backward)
detail_view.animation.start()
......@@ -2068,6 +2127,7 @@ class ContactDetailDelegate(QStyledItemDelegate, ColorHelperMixin):
class Operation(object):
__params__ = ()
__priority__ = None
def __init__(self, **params):
for name, value in params.iteritems():
setattr(self, name, value)
......@@ -2075,14 +2135,17 @@ class Operation(object):
raise ValueError("missing operation parameter: '%s'" % param)
self.timestamp = datetime.utcnow()
class AddContactOperation(Operation):
__params__ = ('contact', 'group_ids', 'icon', 'alternate_icon')
__priority__ = 0
class AddGroupOperation(Operation):
__params__ = ('group',)
__priority__ = 1
class AddGroupMemberOperation(Operation):
__params__ = ('group_id', 'contact_id')
__priority__ = 2
......@@ -2319,21 +2382,21 @@ class ContactModel(QAbstractListModel):
drop_group = next(group for group in groups if group not in moved_groups)
drop_position = self.contact_list.AboveItem
elif group is groups[-1] and group in moved_groups:
drop_group = (group for group in reversed(groups) if group not in moved_groups).next()
drop_group = next(group for group in reversed(groups) if group not in moved_groups)
drop_position = self.contact_list.BelowItem
elif group in moved_groups:
position = groups.index(group)
if drop_indicator is self.contact_list.AboveItem:
drop_group = (group for group in reversed(groups[:position]) if group not in moved_groups).next()
drop_group = next(group for group in reversed(groups[:position]) if group not in moved_groups)
drop_position = self.contact_list.BelowItem
else:
drop_group = (group for group in groups[position:] if group not in moved_groups).next()
drop_group = next(group for group in groups[position:] if group not in moved_groups)
drop_position = self.contact_list.AboveItem
else:
drop_group = group
drop_position = drop_indicator
items = self._pop_items(selected_indexes)
groups = self.items[GroupList] # get group list again as it changed
groups = self.items[GroupList] # get group list again as it changed
if drop_position is self.contact_list.AboveItem:
position = self.items.index(drop_group)
else:
......@@ -2538,8 +2601,8 @@ class ContactModel(QAbstractListModel):
if contact.presence.policy == 'default':
contact.presence.policy = 'allow' if contact.presence.subscribe else 'block'
contact.save()
elif contact.presence.subscribe != (True if contact.presence.policy=='allow' else False):
contact.presence.subscribe = True if contact.presence.policy=='allow' else False
elif contact.presence.subscribe != (True if contact.presence.policy == 'allow' else False):
contact.presence.subscribe = True if contact.presence.policy == 'allow' else False
contact.save()
@staticmethod
......@@ -2792,7 +2855,7 @@ class ContactSearchModel(QSortFilterProxyModel):
if isinstance(item, Group) or not item.group.virtual:
return False
search_tokens = self.filterRegExp().pattern().lower().split()
searched_item = u' '.join([item.name] + [uri.uri for uri in item.uris]).lower() # should we only search in the username part of the uris? -Dan
searched_item = u' '.join([item.name] + [uri.uri for uri in item.uris]).lower() # should we only search in the username part of the uris? -Dan
return all(token in searched_item for token in search_tokens)
def lessThan(self, left_index, right_index):
......@@ -3163,13 +3226,13 @@ class ContactListView(QListView):
def keyPressEvent(self, event):
if event.key() in (Qt.Key_Enter, Qt.Key_Return):
selected_indexes = self.selectionModel().selectedIndexes()
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes)==1 else None
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes) == 1 else None
if isinstance(item, Contact):
session_manager = SessionManager()
session_manager.create_session(item, item.uri, item.preferred_media.stream_descriptions, connect=item.preferred_media.autoconnect)
elif event.key() == Qt.Key_Space:
selected_indexes = self.selectionModel().selectedIndexes()
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes)==1 else None
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes) == 1 else None
if isinstance(item, Contact) and self.detail_view.isHidden() and self.detail_view.animation.state() == QPropertyAnimation.Stopped:
self.detail_model.contact = item.settings
self.detail_view.animation.setDirection(QPropertyAnimation.Forward)
......@@ -3290,7 +3353,7 @@ class ContactListView(QListView):
def _AH_AddGroup(self):
group = Group(addressbook.Group())
group.settings.save = Null # disable saving until the user provides the name
group.settings.save = Null # disable saving until the user provides the name
model = self.model()
selection_model = self.selectionModel()
model.addGroup(group)
......@@ -3306,7 +3369,7 @@ class ContactListView(QListView):
groups.add(item)
elif isinstance(item, Contact) and not item.group.virtual:
groups.add(item.group)
preferred_group = groups.pop() if len(groups)==1 else None
preferred_group = groups.pop() if len(groups) == 1 else None
main_window = QApplication.instance().main_window
main_window.contact_editor_dialog.open_for_add(main_window.search_box.text(), preferred_group)
......@@ -3624,7 +3687,7 @@ class ContactSearchListView(QListView):
def keyPressEvent(self, event):
if event.key() in (Qt.Key_Enter, Qt.Key_Return):
selected_indexes = self.selectionModel().selectedIndexes()
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes)==1 else None
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes) == 1 else None
if isinstance(item, Contact):
session_manager = SessionManager()
session_manager.create_session(item, item.uri, item.preferred_media.stream_descriptions, connect=item.preferred_media.autoconnect)
......@@ -3632,7 +3695,7 @@ class ContactSearchListView(QListView):
QApplication.instance().main_window.search_box.clear()
elif event.key() == Qt.Key_Space:
selected_indexes = self.selectionModel().selectedIndexes()
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes)==1 else None
item = selected_indexes[0].data(Qt.UserRole) if len(selected_indexes) == 1 else None
if isinstance(item, Contact) and self.detail_view.isHidden() and self.detail_view.animation.state() == QPropertyAnimation.Stopped:
self.detail_model.contact = item.settings
self.detail_view.animation.setDirection(QPropertyAnimation.Forward)
......@@ -4278,7 +4341,7 @@ class ContactURIDelegate(QItemDelegate):
# draw elided text using a fading gradient
color_group = QPalette.Disabled if not option.state & QStyle.State_Enabled else QPalette.Normal if option.state & QStyle.State_Active else QPalette.Inactive
text_margin = option.widget.style().pixelMetric(QStyle.PM_FocusFrameHMargin, None, option.widget) + 1
text_rect = rect.adjusted(text_margin, 0, -text_margin, 0) # remove width padding
text_rect = rect.adjusted(text_margin, 0, -text_margin, 0) # remove width padding
width = text_rect.width()
fade_start = 1 - 50.0/width if width > 50 else 0.0
gradient = QLinearGradient(0, 0, width, 0)
......@@ -4358,7 +4421,7 @@ class ContactURIModel(QAbstractTableModel):
elif column == ContactURIModel.DefaultColumn:
if value:
for position, item in enumerate(self.items):
item.default = position==row
item.default = position == row
else:
self.items[row].default = False
else:
......@@ -4404,7 +4467,7 @@ class ContactURIModel(QAbstractTableModel):
default_item = None
else:
if default_item not in added_items:
default_item = None # only care for the default URI if it was a newly added one, else use the one from the contact
default_item = None # only care for the default URI if it was a newly added one, else use the one from the contact
items = [ContactURIItem(uri.id, uri.uri, uri.type, default=default_item is None and uri is contact.uris.default) for uri in contact.uris]
items.extend(added_items)
items.append(ContactURIItem(None, None, self.default_uri_type, False, ghost=True))
......@@ -4477,6 +4540,7 @@ class ContactURITableView(QTableView):
ui_class, base_class = uic.loadUiType(Resources.get('contact_editor.ui'))
class ContactEditorDialog(base_class, ui_class):
implements(IObserver)
......@@ -4659,13 +4723,13 @@ class URIUtils(object):
uri_str = uri_str.partition(':')[2]
contact_user = uri_str.partition('@')[0]
if cls.is_number(contact_user):
contact_user = cls.trim_number(contact_user) # these could be expensive, maybe cache -Dan
contact_user = cls.trim_number(contact_user) # these could be expensive, maybe cache -Dan
if contact_user.endswith(number):
ratio = len(number) * 100 / len(contact_user)
if ratio >= 50:
heappush(matched_numbers, (100-ratio, next(counter), contact, contact_uri))
if matched_numbers:
return matched_numbers[0][2:] # ratio, index, contact, uri
return matched_numbers[0][2:] # ratio, index, contact, uri
display_name = display_name or "%s@%s" % (uri.user, uri.host)
contact = Contact(DummyContact(display_name, [DummyContactURI(str(uri).partition(':')[2], default=True)]), None)
......
__all__ = ['CallFunctionEvent']
from PyQt4.QtCore import QEvent
from application.python.descriptor import classproperty
__all__ = ['CallFunctionEvent']
class EventMeta(type(QEvent)):
def __init__(cls, name, bases, dct):
super(EventMeta, cls).__init__(name, bases, dct)
......@@ -35,4 +36,3 @@ class CallFunctionEvent(EventBase):
self.args = args
self.kw = kw
__all__ = ['FileTransferWindow']
import os
from PyQt4 import uic
......@@ -18,8 +16,12 @@ from blink.sessions import FileTransferDelegate, FileTransferModel
from blink.widgets.util import ContextMenuActions
__all__ = ['FileTransferWindow']
ui_class, base_class = uic.loadUiType(Resources.get('filetransfer_window.ui'))
class FileTransferWindow(base_class, ui_class):
implements(IObserver)
......@@ -63,7 +65,7 @@ class FileTransferWindow(base_class, ui_class):
def update_status(self):
total = len(self.model.items)
active = len([item for item in self.model.items if not item.ended])
text = u'%d %s' % (total, 'transfer' if total==1 else 'transfers')
text = u'%d %s' % (total, 'transfer' if total == 1 else 'transfers')
if active > 0:
text += u' (%d active)' % active
self.status_label.setText(text)
......
__all__ = ['HistoryManager']
import bisect
import cPickle as pickle
import re
......@@ -23,6 +21,9 @@ from blink.resources import ApplicationData, Resources
from blink.util import run_in_gui_thread
__all__ = ['HistoryManager']
class HistoryManager(object):
__metaclass__ = Singleton
implements(IObserver)
......@@ -86,13 +87,16 @@ class IconDescriptor(object):
def __init__(self, filename):
self.filename = filename
self.icon = None
def __get__(self, obj, objtype):
def __get__(self, instance, owner):
if self.icon is None:
self.icon = QIcon(self.filename)
self.icon.filename = self.filename
return self.icon
def __set__(self, obj, value):
raise AttributeError("attribute cannot be set")
def __delete__(self, obj):
raise AttributeError("attribute cannot be deleted")
......@@ -116,7 +120,7 @@ class HistoryEntry(object):
self.reason = reason
def __reduce__(self):
return (self.__class__, (self.direction, self.name, self.uri, self.account_id, self.call_time, self.duration, self.failed, self.reason))
return self.__class__, (self.direction, self.name, self.uri, self.account_id, self.call_time, self.duration, self.failed, self.reason)
def __eq__(self, other):
return self is other
......@@ -174,7 +178,7 @@ class HistoryEntry(object):
@classmethod
def from_session(cls, session):
if session.start_time is None and session.end_time is not None:
# Session may have anded before it fully started
# Session may have ended before it fully started
session.start_time = session.end_time
call_time = session.start_time or ISOTimestamp.now()
if session.start_time and session.end_time:
......
__all__ = ['LogManager']
import os
import sys
......@@ -21,6 +19,9 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from blink.resources import ApplicationData
__all__ = ['LogManager']
class NotificationQueue(object):
implements(IObserver)
......@@ -161,10 +162,10 @@ class LogManager(object):
direction = "RECEIVED"
else:
direction = "SENDING"
buf = ["%s: Packet %d, +%s" % (direction, self._siptrace_packet_count, (notification.datetime - self._siptrace_start_time))]
buf.append("%(source_ip)s:%(source_port)d -(SIP over %(transport)s)-> %(destination_ip)s:%(destination_port)d" % notification.data.__dict__)
buf.append(notification.data.data)
buf.append('--')
buf = ["%s: Packet %d, +%s" % (direction, self._siptrace_packet_count, (notification.datetime - self._siptrace_start_time)),
"%(source_ip)s:%(source_port)d -(SIP over %(transport)s)-> %(destination_ip)s:%(destination_port)d" % notification.data.__dict__,
notification.data.data,
'--']
message = '\n'.join(buf)
try:
self.siptrace_file.write('%s [%s %d]: %s\n' % (notification.datetime, self.name, self.pid, message))
......@@ -236,4 +237,3 @@ class LogManager(object):
except Exception:
pass
__all__ = ['MainWindow']
import hashlib
import os
......@@ -36,8 +34,12 @@ from blink.util import run_in_gui_thread
from blink.widgets.buttons import AccountState, SwitchViewButton
__all__ = ['MainWindow']
ui_class, base_class = uic.loadUiType(Resources.get('blink.ui'))
class MainWindow(base_class, ui_class):
implements(IObserver)
......@@ -140,7 +142,7 @@ class MainWindow(base_class, ui_class):
self.audio_call_button.clicked.connect(self._SH_AudioCallButtonClicked)
self.video_call_button.clicked.connect(self._SH_VideoCallButtonClicked)
self.chat_session_button.clicked.connect(self._SH_ChatSessionButtonClicked)
self.back_to_contacts_button.clicked.connect(self.search_box.clear) # this can be set in designer -Dan
self.back_to_contacts_button.clicked.connect(self.search_box.clear) # this can be set in designer -Dan
self.conference_button.makeConference.connect(self._SH_MakeConference)
self.conference_button.breakConference.connect(self._SH_BreakConference)
......@@ -385,7 +387,7 @@ class MainWindow(base_class, ui_class):
session_manager = SessionManager()
if session_manager.last_dialed_uri is not None:
contact, contact_uri = URIUtils.find_contact(session_manager.last_dialed_uri)
session_manager.create_session(contact, contact_uri, [StreamDescription('audio')]) # TODO: remember used media types and redial with them. -Saul
session_manager.create_session(contact, contact_uri, [StreamDescription('audio')]) # TODO: remember used media types and redial with them. -Saul
def _AH_SIPServerSettings(self, checked):
account = self.identity.itemData(self.identity.currentIndex()).account
......@@ -451,7 +453,7 @@ class MainWindow(base_class, ui_class):
except KeyError:
account = None
contact, contact_uri = URIUtils.find_contact(action.entry.uri)
session_manager.create_session(contact, contact_uri, [StreamDescription('audio')], account=account) # TODO: memorize media type and use it? -Saul (not sure about history in/out -Dan)
session_manager.create_session(contact, contact_uri, [StreamDescription('audio')], account=account) # TODO: memorize media type and use it? -Saul (not sure about history in/out -Dan)
def _AH_SystemTrayShowWindow(self, checked):
self.show()
......@@ -585,7 +587,7 @@ class MainWindow(base_class, ui_class):
def _SH_ContactListSelectionChanged(self, selected, deselected):
account_manager = AccountManager()
selected_items = self.contact_list.selectionModel().selectedIndexes()
self.enable_call_buttons(account_manager.default_account is not None and len(selected_items)==1 and isinstance(selected_items[0].data(Qt.UserRole), Contact))
self.enable_call_buttons(account_manager.default_account is not None and len(selected_items) == 1 and isinstance(selected_items[0].data(Qt.UserRole), Contact))
def _SH_ContactModelAddedItems(self, items):
if not self.search_box.text():
......@@ -597,7 +599,7 @@ class MainWindow(base_class, ui_class):
if not self.search_box.text():
return
if any(type(item) is Contact for item in items) and self.contact_search_model.rowCount() == 0:
self.search_box.clear() # check this. it is no longer be the correct behaviour as now contacts can be deleted from remote -Dan
self.search_box.clear() # check this. it is no longer be the correct behaviour as now contacts can be deleted from remote -Dan
else:
active_widget = self.search_list_panel if self.contact_search_model.rowCount() else self.not_found_panel
self.search_view.setCurrentWidget(active_widget)
......@@ -663,7 +665,7 @@ class MainWindow(base_class, ui_class):
else:
self.contacts_view.setCurrentWidget(self.contact_list_panel)
selected_items = self.contact_list.selectionModel().selectedIndexes()
self.enable_call_buttons(account_manager.default_account is not None and len(selected_items)==1 and type(selected_items[0].data(Qt.UserRole)) is Contact)
self.enable_call_buttons(account_manager.default_account is not None and len(selected_items) == 1 and type(selected_items[0].data(Qt.UserRole)) is Contact)
self.search_list.detail_model.contact = None
self.search_list.detail_view.hide()
......@@ -698,7 +700,7 @@ class MainWindow(base_class, ui_class):
def _SH_AudioSessionModelChangedStructure(self):
active_sessions = self.session_model.active_sessions
self.active_sessions_label.setText(u'There is 1 active call' if len(active_sessions)==1 else u'There are %d active calls' % len(active_sessions))
self.active_sessions_label.setText(u'There is 1 active call' if len(active_sessions) == 1 else u'There are %d active calls' % len(active_sessions))
self.active_sessions_label.setVisible(any(active_sessions))
self.hangup_all_button.setEnabled(any(active_sessions))
selected_indexes = self.session_list.selectionModel().selectedIndexes()
......@@ -803,16 +805,16 @@ class MainWindow(base_class, ui_class):
self.silent_action.setChecked(settings.audio.silent)
self.silent_button.setChecked(settings.audio.silent)
if 'audio.output_device' in notification.data.modified:
action = (action for action in self.output_devices_group.actions() if action.data() == settings.audio.output_device).next()
action = next(action for action in self.output_devices_group.actions() if action.data() == settings.audio.output_device)
action.setChecked(True)
if 'audio.input_device' in notification.data.modified:
action = (action for action in self.input_devices_group.actions() if action.data() == settings.audio.input_device).next()
action = next(action for action in self.input_devices_group.actions() if action.data() == settings.audio.input_device)
action.setChecked(True)
if 'audio.alert_device' in notification.data.modified:
action = (action for action in self.alert_devices_group.actions() if action.data() == settings.audio.alert_device).next()
action = next(action for action in self.alert_devices_group.actions() if action.data() == settings.audio.alert_device)
action.setChecked(True)
if 'video.device' in notification.data.modified:
action = (action for action in self.video_devices_group.actions() if action.data() == settings.video.device).next()
action = next(action for action in self.video_devices_group.actions() if action.data() == settings.video.device)
action.setChecked(True)
if 'answering_machine.enabled' in notification.data.modified:
self.answering_machine_action.setChecked(settings.answering_machine.enabled)
......@@ -841,12 +843,12 @@ class MainWindow(base_class, ui_class):
account_manager = AccountManager()
account = notification.sender
if 'enabled' in notification.data.modified:
action = (action for action in self.accounts_menu.actions() if action.data() is account).next()
action = next(action for action in self.accounts_menu.actions() if action.data() is account)
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() is account).next()
if {'enabled', 'message_summary.enabled', 'message_summary.voicemail_uri'}.intersection(notification.data.modified):
action = next(action for action in self.voicemail_menu.actions() if action.data() is account)
action.setVisible(False if account is BonjourAccount() else account.enabled and account.message_summary.enabled)
action.setEnabled(False if account is BonjourAccount() else account.voicemail_uri is not None)
......@@ -868,9 +870,9 @@ class MainWindow(base_class, ui_class):
def _NH_SIPAccountManagerDidRemoveAccount(self, notification):
account = notification.data.account
action = (action for action in self.accounts_menu.actions() if action.data() is account).next()
action = next(action for action in self.accounts_menu.actions() if action.data() is account)
self.accounts_menu.removeAction(action)
action = (action for action in self.voicemail_menu.actions() if action.data() is account).next()
action = next(action for action in self.voicemail_menu.actions() if action.data() is account)
self.voicemail_menu.removeAction(action)
def _NH_SIPAccountManagerDidChangeDefaultAccount(self, notification):
......@@ -878,12 +880,12 @@ class MainWindow(base_class, ui_class):
self.enable_call_buttons(False)
else:
selected_items = self.contact_list.selectionModel().selectedIndexes()
self.enable_call_buttons(len(selected_items)==1 and isinstance(selected_items[0].data(Qt.UserRole), Contact))
self.enable_call_buttons(len(selected_items) == 1 and isinstance(selected_items[0].data(Qt.UserRole), Contact))
def _NH_SIPAccountGotMessageSummary(self, notification):
account = notification.sender
summary = notification.data.message_summary
action = (action for action in self.voicemail_menu.actions() if action.data() is account).next()
action = next(action for action in self.voicemail_menu.actions() if action.data() is account)
action.setEnabled(account.voicemail_uri is not None)
if summary.messages_waiting:
try:
......@@ -918,4 +920,3 @@ class MainWindow(base_class, ui_class):
del ui_class, base_class
__all__ = ['PreferencesWindow', 'AccountListView', 'SIPPortEditor']
import os
import urlparse
......@@ -31,6 +29,8 @@ from blink.logging import LogManager
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread
__all__ = ['PreferencesWindow', 'AccountListView', 'SIPPortEditor']
# LineEdit and ComboBox validators
#
......@@ -100,20 +100,20 @@ class SIPPortEditor(QSpinBox):
def __init__(self, parent=None):
super(SIPPortEditor, self).__init__(parent)
self.setRange(0, 65535)
self.sibling = Null # if there is a sibling port, its value is invalid for this port
self.sibling = Null # if there is a sibling port, its value is invalid for this port
def stepBy(self, steps):
value = self.value()
sibling_value = self.sibling.value()
if value+steps == sibling_value != 0:
steps += steps/abs(steps) # add one more unit in the right direction
steps += steps/abs(steps) # add one more unit in the right direction
if 0 < value+steps < 1024:
if steps < 0:
steps = -value
else:
steps = 1024 - value
if value+steps == sibling_value != 0:
steps += steps/abs(steps) # add one more unit in the right direction
steps += steps/abs(steps) # add one more unit in the right direction
return super(SIPPortEditor, self).stepBy(steps)
def validate(self, input, pos):
......@@ -139,7 +139,7 @@ class AccountListView(QListView):
def __init__(self, parent=None):
super(AccountListView, self).__init__(parent)
self.setItemDelegate(AccountDelegate(self))
#self.setDropIndicatorShown(False)
# self.setDropIndicatorShown(False)
def selectionChanged(self, selected, deselected):
super(AccountListView, self).selectionChanged(selected, deselected)
......@@ -153,9 +153,11 @@ class AccountListView(QListView):
class blocked_qt_signals(object):
def __init__(self, qobject):
self.qobject = qobject
def __enter__(self):
self.qobject.blockSignals(True)
return self.qobject
def __exit__(self, type, value, traceback):
self.qobject.blockSignals(False)
......@@ -174,6 +176,7 @@ class UnspecifiedMSRPRelay(object):
ui_class, base_class = uic.loadUiType(Resources.get('preferences.ui'))
class PreferencesWindow(base_class, ui_class):
__metaclass__ = QSingleton
......@@ -331,7 +334,7 @@ class PreferencesWindow(base_class, ui_class):
self.tls_ca_file_editor.locationCleared.connect(self._SH_TLSCAFileEditorLocationCleared)
self.tls_ca_file_browse_button.clicked.connect(self._SH_TLSCAFileBrowseButtonClicked)
# Setup initial state (show the acounts page right after start)
# Setup initial state (show the accounts page right after start)
self.accounts_action.trigger()
self.account_tab_widget.setCurrentIndex(0)
......@@ -433,7 +436,7 @@ class PreferencesWindow(base_class, ui_class):
# Adjust some minimum label widths in order to better align settings in different group boxes, widgets or tabs
# account server and network tab
font_metrics = self.outbound_proxy_label.fontMetrics() # we assume all labels have the same font
font_metrics = self.outbound_proxy_label.fontMetrics() # we assume all labels have the same font
labels = (self.outbound_proxy_label, self.auth_username_label, self.msrp_relay_label,
self.voicemail_uri_label, self.xcap_root_label, self.server_tools_url_label,
self.conference_server_label, self.msrp_transport_label)
......@@ -442,7 +445,7 @@ class PreferencesWindow(base_class, ui_class):
self.msrp_transport_label.setMinimumWidth(text_width)
# account advanced tab
font_metrics = self.register_interval_label.fontMetrics() # we assume all labels have the same font
font_metrics = self.register_interval_label.fontMetrics() # we assume all labels have the same font
labels = (self.register_interval_label, self.publish_interval_label, self.subscribe_interval_label,
self.idd_prefix_label, self.prefix_label, self.account_tls_cert_file_label)
text_width = max(font_metrics.width(label.text()) for label in labels) + 15
......@@ -451,7 +454,7 @@ class PreferencesWindow(base_class, ui_class):
self.account_tls_cert_file_label.setMinimumWidth(text_width)
# audio settings
font_metrics = self.answer_delay_label.fontMetrics() # we assume all labels have the same font
font_metrics = self.answer_delay_label.fontMetrics() # we assume all labels have the same font
labels = (self.audio_input_device_label, self.audio_output_device_label, self.audio_alert_device_label, self.audio_sample_rate_label,
self.answer_delay_label, self.max_recording_label, self.unavailable_message_label)
text_width = max(font_metrics.width(label.text()) for label in labels)
......@@ -516,8 +519,8 @@ class PreferencesWindow(base_class, ui_class):
added_codecs = set(SIPSimpleSettings.rtp.audio_codec_order.default).difference(settings.rtp.audio_codec_order)
removed_codecs = set(settings.rtp.audio_codec_order).difference(SIPSimpleSettings.rtp.audio_codec_order.default)
if added_codecs:
settings.rtp.audio_codec_order = DefaultValue # reset codec order
settings.rtp.audio_codec_list = DefaultValue # reset codec list
settings.rtp.audio_codec_order = DefaultValue # reset codec order
settings.rtp.audio_codec_list = DefaultValue # reset codec list
settings.save()
elif removed_codecs:
codec_order = [codec for codec in settings.rtp.audio_codec_order if codec not in removed_codecs]
......@@ -535,8 +538,8 @@ class PreferencesWindow(base_class, ui_class):
added_codecs = set(SIPSimpleSettings.rtp.audio_codec_order.default).difference(account.rtp.audio_codec_order)
removed_codecs = set(account.rtp.audio_codec_order).difference(SIPSimpleSettings.rtp.audio_codec_order.default)
if added_codecs:
account.rtp.audio_codec_order = DefaultValue # reset codec order
account.rtp.audio_codec_list = DefaultValue # reset codec list
account.rtp.audio_codec_order = DefaultValue # reset codec order
account.rtp.audio_codec_list = DefaultValue # reset codec list
account.save()
elif removed_codecs:
codec_order = [codec for codec in account.rtp.audio_codec_order if codec not in removed_codecs]
......@@ -553,8 +556,8 @@ class PreferencesWindow(base_class, ui_class):
added_codecs = set(SIPSimpleSettings.rtp.video_codec_order.default).difference(settings.rtp.video_codec_order)
removed_codecs = set(settings.rtp.video_codec_order).difference(SIPSimpleSettings.rtp.video_codec_order.default)
if added_codecs:
settings.rtp.video_codec_order = DefaultValue # reset codec order
settings.rtp.video_codec_list = DefaultValue # reset codec list
settings.rtp.video_codec_order = DefaultValue # reset codec order
settings.rtp.video_codec_list = DefaultValue # reset codec list
settings.save()
elif removed_codecs:
codec_order = [codec for codec in settings.rtp.video_codec_order if codec not in removed_codecs]
......@@ -572,8 +575,8 @@ class PreferencesWindow(base_class, ui_class):
added_codecs = set(SIPSimpleSettings.rtp.video_codec_order.default).difference(account.rtp.video_codec_order)
removed_codecs = set(account.rtp.video_codec_order).difference(SIPSimpleSettings.rtp.video_codec_order.default)
if added_codecs:
account.rtp.video_codec_order = DefaultValue # reset codec order
account.rtp.video_codec_list = DefaultValue # reset codec list
account.rtp.video_codec_order = DefaultValue # reset codec order
account.rtp.video_codec_list = DefaultValue # reset codec list
account.save()
elif removed_codecs:
codec_order = [codec for codec in account.rtp.video_codec_order if codec not in removed_codecs]
......@@ -593,7 +596,7 @@ class PreferencesWindow(base_class, ui_class):
self.audio_input_device_button.clear()
self.audio_input_device_button.addItem(u'System Default', 'system_default')
self.audio_input_device_button.insertSeparator(1)
self.audio_input_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
self.audio_input_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.input_devices:
self.audio_input_device_button.addItem(device, device)
self.audio_input_device_button.addItem(u'None', None)
......@@ -602,7 +605,7 @@ class PreferencesWindow(base_class, ui_class):
self.audio_output_device_button.clear()
self.audio_output_device_button.addItem(u'System Default', 'system_default')
self.audio_output_device_button.insertSeparator(1)
self.audio_output_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
self.audio_output_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.output_devices:
self.audio_output_device_button.addItem(device, device)
self.audio_output_device_button.addItem(u'None', None)
......@@ -611,7 +614,7 @@ class PreferencesWindow(base_class, ui_class):
self.audio_alert_device_button.clear()
self.audio_alert_device_button.addItem(u'System Default', 'system_default')
self.audio_alert_device_button.insertSeparator(1)
self.audio_alert_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
self.audio_alert_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.output_devices:
self.audio_alert_device_button.addItem(device, device)
self.audio_alert_device_button.addItem(u'None', None)
......@@ -625,7 +628,7 @@ class PreferencesWindow(base_class, ui_class):
self.video_camera_button.clear()
self.video_camera_button.addItem(u'System Default', 'system_default')
self.video_camera_button.insertSeparator(1)
self.video_camera_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
self.video_camera_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.video_devices:
self.video_camera_button.addItem(device, device)
self.video_camera_button.addItem(u'None', None)
......@@ -653,7 +656,7 @@ class PreferencesWindow(base_class, ui_class):
item = QListWidgetItem(codec, self.audio_codecs_list)
item.setCheckState(Qt.Checked if codec in settings.rtp.audio_codec_list else Qt.Unchecked)
# Asnwering Machine settings
# Answering Machine settings
self.enable_answering_machine_button.setChecked(settings.answering_machine.enabled)
with blocked_qt_signals(self.answer_delay):
self.answer_delay.setValue(settings.answering_machine.answer_delay)
......@@ -729,9 +732,9 @@ class PreferencesWindow(base_class, ui_class):
for button in self.sip_transports_button_group.buttons():
button.setChecked(button.name in settings.sip.transport_list)
if settings.sip.tcp_port and settings.sip.tcp_port==settings.sip.tls_port:
if settings.sip.tcp_port and settings.sip.tcp_port == settings.sip.tls_port:
log.warning("the SIP TLS and TCP ports cannot be the same")
settings.sip.tls_port = settings.sip.tcp_port+1 if settings.sip.tcp_port<65535 else 65534
settings.sip.tls_port = settings.sip.tcp_port+1 if settings.sip.tcp_port < 65535 else 65534
settings.save()
with blocked_qt_signals(self.udp_port):
......@@ -893,7 +896,7 @@ class PreferencesWindow(base_class, ui_class):
account_manager = AccountManager()
default_account = account_manager.default_account
try:
index = (index for index, account in enumerate(model.accounts) if account==default_account).next()
index = next(index for index, account in enumerate(model.accounts) if account is default_account)
except StopIteration:
index = 0
selection_model.select(model.index(index), selection_model.ClearAndSelect)
......@@ -944,7 +947,7 @@ class PreferencesWindow(base_class, ui_class):
def _update_pstn_example_label(self):
prefix = self.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')
......@@ -1065,14 +1068,14 @@ class PreferencesWindow(base_class, ui_class):
def _SH_AccountAudioCodecsListItemChanged(self, item):
account = self.selected_account
items = [self.account_audio_codecs_list.item(row) for row in xrange(self.account_audio_codecs_list.count())]
account.rtp.audio_codec_list = [item.text() for item in items if item.checkState()==Qt.Checked]
account.rtp.audio_codec_list = [item.text() for item in items if item.checkState() == Qt.Checked]
account.rtp.audio_codec_order = [item.text() for item in items]
account.save()
def _SH_AccountAudioCodecsListModelRowsMoved(self, source_parent, source_start, source_end, dest_parent, dest_row):
account = self.selected_account
items = [self.account_audio_codecs_list.item(row) for row in xrange(self.account_audio_codecs_list.count())]
account.rtp.audio_codec_list = [item.text() for item in items if item.checkState()==Qt.Checked]
account.rtp.audio_codec_list = [item.text() for item in items if item.checkState() == Qt.Checked]
account.rtp.audio_codec_order = [item.text() for item in items]
account.save()
......@@ -1095,14 +1098,14 @@ class PreferencesWindow(base_class, ui_class):
def _SH_AccountVideoCodecsListItemChanged(self, item):
account = self.selected_account
items = [self.account_video_codecs_list.item(row) for row in xrange(self.account_video_codecs_list.count())]
account.rtp.video_codec_list = [item.text() for item in items if item.checkState()==Qt.Checked]
account.rtp.video_codec_list = [item.text() for item in items if item.checkState() == Qt.Checked]
account.rtp.video_codec_order = [item.text() for item in items]
account.save()
def _SH_AccountVideoCodecsListModelRowsMoved(self, source_parent, source_start, source_end, dest_parent, dest_row):
account = self.selected_account
items = [self.account_video_codecs_list.item(row) for row in xrange(self.account_video_codecs_list.count())]
account.rtp.video_codec_list = [item.text() for item in items if item.checkState()==Qt.Checked]
account.rtp.video_codec_list = [item.text() for item in items if item.checkState() == Qt.Checked]
account.rtp.video_codec_order = [item.text() for item in items]
account.save()
......@@ -1260,7 +1263,7 @@ class PreferencesWindow(base_class, ui_class):
def _SH_IDDPrefixButtonActivated(self, text):
self._update_pstn_example_label()
account = self.selected_account
idd_prefix = None if text=='+' else text
idd_prefix = None if text == '+' else text
if account.pstn.idd_prefix != idd_prefix:
account.pstn.idd_prefix = idd_prefix
account.save()
......@@ -1268,7 +1271,7 @@ class PreferencesWindow(base_class, ui_class):
def _SH_PrefixButtonActivated(self, text):
self._update_pstn_example_label()
account = self.selected_account
prefix = None if text=='None' else text
prefix = None if text == 'None' else text
if account.pstn.prefix != prefix:
account.pstn.prefix = prefix
account.save()
......@@ -1342,14 +1345,14 @@ class PreferencesWindow(base_class, ui_class):
def _SH_AudioCodecsListItemChanged(self, item):
settings = SIPSimpleSettings()
item_iterator = (self.audio_codecs_list.item(row) for row in xrange(self.audio_codecs_list.count()))
settings.rtp.audio_codec_list = [item.text() for item in item_iterator if item.checkState()==Qt.Checked]
settings.rtp.audio_codec_list = [item.text() for item in item_iterator if item.checkState() == Qt.Checked]
settings.save()
def _SH_AudioCodecsListModelRowsMoved(self, source_parent, source_start, source_end, dest_parent, dest_row):
settings = SIPSimpleSettings()
items = [self.audio_codecs_list.item(row) for row in xrange(self.audio_codecs_list.count())]
settings.rtp.audio_codec_order = [item.text() for item in items]
settings.rtp.audio_codec_list = [item.text() for item in items if item.checkState()==Qt.Checked]
settings.rtp.audio_codec_list = [item.text() for item in items if item.checkState() == Qt.Checked]
settings.save()
# Answering machine signal handlers
......@@ -1371,7 +1374,7 @@ class PreferencesWindow(base_class, ui_class):
settings.save()
def _SH_MaxRecordingValueChanged(self, value):
self.max_recording_minutes_label.setText(u'minute' if value==1 else u'minutes')
self.max_recording_minutes_label.setText(u'minute' if value == 1 else u'minutes')
settings = SIPSimpleSettings()
if settings.answering_machine.max_recording != value:
settings.answering_machine.max_recording = value
......@@ -1401,14 +1404,14 @@ class PreferencesWindow(base_class, ui_class):
def _SH_VideoCodecsListItemChanged(self, item):
settings = SIPSimpleSettings()
item_iterator = (self.video_codecs_list.item(row) for row in xrange(self.video_codecs_list.count()))
settings.rtp.video_codec_list = [item.text() for item in item_iterator if item.checkState()==Qt.Checked]
settings.rtp.video_codec_list = [item.text() for item in item_iterator if item.checkState() == Qt.Checked]
settings.save()
def _SH_VideoCodecsListModelRowsMoved(self, source_parent, source_start, source_end, dest_parent, dest_row):
settings = SIPSimpleSettings()
items = [self.video_codecs_list.item(row) for row in xrange(self.video_codecs_list.count())]
settings.rtp.video_codec_order = [item.text() for item in items]
settings.rtp.video_codec_list = [item.text() for item in items if item.checkState()==Qt.Checked]
settings.rtp.video_codec_list = [item.text() for item in items if item.checkState() == Qt.Checked]
settings.save()
def _SH_VideoCodecBitrateButtonActivated(self, index):
......
__all__ = ['PresenceManager', 'PendingWatcherDialog']
import base64
import hashlib
import re
......@@ -36,6 +34,9 @@ from blink.resources import IconManager, Resources
from blink.util import run_in_gui_thread
__all__ = ['PresenceManager', 'PendingWatcherDialog']
epoch = datetime.fromtimestamp(0, tzutc())
......@@ -50,7 +51,7 @@ class BlinkPresenceState(object):
state = blink_settings.presence.current_state.state
note = blink_settings.presence.current_state.note
state = 'offline' if state=='Invisible' else state.lower()
state = 'offline' if state == 'Invisible' else state.lower()
if self.account is BonjourAccount():
return BonjourPresenceState(state, note)
......@@ -182,10 +183,10 @@ class PresencePublicationHandler(object):
account.presence_state = BlinkPresenceState(account).online_state
else:
account = notification.sender
if set(['xcap.enabled', 'xcap.xcap_root']).intersection(notification.data.modified):
if {'xcap.enabled', 'xcap.xcap_root'}.intersection(notification.data.modified):
account.xcap.icon = None
account.save()
elif set(['presence.enabled', 'display_name', 'xcap.icon']).intersection(notification.data.modified) and account.presence.enabled:
elif {'presence.enabled', 'display_name', 'xcap.icon'}.intersection(notification.data.modified) and account.presence.enabled:
account.presence_state = BlinkPresenceState(account).online_state
def _NH_SIPAccountWillActivate(self, notification):
......@@ -219,11 +220,11 @@ class PresencePublicationHandler(object):
blink_settings.presence.current_state = new_state
if new_state.note:
try:
next(state for state in blink_settings.presence.state_history if state==new_state)
next(state for state in blink_settings.presence.state_history if state == new_state)
except StopIteration:
blink_settings.presence.state_history = [new_state] + blink_settings.presence.state_history
else:
blink_settings.presence.state_history = [new_state] + [state for state in blink_settings.presence.state_history if state!=new_state]
blink_settings.presence.state_history = [new_state] + [state for state in blink_settings.presence.state_history if state != new_state]
blink_settings.save()
def _NH_SIPAccountDidDiscoverXCAPSupport(self, notification):
......@@ -334,11 +335,11 @@ class PresenceSubscriptionHandler(object):
def service_sort_key(service):
timestamp = service.timestamp.value if service.timestamp else epoch
if service.status.extended is not None:
return (100, timestamp)
return 100, timestamp
elif service.status.basic == 'open':
return (10, timestamp)
return 10, timestamp
else:
return (0, timestamp)
return 0, timestamp
current_pidf_map = {}
contact_pidf_map = {}
......@@ -365,7 +366,7 @@ class PresenceSubscriptionHandler(object):
if service.status.extended:
state = unicode(service.status.extended)
else:
state = 'available' if service.status.basic=='open' else 'offline'
state = 'available' if service.status.basic == 'open' else 'offline'
note = unicode(next(iter(service.notes))) if service.notes else None
icon_url = unicode(service.icon) if service.icon else None
......@@ -406,7 +407,7 @@ class PresenceSubscriptionHandler(object):
self._winfo_map.pop(old_id, None)
self._process_presence_data()
return
if set(['enabled', 'presence.enabled']).intersection(notification.data.modified):
if {'enabled', 'presence.enabled'}.intersection(notification.data.modified):
if not account.enabled or not account.presence.enabled:
self._pidf_map.pop(account.id, None)
self._winfo_map.pop(account.id, None)
......
"""Provide access to Blink's resources"""
__all__ = ['ApplicationData', 'Resources', 'IconManager']
import __main__
import imghdr
import os
......@@ -21,10 +19,14 @@ from sipsimple.configuration.datatypes import Path
from blink.util import run_in_gui_thread
__all__ = ['ApplicationData', 'Resources', 'IconManager']
class DirectoryContextManager(unicode):
def __enter__(self):
self.directory = os.getcwdu()
os.chdir(self)
def __exit__(self, type, value, traceback):
os.chdir(self.directory)
......
__all__ = ['ScreensharingWindow', 'VNCViewer', 'VNCClient', 'RFBSettings', 'ServerDefault', 'TrueColor', 'HighColor', 'LowColor']
from blink.screensharing.vncclient import VNCClient, RFBSettings, ServerDefault, TrueColor, HighColor, LowColor
from blink.screensharing.vncviewer import ScreensharingWindow, VNCViewer
__all__ = ['ScreensharingWindow', 'VNCViewer', 'VNCClient', 'RFBSettings', 'ServerDefault', 'TrueColor', 'HighColor', 'LowColor']
__all__ = ['RFBClient', 'RFBClientError']
from sip import voidptr
from PyQt4.QtCore import QThread
from PyQt4.QtGui import QImage
......@@ -12,6 +10,9 @@ from libc.stdlib cimport calloc, malloc, free
from libc.string cimport memcpy, strlen
__all__ = ['RFBClient', 'RFBClientError']
# external declarations
#
......
__all__ = ['VNCClient', 'RFBSettings', 'ServerDefault', 'TrueColor', 'HighColor', 'LowColor']
from PyQt4.QtCore import QObject, QSize, QSocketNotifier, QThread, pyqtSignal
from PyQt4.QtGui import QApplication
......@@ -10,7 +7,10 @@ from application.python import Null
from application.python.descriptor import WriteOnceAttribute
from blink.event import EventBase
from blink.screensharing._rfb import RFBClient, RFBClientError
from blink.screensharing._rfb import RFBClient, RFBClientError
__all__ = ['VNCClient', 'RFBSettings', 'ServerDefault', 'TrueColor', 'HighColor', 'LowColor']
class RFBSettings(object):
......@@ -101,6 +101,7 @@ class VNCClient(QObject):
def _get_settings(self):
return self.__dict__['settings']
def _set_settings(self, settings):
old_settings = self.__dict__.get('settings', None)
if settings == old_settings:
......@@ -108,6 +109,7 @@ class VNCClient(QObject):
self.__dict__['settings'] = settings
if self.thread.isRunning():
QApplication.postEvent(self, RFBConfigureClientEvent())
settings = property(_get_settings, _set_settings)
del _get_settings, _set_settings
......
from __future__ import division
__all__ = ['ScreensharingWindow', 'VNCViewer']
import os
import platform
......@@ -28,6 +24,9 @@ from blink.resources import Resources
from blink.screensharing.vncclient import ServerDefault, TrueColor, HighColor, LowColor
__all__ = ['ScreensharingWindow', 'VNCViewer']
class ButtonMaskMapper(dict):
class qt:
NoButton = Qt.NoButton
......@@ -42,12 +41,12 @@ class ButtonMaskMapper(dict):
RightButton = 0b00000100
WheelUp = 0b00001000
WheelDown = 0b00010000
WheelLeft = 0b00100000 # not sure about the horizontal wheel button mappings as button 6&7 -Dan
WheelRight = 0b01000000 # it seems different on windows (i.e. buttons 6&7 do not translate to horizontal wheel) -Dan
WheelLeft = 0b00100000 # not sure about the horizontal wheel button mappings as button 6&7 -Dan
WheelRight = 0b01000000 # it seems different on windows (i.e. buttons 6&7 do not translate to horizontal wheel) -Dan
def __init__(self):
mapping = {self.qt.NoButton: self.vnc.NoButton, self.qt.LeftButton: self.vnc.LeftButton, self.qt.MidButton: self.vnc.MidButton, self.qt.RightButton: self.vnc.RightButton}
super(ButtonMaskMapper, self).__init__({int(b1|b2|b3): mapping[b1]|mapping[b2]|mapping[b3] for b1 in mapping for b2 in mapping for b3 in mapping})
super(ButtonMaskMapper, self).__init__({int(b1 | b2 | b3): mapping[b1] | mapping[b2] | mapping[b3] for b1 in mapping for b2 in mapping for b3 in mapping})
self._button_mask = int(self.qt.LeftButton|self.qt.MidButton|self.qt.RightButton)
def __getitem__(self, key):
......@@ -61,63 +60,63 @@ class VNCNativeKeyMap(object):
class VNCWindowsKeyMap(object):
__keymap__ = {
0x10: 0xffe1, # VK_SHIFT : XK_Shift_L
0x11: 0xffe3, # VK_CONTROL : XK_Control_L
0x12: 0xffe9, # VK_MENU : XK_Alt_L
0x5b: 0xffe7, # VK_LWIN : XK_Meta_L
0x1b: 0xff1b, # VK_ESCAPE : XK_Escape
0x09: 0xff09, # VK_TAB : XK_Tab
0x08: 0xff08, # VK_BACK : XK_BackSpace
0x0d: 0xff0d, # VK_RETURN : XK_Return
0x2d: 0xff63, # VK_INSERT : XK_Insert
0x2e: 0xffff, # VK_DELETE : XK_Delete
0x13: 0xff13, # VK_PAUSE : XK_Pause
0x2c: 0xff61, # VK_SNAPSHOT : XK_Print
0x24: 0xff50, # VK_HOME : XK_Home
0x23: 0xff57, # VK_END : XK_End
0x25: 0xff51, # VK_LEFT : XK_Left
0x26: 0xff52, # VK_UP : XK_Up
0x27: 0xff53, # VK_RIGHT : XK_Right
0x28: 0xff54, # VK_DOWN : XK_Down
0x21: 0xff55, # VK_PRIOR : XK_Prior
0x22: 0xff56, # VK_NEXT : XK_Next
0x14: 0xffe5, # VK_CAPITAL : XK_Caps_Lock
0x90: 0xff7f, # VK_NUMLOCK : XK_Num_Lock
0x91: 0xff14, # VK_SCROLL : XK_Scroll_Lock
0x5d: 0xff67, # VK_APPS : XK_Menu
0x2f: 0xff6a, # VK_HELP : XK_Help
0x1f: 0xff7e, # VK_MODECHANGE : XK_Mode_switch
0x70: 0xffbe, # VK_F1 : XK_F1
0x71: 0xffbf, # VK_F2 : XK_F2
0x72: 0xffc0, # VK_F3 : XK_F3
0x73: 0xffc1, # VK_F4 : XK_F4
0x74: 0xffc2, # VK_F5 : XK_F5
0x75: 0xffc3, # VK_F6 : XK_F6
0x76: 0xffc4, # VK_F7 : XK_F7
0x77: 0xffc5, # VK_F8 : XK_F8
0x78: 0xffc6, # VK_F9 : XK_F9
0x79: 0xffc7, # VK_F10 : XK_F10
0x7a: 0xffc8, # VK_F11 : XK_F11
0x7b: 0xffc9, # VK_F12 : XK_F12
0x7c: 0xffca, # VK_F13 : XK_F13
0x7d: 0xffcb, # VK_F14 : XK_F14
0x7e: 0xffcc, # VK_F15 : XK_F15
0x7f: 0xffcd, # VK_F16 : XK_F16
0x80: 0xffce, # VK_F17 : XK_F17
0x81: 0xffcf, # VK_F18 : XK_F18
0x82: 0xffd0, # VK_F19 : XK_F19
0x83: 0xffd1, # VK_F20 : XK_F20
0x84: 0xffd2, # VK_F21 : XK_F21
0x85: 0xffd3, # VK_F22 : XK_F22
0x86: 0xffd4, # VK_F23 : XK_F23
0x87: 0xffd5, # VK_F24 : XK_F24
0x93: 0xff2c, # VK_OEM_FJ_MASSHOU : XK_Massyo
0x94: 0xff2b, # VK_OEM_FJ_TOUROKU : XK_Touroku
0x10: 0xffe1, # VK_SHIFT : XK_Shift_L
0x11: 0xffe3, # VK_CONTROL : XK_Control_L
0x12: 0xffe9, # VK_MENU : XK_Alt_L
0x5b: 0xffe7, # VK_LWIN : XK_Meta_L
0x1b: 0xff1b, # VK_ESCAPE : XK_Escape
0x09: 0xff09, # VK_TAB : XK_Tab
0x08: 0xff08, # VK_BACK : XK_BackSpace
0x0d: 0xff0d, # VK_RETURN : XK_Return
0x2d: 0xff63, # VK_INSERT : XK_Insert
0x2e: 0xffff, # VK_DELETE : XK_Delete
0x13: 0xff13, # VK_PAUSE : XK_Pause
0x2c: 0xff61, # VK_SNAPSHOT : XK_Print
0x24: 0xff50, # VK_HOME : XK_Home
0x23: 0xff57, # VK_END : XK_End
0x25: 0xff51, # VK_LEFT : XK_Left
0x26: 0xff52, # VK_UP : XK_Up
0x27: 0xff53, # VK_RIGHT : XK_Right
0x28: 0xff54, # VK_DOWN : XK_Down
0x21: 0xff55, # VK_PRIOR : XK_Prior
0x22: 0xff56, # VK_NEXT : XK_Next
0x14: 0xffe5, # VK_CAPITAL : XK_Caps_Lock
0x90: 0xff7f, # VK_NUMLOCK : XK_Num_Lock
0x91: 0xff14, # VK_SCROLL : XK_Scroll_Lock
0x5d: 0xff67, # VK_APPS : XK_Menu
0x2f: 0xff6a, # VK_HELP : XK_Help
0x1f: 0xff7e, # VK_MODECHANGE : XK_Mode_switch
0x70: 0xffbe, # VK_F1 : XK_F1
0x71: 0xffbf, # VK_F2 : XK_F2
0x72: 0xffc0, # VK_F3 : XK_F3
0x73: 0xffc1, # VK_F4 : XK_F4
0x74: 0xffc2, # VK_F5 : XK_F5
0x75: 0xffc3, # VK_F6 : XK_F6
0x76: 0xffc4, # VK_F7 : XK_F7
0x77: 0xffc5, # VK_F8 : XK_F8
0x78: 0xffc6, # VK_F9 : XK_F9
0x79: 0xffc7, # VK_F10 : XK_F10
0x7a: 0xffc8, # VK_F11 : XK_F11
0x7b: 0xffc9, # VK_F12 : XK_F12
0x7c: 0xffca, # VK_F13 : XK_F13
0x7d: 0xffcb, # VK_F14 : XK_F14
0x7e: 0xffcc, # VK_F15 : XK_F15
0x7f: 0xffcd, # VK_F16 : XK_F16
0x80: 0xffce, # VK_F17 : XK_F17
0x81: 0xffcf, # VK_F18 : XK_F18
0x82: 0xffd0, # VK_F19 : XK_F19
0x83: 0xffd1, # VK_F20 : XK_F20
0x84: 0xffd2, # VK_F21 : XK_F21
0x85: 0xffd3, # VK_F22 : XK_F22
0x86: 0xffd4, # VK_F23 : XK_F23
0x87: 0xffd5, # VK_F24 : XK_F24
0x93: 0xff2c, # VK_OEM_FJ_MASSHOU : XK_Massyo
0x94: 0xff2b, # VK_OEM_FJ_TOUROKU : XK_Touroku
}
__capsmodifier__ = 0x100 # the native CapsLock modifier
__capsmodifier__ = 0x100 # the native CapsLock modifier
def __getitem__(self, event):
key = event.key()
......@@ -125,7 +124,7 @@ class VNCWindowsKeyMap(object):
if bool(event.modifiers() & Qt.ShiftModifier) ^ bool(event.nativeModifiers() & self.__capsmodifier__):
return key
else:
return key + 0x20 # make it lowercase
return key + 0x20 # make it lowercase
else:
native_key = event.nativeVirtualKey()
return self.__keymap__.get(native_key, native_key)
......@@ -172,14 +171,14 @@ class VNCViewer(QWidget):
super(VNCViewer, self).__init__(parent)
self.setMouseTracking(True)
self.setFocusPolicy(Qt.WheelFocus)
#self.setCursor(Qt.BlankCursor)
# self.setCursor(Qt.BlankCursor)
self.client = vncclient or parent.client
self.client.started.connect(self._SH_ClientStarted)
self.client.finished.connect(self._SH_ClientFinished)
self.client.imageSizeChanged.connect(self._SH_ImageSizeChanged)
self.client.imageChanged.connect(self._SH_ImageUpdated)
self.client.passwordRequested.connect(self._SH_PasswordRequested, Qt.BlockingQueuedConnection)
self.colors_8bit = [qRgb((i&0x07) << 5, (i&0x38) << 2, i&0xc0) for i in range(256)]
self.colors_8bit = [qRgb((i & 0x07) << 5, (i & 0x38) << 2, i & 0xc0) for i in range(256)]
self.scale = False
self.view_only = False
self._client_active = False
......@@ -188,6 +187,7 @@ class VNCViewer(QWidget):
def _get_scale(self):
return self.__dict__['scale']
def _set_scale(self, scale):
self.__dict__['scale'] = scale
if not scale:
......@@ -197,11 +197,13 @@ class VNCViewer(QWidget):
elif self.parent() is not None:
self.resize(self.parent().size())
self.update()
scale = property(_get_scale, _set_scale)
del _get_scale, _set_scale
def _get_view_only(self):
return self.__dict__['view_only']
def _set_view_only(self, view_only):
old_value = self.__dict__.get('view_only', None)
new_value = self.__dict__['view_only'] = view_only
......@@ -211,6 +213,7 @@ class VNCViewer(QWidget):
self.grabKeyboard()
else:
self.releaseKeyboard()
view_only = property(_get_view_only, _set_view_only)
del _get_view_only, _set_view_only
......@@ -307,25 +310,25 @@ class VNCViewer(QWidget):
button_mask = self.button_mask_map[event.buttons()]
if event.type() == QEvent.Wheel:
if event.delta() > 0:
wheel_button_mask = self.button_mask_map.vnc.WheelUp if event.orientation()==Qt.Vertical else self.button_mask_map.vnc.WheelLeft
wheel_button_mask = self.button_mask_map.vnc.WheelUp if event.orientation() == Qt.Vertical else self.button_mask_map.vnc.WheelLeft
else:
wheel_button_mask = self.button_mask_map.vnc.WheelDown if event.orientation()==Qt.Vertical else self.button_mask_map.vnc.WheelRight
wheel_button_mask = self.button_mask_map.vnc.WheelDown if event.orientation() == Qt.Vertical else self.button_mask_map.vnc.WheelRight
self.client.mouse_event(x, y, button_mask | wheel_button_mask)
self.client.mouse_event(x, y, button_mask)
def keyEvent(self, event):
vnc_key = VNCKey.from_event(event)
key_down = event.type()==QEvent.KeyPress
key_down = event.type() == QEvent.KeyPress
if vnc_key:
expected_modifiers = self._active_keys.modifiers
keyboard_modifiers = event.modifiers()
if vnc_key.qt_key in VNCKey.__modifiermap__ and vnc_key.qt_key != Qt.Key_AltGr and vnc_key != 0xffe7:
keyboard_modifiers ^= vnc_key.qt_modifier # we want the modifier mask as it was before this modifier key was pressed/released
keyboard_modifiers ^= vnc_key.qt_modifier # we want the modifier mask as it was before this modifier key was pressed/released
if (keyboard_modifiers ^ expected_modifiers) & expected_modifiers:
self._reset_keyboard(preserve_modifiers=keyboard_modifiers)
if key_down:
if vnc_key in self._active_keys:
self.client.key_event(vnc_key, False) # key was already pressed and now we got another press event. emulate the missing key release event.
self.client.key_event(vnc_key, False) # key was already pressed and now we got another press event. emulate the missing key release event.
else:
self._active_keys.add(vnc_key)
self.client.key_event(vnc_key, True)
......@@ -376,6 +379,7 @@ class VNCViewer(QWidget):
ui_class, base_class = uic.loadUiType(Resources.get('screensharing_dialog.ui'))
class ScreensharingDialog(base_class, ui_class):
def __init__(self, parent=None):
super(ScreensharingDialog, self).__init__(parent)
......@@ -399,7 +403,7 @@ class ScreensharingDialog(base_class, ui_class):
self.setMinimumHeight(190)
self.resize(self.minimumSize())
result = self.exec_()
return (self.username_editor.text(), self.password_editor.text()) if result==self.Accepted else (None, None)
return (self.username_editor.text(), self.password_editor.text()) if result == self.Accepted else (None, None)
def get_password(self):
self.message_label.setText(u'Screen sharing requires a password')
......@@ -411,13 +415,14 @@ class ScreensharingDialog(base_class, ui_class):
self.setMinimumHeight(165)
self.resize(self.minimumSize())
result = self.exec_()
return self.password_editor.text() if result==self.Accepted else None
return self.password_editor.text() if result == self.Accepted else None
del ui_class, base_class
ui_class, base_class = uic.loadUiType(Resources.get('screensharing_toolbox.ui'))
class ScreensharingToolbox(base_class, ui_class):
exposedPixels = 3
......@@ -429,7 +434,7 @@ class ScreensharingToolbox(base_class, ui_class):
self.animation = QPropertyAnimation(self, 'pos')
self.animation.setDuration(250)
self.animation.setDirection(QPropertyAnimation.Forward)
self.animation.setEasingCurve(QEasingCurve.Linear) # or OutCirc with 300ms
self.animation.setEasingCurve(QEasingCurve.Linear) # or OutCirc with 300ms
self.retract_timer = QTimer(self)
self.retract_timer.setInterval(3000)
self.retract_timer.setSingleShot(True)
......@@ -503,7 +508,7 @@ class ScreensharingToolbox(base_class, ui_class):
super(ScreensharingToolbox, self).leaveEvent(event)
self.retract_timer.start()
def paintEvent(self, event): # make the widget style aware
def paintEvent(self, event): # make the widget style aware
option = QStyleOption()
option.init(self)
painter = QStylePainter(self)
......@@ -530,6 +535,7 @@ del ui_class, base_class
ui_class, base_class = uic.loadUiType(Resources.get('screensharing_window.ui'))
class ScreensharingWindow(base_class, ui_class):
def __init__(self, vncclient, parent=None):
super(ScreensharingWindow, self).__init__(parent)
......@@ -553,7 +559,7 @@ class ScreensharingWindow(base_class, ui_class):
def setupUi(self):
super(ScreensharingWindow, self).setupUi(self)
self.scroll_area.viewport().setObjectName('vnc_viewport')
self.scroll_area.viewport().setStyleSheet('QWidget#vnc_viewport { background-color: #101010; }') # #101010, #004488, #004480, #0044aa, #0055ff, #0066cc, #3366cc, #0066d5
self.scroll_area.viewport().setStyleSheet('QWidget#vnc_viewport { background-color: #101010; }') # #101010, #004488, #004480, #0044aa, #0055ff, #0066cc, #3366cc, #0066d5
self.vncviewer = VNCViewer(self.vncclient)
self.vncviewer.setGeometry(self.scroll_area.viewport().geometry())
......
__all__ = ['ClientConference', 'ConferenceDialog', 'AudioSessionModel', 'AudioSessionListView', 'ChatSessionModel', 'ChatSessionListView', 'SessionManager']
import bisect
import cPickle as pickle
import contextlib
......@@ -55,7 +53,11 @@ from blink.widgets.util import ContextMenuActions, QtDynamicProperty
from blink.widgets.zrtp import ZRTPWidget
class Container(object): pass
__all__ = ['ClientConference', 'ConferenceDialog', 'AudioSessionModel', 'AudioSessionListView', 'ChatSessionModel', 'ChatSessionListView', 'SessionManager']
class Container(object):
pass
class RTPStreamInfo(object):
......@@ -119,8 +121,8 @@ class RTPStreamInfo(object):
self._total_packets_discarded += packets_discarded
self.latency.append(statistics['rtt']['last'] / 1000 / 2)
self.jitter.append(statistics['rx']['jitter']['last'] / 1000)
self.incoming_traffic.append(float(statistics['rx']['bytes'] - self.bytes_received)) # bytes/second
self.outgoing_traffic.append(float(statistics['tx']['bytes'] - self.bytes_sent)) # bytes/second
self.incoming_traffic.append(float(statistics['rx']['bytes'] - self.bytes_received)) # bytes/second
self.outgoing_traffic.append(float(statistics['tx']['bytes'] - self.bytes_sent)) # bytes/second
self.bytes_sent = statistics['tx']['bytes']
self.bytes_received = statistics['rx']['bytes']
self.packet_loss.append(sum(self._average_loss_queue) / self.average_interval)
......@@ -251,7 +253,7 @@ class SessionInfo(object):
if sip_session is not None:
self.transport = sip_session.transport
self.local_address = session.account.contact[self.transport].host
self.remote_address = sip_session.peer_address # consider reading from sip_session.route if peer_address is None (route can also be None) -Dan
self.remote_address = sip_session.peer_address # consider reading from sip_session.route if peer_address is None (route can also be None) -Dan
self.remote_user_agent = sip_session.remote_user_agent
self.streams.update(session.streams)
......@@ -379,10 +381,10 @@ class StreamListDescriptor(object):
def __init__(self):
self.values = defaultweakobjectmap(StreamMap)
def __get__(self, obj, objtype):
if obj is None:
def __get__(self, instance, owner):
if instance is None:
return self
return StreamContainer(obj, self.values[obj])
return StreamContainer(instance, self.values[instance])
def __set__(self, obj, value):
raise AttributeError("Attribute cannot be set")
......@@ -421,8 +423,8 @@ class SessionItemsDescriptor(object):
def __init__(self):
self.values = defaultweakobjectmap(self.SessionItems)
def __get__(self, obj, objtype):
return self.values[obj] if obj is not None else None
def __get__(self, instance, owner):
return self.values[instance] if instance is not None else None
def __set__(self, obj, value):
raise AttributeError("Attribute cannot be set")
......@@ -636,7 +638,7 @@ class BlinkSession(BlinkSessionBase):
notification_center.post_notification('BlinkSessionWillReinitialize', sender=self)
self._initialize(reinitialize=True)
else:
self._delete_when_done = len(streams)==1 and streams[0].type=='audio'
self._delete_when_done = len(streams) == 1 and streams[0].type == 'audio'
self.direction = 'incoming'
self.sip_session = sip_session
self.account = sip_session.account
......@@ -662,7 +664,7 @@ class BlinkSession(BlinkSessionBase):
notification_center.post_notification('BlinkSessionWillReinitialize', sender=self)
self._initialize(reinitialize=True)
else:
self._delete_when_done = len(stream_descriptions)==1 and stream_descriptions[0].type=='audio'
self._delete_when_done = len(stream_descriptions) == 1 and stream_descriptions[0].type == 'audio'
self.direction = 'outgoing'
self.account = account
self.contact = contact
......@@ -998,7 +1000,7 @@ class BlinkSession(BlinkSessionBase):
self._terminate(reason=reason, error=True)
def _NH_SIPSessionDidEnd(self, notification):
self._terminate('Call ended' if notification.data.originator=='local' else 'Call ended by remote')
self._terminate('Call ended' if notification.data.originator == 'local' else 'Call ended by remote')
def _NH_SIPSessionDidChangeHoldState(self, notification):
if notification.data.originator == 'remote':
......@@ -1085,7 +1087,7 @@ class BlinkSession(BlinkSessionBase):
stream = notification.sender
audio_stream = self.streams.get('audio')
if stream.type == 'chat' and stream.session.remote_focus and 'com.ag-projects.zrtp-sas' in stream.chatroom_capabilities and audio_stream is not None:
secure_chat = stream.transport == 'tls' and all(len(path)==1 for path in (stream.msrp.full_local_path, stream.msrp.full_remote_path)) # tls & direct connection
secure_chat = stream.transport == 'tls' and all(len(path) == 1 for path in (stream.msrp.full_local_path, stream.msrp.full_remote_path)) # tls & direct connection
if audio_stream.encryption.type == 'ZRTP' and audio_stream.encryption.zrtp.sas is not None and not audio_stream.encryption.zrtp.verified and secure_chat:
stream.send_message(audio_stream.encryption.zrtp.sas, 'application/blink-zrtp-sas')
......@@ -1372,8 +1374,8 @@ class ConferenceParticipant(object):
self.active_media.add('chat')
else:
self.active_media.add(media.media_type.value)
audio_endpoints = [endpt for endpt in data if any(media.media_type=='audio' for media in endpt)]
self.on_hold = all(endpt.status=='on-hold' for endpt in audio_endpoints) if audio_endpoints else False
audio_endpoints = [endpt for endpt in data if any(media.media_type == 'audio' for media in endpt)]
self.on_hold = all(endpt.status == 'on-hold' for endpt in audio_endpoints) if audio_endpoints else False
for attr, value in old_values.iteritems():
if value != getattr(self, attr):
NotificationCenter().post_notification('ConferenceParticipantDidChange', sender=self)
......@@ -1742,7 +1744,7 @@ class AudioSessionWidget(base_class, ui_class):
if self.drop_indicator:
painter.setPen(QPen(QBrush(QColor('#dc3169')), 2.0))
elif self.selected:
painter.setPen(QPen(QBrush(QColor('#3075c0')), 2.0)) # or #2070c0 (next best look) or gray: #606060
painter.setPen(QPen(QBrush(QColor('#3075c0')), 2.0)) # or #2070c0 (next best look) or gray: #606060
if self.position_in_conference is Top:
painter.drawRoundedRect(rect.adjusted(2, 2, -2, 5), 3, 3)
......@@ -1758,7 +1760,7 @@ class AudioSessionWidget(base_class, ui_class):
painter.drawRoundedRect(rect.adjusted(1, 1, -1, -1), 3, 3)
elif self.position_in_conference is not None:
painter.setBrush(Qt.NoBrush)
painter.setPen(QPen(QBrush(QColor('#309030')), 2.0)) # or 237523, #2b8f2b
painter.setPen(QPen(QBrush(QColor('#309030')), 2.0)) # or 237523, #2b8f2b
if self.position_in_conference is Top:
painter.drawRoundedRect(rect.adjusted(2, 2, -2, 5), 3, 3)
elif self.position_in_conference is Middle:
......@@ -2008,7 +2010,7 @@ class AudioSessionItem(object):
if self.audio_stream in self.blink_session.streams.proposed and self.blink_session.state == 'connected/sent_proposal':
self.blink_session.sip_session.cancel_proposal()
# review this -Dan
#elif len(self.blink_session.streams) > 1 and self.blink_session.state == 'connected':
# elif len(self.blink_session.streams) > 1 and self.blink_session.state == 'connected':
# self.blink_session.remove_stream(self.audio_stream)
elif 'chat' in self.blink_session.streams and self.blink_session.state == 'connected':
self.blink_session.remove_streams([stream for stream in self.blink_session.streams if stream.type != 'chat'])
......@@ -2078,7 +2080,7 @@ class AudioSessionItem(object):
if stage == 'dns_lookup':
self.status = Status('Looking up destination...')
elif stage == 'connecting':
self.tls = self.blink_session.transport=='tls'
self.tls = self.blink_session.transport == 'tls'
self.status = Status('Connecting...')
elif stage == 'ringing':
self.status = Status('Ringing...')
......@@ -2117,7 +2119,7 @@ class AudioSessionItem(object):
def _NH_BlinkSessionDidConnect(self, notification):
session = notification.sender
self.tls = session.transport=='tls'
self.tls = session.transport == 'tls'
if 'audio' in session.streams:
self.widget.mute_button.setEnabled(True)
self.widget.hold_button.setEnabled(True)
......@@ -2140,7 +2142,7 @@ class AudioSessionItem(object):
def _NH_BlinkSessionDidNotAddStream(self, notification):
if notification.data.stream.type == 'audio':
self.status = Status('Audio refused', color='#900000') # where can we get the reason from? (rejected, cancelled, failed, ...) -Dan
self.status = Status('Audio refused', color='#900000') # where can we get the reason from? (rejected, cancelled, failed, ...) -Dan
self._cleanup()
def _NH_BlinkSessionWillRemoveStream(self, notification):
......@@ -2334,7 +2336,7 @@ class AudioSessionModel(QAbstractListModel):
self.sessions.insert(insert_point, source)
self.endMoveRows()
source.client_conference = target.client_conference
session_list.scrollTo(self.index(self.sessions.index(source)), session_list.EnsureVisible) # is this even needed? -Dan
session_list.scrollTo(self.index(self.sessions.index(source)), session_list.EnsureVisible) # is this even needed? -Dan
else:
target_row = self.sessions.index(target)
if self.beginMoveRows(QModelIndex(), target_row, target_row, QModelIndex(), 0):
......@@ -2347,7 +2349,7 @@ class AudioSessionModel(QAbstractListModel):
self.sessions.insert(1, source)
self.endMoveRows()
conference = ClientConference()
target.client_conference = conference # must add them to the conference in the same order they are in the list (target is first, source is last)
target.client_conference = conference # must add them to the conference in the same order they are in the list (target is first, source is last)
source.client_conference = conference
session_list.scrollToTop()
for session in source.client_conference.sessions:
......@@ -2364,12 +2366,12 @@ class AudioSessionModel(QAbstractListModel):
if len(dragged.client_conference.sessions) == 2:
dragged.client_conference = None
sibling.client_conference = None
## eventually only move past the last conference to minimize movement. see how this feels during usage. (or sort them alphabetically with conferences at the top) -Dan
#for position, session in enumerate(self.sessions):
# # maybe only move past the last conference to minimize movement. see how this feels during usage. (or sort them alphabetically with conferences at the top) -Dan
# for position, session in enumerate(self.sessions):
# if session not in (dragged, sibling) and session.client_conference is None:
# move_point = position
# break
#else:
# else:
# move_point = len(self.sessions)
move_point = len(self.sessions)
dragged_row = self.sessions.index(dragged)
......@@ -2462,7 +2464,7 @@ class AudioSessionModel(QAbstractListModel):
self.endInsertRows()
session_list.openPersistentEditor(self.index(position))
session.client_conference = sibling.client_conference
session_list.scrollTo(self.index(position), session_list.EnsureVisible) # or PositionAtBottom (is this even needed? -Dan)
session_list.scrollTo(self.index(position), session_list.EnsureVisible) # or PositionAtBottom (is this even needed? -Dan)
else:
sibling_row = self.sessions.index(sibling)
if self.beginMoveRows(QModelIndex(), sibling_row, sibling_row, QModelIndex(), 0):
......@@ -2474,7 +2476,7 @@ class AudioSessionModel(QAbstractListModel):
self.endInsertRows()
session_list.openPersistentEditor(self.index(1))
conference = ClientConference()
sibling.client_conference = conference # must add them to the conference in the same order they are in the list (sibling first, new session last)
sibling.client_conference = conference # must add them to the conference in the same order they are in the list (sibling first, new session last)
session.client_conference = conference
if sibling.active:
conference.unhold()
......@@ -2747,7 +2749,7 @@ class AudioSessionListView(QListView):
else:
self.setCurrentIndex(self.model().index(-1))
self.context_menu.hide()
#print "-- audio selection changed %s -> %s (ignore=%s)" % ([x.row() for x in deselected.indexes()], [x.row() for x in selected.indexes()], self.ignore_selection_changes)
# print "-- audio selection changed %s -> %s (ignore=%s)" % ([x.row() for x in deselected.indexes()], [x.row() for x in selected.indexes()], self.ignore_selection_changes)
if self.ignore_selection_changes:
return
notification_center = NotificationCenter()
......@@ -2911,14 +2913,14 @@ class AudioSessionListView(QListView):
if notification.data.active_session is None:
selection = selection_model.selection()
# check the code in this if branch if it's needed -Dan
#selected_blink_session = selection[0].topLeft().data(Qt.UserRole).blink_session if selection else None
#if notification.data.previous_active_session is selected_blink_session:
# selected_blink_session = selection[0].topLeft().data(Qt.UserRole).blink_session if selection else None
# if notification.data.previous_active_session is selected_blink_session:
# print "-- audio session list updating selection to None None"
# selection_model.clearSelection()
else:
model = self.model()
position = model.sessions.index(notification.data.active_session.items.audio)
#print "-- audio session list updating selection to", position, notification.data.active_session
# print "-- audio session list updating selection to", position, notification.data.active_session
selection_model.select(model.index(position), selection_model.ClearAndSelect)
self.ignore_selection_changes = False
......@@ -2983,9 +2985,9 @@ class ChatSessionWidget(base_class, ui_class):
self.palettes.standard = self.palette()
self.palettes.alternate = self.palette()
self.palettes.selected = self.palette()
self.palettes.standard.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Base)) # We modify the palettes because only the Oxygen theme honors the BackgroundRole if set
self.palettes.alternate.setColor(QPalette.Window, self.palettes.standard.color(QPalette.AlternateBase)) # AlternateBase set to #f0f4ff or #e0e9ff by designer
self.palettes.selected.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Highlight)) # #0066cc #0066d5 #0066dd #0066aa (0, 102, 170) '#256182' (37, 97, 130), #2960a8 (41, 96, 168), '#2d6bbc' (45, 107, 188), '#245897' (36, 88, 151) #0044aa #0055d4
self.palettes.standard.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Base)) # We modify the palettes because only the Oxygen theme honors the BackgroundRole if set
self.palettes.alternate.setColor(QPalette.Window, self.palettes.standard.color(QPalette.AlternateBase)) # AlternateBase set to #f0f4ff or #e0e9ff by designer
self.palettes.selected.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Highlight)) # #0066cc #0066d5 #0066dd #0066aa (0, 102, 170) '#256182' (37, 97, 130), #2960a8 (41, 96, 168), '#2d6bbc' (45, 107, 188), '#245897' (36, 88, 151) #0044aa #0055d4
self.setBackgroundRole(QPalette.Window)
self.display_mode = self.StandardDisplayMode
self.hold_icon.installEventFilter(self)
......@@ -2996,8 +2998,8 @@ class ChatSessionWidget(base_class, ui_class):
self.screen_sharing_icon.installEventFilter(self)
self.widget_layout.invalidate()
self.widget_layout.activate()
#self.setAttribute(103) # Qt.WA_DontShowOnScreen == 103 and is missing from pyqt, but is present in qt and pyside -Dan
#self.show()
# self.setAttribute(103) # Qt.WA_DontShowOnScreen == 103 and is missing from pyqt, but is present in qt and pyside -Dan
# self.show()
def _get_display_mode(self):
return self.__dict__['display_mode']
......@@ -3227,9 +3229,9 @@ class ChatSessionDelegate(QStyledItemDelegate, ColorHelperMixin):
super(ChatSessionDelegate, self).__init__(parent)
def editorEvent(self, event, model, option, index):
if event.type()==QEvent.MouseButtonRelease and event.button()==Qt.LeftButton and event.modifiers()==Qt.NoModifier:
arrow_rect = option.rect.adjusted(option.rect.width()-14, option.rect.height()/2, 0, 0) # bottom half of the rightmost 14 pixels
cross_rect = option.rect.adjusted(option.rect.width()-14, 0, 0, -option.rect.height()/2) # top half of the rightmost 14 pixels
if event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton and event.modifiers() == Qt.NoModifier:
arrow_rect = option.rect.adjusted(option.rect.width()-14, option.rect.height()/2, 0, 0) # bottom half of the rightmost 14 pixels
cross_rect = option.rect.adjusted(option.rect.width()-14, 0, 0, -option.rect.height()/2) # top half of the rightmost 14 pixels
if arrow_rect.contains(event.pos()):
session_list = self.parent()
session_list.animation.setDirection(QPropertyAnimation.Backward)
......@@ -3277,8 +3279,8 @@ class ChatSessionDelegate(QStyledItemDelegate, ColorHelperMixin):
gradient.setColorAt(1.0, self.color_with_alpha(base_contrast_color, 0.8*255))
contrast_color = QBrush(gradient)
else:
#foreground_color = option.palette.color(QPalette.Normal, QPalette.WindowText)
#background_color = option.palette.color(QPalette.Window)
# foreground_color = option.palette.color(QPalette.Normal, QPalette.WindowText)
# background_color = option.palette.color(QPalette.Window)
foreground_color = widget.palette().color(QPalette.Normal, widget.foregroundRole())
background_color = widget.palette().color(widget.backgroundRole())
contrast_color = self.calc_light_color(background_color)
......@@ -3374,7 +3376,7 @@ class ChatSessionModel(QAbstractListModel):
return None
def supportedDropActions(self):
return Qt.CopyAction# | Qt.MoveAction
return Qt.CopyAction # | Qt.MoveAction
def dropMimeData(self, mime_data, action, row, column, parent_index):
# this is here just to keep the default Qt DnD API happy
......@@ -3465,11 +3467,11 @@ class ChatSessionListView(QListView):
self.setAutoFillBackground(True)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
#self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) # default
# self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) # default
self.setDragEnabled(False) # default
#self.setDropIndicatorShown(True)
# self.setDropIndicatorShown(True)
self.setDragDropMode(QListView.DropOnly)
self.setSelectionMode(QListView.SingleSelection) # default
self.setSelectionMode(QListView.SingleSelection) # default
self.setStyleSheet("""QListView { border: 1px inset palette(dark); border-radius: 3px; }""")
self.animation = QPropertyAnimation(self, 'geometry')
......@@ -3480,7 +3482,7 @@ class ChatSessionListView(QListView):
self.actions = ContextMenuActions()
self.drop_indicator_index = QModelIndex()
self.ignore_selection_changes = False
self.doubleClicked.connect(self._SH_DoubleClicked) # activated is emitted on single click
self.doubleClicked.connect(self._SH_DoubleClicked) # activated is emitted on single click
chat_window.session_panel.installEventFilter(self)
notification_center = NotificationCenter()
......@@ -3514,7 +3516,7 @@ class ChatSessionListView(QListView):
return selection_model.NoUpdate
elif event.type() == QEvent.MouseButtonRelease:
index_rect = self.visualRect(index)
cross_rect = index_rect.adjusted(index_rect.width()-14, 0, 0, -index_rect.height()/2) # the top half of the rightmost 14 pixels
cross_rect = index_rect.adjusted(index_rect.width()-14, 0, 0, -index_rect.height()/2) # the top half of the rightmost 14 pixels
if cross_rect.contains(event.pos()):
return selection_model.NoUpdate
else:
......@@ -3638,14 +3640,14 @@ class ChatSessionListView(QListView):
if notification.data.active_session is None:
selection = selection_model.selection()
# check the code in this if branch if it's needed -Dan (if not also remove previous_active_session maybe)
#selected_blink_session = selection[0].topLeft().data(Qt.UserRole).blink_session if selection else None
#if notification.data.previous_active_session is selected_blink_session:
# selected_blink_session = selection[0].topLeft().data(Qt.UserRole).blink_session if selection else None
# if notification.data.previous_active_session is selected_blink_session:
# print "-- chat session list updating selection to None None"
# selection_model.clearSelection()
else:
model = self.model()
position = model.sessions.index(notification.data.active_session.items.chat)
#print "-- chat session list updating selection to", position, notification.data.active_session
# print "-- chat session list updating selection to", position, notification.data.active_session
selection_model.select(model.index(position), selection_model.ClearAndSelect)
self.ignore_selection_changes = False
......@@ -4096,6 +4098,7 @@ class TransferStateLabel(QLabel, ColorHelperMixin):
def _get_display_mode(self):
return self.__dict__['display_mode']
def _set_display_mode(self, value):
if value not in (self.ProgressDisplayMode, self.InactiveDisplayMode):
raise ValueError("invalid display_mode: %r" % value)
......@@ -4103,34 +4106,41 @@ class TransferStateLabel(QLabel, ColorHelperMixin):
new_value = self.__dict__['display_mode'] = value
if new_value != old_value:
self.update()
display_mode = property(_get_display_mode, _set_display_mode)
del _get_display_mode, _set_display_mode
def _get_show_cancel_button(self):
return self.__dict__['show_cancel_button']
def _set_show_cancel_button(self, value):
old_value = self.__dict__.get('show_cancel_button', False)
new_value = self.__dict__['show_cancel_button'] = bool(value)
if new_value != old_value:
self.update()
show_cancel_button = property(_get_show_cancel_button, _set_show_cancel_button)
del _get_show_cancel_button, _set_show_cancel_button
def _get_show_retry_button(self):
return self.__dict__['show_retry_button']
def _set_show_retry_button(self, value):
old_value = self.__dict__.get('show_retry_button', False)
new_value = self.__dict__['show_retry_button'] = bool(value)
if new_value != old_value:
self.update()
show_retry_button = property(_get_show_retry_button, _set_show_retry_button)
del _get_show_retry_button, _set_show_retry_button
def _get_progress(self):
return self.__dict__['progress']
def _set_progress(self, value):
self.__dict__['progress'] = value
self.update()
progress = property(_get_progress, _set_progress)
del _get_progress, _set_progress
......@@ -4208,6 +4218,7 @@ class TransferStateLabel(QLabel, ColorHelperMixin):
ui_class, base_class = uic.loadUiType(Resources.get('filetransfer_item.ui'))
class FileTransferItemWidget(base_class, ui_class):
class StandardDisplayMode: __metaclass__ = MarkerType
class AlternateDisplayMode: __metaclass__ = MarkerType
......@@ -4221,9 +4232,9 @@ class FileTransferItemWidget(base_class, ui_class):
self.palettes.standard = self.palette()
self.palettes.alternate = self.palette()
self.palettes.selected = self.palette()
self.palettes.standard.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Base)) # We modify the palettes because only the Oxygen theme honors the BackgroundRole if set
self.palettes.alternate.setColor(QPalette.Window, self.palettes.standard.color(QPalette.AlternateBase)) # AlternateBase set to #f0f4ff or #e0e9ff by designer
self.palettes.selected.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Highlight)) # #0066cc #0066d5 #0066dd #0066aa (0, 102, 170) '#256182' (37, 97, 130), #2960a8 (41, 96, 168), '#2d6bbc' (45, 107, 188), '#245897' (36, 88, 151) #0044aa #0055d4
self.palettes.standard.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Base)) # We modify the palettes because only the Oxygen theme honors the BackgroundRole if set
self.palettes.alternate.setColor(QPalette.Window, self.palettes.standard.color(QPalette.AlternateBase)) # AlternateBase set to #f0f4ff or #e0e9ff by designer
self.palettes.selected.setColor(QPalette.Window, self.palettes.standard.color(QPalette.Highlight)) # #0066cc #0066d5 #0066dd #0066aa (0, 102, 170) '#256182' (37, 97, 130), #2960a8 (41, 96, 168), '#2d6bbc' (45, 107, 188), '#245897' (36, 88, 151) #0044aa #0055d4
self.pixmaps = Container()
self.pixmaps.incoming_transfer = QPixmap(Resources.get('icons/folder-downloads.png'))
......@@ -4419,7 +4430,7 @@ class FileTransferDelegate(QStyledItemDelegate):
super(FileTransferDelegate, self).__init__(parent)
def editorEvent(self, event, model, option, index):
if event.type()==QEvent.MouseButtonDblClick and event.button()==Qt.LeftButton and event.modifiers()==Qt.NoModifier:
if event.type() == QEvent.MouseButtonDblClick and event.button() == Qt.LeftButton and event.modifiers() == Qt.NoModifier:
item = index.data(Qt.UserRole)
if item.ended and not item.failed:
QDesktopServices.openUrl(QUrl.fromLocalFile(item.filename))
......@@ -4436,7 +4447,7 @@ class FileTransferDelegate(QStyledItemDelegate):
if not rect.contains(event.pos()):
QDesktopServices.openUrl(QUrl.fromLocalFile(item.filename))
return True
elif event.type()==QEvent.MouseButtonRelease and event.button()==Qt.LeftButton and event.modifiers()==Qt.NoModifier:
elif event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton and event.modifiers() == Qt.NoModifier:
item = index.data(Qt.UserRole)
indicator = item.widget.state_indicator
margin = indicator.margin()
......@@ -4588,7 +4599,7 @@ class FileTransferModel(QAbstractListModel):
def _NH_BlinkFileTransferNewIncoming(self, notification):
transfer = notification.sender
with self.history.transaction():
for item in (item for item in self.items if item.failed and item.direction=='incoming'):
for item in (item for item in self.items if item.failed and item.direction == 'incoming'):
if item.transfer.contact == transfer.contact and item.transfer.hash == transfer.hash:
self.removeItem(item)
break
......@@ -4696,8 +4707,8 @@ class ConferenceParticipantDelegate(QStyledItemDelegate, ColorHelperMixin):
super(ConferenceParticipantDelegate, self).__init__(parent)
def editorEvent(self, event, model, option, index):
if event.type()==QEvent.MouseButtonRelease and event.button()==Qt.LeftButton and event.modifiers()==Qt.NoModifier:
cross_rect = option.rect.adjusted(option.rect.width()-14, 0, 0, -option.rect.height()/2) # top half of the rightmost 14 pixels
if event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton and event.modifiers() == Qt.NoModifier:
cross_rect = option.rect.adjusted(option.rect.width()-14, 0, 0, -option.rect.height()/2) # top half of the rightmost 14 pixels
if cross_rect.contains(event.pos()):
item = index.data(Qt.UserRole)
model.session.server_conference.remove_participant(item.participant)
......@@ -4813,7 +4824,7 @@ class ConferenceParticipantModel(QAbstractListModel):
return None
def supportedDropActions(self):
return Qt.CopyAction# | Qt.MoveAction
return Qt.CopyAction # | Qt.MoveAction
def dropMimeData(self, mime_data, action, row, column, parent_index):
# this is here just to keep the default Qt DnD API happy
......@@ -4902,7 +4913,7 @@ class ConferenceParticipantModel(QAbstractListModel):
def addParticipant(self, participant):
if participant in self.participants:
return
participant.participant.participant_item = participant # add a back reference to this item so we can find it later without iterating all participants
participant.participant.participant_item = participant # add a back reference to this item so we can find it later without iterating all participants
self.participantAboutToBeAdded.emit(participant)
self._add_participant(participant)
self.participantAdded.emit(participant)
......@@ -4954,13 +4965,13 @@ class ConferenceParticipantListView(QListView, ColorHelperMixin):
def paintEvent(self, event):
super(ConferenceParticipantListView, self).paintEvent(event)
if self.paint_drop_indicator:
rect = self.viewport().rect() # or should this be self.contentsRect() ? -Dan
#color = QColor('#b91959')
#color = QColor('#00aaff')
#color = QColor('#55aaff')
#color = QColor('#00aa00')
#color = QColor('#aa007f')
#color = QColor('#dd44aa')
rect = self.viewport().rect() # or should this be self.contentsRect() ? -Dan
# color = QColor('#b91959')
# color = QColor('#00aaff')
# color = QColor('#55aaff')
# color = QColor('#00aa00')
# color = QColor('#aa007f')
# color = QColor('#dd44aa')
color = QColor('#aa007f')
pen_color = self.color_with_alpha(color, 120)
brush_color = self.color_with_alpha(color, 10)
......@@ -5016,8 +5027,8 @@ class ConferenceParticipantListView(QListView, ColorHelperMixin):
def _DH_TextUriList(self, event):
event.ignore(self.viewport().rect())
#event.accept(self.viewport().rect())
#self.paint_drop_indicator = True
# event.accept(self.viewport().rect())
# self.paint_drop_indicator = True
# Session management
......@@ -5082,6 +5093,7 @@ class IncomingDialogBase(QDialog):
ui_class, base_class = uic.loadUiType(Resources.get('incoming_dialog.ui'))
class IncomingDialog(IncomingDialogBase, ui_class):
def __init__(self, parent=None):
super(IncomingDialog, self).__init__(parent)
......@@ -5113,7 +5125,7 @@ class IncomingDialog(IncomingDialogBase, ui_class):
@property
def streams(self):
return (self.audio_stream, self.chat_stream, self.screensharing_stream, self.video_stream)
return self.audio_stream, self.chat_stream, self.screensharing_stream, self.video_stream
@property
def accepted_streams(self):
......@@ -5363,6 +5375,7 @@ class IncomingFileTransferRequest(QObject):
ui_class, base_class = uic.loadUiType(Resources.get('incoming_calltransfer_dialog.ui'))
class IncomingCallTransferDialog(IncomingDialogBase, ui_class):
def __init__(self, parent=None):
super(IncomingCallTransferDialog, self).__init__(parent)
......@@ -5441,6 +5454,7 @@ class IncomingCallTransferRequest(QObject):
ui_class, base_class = uic.loadUiType(Resources.get('conference_dialog.ui'))
class ConferenceDialog(base_class, ui_class):
def __init__(self, parent=None):
super(ConferenceDialog, self).__init__(parent)
......@@ -5495,10 +5509,10 @@ class RingtoneDescriptor(object):
def __init__(self):
self.values = weakobjectmap()
def __get__(self, obj, objtype):
if obj is None:
def __get__(self, instance, owner):
if instance is None:
return self
return self.values[obj]
return self.values[instance]
def __set__(self, obj, ringtone):
old_ringtone = self.values.get(obj, Null)
......@@ -5659,7 +5673,7 @@ class SessionManager(object):
inbound_ringtone = Null
# Hold tone
connected_sessions = [session for session in self.sessions if session.state=='connected/*']
connected_sessions = [session for session in self.sessions if session.state == 'connected/*']
connected_on_hold_sessions = [session for session in connected_sessions if session.on_hold]
if outbound_ringtone is Null and inbound_ringtone is Null and connected_sessions:
if len(connected_sessions) == len(connected_on_hold_sessions):
......@@ -5934,7 +5948,7 @@ class SessionManager(object):
deselected_session = notification.data.deselected_session
old_active_session = self.active_session
if selected_session is self.active_session: # both None or both the same session. nothing to do in either case.
if selected_session is self.active_session: # both None or both the same session. nothing to do in either case.
return
elif selected_session is None and deselected_session is old_active_session is not None:
self.active_session = None
......
__all__ = ['IUpdateManager', 'UpdateManager']
import sys
from application.python import Null
from zope.interface import Interface
__all__ = ['IUpdateManager', 'UpdateManager']
class IUpdateManager(Interface):
def initialize(self):
pass
def shutdown(self):
pass
def check_for_updates(self):
pass
......@@ -23,4 +26,3 @@ if sys.platform == 'win32':
UpdateManager = Null
else:
UpdateManager = Null
......@@ -19,14 +19,13 @@ def library_locations(name):
for path in additional_paths:
yield os.path.join(path, library_name)
def load_library(name):
for library in library_locations(name):
try:
return CDLL(library)
except OSError:
pass
else:
break
else:
raise RuntimeError('cannot find %s on this system' % name)
......
__all__ = ['QSingleton', 'call_in_gui_thread', 'call_later', 'run_in_gui_thread']
from PyQt4.QtCore import QObject, QThread, QTimer
from PyQt4.QtGui import QApplication
from application.python.decorator import decorator, preserve_signature
......@@ -9,6 +7,9 @@ from application.python.types import Singleton
from blink.event import CallFunctionEvent
__all__ = ['QSingleton', 'call_in_gui_thread', 'call_later', 'run_in_gui_thread']
class QSingleton(Singleton, type(QObject)):
"""A metaclass for making Qt objects singletons"""
......
__all__ = ['ToolButton', 'ConferenceButton', 'StreamButton', 'SegmentButton', 'SingleSegment', 'LeftSegment', 'MiddleSegment', 'RightSegment', 'RecordButton', 'SwitchViewButton',
'StateButton', 'AccountState']
from PyQt4.QtCore import Qt, QLineF, QPointF, QRectF, QSize, QTimer, pyqtSignal, pyqtSignature
from PyQt4.QtGui import QAction, QBrush, QColor, QCommonStyle, QLinearGradient, QIcon, QMenu, QPainter, QPainterPath, QPalette, QPen, QPixmap
from PyQt4.QtGui import QPolygonF, QPushButton, QStyle, QStyleOptionToolButton, QStylePainter, QToolButton
......@@ -10,8 +7,13 @@ from blink.resources import Resources
from blink.widgets.color import ColorScheme, ColorUtils, ColorHelperMixin
__all__ = ['ToolButton', 'ConferenceButton', 'StreamButton', 'SegmentButton', 'SingleSegment', 'LeftSegment', 'MiddleSegment', 'RightSegment',
'RecordButton', 'SwitchViewButton', 'StateButton', 'AccountState']
class ToolButton(QToolButton):
"""A custom QToolButton that doesn't show a menu indicator arrow"""
def paintEvent(self, event):
painter = QStylePainter(self)
option = QStyleOptionToolButton()
......@@ -102,10 +104,12 @@ class SegmentTypeMeta(type):
def __repr__(cls):
return cls.__name__
class SegmentType(object):
__metaclass__ = SegmentTypeMeta
style_sheet = ''
class SingleSegment(SegmentType):
style_sheet = """
QToolButton {
......@@ -122,6 +126,7 @@ class SingleSegment(SegmentType):
}
"""
class LeftSegment(SegmentType):
style_sheet = """
QToolButton {
......@@ -139,6 +144,7 @@ class LeftSegment(SegmentType):
}
"""
class MiddleSegment(SegmentType):
style_sheet = """
QToolButton {
......@@ -155,6 +161,7 @@ class MiddleSegment(SegmentType):
}
"""
class RightSegment(SegmentType):
style_sheet = """
QToolButton {
......@@ -285,7 +292,7 @@ class SwitchViewButton(QPushButton):
self.setAcceptDrops(True)
self.__dict__['dnd_active'] = False
self.view = self.ContactView
self.original_height = 20 # used to restore button size after DND
self.original_height = 20 # used to restore button size after DND
self.dnd_timer = QTimer(self)
self.dnd_timer.setInterval(100)
self.dnd_timer.timeout.connect(self._update_dnd)
......@@ -355,16 +362,16 @@ class SwitchViewButton(QPushButton):
def dragLeaveEvent(self, event):
if self.dnd_active:
self.dnd_timer.stop()
self.dnd_timer.phase = 0
self.setStyleSheet(self.dnd_style_sheet1)
self.dnd_timer.stop()
self.dnd_timer.phase = 0
self.setStyleSheet(self.dnd_style_sheet1)
super(SwitchViewButton, self).dragLeaveEvent(event)
def dropEvent(self, event):
if self.dnd_active:
self.dnd_timer.stop()
self.dnd_timer.phase = 0
self.setStyleSheet(self.dnd_style_sheet1)
self.dnd_timer.stop()
self.dnd_timer.phase = 0
self.setStyleSheet(self.dnd_style_sheet1)
event.ignore()
......@@ -387,7 +394,7 @@ class StateButtonStyle(QCommonStyle, ColorHelperMixin):
return super(StateButtonStyle, self).sizeFromContents(element, option, size, widget)
def toolButtonSizeFromContents(self, option, size, widget):
# Make width >= height to avoid super-skiny buttons
# Make width >= height to avoid super-skinny buttons
margin = 2 * (self._pixel_metrics[QStyle.PM_DefaultFrameWidth] + self._pixel_metrics[QStyle.PM_ButtonMargin])
if option.features & QStyleOptionToolButton.MenuButtonPopup:
margin_size = QSize(margin+1, margin)
......@@ -448,12 +455,12 @@ class StateButtonStyle(QCommonStyle, ColorHelperMixin):
blend.setColorAt(0.9, self.color_with_alpha(focus_color, 0x45))
blend.setColorAt(1.0, self.color_with_alpha(ColorUtils.mix(focus_color, shadow_color, 0.4), 0x55))
else:
blend.setColorAt(0.0, Qt.transparent) # or @0.5
blend.setColorAt(0.0, Qt.transparent) # or @0.5
blend.setColorAt(0.9, self.color_with_alpha(shadow_color, 0x10))
#blend.setColorAt(1-4.0/glow_rect.height(), self.color_with_alpha(shadow_color, 0x10)) # this is for exactly 4 pixels from bottom
# blend.setColorAt(1-4.0/glow_rect.height(), self.color_with_alpha(shadow_color, 0x10)) # this is for exactly 4 pixels from bottom
blend.setColorAt(1.0, self.color_with_alpha(shadow_color, 0x30)) # 0x25, 0x30 or 0x35
painter.setBrush(blend)
painter.drawRoundedRect(glow_rect, 5, 5) # 5 or 6
painter.drawRoundedRect(glow_rect, 5, 5) # 5 or 6
# shadow
painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
......@@ -466,7 +473,7 @@ class StateButtonStyle(QCommonStyle, ColorHelperMixin):
blend.setColorAt(0.00, self.color_with_alpha(shadow_color, 0x10))
blend.setColorAt(1.00, self.color_with_alpha(shadow_color, 0x80))
painter.setBrush(blend)
painter.drawRoundedRect(shadow_rect, 4, 4) # 4 or 5
painter.drawRoundedRect(shadow_rect, 4, 4) # 4 or 5
# border
painter.setCompositionMode(QPainter.CompositionMode_Source)
......@@ -510,12 +517,12 @@ class StateButtonStyle(QCommonStyle, ColorHelperMixin):
blend.setColorAt(0.0, self.color_with_alpha(shadow_color, 0x80))
blend.setColorAt(1.0, self.color_with_alpha(shadow_color, 0x20))
painter.setBrush(blend)
painter.drawRoundedRect(hole_rect, 4, 4) # 4 or 5
painter.drawRoundedRect(hole_rect, 4, 4) # 4 or 5
# shadow
painter.setCompositionMode(QPainter.CompositionMode_Source)
painter.setBrush(content_grad)
painter.drawRoundedRect(shadow_rect, 4, 4) # 5 or 6
painter.drawRoundedRect(shadow_rect, 4, 4) # 5 or 6
painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
blend = QLinearGradient(shadow_rect.topLeft(), shadow_rect.bottomLeft())
blend.setColorAt(0.0, self.color_with_alpha(shadow_color, 0x40))
......@@ -523,7 +530,7 @@ class StateButtonStyle(QCommonStyle, ColorHelperMixin):
blend.setColorAt(0.9, self.color_with_alpha(shadow_color, 0x07))
blend.setColorAt(1.0, shade_color)
painter.setBrush(blend)
painter.drawRoundedRect(shadow_rect, 4, 4) # 5 or 6
painter.drawRoundedRect(shadow_rect, 4, 4) # 5 or 6
# content
painter.setCompositionMode(QPainter.CompositionMode_Source)
......@@ -685,6 +692,7 @@ class AccountState(StateButton):
def _get_history(self):
return [(action.state.name, action.note) for action in self.menu().actions()[5:]]
def _set_history(self, values):
menu = self.menu()
for action in menu.actions()[5:]:
......@@ -698,6 +706,7 @@ class AccountState(StateButton):
action.state = state
action.note = note
menu.addAction(action)
history = property(_get_history, _set_history)
del _get_history, _set_history
......
__all__ = ['ColorScheme', 'ColorUtils', 'ColorHelperMixin']
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QColor
from application.python import limit
......@@ -8,8 +6,11 @@ from application.python.decorator import decorator, preserve_signature
from math import fmod, isnan
__all__ = ['ColorScheme', 'ColorUtils', 'ColorHelperMixin']
class HCYColor(object):
"""Hue/chroma/luma colorspace"""
"""Hue/chroma/luma color space"""
luma_r = 0.2126
luma_g = 0.7152
......@@ -202,9 +203,11 @@ class ColorUtils(object):
def color_key(instance, color):
return color.rgba()
def color_ratio_key(instance, color, ratio):
return color.rgba() << 32 | int(ratio*512)
def background_color_key(instance, background, color):
return background.rgba() << 32 | color.rgba()
......@@ -276,7 +279,7 @@ class ColorHelperMixin(object):
shadow_color = ColorUtils.mix(Qt.black, color, color.alphaF())
else:
shadow_color = ColorScheme.shade(ColorUtils.mix(Qt.black, color, color.alphaF()), ColorScheme.ShadowShade, self._contrast)
shadow_color.setAlpha(color.alpha()) # make sure shadow color has the same alpha channel as the input
shadow_color.setAlpha(color.alpha()) # make sure shadow color has the same alpha channel as the input
return shadow_color
@cache_result(color_ratio_key)
......
__all__ = ['SlidingStackedWidget']
from PyQt4.QtCore import QEasingCurve, QParallelAnimationGroup, QPropertyAnimation, QPoint, pyqtSignal
from PyQt4.QtGui import QStackedWidget
from blink.widgets.util import QtDynamicProperty
__all__ = ['SlidingStackedWidget']
class SlidingStackedWidget(QStackedWidget):
animationEasingCurve = QtDynamicProperty('animationEasingCurve', int)
animationDuration = QtDynamicProperty('animationDuration', int)
......@@ -65,9 +66,9 @@ class SlidingStackedWidget(QStackedWidget):
next_widget.setGeometry(0, 0, width, height)
if direction in (self.TopToBottom, self.BottomToTop):
offset = QPoint(0, height if direction==self.TopToBottom else -height)
offset = QPoint(0, height if direction == self.TopToBottom else -height)
elif direction in (self.LeftToRight, self.RightToLeft):
offset = QPoint(width if direction==self.LeftToRight else -width, 0)
offset = QPoint(width if direction == self.LeftToRight else -width, 0)
# re-position the next widget outside of the display area
prev_widget_position = prev_widget.pos()
......@@ -100,8 +101,8 @@ class SlidingStackedWidget(QStackedWidget):
prev_widget = prev_widget_animation.targetObject()
next_widget = next_widget_animation.targetObject()
self.setCurrentWidget(next_widget)
prev_widget.hide() # this may have been done already by QStackedWidget when changing the current widget above -Dan
prev_widget.move(prev_widget_animation.startValue()) # move the outshifted widget back to its original position
prev_widget.hide() # this may have been done already by QStackedWidget when changing the current widget above -Dan
prev_widget.move(prev_widget_animation.startValue()) # move the out-shifted widget back to its original position
self._animation_group.clear()
self._active = False
self.animationFinished.emit()
......
__all__ = ['BackgroundFrame']
from PyQt4.QtCore import Qt, QEvent, QPoint, QRect, QSize
from PyQt4.QtGui import QColor, QFrame, QPainter, QPixmap
......@@ -8,6 +6,9 @@ from blink.resources import Resources
from blink.widgets.util import QtDynamicProperty
__all__ = ['BackgroundFrame']
class BackgroundFrame(QFrame):
backgroundColor = QtDynamicProperty('backgroundColor', unicode)
backgroundImage = QtDynamicProperty('backgroundImage', unicode)
......@@ -28,7 +29,7 @@ class BackgroundFrame(QFrame):
@property
def image_size(self):
if self.imageGeometry is not None:
size = self.imageGeometry.size().expandedTo(QSize(0, 0)) # requested size with negative values turned to 0
size = self.imageGeometry.size().expandedTo(QSize(0, 0)) # requested size with negative values turned to 0
if size.isNull():
return size if self.pixmap is None else self.pixmap.size()
elif size.width() == 0:
......@@ -70,4 +71,3 @@ class BackgroundFrame(QFrame):
painter.drawPixmap(self.image_position, self.scaled_pixmap)
painter.end()
__all__ = ['Graph', 'GraphWidget', 'HeightScaler', 'LogarithmicScaler', 'MaxScaler', 'SoftScaler']
from PyQt4.QtCore import Qt, QLine, QPointF, QMetaObject, pyqtSignal
from PyQt4.QtGui import QColor, QLinearGradient, QPainterPath, QPen, QPolygonF, QStyle, QStyleOption, QStylePainter, QWidget
......@@ -14,6 +12,9 @@ from blink.widgets.color import ColorHelperMixin
from blink.widgets.util import QtDynamicProperty
__all__ = ['Graph', 'GraphWidget', 'HeightScaler', 'LogarithmicScaler', 'MaxScaler', 'SoftScaler']
class HeightScaler(object):
__metaclass__ = ABCMeta
......@@ -140,7 +141,7 @@ class GraphWidget(QWidget, ColorHelperMixin):
option = QStyleOption()
option.initFrom(self)
contents_rect = self.style().subElementRect(QStyle.SE_FrameContents, option, self) or self.contentsRect() # the SE_FrameContents rect is Null unless the stylesheet defines decorations
contents_rect = self.style().subElementRect(QStyle.SE_FrameContents, option, self) or self.contentsRect() # the SE_FrameContents rect is Null unless the stylesheet defines decorations
if self.graphStyle == self.BarStyle:
graph_width = self.__dict__['graph_width'] = int(ceil(float(contents_rect.width()) / self.horizontalPixelsPerUnit))
......@@ -196,18 +197,18 @@ class GraphWidget(QWidget, ColorHelperMixin):
cx_offset = self.horizontalPixelsPerUnit / 3.0
smoothness = self.smoothFactor
last_values = deque(3*[dataset.next() * height_scaling], maxlen=3) # last 3 values: 0 last, 1 previous, 2 previous previous
last_values = deque(3*[next(dataset) * height_scaling], maxlen=3) # last 3 values: 0 last, 1 previous, 2 previous previous
envelope = QPainterPath()
envelope.moveTo(0, last_values[0])
for x, y in enumerate(dataset, 1):
x = x * self.horizontalPixelsPerUnit
y = y * height_scaling * (1 - smoothness) + last_values[0] * smoothness
x *= self.horizontalPixelsPerUnit
y *= height_scaling * (1 - smoothness) + last_values[0] * smoothness
last_values.appendleft(y)
c1x = x - cx_offset * 2
c2x = x - cx_offset
c1y = limit((1 + smoothness) * last_values[1] - smoothness * last_values[2], min_value, max_value) # same gradient as previous previous value to previous value
c2y = limit((1 - smoothness) * last_values[0] + smoothness * last_values[1], min_value, max_value) # same gradient as previous value to last value
c1y = limit((1 + smoothness) * last_values[1] - smoothness * last_values[2], min_value, max_value) # same gradient as previous previous value to previous value
c2y = limit((1 - smoothness) * last_values[0] + smoothness * last_values[1], min_value, max_value) # same gradient as previous value to last value
envelope.cubicTo(c1x, c1y, c2x, c2y, x, y)
else:
envelope = QPainterPath()
......@@ -236,7 +237,7 @@ class GraphWidget(QWidget, ColorHelperMixin):
painter.restore()
# queue the 'updated' signal to be emited after returning to the main loop
# queue the 'updated' signal to be emitted after returning to the main loop
QMetaObject.invokeMethod(self, 'updated', Qt.QueuedConnection)
def add_graph(self, graph):
......@@ -254,4 +255,3 @@ class GraphWidget(QWidget, ColorHelperMixin):
self.graphs = []
self.update()
__all__ = ['DurationLabel', 'IconSelector', 'LatencyLabel', 'PacketLossLabel', 'Status', 'StatusLabel', 'StreamInfoLabel', 'ElidedLabel', 'ContactState']
import os
from datetime import timedelta
......@@ -16,6 +14,9 @@ from blink.widgets.color import ColorHelperMixin
from blink.widgets.util import QtDynamicProperty, ContextMenuActions
__all__ = ['DurationLabel', 'IconSelector', 'LatencyLabel', 'PacketLossLabel', 'Status', 'StatusLabel', 'StreamInfoLabel', 'ElidedLabel', 'ContactState']
class IconSelector(QLabel):
default_icon = QtDynamicProperty('default_icon', QIcon)
icon_size = QtDynamicProperty('icon_size', int)
......@@ -283,6 +284,7 @@ class StateColor(QColor):
def stroke(self):
return self.darker(200)
class StateColorMapping(dict):
def __missing__(self, key):
if key == 'offline':
......@@ -294,7 +296,7 @@ class StateColorMapping(dict):
elif key == 'busy':
return self.setdefault(key, StateColor('#ff0000'))
else:
return StateColor(Qt.transparent) #StateColor('#d0d0d0')
return StateColor(Qt.transparent) # StateColor('#d0d0d0')
class ContactState(QLabel, ColorHelperMixin):
......@@ -323,4 +325,3 @@ class ContactState(QLabel, ColorHelperMixin):
painter.setPen(QPen(QBrush(gradient), 1))
painter.drawRoundedRect(-4, 0, self.width()+4, self.height(), 3.7, 3.7)
__all__ = ['LineEdit', 'ValidatingLineEdit', 'SearchBox', 'LocationBar']
import re
from PyQt4.QtCore import Qt, QEvent, pyqtSignal
......@@ -10,6 +8,9 @@ from blink.resources import Resources
from blink.widgets.util import QtDynamicProperty
__all__ = ['LineEdit', 'ValidatingLineEdit', 'SearchBox', 'LocationBar']
class SideWidget(QWidget):
sizeHintChanged = pyqtSignal()
......@@ -64,7 +65,7 @@ class LineEdit(QLineEdit):
spacing = self.right_layout.spacing()
text_rect = self.style().subElementRect(QStyle.SE_LineEditContents, option, self)
text_rect.adjust(spacing, 0, -spacing, 0)
mid_height = text_rect.center().y() + 1 - (text_rect.height() % 2) # need -1 correction for odd heights -Dan
mid_height = text_rect.center().y() + 1 - (text_rect.height() % 2) # need -1 correction for odd heights -Dan
if self.left_layout.count() > 0:
left_height = mid_height - self.left_widget.height()/2
left_width = self.left_widget.width()
......@@ -244,7 +245,7 @@ class ClearButton(QAbstractButton):
# Mid is darker than Dark. Go figure... -Dan
bg_color = palette.color(QPalette.Mid) if self.isDown() else palette.color(QPalette.Dark)
fg_color = palette.color(QPalette.Window) # or QPalette.Base for white
fg_color = palette.color(QPalette.Window) # or QPalette.Base for white
painter.setRenderHint(QPainter.Antialiasing, True)
painter.setBrush(bg_color)
......@@ -307,4 +308,3 @@ class LocationBar(LineEdit):
def _SH_TextChanged(self, text):
self.clear_button.setVisible(bool(text))
__all__ = ['QtDynamicProperty', 'ContextMenuActions']
from PyQt4.QtCore import QPyNullVariant
__all__ = ['QtDynamicProperty', 'ContextMenuActions']
class QtDynamicProperty(object):
def __init__(self, name, type=unicode):
self.name = name
self.type = type
def __get__(self, obj, objtype):
if obj is None:
def __get__(self, instance, owner):
if instance is None:
return self
value = obj.property(self.name)
value = instance.property(self.name)
if isinstance(value, QPyNullVariant):
value = self.type()
return value
def __set__(self, obj, value):
if value is not None and not isinstance(value, self.type):
value = self.type(value)
obj.setProperty(self.name, value)
def __delete__(self, obj):
raise AttributeError("attribute cannot be deleted")
......
from __future__ import division
__all__ = ['VideoSurface']
from PyQt4.QtCore import Qt, QMetaObject, QPoint, QRect, QTimer, pyqtSignal
from PyQt4.QtGui import QColor, QCursor, QIcon, QImage, QPainter, QPixmap, QTransform, QWidget
......@@ -16,7 +12,11 @@ from sipsimple.core import FrameBufferVideoRenderer
from blink.resources import Resources
class Container(object): pass
__all__ = ['VideoSurface']
class Container(object):
pass
class InteractionState(object):
......@@ -41,7 +41,7 @@ class VideoSurface(QWidget):
class BottomLeftCorner: __metaclass__ = MarkerType
class BottomRightCorner: __metaclass__ = MarkerType
adjusted = pyqtSignal(QRect, QRect) # the widget was adjusted by the user (if interactive)
adjusted = pyqtSignal(QRect, QRect) # the widget was adjusted by the user (if interactive)
interactive = False # if the widget can be interacted with (moved, resized)
mirror = False # mirror the image horizontally
......@@ -197,13 +197,13 @@ class VideoSurface(QWidget):
delta_x = -(self.width_for_height(geometry.height() - delta_y) - geometry.width())
geometry.setTopLeft(geometry.topLeft() + QPoint(delta_x, delta_y))
elif self._interaction.resize_corner is self.TopRightCorner:
delta_x = (self.width_for_height(geometry.height() - delta_y) - geometry.width())
delta_x = +(self.width_for_height(geometry.height() - delta_y) - geometry.width())
geometry.setTopRight(geometry.topRight() + QPoint(delta_x, delta_y))
elif self._interaction.resize_corner is self.BottomLeftCorner:
delta_x = -(self.width_for_height(geometry.height() + delta_y) - geometry.width())
geometry.setBottomLeft(geometry.bottomLeft() + QPoint(delta_x, delta_y))
else:
delta_x = (self.width_for_height(geometry.height() + delta_y) - geometry.width())
delta_x = +(self.width_for_height(geometry.height() + delta_y) - geometry.width())
geometry.setBottomRight(geometry.bottomRight() + QPoint(delta_x, delta_y))
if self.minimumHeight() <= geometry.height() <= self.maximumHeight() and (self.parent() is None or self.parent().rect().contains(geometry)):
......@@ -224,4 +224,3 @@ class VideoSurface(QWidget):
super(VideoSurface, self).closeEvent(event)
self.stop()
__all__ = ['ZRTPWidget']
from PyQt4 import uic
from PyQt4.QtCore import Qt, pyqtSignal
from PyQt4.QtGui import QStyle, QStyleOption, QStylePainter
......@@ -9,8 +6,12 @@ from PyQt4.QtGui import QStyle, QStyleOption, QStylePainter
from blink.resources import Resources
__all__ = ['ZRTPWidget']
ui_class, base_class = uic.loadUiType(Resources.get('zrtp_widget.ui'))
class ZRTPWidget(base_class, ui_class):
closed = pyqtSignal()
nameChanged = pyqtSignal()
......
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