Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vmj-qt
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kulya
vmj-qt
Commits
8fd67017
Commit
8fd67017
authored
May 17, 2010
by
Dan Pascu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added contacts editing panel
parent
4147aca4
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
744 additions
and
90 deletions
+744
-90
contacts.py
blink/contacts.py
+221
-58
mainwindow.py
blink/mainwindow.py
+43
-21
iconselector.py
blink/widgets/iconselector.py
+46
-0
blink.ui
resources/blink.ui
+11
-11
contact_editor.ui
resources/contact_editor.ui
+423
-0
No files found.
blink/contacts.py
View file @
8fd67017
...
...
@@ -3,16 +3,16 @@
from
__future__
import
with_statement
__all__
=
[
'BonjourGroup'
,
'BonjourNeighbour'
,
'Contact'
,
'ContactGroup'
,
'ContactModel'
,
'ContactSearchModel'
,
'ContactListView'
,
'ContactSearchListView'
]
__all__
=
[
'BonjourGroup'
,
'BonjourNeighbour'
,
'Contact'
,
'ContactGroup'
,
'ContactModel'
,
'ContactSearchModel'
,
'ContactListView'
,
'ContactSearchListView'
,
'ContactEditorDialog'
]
import
cPickle
as
pickle
import
errno
import
os
from
PyQt4
import
uic
from
PyQt4.QtCore
import
Qt
,
QAbstractListModel
,
QByteArray
,
QEvent
,
QMimeData
,
QModelIndex
,
QPointF
,
QRectF
,
Q
Size
,
QStringList
,
QTimer
from
PyQt4.QtCore
import
Qt
,
QAbstractListModel
,
QByteArray
,
QEvent
,
QMimeData
,
QModelIndex
,
QPointF
,
QRectF
,
Q
RegExp
,
QSize
,
QStringList
,
QTimer
,
pyqtSignal
from
PyQt4.QtGui
import
QBrush
,
QColor
,
QLinearGradient
,
QPainter
,
QPainterPath
,
QPalette
,
QPen
,
QPixmap
,
QPolygonF
,
QStyle
from
PyQt4.QtGui
import
QAction
,
QKeyEvent
,
QListView
,
QMenu
,
QMouseEvent
,
QSortFilterProxyModel
,
QStyledItemDelegate
from
PyQt4.QtGui
import
QAction
,
QKeyEvent
,
QListView
,
QMenu
,
QMouseEvent
,
Q
RegExpValidator
,
Q
SortFilterProxyModel
,
QStyledItemDelegate
from
application.notification
import
IObserver
,
NotificationCenter
from
application.python.decorator
import
decorator
,
preserve_signature
...
...
@@ -155,7 +155,7 @@ class Contact(object):
self
.
name
=
name
self
.
uri
=
uri
self
.
image
=
image
self
.
icon
=
self
.
default_user_icon
if
image
is
None
else
ContactIconDescriptor
(
image
)
.
__get__
(
self
,
self
.
__class__
)
self
.
preferred_media
=
'Audio'
self
.
status
=
'unknown'
def
__repr__
(
self
):
...
...
@@ -165,7 +165,21 @@ class Contact(object):
return
u'
%
s <
%
s>'
%
(
self
.
name
,
self
.
uri
)
if
self
.
name
else
self
.
uri
def
__reduce__
(
self
):
return
(
self
.
__class__
,
(
self
.
group
,
self
.
name
,
self
.
uri
,
self
.
image
),
None
)
return
(
self
.
__class__
,
(
self
.
group
,
self
.
name
,
self
.
uri
,
self
.
image
),
dict
(
preferred_media
=
self
.
preferred_media
))
def
_get_image
(
self
):
return
self
.
__dict__
[
'image'
]
def
_set_image
(
self
,
image
):
self
.
__dict__
[
'image'
]
=
image
self
.
__dict__
[
'icon'
]
=
self
.
default_user_icon
if
image
is
None
else
ContactIconDescriptor
(
image
)
.
__get__
(
self
,
self
.
__class__
)
image
=
property
(
_get_image
,
_set_image
)
del
_get_image
,
_set_image
@
property
def
icon
(
self
):
return
self
.
__dict__
[
'icon'
]
class
NoGroup
(
object
):
...
...
@@ -198,7 +212,7 @@ class ContactWidget(base_class, ui_class):
self
.
setupUi
(
self
)
def
set_contact
(
self
,
contact
):
self
.
name
.
setText
(
contact
.
name
)
self
.
name
.
setText
(
contact
.
name
or
contact
.
uri
)
self
.
uri
.
setText
(
contact
.
uri
)
self
.
icon
.
setPixmap
(
contact
.
icon
)
...
...
@@ -454,6 +468,9 @@ class ContactDelegate(QStyledItemDelegate):
class
ContactModel
(
QAbstractListModel
):
implements
(
IObserver
)
itemsAdded
=
pyqtSignal
(
list
)
itemsRemoved
=
pyqtSignal
(
list
)
# The MIME types we accept in drop operations, in the order they should be handled
accepted_mime_types
=
[
'application/x-blink-contact-group-list'
,
'application/x-blink-contact-list'
,
'text/uri-list'
]
...
...
@@ -461,6 +478,7 @@ class ContactModel(QAbstractListModel):
super
(
ContactModel
,
self
)
.
__init__
(
parent
)
self
.
items
=
[]
self
.
deleted_items
=
[]
self
.
main_window
=
parent
self
.
contact_list
=
parent
.
contact_list
if
not
hasattr
(
self
,
'beginResetModel'
):
# emulate beginResetModel/endResetModel for QT < 4.6
...
...
@@ -555,7 +573,7 @@ class ContactModel(QAbstractListModel):
else
:
drop_group
=
group
drop_position
=
drop_indicator
items
=
self
.
popI
tems
(
selected_indexes
)
items
=
self
.
_pop_i
tems
(
selected_indexes
)
contact_groups
=
self
.
contact_groups
# they changed so refresh them
if
drop_position
is
self
.
contact_list
.
AboveItem
:
position
=
self
.
items
.
index
(
drop_group
)
...
...
@@ -579,9 +597,9 @@ class ContactModel(QAbstractListModel):
if
group
.
widget
.
drop_indicator
is
None
:
return
False
indexes
=
[
index
for
index
in
self
.
contact_list
.
selectionModel
()
.
selectedIndexes
()
if
self
.
items
[
index
.
row
()]
.
movable
]
for
contact
in
self
.
popI
tems
(
indexes
):
for
contact
in
self
.
_pop_i
tems
(
indexes
):
contact
.
group
=
group
self
.
addC
ontact
(
contact
)
self
.
_add_c
ontact
(
contact
)
return
True
def
_DH_TextUriList
(
self
,
mime_data
,
action
,
index
):
...
...
@@ -617,8 +635,7 @@ class ContactModel(QAbstractListModel):
if
indexes
:
yield
(
last
,
end
)
@
updates_contacts_db
def
addContact
(
self
,
contact
):
def
_add_contact
(
self
,
contact
):
if
contact
.
group
in
self
.
items
:
for
position
in
xrange
(
self
.
items
.
index
(
contact
.
group
)
+
1
,
len
(
self
.
items
)):
item
=
self
.
items
[
position
]
...
...
@@ -638,41 +655,96 @@ class ContactModel(QAbstractListModel):
self
.
contact_list
.
openPersistentEditor
(
self
.
index
(
position
))
self
.
endInsertRows
()
@
updates_contacts_db
def
removeContact
(
self
,
contact
):
if
contact
not
in
self
.
items
:
return
def
_add_group
(
self
,
group
):
position
=
len
(
self
.
items
)
self
.
beginInsertRows
(
QModelIndex
(),
position
,
position
)
self
.
items
.
append
(
group
)
self
.
contact_list
.
openPersistentEditor
(
self
.
index
(
position
))
self
.
endInsertRows
()
def
_pop_contact
(
self
,
contact
):
position
=
self
.
items
.
index
(
contact
)
self
.
beginRemoveRows
(
QModelIndex
(),
position
,
position
)
del
self
.
items
[
position
]
self
.
endRemoveRows
()
return
contact
def
_pop_group
(
self
,
group
):
start
=
self
.
items
.
index
(
group
)
end
=
start
+
len
([
item
for
item
in
self
.
items
if
isinstance
(
item
,
Contact
)
and
item
.
group
==
group
])
self
.
beginRemoveRows
(
QModelIndex
(),
start
,
end
)
items
=
self
.
items
[
start
:
end
+
1
]
del
self
.
items
[
start
:
end
+
1
]
self
.
endRemoveRows
()
return
items
def
_pop_items
(
self
,
indexes
):
items
=
[]
rows
=
set
(
index
.
row
()
for
index
in
indexes
if
index
.
isValid
())
removed_groups
=
set
(
self
.
items
[
row
]
for
row
in
rows
if
isinstance
(
self
.
items
[
row
],
ContactGroup
))
rows
.
update
(
row
for
row
,
item
in
enumerate
(
self
.
items
)
if
isinstance
(
item
,
Contact
)
and
item
.
group
in
removed_groups
)
for
start
,
end
in
self
.
reversed_range_iterator
(
rows
):
self
.
beginRemoveRows
(
QModelIndex
(),
start
,
end
)
items
[
0
:
0
]
=
self
.
items
[
start
:
end
+
1
]
del
self
.
items
[
start
:
end
+
1
]
self
.
endRemoveRows
()
return
items
@
updates_contacts_db
def
addContact
(
self
,
contact
):
if
contact
in
self
.
items
:
return
added_items
=
[
contact
.
group
,
contact
]
if
contact
.
group
in
self
.
items
else
[
contact
]
self
.
_add_contact
(
contact
)
self
.
itemsAdded
.
emit
(
added_items
)
@
updates_contacts_db
def
updateContact
(
self
,
contact
,
attributes
):
group
=
attributes
.
pop
(
'group'
)
for
name
,
value
in
attributes
.
iteritems
():
setattr
(
contact
,
name
,
value
)
if
contact
.
group
!=
group
:
new_group
=
group
not
in
self
.
items
self
.
_pop_contact
(
contact
)
contact
.
group
=
group
self
.
_add_contact
(
contact
)
if
new_group
:
self
.
itemsAdded
.
emit
([
group
])
else
:
index
=
self
.
index
(
self
.
items
.
index
(
contact
))
self
.
dataChanged
.
emit
(
index
,
index
)
@
updates_contacts_db
def
removeContact
(
self
,
contact
):
if
contact
not
in
self
.
items
:
return
self
.
_pop_contact
(
contact
)
if
type
(
contact
)
is
Contact
:
self
.
deleted_items
.
append
([
contact
])
self
.
itemsRemoved
.
emit
([
contact
])
@
updates_contacts_db
def
addGroup
(
self
,
group
):
if
group
in
self
.
items
:
return
position
=
len
(
self
.
items
)
self
.
beginInsertRows
(
QModelIndex
(),
position
,
position
)
self
.
items
.
append
(
group
)
self
.
contact_list
.
openPersistentEditor
(
self
.
index
(
position
))
self
.
endInsertRows
()
self
.
_add_group
(
group
)
self
.
itemsAdded
.
emit
([
group
])
@
updates_contacts_db
def
removeGroup
(
self
,
group
):
if
group
not
in
self
.
items
:
return
start
=
self
.
items
.
index
(
group
)
end
=
start
+
len
([
item
for
item
in
self
.
items
if
isinstance
(
item
,
Contact
)
and
item
.
group
==
group
])
self
.
beginRemoveRows
(
QModelIndex
(),
start
,
end
)
del
self
.
items
[
start
:
end
+
1
]
self
.
endRemoveRows
()
items
=
self
.
_pop_group
(
group
)
if
type
(
group
)
is
ContactGroup
:
self
.
deleted_items
.
append
(
items
)
self
.
itemsRemoved
.
emit
(
items
)
@
updates_contacts_db
def
moveGroup
(
self
,
group
,
reference
):
contact_groups
=
self
.
contact_groups
if
group
not
in
contact_groups
or
contact_groups
.
index
(
group
)
+
1
==
(
contact_groups
.
index
(
reference
)
if
reference
in
contact_groups
else
len
(
contact_groups
)):
return
items
=
self
.
popItems
([
self
.
index
(
self
.
items
.
index
(
group
))]
)
items
=
self
.
_pop_group
(
group
)
start
=
self
.
items
.
index
(
reference
)
if
reference
in
contact_groups
else
len
(
self
.
items
)
end
=
start
+
len
(
items
)
-
1
self
.
beginInsertRows
(
QModelIndex
(),
start
,
end
)
...
...
@@ -682,30 +754,11 @@ class ContactModel(QAbstractListModel):
@
updates_contacts_db
def
removeItems
(
self
,
indexes
):
rows
=
set
(
index
.
row
()
for
index
in
indexes
if
index
.
isValid
())
removed_groups
=
set
(
self
.
items
[
row
]
for
row
in
rows
if
isinstance
(
self
.
items
[
row
],
ContactGroup
))
rows
.
update
(
row
for
row
,
item
in
enumerate
(
self
.
items
)
if
isinstance
(
item
,
Contact
)
and
item
.
group
in
removed_groups
)
for
start
,
end
in
self
.
reversed_range_iterator
(
rows
):
self
.
beginRemoveRows
(
QModelIndex
(),
start
,
end
)
deleted_items
=
self
.
items
[
start
:
end
+
1
]
for
item
in
(
item
for
item
in
deleted_items
if
isinstance
(
item
,
ContactGroup
)):
items
=
self
.
_pop_items
(
indexes
)
for
item
in
(
item
for
item
in
items
if
isinstance
(
item
,
ContactGroup
)):
item
.
widget
=
Null
self
.
deleted_items
.
append
(
deleted_items
)
del
self
.
items
[
start
:
end
+
1
]
self
.
endRemoveRows
()
@
updates_contacts_db
def
popItems
(
self
,
indexes
):
items
=
[]
rows
=
set
(
index
.
row
()
for
index
in
indexes
if
index
.
isValid
())
removed_groups
=
set
(
self
.
items
[
row
]
for
row
in
rows
if
isinstance
(
self
.
items
[
row
],
ContactGroup
))
rows
.
update
(
row
for
row
,
item
in
enumerate
(
self
.
items
)
if
isinstance
(
item
,
Contact
)
and
item
.
group
in
removed_groups
)
for
start
,
end
in
self
.
reversed_range_iterator
(
rows
):
self
.
beginRemoveRows
(
QModelIndex
(),
start
,
end
)
items
[
0
:
0
]
=
self
.
items
[
start
:
end
+
1
]
del
self
.
items
[
start
:
end
+
1
]
self
.
endRemoveRows
()
return
items
self
.
deleted_items
.
append
(
items
)
self
.
itemsRemoved
.
emit
(
items
)
def
load
(
self
):
try
:
...
...
@@ -785,7 +838,7 @@ class ContactModel(QAbstractListModel):
@
ignore_contacts_db_updates
def
_NH_BonjourAccountDidRemoveNeighbour
(
self
,
notification
):
for
contact
in
(
c
for
c
in
self
.
items
[:]
if
type
(
c
)
is
BonjourNeighbour
)
:
for
contact
in
[
c
for
c
in
self
.
items
if
type
(
c
)
is
BonjourNeighbour
]
:
if
contact
.
uri
==
unicode
(
notification
.
data
.
uri
):
self
.
removeContact
(
contact
)
...
...
@@ -801,7 +854,7 @@ class ContactModel(QAbstractListModel):
@
ignore_contacts_db_updates
def
_NH_SIPAccountManagerDidStart
(
self
,
notification
):
if
not
BonjourAccount
()
.
enabled
and
self
.
bonjour_group
in
self
.
contact_group
s
:
if
not
BonjourAccount
()
.
enabled
and
self
.
bonjour_group
in
self
.
item
s
:
self
.
removeGroup
(
self
.
bonjour_group
)
if
notification
.
sender
.
default_account
is
BonjourAccount
():
group
=
self
.
bonjour_group
...
...
@@ -845,6 +898,7 @@ class ContactSearchModel(QSortFilterProxyModel):
def
__init__
(
self
,
model
,
parent
=
None
):
super
(
ContactSearchModel
,
self
)
.
__init__
(
parent
)
self
.
main_window
=
parent
self
.
setSourceModel
(
model
)
self
.
setDynamicSortFilter
(
True
)
self
.
sort
(
0
)
...
...
@@ -1112,22 +1166,27 @@ class ContactListView(QListView):
def
_AH_AddContact
(
self
):
model
=
self
.
model
()
selected_rows
=
sorted
(
index
.
row
()
for
index
in
self
.
selectionModel
()
.
selectedIndexes
()
if
type
(
model
.
data
(
index
))
in
(
Contact
,
ContactGroup
))
if
selected_rows
:
item
=
model
.
items
[
selected_rows
[
0
]]
main_window
=
model
.
main_window
selected_items
=
((
index
.
row
(),
model
.
data
(
index
))
for
index
in
self
.
selectionModel
()
.
selectedIndexes
())
try
:
item
=
(
item
for
row
,
item
in
sorted
(
selected_items
)
if
type
(
item
)
in
(
Contact
,
ContactGroup
))
.
next
()
preferred_group
=
item
if
type
(
item
)
is
ContactGroup
else
item
.
group
e
lse
:
e
xcept
StopIteration
:
try
:
preferred_group
=
(
group
for
group
in
model
.
contact_groups
if
type
(
group
)
is
ContactGroup
)
.
next
()
except
StopIteration
:
preferred_group
=
None
main_window
.
contact_editor
.
open_for_add
(
main_window
.
search_box
.
text
(),
preferred_group
)
def
_AH_EditItem
(
self
):
model
=
self
.
model
()
index
=
self
.
selectionModel
()
.
selectedIndexes
()[
0
]
item
=
self
.
model
()
.
data
(
index
)
item
=
model
.
data
(
index
)
if
isinstance
(
item
,
ContactGroup
):
self
.
scrollTo
(
index
)
item
.
widget
.
edit
()
else
:
model
.
main_window
.
contact_editor
.
open_for_edit
(
item
)
def
_AH_DeleteSelection
(
self
):
model
=
self
.
model
()
...
...
@@ -1330,7 +1389,9 @@ class ContactSearchListView(QListView):
self
.
drop_indicator_index
=
QModelIndex
()
def
_AH_EditItem
(
self
):
contact
=
self
.
model
()
.
data
(
self
.
selectionModel
()
.
selectedIndexes
()[
0
])
model
=
self
.
model
()
contact
=
model
.
data
(
self
.
selectionModel
()
.
selectedIndexes
()[
0
])
model
.
main_window
.
contact_editor
.
open_for_edit
(
contact
)
def
_AH_DeleteSelection
(
self
):
model
=
self
.
model
()
...
...
@@ -1372,3 +1433,105 @@ class ContactSearchListView(QListView):
event
.
ignore
(
rect
)
# The contact editor dialog
#
class
ContactEditorGroupModel
(
QSortFilterProxyModel
):
def
__init__
(
self
,
contact_model
,
parent
=
None
):
super
(
ContactEditorGroupModel
,
self
)
.
__init__
(
parent
)
self
.
setSourceModel
(
contact_model
)
def
data
(
self
,
index
,
role
=
Qt
.
DisplayRole
):
if
role
in
(
Qt
.
DisplayRole
,
Qt
.
EditRole
):
return
super
(
ContactEditorGroupModel
,
self
)
.
data
(
index
,
Qt
.
DisplayRole
)
.
toPyObject
()
.
name
elif
role
==
Qt
.
UserRole
:
return
super
(
ContactEditorGroupModel
,
self
)
.
data
(
index
,
Qt
.
DisplayRole
)
.
toPyObject
()
else
:
return
super
(
ContactEditorGroupModel
,
self
)
.
data
(
index
,
role
)
def
filterAcceptsRow
(
self
,
source_row
,
source_parent
):
source_model
=
self
.
sourceModel
()
item
=
source_model
.
data
(
source_model
.
index
(
source_row
,
0
,
source_parent
),
Qt
.
DisplayRole
)
return
True
if
type
(
item
)
is
ContactGroup
else
False
ui_class
,
base_class
=
uic
.
loadUiType
(
Resources
.
get
(
'contact_editor.ui'
))
class
ContactEditorDialog
(
base_class
,
ui_class
):
def
__init__
(
self
,
contact_model
,
parent
=
None
):
super
(
ContactEditorDialog
,
self
)
.
__init__
(
parent
)
with
Resources
.
directory
:
self
.
setupUi
(
self
)
self
.
edited_contact
=
None
self
.
group
.
setModel
(
ContactEditorGroupModel
(
contact_model
,
parent
))
self
.
sip_address_editor
.
setValidator
(
QRegExpValidator
(
QRegExp
(
"
\
S+"
),
self
))
self
.
sip_address_editor
.
textChanged
.
connect
(
self
.
enable_accept_button
)
self
.
clear_button
.
clicked
.
connect
(
self
.
reset_icon
)
self
.
accepted
.
connect
(
self
.
process_contact
)
def
open_for_add
(
self
,
sip_address
=
u''
,
target_group
=
None
):
self
.
sip_address_editor
.
setText
(
sip_address
)
self
.
display_name_editor
.
setText
(
u''
)
for
index
in
xrange
(
self
.
group
.
count
()):
if
self
.
group
.
itemData
(
index
)
.
toPyObject
()
is
target_group
:
break
else
:
index
=
0
self
.
group
.
setCurrentIndex
(
index
)
self
.
icon_selector
.
filename
=
None
self
.
preferred_media
.
setCurrentIndex
(
0
)
self
.
accept_button
.
setText
(
u'Add'
)
self
.
accept_button
.
setEnabled
(
sip_address
!=
u''
)
self
.
open
()
def
open_for_edit
(
self
,
contact
):
self
.
edited_contact
=
contact
self
.
sip_address_editor
.
setText
(
contact
.
uri
)
self
.
display_name_editor
.
setText
(
contact
.
name
)
for
index
in
xrange
(
self
.
group
.
count
()):
if
self
.
group
.
itemData
(
index
)
.
toPyObject
()
is
contact
.
group
:
break
else
:
index
=
0
self
.
group
.
setCurrentIndex
(
index
)
self
.
icon_selector
.
filename
=
contact
.
image
self
.
preferred_media
.
setCurrentIndex
(
self
.
preferred_media
.
findText
(
contact
.
preferred_media
))
self
.
accept_button
.
setText
(
u'Ok'
)
self
.
accept_button
.
setEnabled
(
True
)
self
.
open
()
def
reset_icon
(
self
):
self
.
icon_selector
.
filename
=
None
def
enable_accept_button
(
self
,
text
):
self
.
accept_button
.
setEnabled
(
text
!=
u''
)
@
updates_contacts_db
def
process_contact
(
self
):
contact_model
=
self
.
parent
()
.
contact_model
uri
=
unicode
(
self
.
sip_address_editor
.
text
())
name
=
unicode
(
self
.
display_name_editor
.
text
())
image
=
IconCache
()
.
store
(
self
.
icon_selector
.
filename
)
preferred_media
=
unicode
(
self
.
preferred_media
.
currentText
())
group_index
=
self
.
group
.
currentIndex
()
group_name
=
self
.
group
.
currentText
()
if
group_name
!=
self
.
group
.
itemText
(
group_index
):
# user edited the group name. first look if we already have a group with that name
index
=
self
.
group
.
findText
(
group_name
)
if
index
>=
0
:
group
=
self
.
group
.
itemData
(
index
)
.
toPyObject
()
else
:
group
=
ContactGroup
(
unicode
(
group_name
))
else
:
group
=
self
.
group
.
itemData
(
group_index
)
.
toPyObject
()
if
self
.
edited_contact
is
None
:
contact
=
Contact
(
group
,
name
,
uri
,
image
)
contact_model
.
addContact
(
contact
)
else
:
attributes
=
dict
(
group
=
group
,
name
=
name
,
uri
=
uri
,
image
=
image
,
preferred_media
=
preferred_media
)
contact_model
.
updateContact
(
self
.
edited_contact
,
attributes
)
self
.
edited_contact
=
None
del
ui_class
,
base_class
blink/mainwindow.py
View file @
8fd67017
...
...
@@ -15,7 +15,7 @@ from zope.interface import implements
from
sipsimple.account
import
AccountManager
,
BonjourAccount
from
blink.contacts
import
Contact
,
ContactModel
,
ContactSearchModel
from
blink.contacts
import
Contact
,
Contact
Group
,
ContactEditorDialog
,
Contact
Model
,
ContactSearchModel
from
blink.resources
import
Resources
from
blink.util
import
run_in_gui_thread
...
...
@@ -47,6 +47,8 @@ class MainWindow(base_class, ui_class):
self
.
contact_model
.
load
()
self
.
contact_editor
=
ContactEditorDialog
(
self
.
contact_model
,
self
)
self
.
contacts_panel
.
sibling_panel
=
self
.
sessions_panel
self
.
contacts_panel
.
sibling_name
=
u'Sessions'
self
.
sessions_panel
.
sibling_panel
=
self
.
contacts_panel
...
...
@@ -56,12 +58,16 @@ class MainWindow(base_class, ui_class):
self
.
contacts_view
.
setCurrentWidget
(
self
.
contact_list_panel
)
self
.
search_view
.
setCurrentWidget
(
self
.
search_list_panel
)
self
.
switch_view
.
clicked
.
connect
(
self
.
switch_main_view
)
self
.
switch_view
_button
.
clicked
.
connect
(
self
.
switch_main_view
)
self
.
search_box
.
textChanged
.
connect
(
self
.
search_box_text_changed
)
self
.
contact_model
.
itemsAdded
.
connect
(
self
.
contact_model_added_items
)
self
.
contact_model
.
itemsRemoved
.
connect
(
self
.
contact_model_removed_items
)
self
.
back_to_contacts_button
.
clicked
.
connect
(
self
.
search_box
.
clear
)
# this can be set in designer -Dan
self
.
back_to_contacts
.
clicked
.
connect
(
self
.
search_box
.
clear
)
# this can be set in designer -Dan
self
.
add_
contact
.
clicked
.
connect
(
self
.
test_
add_contact
)
self
.
add_contact_button
.
clicked
.
connect
(
self
.
add_contact
)
self
.
add_
search_contact_button
.
clicked
.
connect
(
self
.
add_contact
)
self
.
identity
.
activated
[
int
]
.
connect
(
self
.
set_identity
)
...
...
@@ -73,6 +79,19 @@ class MainWindow(base_class, ui_class):
notification_center
.
add_observer
(
self
,
name
=
"SIPAccountDidActivate"
)
notification_center
.
add_observer
(
self
,
name
=
"SIPAccountDidDeactivate"
)
def
add_contact
(
self
,
clicked
):
model
=
self
.
contact_model
selected_items
=
((
index
.
row
(),
model
.
data
(
index
))
for
index
in
self
.
contact_list
.
selectionModel
()
.
selectedIndexes
())
try
:
item
=
(
item
for
row
,
item
in
sorted
(
selected_items
)
if
type
(
item
)
in
(
Contact
,
ContactGroup
))
.
next
()
preferred_group
=
item
if
type
(
item
)
is
ContactGroup
else
item
.
group
except
StopIteration
:
try
:
preferred_group
=
(
group
for
group
in
model
.
contact_groups
if
type
(
group
)
is
ContactGroup
)
.
next
()
except
StopIteration
:
preferred_group
=
None
self
.
contact_editor
.
open_for_add
(
self
.
search_box
.
text
(),
preferred_group
)
def
set_user_icon
(
self
,
image_file_name
):
pixmap
=
QPixmap
(
32
,
32
)
pixmap
.
fill
(
QColor
(
Qt
.
transparent
))
...
...
@@ -91,9 +110,9 @@ class MainWindow(base_class, ui_class):
self
.
image
.
setPixmap
(
pixmap
)
def
enable_call_buttons
(
self
,
enabled
):
self
.
audio_call
.
setEnabled
(
enabled
)
self
.
im_session
.
setEnabled
(
enabled
)
self
.
ds_session
.
setEnabled
(
enabled
)
self
.
audio_call
_button
.
setEnabled
(
enabled
)
self
.
im_session
_button
.
setEnabled
(
enabled
)
self
.
ds_session
_button
.
setEnabled
(
enabled
)
def
set_identity
(
self
,
index
):
account_manager
=
AccountManager
()
...
...
@@ -102,7 +121,7 @@ class MainWindow(base_class, ui_class):
def
search_box_text_changed
(
self
,
text
):
if
text
:
self
.
main_view
.
setCurrentWidget
(
self
.
contacts_panel
)
self
.
switch_view
.
setText
(
u"Sessions"
)
self
.
switch_view
_button
.
setText
(
u"Sessions"
)
self
.
enable_call_buttons
(
True
)
else
:
selected_items
=
self
.
contact_list
.
selectionModel
()
.
selectedIndexes
()
...
...
@@ -113,6 +132,21 @@ class MainWindow(base_class, ui_class):
active_widget
=
self
.
search_list_panel
if
self
.
contact_search_model
.
rowCount
()
else
self
.
not_found_panel
self
.
search_view
.
setCurrentWidget
(
active_widget
)
def
contact_model_added_items
(
self
,
items
):
if
self
.
search_box
.
text
()
.
isEmpty
():
return
active_widget
=
self
.
search_list_panel
if
self
.
contact_search_model
.
rowCount
()
else
self
.
not_found_panel
self
.
search_view
.
setCurrentWidget
(
active_widget
)
def
contact_model_removed_items
(
self
,
items
):
if
self
.
search_box
.
text
()
.
isEmpty
():
return
if
any
(
type
(
item
)
is
Contact
for
item
in
items
)
and
self
.
contact_search_model
.
rowCount
()
==
0
:
self
.
search_box
.
clear
()
else
:
active_widget
=
self
.
search_list_panel
if
self
.
contact_search_model
.
rowCount
()
else
self
.
not_found_panel
self
.
search_view
.
setCurrentWidget
(
active_widget
)
def
contact_list_selection_changed
(
self
,
selected
,
deselected
):
selected_items
=
self
.
contact_list
.
selectionModel
()
.
selectedIndexes
()
self
.
enable_call_buttons
(
len
(
selected_items
)
==
1
and
type
(
self
.
contact_model
.
data
(
selected_items
[
0
]))
is
Contact
)
...
...
@@ -120,19 +154,7 @@ class MainWindow(base_class, ui_class):
def
switch_main_view
(
self
):
widget
=
self
.
main_view
.
currentWidget
()
.
sibling_panel
self
.
main_view
.
setCurrentWidget
(
widget
)
self
.
switch_view
.
setText
(
widget
.
sibling_name
)
def
test_add_contact
(
self
):
from
blink.contacts
import
Contact
,
ContactGroup
import
random
no
=
random
.
randrange
(
1
,
100
)
try
:
test_group
=
(
group
for
group
in
self
.
contact_model
.
contact_groups
if
group
.
name
==
'Test'
)
.
next
()
except
StopIteration
:
test_group
=
ContactGroup
(
'Test'
)
contact
=
Contact
(
test_group
,
'John Doe
%02
d'
%
no
,
'user
%02
d@test.com'
%
no
)
contact
.
status
=
random
.
choice
((
'online'
,
'away'
,
'busy'
,
'offline'
))
self
.
contact_model
.
addContact
(
contact
)
self
.
switch_view_button
.
setText
(
widget
.
sibling_name
)
@
run_in_gui_thread
def
handle_notification
(
self
,
notification
):
...
...
blink/widgets/iconselector.py
0 → 100644
View file @
8fd67017
# Copyright (c) 2010 AG Projects. See LICENSE for details.
#
import
os
from
PyQt4.QtCore
import
Qt
from
PyQt4.QtGui
import
QFileDialog
,
QLabel
,
QPixmap
from
blink.resources
import
ApplicationData
,
Resources
from
blink.widgets.util
import
QtDynamicProperty
class
IconSelector
(
QLabel
):
default_icon
=
QtDynamicProperty
(
'default_icon'
,
unicode
)
def
__init__
(
self
,
parent
=
None
):
super
(
QLabel
,
self
)
.
__init__
(
parent
)
self
.
setMinimumSize
(
36
,
36
)
self
.
filename
=
None
self
.
default_icon
=
None
self
.
last_icon_directory
=
os
.
path
.
expanduser
(
'~'
)
def
_get_filename
(
self
):
return
self
.
__dict__
[
'filename'
]
def
_set_filename
(
self
,
filename
):
self
.
__dict__
[
'filename'
]
=
filename
filename
=
ApplicationData
.
get
(
filename
)
if
filename
else
Resources
.
get
(
self
.
default_icon
)
pixmap
=
QPixmap
()
if
pixmap
.
load
(
filename
):
self
.
setPixmap
(
pixmap
.
scaled
(
32
,
32
,
Qt
.
KeepAspectRatio
,
Qt
.
SmoothTransformation
))
else
:
self
.
setPixmap
(
pixmap
)
filename
=
property
(
_get_filename
,
_set_filename
)
del
_get_filename
,
_set_filename
def
mouseReleaseEvent
(
self
,
event
):
if
event
.
button
()
==
Qt
.
LeftButton
and
self
.
rect
()
.
contains
(
event
.
pos
()):
filename
=
unicode
(
QFileDialog
.
getOpenFileName
(
self
,
u'Select Icon'
,
self
.
last_icon_directory
,
u"Images (*.png *.tiff *.jpg *.xmp *.svg)"
))
if
filename
:
self
.
last_icon_directory
=
os
.
path
.
dirname
(
filename
)
self
.
filename
=
filename
if
os
.
path
.
realpath
(
filename
)
!=
os
.
path
.
realpath
(
Resources
.
get
(
self
.
default_icon
))
else
None
super
(
IconSelector
,
self
)
.
mouseReleaseEvent
(
event
)
resources/blink.ui
View file @
8fd67017
...
...
@@ -230,7 +230,7 @@
</layout>
</item>
<item>
<widget
class=
"QPushButton"
name=
"switch_view"
>
<widget
class=
"QPushButton"
name=
"switch_view
_button
"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Expanding"
vsizetype=
"Minimum"
>
<horstretch>
0
</horstretch>
...
...
@@ -345,7 +345,7 @@
<number>
3
</number>
</property>
<item>
<widget
class=
"QPushButton"
name=
"back_to_contacts"
>
<widget
class=
"QPushButton"
name=
"back_to_contacts
_button
"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Expanding"
vsizetype=
"Fixed"
>
<horstretch>
0
</horstretch>
...
...
@@ -367,7 +367,7 @@
</widget>
</item>
<item>
<widget
class=
"QPushButton"
name=
"add_search_contact"
>
<widget
class=
"QPushButton"
name=
"add_search_contact
_button
"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Expanding"
vsizetype=
"Fixed"
>
<horstretch>
0
</horstretch>
...
...
@@ -486,7 +486,7 @@ buttons below.</string>
<number>
3
</number>
</property>
<item>
<widget
class=
"QToolButton"
name=
"add_contact"
>
<widget
class=
"QToolButton"
name=
"add_contact
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
29
</width>
...
...
@@ -533,7 +533,7 @@ buttons below.</string>
<number>
0
</number>
</property>
<item>
<widget
class=
"QToolButton"
name=
"audio_call"
>
<widget
class=
"QToolButton"
name=
"audio_call
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
29
</width>
...
...
@@ -559,7 +559,7 @@ buttons below.</string>
</widget>
</item>
<item>
<widget
class=
"QToolButton"
name=
"im_session"
>
<widget
class=
"QToolButton"
name=
"im_session
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
29
</width>
...
...
@@ -584,7 +584,7 @@ buttons below.</string>
</widget>
</item>
<item>
<widget
class=
"QToolButton"
name=
"ds_session"
>
<widget
class=
"QToolButton"
name=
"ds_session
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
29
</width>
...
...
@@ -625,7 +625,7 @@ buttons below.</string>
</spacer>
</item>
<item>
<widget
class=
"QToolButton"
name=
"silent"
>
<widget
class=
"QToolButton"
name=
"silent
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
29
</width>
...
...
@@ -690,7 +690,7 @@ buttons below.</string>
<number>
3
</number>
</property>
<item>
<widget
class=
"QPushButton"
name=
"hangup_all"
>
<widget
class=
"QPushButton"
name=
"hangup_all
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
0
</width>
...
...
@@ -712,7 +712,7 @@ buttons below.</string>
</widget>
</item>
<item>
<widget
class=
"QPushButton"
name=
"conference"
>
<widget
class=
"QPushButton"
name=
"conference
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
0
</width>
...
...
@@ -747,7 +747,7 @@ buttons below.</string>
</spacer>
</item>
<item>
<widget
class=
"QToolButton"
name=
"mute"
>
<widget
class=
"QToolButton"
name=
"mute
_button
"
>
<property
name=
"minimumSize"
>
<size>
<width>
29
</width>
...
...
resources/contact_editor.ui
0 → 100644
View file @
8fd67017
<?xml version="1.0" encoding="UTF-8"?>
<ui
version=
"4.0"
>
<class>
ContactEditor
</class>
<widget
class=
"QDialog"
name=
"ContactEditor"
>
<property
name=
"geometry"
>
<rect>
<x>
0
</x>
<y>
0
</y>
<width>
435
</width>
<height>
325
</height>
</rect>
</property>
<property
name=
"minimumSize"
>
<size>
<width>
435
</width>
<height>
325
</height>
</size>
</property>
<property
name=
"windowTitle"
>
<string>
Contact Editor
</string>
</property>
<layout
class=
"QGridLayout"
name=
"grid_layout"
>
<property
name=
"leftMargin"
>
<number>
10
</number>
</property>
<property
name=
"topMargin"
>
<number>
15
</number>
</property>
<property
name=
"rightMargin"
>
<number>
10
</number>
</property>
<property
name=
"bottomMargin"
>
<number>
10
</number>
</property>
<item
row=
"0"
column=
"0"
>
<widget
class=
"QLabel"
name=
"sip_address_label"
>
<property
name=
"text"
>
<string>
SIP Address:
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
</set>
</property>
</widget>
</item>
<item
row=
"0"
column=
"1"
>
<widget
class=
"LineEdit"
name=
"sip_address_editor"
>
<property
name=
"inactiveText"
stdset=
"0"
>
<string>
user@domain
</string>
</property>
<property
name=
"widgetSpacing"
stdset=
"0"
>
<number>
0
</number>
</property>
</widget>
</item>
<item
row=
"0"
column=
"2"
rowspan=
"3"
>
<layout
class=
"QVBoxLayout"
name=
"icon_layout"
>
<property
name=
"spacing"
>
<number>
3
</number>
</property>
<property
name=
"leftMargin"
>
<number>
5
</number>
</property>
<property
name=
"rightMargin"
>
<number>
5
</number>
</property>
<item>
<widget
class=
"IconSelector"
name=
"icon_selector"
>
<property
name=
"minimumSize"
>
<size>
<width>
60
</width>
<height>
60
</height>
</size>
</property>
<property
name=
"maximumSize"
>
<size>
<width>
60
</width>
<height>
60
</height>
</size>
</property>
<property
name=
"frameShape"
>
<enum>
QFrame::StyledPanel
</enum>
</property>
<property
name=
"frameShadow"
>
<enum>
QFrame::Sunken
</enum>
</property>
<property
name=
"text"
>
<string/>
</property>
<property
name=
"pixmap"
>
<pixmap>
icons/default-avatar.png
</pixmap>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignCenter
</set>
</property>
<property
name=
"default_icon"
stdset=
"0"
>
<string>
icons/default-avatar.png
</string>
</property>
</widget>
</item>
<item>
<widget
class=
"QPushButton"
name=
"clear_button"
>
<property
name=
"maximumSize"
>
<size>
<width>
60
</width>
<height>
20
</height>
</size>
</property>
<property
name=
"text"
>
<string>
Clear
</string>
</property>
</widget>
</item>
</layout>
</item>
<item
row=
"1"
column=
"0"
>
<widget
class=
"QLabel"
name=
"display_name_label"
>
<property
name=
"text"
>
<string>
Display Name:
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
</set>
</property>
</widget>
</item>
<item
row=
"1"
column=
"1"
>
<widget
class=
"LineEdit"
name=
"display_name_editor"
>
<property
name=
"inactiveText"
stdset=
"0"
>
<string>
Contact Name
</string>
</property>
<property
name=
"widgetSpacing"
stdset=
"0"
>
<number>
0
</number>
</property>
</widget>
</item>
<item
row=
"2"
column=
"0"
>
<widget
class=
"QLabel"
name=
"group_label"
>
<property
name=
"text"
>
<string>
Group:
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
</set>
</property>
</widget>
</item>
<item
row=
"2"
column=
"1"
>
<widget
class=
"QComboBox"
name=
"group"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Expanding"
vsizetype=
"Fixed"
>
<horstretch>
0
</horstretch>
<verstretch>
0
</verstretch>
</sizepolicy>
</property>
<property
name=
"editable"
>
<bool>
true
</bool>
</property>
<property
name=
"insertPolicy"
>
<enum>
QComboBox::NoInsert
</enum>
</property>
</widget>
</item>
<item
row=
"3"
column=
"0"
colspan=
"3"
>
<spacer
name=
"section_spacer"
>
<property
name=
"orientation"
>
<enum>
Qt::Vertical
</enum>
</property>
<property
name=
"sizeType"
>
<enum>
QSizePolicy::Fixed
</enum>
</property>
<property
name=
"sizeHint"
stdset=
"0"
>
<size>
<width>
412
</width>
<height>
20
</height>
</size>
</property>
</spacer>
</item>
<item
row=
"5"
column=
"0"
>
<widget
class=
"QLabel"
name=
"chat_aliases_label"
>
<property
name=
"text"
>
<string>
SIP Aliases:
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
</set>
</property>
</widget>
</item>
<item
row=
"5"
column=
"1"
colspan=
"2"
>
<widget
class=
"LineEdit"
name=
"chat_aliases_editor"
>
<property
name=
"inactiveText"
stdset=
"0"
>
<string>
List of SIP addresses separated by ;
</string>
</property>
<property
name=
"widgetSpacing"
stdset=
"0"
>
<number>
0
</number>
</property>
</widget>
</item>
<item
row=
"6"
column=
"0"
>
<widget
class=
"QLabel"
name=
"storage_place_label"
>
<property
name=
"text"
>
<string>
Storage Place:
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
</set>
</property>
</widget>
</item>
<item
row=
"6"
column=
"1"
colspan=
"2"
>
<widget
class=
"QComboBox"
name=
"storage_place"
>
<property
name=
"enabled"
>
<bool>
false
</bool>
</property>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Expanding"
vsizetype=
"Fixed"
>
<horstretch>
0
</horstretch>
<verstretch>
0
</verstretch>
</sizepolicy>
</property>
<item>
<property
name=
"text"
>
<string>
Local
</string>
</property>
</item>
<item>
<property
name=
"text"
>
<string>
XCAP
</string>
</property>
</item>
</widget>
</item>
<item
row=
"8"
column=
"1"
colspan=
"2"
>
<widget
class=
"QCheckBox"
name=
"subscribe_presence"
>
<property
name=
"enabled"
>
<bool>
false
</bool>
</property>
<property
name=
"text"
>
<string>
Subscribe To Presence
</string>
</property>
</widget>
</item>
<item
row=
"9"
column=
"1"
colspan=
"2"
>
<widget
class=
"QCheckBox"
name=
"subscribe_dialogs"
>
<property
name=
"enabled"
>
<bool>
false
</bool>
</property>
<property
name=
"text"
>
<string>
Subscribe To Dialogs
</string>
</property>
</widget>
</item>
<item
row=
"10"
column=
"0"
colspan=
"3"
>
<spacer
name=
"grid_spacer"
>
<property
name=
"orientation"
>
<enum>
Qt::Vertical
</enum>
</property>
<property
name=
"sizeHint"
stdset=
"0"
>
<size>
<width>
412
</width>
<height>
20
</height>
</size>
</property>
</spacer>
</item>
<item
row=
"11"
column=
"0"
colspan=
"3"
>
<layout
class=
"QHBoxLayout"
name=
"button_box_layout"
>
<property
name=
"spacing"
>
<number>
6
</number>
</property>
<item>
<spacer
name=
"button_box_spacer"
>
<property
name=
"orientation"
>
<enum>
Qt::Horizontal
</enum>
</property>
<property
name=
"sizeHint"
stdset=
"0"
>
<size>
<width>
40
</width>
<height>
20
</height>
</size>
</property>
</spacer>
</item>
<item>
<widget
class=
"QPushButton"
name=
"reject_button"
>
<property
name=
"minimumSize"
>
<size>
<width>
85
</width>
<height>
25
</height>
</size>
</property>
<property
name=
"text"
>
<string>
Cancel
</string>
</property>
</widget>
</item>
<item>
<widget
class=
"QPushButton"
name=
"accept_button"
>
<property
name=
"minimumSize"
>
<size>
<width>
85
</width>
<height>
25
</height>
</size>
</property>
<property
name=
"text"
>
<string>
Add
</string>
</property>
</widget>
</item>
</layout>
</item>
<item
row=
"4"
column=
"1"
colspan=
"2"
>
<widget
class=
"QComboBox"
name=
"preferred_media"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Expanding"
vsizetype=
"Fixed"
>
<horstretch>
0
</horstretch>
<verstretch>
0
</verstretch>
</sizepolicy>
</property>
<item>
<property
name=
"text"
>
<string>
Audio
</string>
</property>
</item>
<item>
<property
name=
"text"
>
<string>
Chat
</string>
</property>
</item>
<item>
<property
name=
"text"
>
<string>
SMS
</string>
</property>
</item>
</widget>
</item>
<item
row=
"4"
column=
"0"
>
<widget
class=
"QLabel"
name=
"preferred_media_label"
>
<property
name=
"text"
>
<string>
Preferred Media:
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>
LineEdit
</class>
<extends>
QLineEdit
</extends>
<header>
blink.widgets.lineedit
</header>
</customwidget>
<customwidget>
<class>
IconSelector
</class>
<extends>
QLabel
</extends>
<header>
blink.widgets.iconselector
</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>
sip_address_editor
</tabstop>
<tabstop>
display_name_editor
</tabstop>
<tabstop>
group
</tabstop>
<tabstop>
clear_button
</tabstop>
<tabstop>
preferred_media
</tabstop>
<tabstop>
chat_aliases_editor
</tabstop>
<tabstop>
storage_place
</tabstop>
<tabstop>
subscribe_presence
</tabstop>
<tabstop>
subscribe_dialogs
</tabstop>
<tabstop>
reject_button
</tabstop>
<tabstop>
accept_button
</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>
accept_button
</sender>
<signal>
clicked()
</signal>
<receiver>
ContactEditor
</receiver>
<slot>
accept()
</slot>
<hints>
<hint
type=
"sourcelabel"
>
<x>
396
</x>
<y>
301
</y>
</hint>
<hint
type=
"destinationlabel"
>
<x>
341
</x>
<y>
190
</y>
</hint>
</hints>
</connection>
<connection>
<sender>
reject_button
</sender>
<signal>
clicked()
</signal>
<receiver>
ContactEditor
</receiver>
<slot>
reject()
</slot>
<hints>
<hint
type=
"sourcelabel"
>
<x>
292
</x>
<y>
306
</y>
</hint>
<hint
type=
"destinationlabel"
>
<x>
272
</x>
<y>
131
</y>
</hint>
</hints>
</connection>
</connections>
<designerdata>
<property
name=
"gridDeltaX"
>
<number>
10
</number>
</property>
<property
name=
"gridDeltaY"
>
<number>
10
</number>
</property>
<property
name=
"gridSnapX"
>
<bool>
true
</bool>
</property>
<property
name=
"gridSnapY"
>
<bool>
true
</bool>
</property>
<property
name=
"gridVisible"
>
<bool>
true
</bool>
</property>
</designerdata>
</ui>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment