Commit 67fbb869 authored by Dan Pascu's avatar Dan Pascu

Improved displaying the account states in preferences and the main window

parent 7f72b155
...@@ -12,7 +12,7 @@ from collections import defaultdict ...@@ -12,7 +12,7 @@ from collections import defaultdict
from PyQt4 import uic from PyQt4 import uic
from PyQt4.QtCore import Qt, QAbstractListModel, QModelIndex, QUrl from PyQt4.QtCore import Qt, QAbstractListModel, QModelIndex, QUrl
from PyQt4.QtGui import QAction, QButtonGroup, QComboBox, QIcon, QMenu, QMovie, QPalette, QPixmap, QSortFilterProxyModel, QStyledItemDelegate from PyQt4.QtGui import QAction, QButtonGroup, QComboBox, QIcon, QMenu, QMovie, QSortFilterProxyModel
from PyQt4.QtNetwork import QNetworkAccessManager from PyQt4.QtNetwork import QNetworkAccessManager
from PyQt4.QtWebKit import QWebView from PyQt4.QtWebKit import QWebView
...@@ -33,16 +33,43 @@ from blink.widgets.labels import Status ...@@ -33,16 +33,43 @@ from blink.widgets.labels import Status
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread
class IconDescriptor(object):
def __init__(self, filename):
self.filename = filename
self.icon = None
def __get__(self, obj, objtype):
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")
class AccountInfo(object): class AccountInfo(object):
def __init__(self, account, icon=None): active_icon = IconDescriptor(Resources.get('icons/circle-dot.svg'))
inactive_icon = IconDescriptor(Resources.get('icons/circle-grey.svg'))
activity_icon = IconDescriptor(Resources.get('icons/circle-progress.svg'))
def __init__(self, account):
self.account = account self.account = account
self.icon = icon
self.registration_state = None self.registration_state = None
@property @property
def name(self): def name(self):
return u'Bonjour' if self.account is BonjourAccount() else unicode(self.account.id) return u'Bonjour' if self.account is BonjourAccount() else unicode(self.account.id)
@property
def icon(self):
if self.registration_state == 'started':
return self.activity_icon
elif self.registration_state == 'succeeded':
return self.active_icon
else:
return self.inactive_icon
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, basestring): if isinstance(other, basestring):
return self.name == other return self.name == other
...@@ -97,14 +124,8 @@ class AccountModel(QAbstractListModel): ...@@ -97,14 +124,8 @@ class AccountModel(QAbstractListModel):
def _NH_SIPAccountManagerDidAddAccount(self, notification): def _NH_SIPAccountManagerDidAddAccount(self, notification):
account = notification.data.account account = notification.data.account
icon = None
if account is BonjourAccount():
pixmap = QPixmap()
if pixmap.load(Resources.get('icons/bonjour.png')):
pixmap = pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation)
icon = QIcon(pixmap)
self.beginInsertRows(QModelIndex(), len(self.accounts), len(self.accounts)) self.beginInsertRows(QModelIndex(), len(self.accounts), len(self.accounts))
self.accounts.append(AccountInfo(account, icon)) self.accounts.append(AccountInfo(account))
self.endInsertRows() self.endInsertRows()
def _NH_CFGSettingsObjectDidChange(self, notification): def _NH_CFGSettingsObjectDidChange(self, notification):
...@@ -169,60 +190,16 @@ class ActiveAccountModel(QSortFilterProxyModel): ...@@ -169,60 +190,16 @@ class ActiveAccountModel(QSortFilterProxyModel):
return account_info.account.enabled return account_info.account.enabled
class AccountStatePalettes(dict):
def __init__(self, palette):
super(AccountStatePalettes, self).__init__(succeeded=palette)
self.alternate_palette = QPalette(palette)
self.alternate_palette.setColor(QPalette.Text, self.alternate_palette.color(QPalette.Mid))
self.alternate_palette.setColor(QPalette.ButtonText, self.alternate_palette.color(QPalette.Mid))
self.alternate_palette.setColor(QPalette.WindowText, self.alternate_palette.color(QPalette.Mid))
def __missing__(self, key):
return self.setdefault(key, self.alternate_palette)
class AccountDelegate(QStyledItemDelegate):
def __init__(self, parent):
super(AccountDelegate, self).__init__(parent)
self.state_palette_map = AccountStatePalettes(parent.palette())
def paint(self, painter, option, index):
account_info = index.data(Qt.UserRole)
option.palette = self.state_palette_map[account_info.registration_state]
super(AccountDelegate, self).paint(painter, option, index)
class AccountSelector(QComboBox): class AccountSelector(QComboBox):
implements(IObserver) implements(IObserver)
def __init__(self, parent=None): def __init__(self, parent=None):
super(AccountSelector, self).__init__(parent) super(AccountSelector, self).__init__(parent)
self.state_palette_map = AccountStatePalettes(self.palette())
self.setItemDelegate(AccountDelegate(self.view()))
self.currentIndexChanged[int].connect(self._SH_SelectionChanged)
self.model().dataChanged.connect(self._SH_DataChanged)
notification_center = NotificationCenter() notification_center = NotificationCenter()
notification_center.add_observer(self, name="SIPAccountManagerDidChangeDefaultAccount") notification_center.add_observer(self, name="SIPAccountManagerDidChangeDefaultAccount")
notification_center.add_observer(self, name="SIPAccountManagerDidStart") notification_center.add_observer(self, name="SIPAccountManagerDidStart")
def setModel(self, model):
self.model().dataChanged.disconnect(self._SH_DataChanged)
model.dataChanged.connect(self._SH_DataChanged)
super(AccountSelector, self).setModel(model)
def _SH_DataChanged(self, topLeft, bottomRight):
index = self.currentIndex()
if topLeft.row() <= index <= bottomRight.row():
account_info = self.itemData(index)
self.setPalette(self.state_palette_map[account_info.registration_state])
def _SH_SelectionChanged(self, index):
if index == -1:
return
account_info = self.itemData(index)
self.setPalette(self.state_palette_map[account_info.registration_state])
@run_in_gui_thread @run_in_gui_thread
def handle_notification(self, notification): def handle_notification(self, notification):
handler = getattr(self, '_NH_%s' % notification.name, Null) handler = getattr(self, '_NH_%s' % notification.name, Null)
......
...@@ -8,7 +8,7 @@ import urlparse ...@@ -8,7 +8,7 @@ import urlparse
from PyQt4 import uic from PyQt4 import uic
from PyQt4.QtCore import Qt, QRegExp from PyQt4.QtCore import Qt, QRegExp
from PyQt4.QtGui import QActionGroup, QButtonGroup, QFileDialog, QFont, QListView, QListWidgetItem, QMessageBox, QRegExpValidator, QSpinBox, QStyle, QStyleOptionComboBox, QValidator from PyQt4.QtGui import QActionGroup, QButtonGroup, QFileDialog, QFont, QListView, QListWidgetItem, QMessageBox, QRegExpValidator, QSpinBox, QStyle, QStyleOptionComboBox, QStyledItemDelegate, QValidator
from application import log from application import log
from application.notification import IObserver, NotificationCenter from application.notification import IObserver, NotificationCenter
...@@ -129,10 +129,18 @@ class SIPPortEditor(QSpinBox): ...@@ -129,10 +129,18 @@ class SIPPortEditor(QSpinBox):
return state, input, pos return state, input, pos
class AccountDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
account_info = index.data(Qt.UserRole)
if not account_info.account.enabled:
option.state &= ~QStyle.State_Enabled
super(AccountDelegate, self).paint(painter, option, index)
class AccountListView(QListView): class AccountListView(QListView):
def __init__(self, parent=None): def __init__(self, parent=None):
super(AccountListView, self).__init__(parent) super(AccountListView, self).__init__(parent)
#self.setItemDelegate(AccountDelegate(self)) self.setItemDelegate(AccountDelegate(self))
#self.setDropIndicatorShown(False) #self.setDropIndicatorShown(False)
def selectionChanged(self, selected, deselected): def selectionChanged(self, selected, deselected):
......
...@@ -45,12 +45,26 @@ ...@@ -45,12 +45,26 @@
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>16777215</width> <width>16777215</width>
<height>20</height> <height>21</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Select the SIP account used for outbound calls</string> <string>Select the SIP account used for outbound calls</string>
</property> </property>
<property name="maxVisibleItems">
<number>20</number>
</property>
<property name="iconSize">
<size>
<width>14</width>
<height>14</height>
</size>
</property>
</widget> </widget>
</item> </item>
<item> <item>
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="12"
height="12"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="circle-dot.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="69.166667"
inkscape:cx="6"
inkscape:cy="6"
inkscape:document-units="px"
inkscape:current-layer="g3931"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1373"
inkscape:window-height="1040"
inkscape:window-x="211"
inkscape:window-y="4"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
inkscape:label="Circle dot"
inkscape:groupmode="layer"
id="g3931"
transform="translate(0,-1040.3622)">
<path
transform="matrix(1.268496,0,0,1.268496,1.560264,1043.191)"
d="M 6.9686747,2.5 C 6.9686747,4.4156961 5.4156961,5.9686747 3.5,5.9686747 1.5843039,5.9686747 0.03132534,4.4156961 0.03132534,2.5 c 0,-1.91569612 1.55297856,-3.46867466 3.46867466,-3.46867466 1.9156961,0 3.4686747,1.55297854 3.4686747,3.46867466 z"
sodipodi:ry="3.4686747"
sodipodi:rx="3.4686747"
sodipodi:cy="2.5"
sodipodi:cx="3.5"
id="path3933"
style="fill:none;stroke:#272727;stroke-width:0.94600224;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:#272727;fill-opacity:1;stroke:#272727;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6"
id="path3935"
sodipodi:cx="4.5"
sodipodi:cy="4.5"
sodipodi:rx="0.5"
sodipodi:ry="0.5"
d="M 5,4.5 C 5,4.7761424 4.7761424,5 4.5,5 4.2238576,5 4,4.7761424 4,4.5 4,4.2238576 4.2238576,4 4.5,4 4.7761424,4 5,4.2238576 5,4.5 z"
transform="matrix(2,0,0,2,-3,1037.3622)" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="12"
height="12"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="circle-grey.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="69.166667"
inkscape:cx="6"
inkscape:cy="6"
inkscape:document-units="px"
inkscape:current-layer="g4210"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1373"
inkscape:window-height="1040"
inkscape:window-x="211"
inkscape:window-y="4"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
inkscape:label="Circle grey"
inkscape:groupmode="layer"
id="g4210"
transform="translate(0,-1040.3622)">
<path
transform="matrix(1.268496,0,0,1.268496,1.560264,1043.191)"
d="m 6.9686747,2.5 a 3.4686747,3.4686747 0 1 1 -6.93734936,0 3.4686747,3.4686747 0 1 1 6.93734936,0 z"
sodipodi:ry="3.4686747"
sodipodi:rx="3.4686747"
sodipodi:cy="2.5"
sodipodi:cx="3.5"
id="path4212"
style="fill:none;stroke:#909090;stroke-width:0.94600224;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6"
sodipodi:type="arc" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="12"
height="12"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="circle-progress.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="69.166667"
inkscape:cx="6"
inkscape:cy="6"
inkscape:document-units="px"
inkscape:current-layer="g4117"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1373"
inkscape:window-height="1040"
inkscape:window-x="211"
inkscape:window-y="4"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-1040.3622)"
id="g4117"
inkscape:groupmode="layer"
inkscape:label="Circle progress"
style="display:inline">
<path
sodipodi:type="arc"
style="fill:none;stroke:#272727;stroke-width:0.94600224;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6"
id="path4119"
sodipodi:cx="3.5"
sodipodi:cy="2.5"
sodipodi:rx="3.4686747"
sodipodi:ry="3.4686747"
d="m 6.9686747,2.5 a 3.4686747,3.4686747 0 1 1 -6.93734936,0 3.4686747,3.4686747 0 1 1 6.93734936,0 z"
transform="matrix(1.268496,0,0,1.268496,1.560264,1043.191)" />
<path
sodipodi:type="arc"
style="fill:#272727;fill-opacity:1;stroke:none;display:inline"
id="path4119-4"
sodipodi:cx="3.5"
sodipodi:cy="2.5"
sodipodi:rx="3.4686747"
sodipodi:ry="3.4686747"
d="M 3.5000001,-0.96867466 A 3.4686747,3.4686747 0 0 1 5.9527233,4.9527234 L 3.5,2.5 z"
transform="matrix(1.268496,0,0,1.268496,1.560264,1043.191)"
sodipodi:start="4.712389"
sodipodi:end="7.0685835" />
</g>
</svg>
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