570
579
self.later = gtk.Button()
571
580
self.later.add(gtk.Label(_('Remind me later')))
572
581
self.later.connect('clicked', self.cb_cancel)
573
self.profile = gtk.Button()
574
self.profile.add(gtk.Label(_('View profile')))
575
self.profile.connect('clicked', self.cb_profile)
576
582
self.addbutton = gtk.Button(stock=gtk.STOCK_ADD)
577
583
self.addbutton.connect('clicked', self.cb_add)
578
584
self.bReject = gtk.Button()
579
585
self.bReject.add(gtk.Label(_('Reject')))
580
586
self.bReject.connect('clicked', self.cb_rej)
587
self.profile = gtk.Button()
588
self.profile.add(gtk.Label(_('View profile')))
589
self.profile.connect('clicked', self.cb_profile)
590
self.showDetails = False
591
self.bDetails = gtk.Button()
592
self.bDetails.add(gtk.Label(_('Details')))
593
self.bDetails.connect('clicked', self.cb_show_details)
594
self.bHideDetails = gtk.Button()
595
self.bHideDetails.add(gtk.Label(_('Hide details')))
596
self.bHideDetails.connect('clicked', self.cb_hide_details)
583
599
self.add(self.vbox)
593
609
self.hboxbuttons.pack_start(self.bReject, False, False)
594
610
self.hboxbuttons.pack_start(self.later, False, False)
595
self.hboxbuttons.pack_start(self.profile, False, False)
596
self.hboxbuttons.pack_start(self.buttonbox)
597
self.buttonbox.pack_start(self.addbutton)
611
self.hboxbuttons.pack_start(self.addbutton, False, False)
612
self.hboxbuttons.pack_end(self.buttonbox)
613
self.buttonbox.pack_start(self.bDetails)
615
def cb_show_details(self, button):
616
#the widget that appears when the user clicks "details"
617
self.detailsSection = gtk.HBox(spacing=5)
618
genericAvatar = gtk.image_new_from_pixbuf(self.controller.theme.getImage('login'))
619
genericAvatar.set_size_request(96, 96)
620
labelAlias = gtk.Label(_("Alias"))
621
labelAlias_align = gtk.Alignment(0.0, 0.5)
622
labelAlias_align.add(labelAlias)
623
self.entryAlias = gtk.Entry()
625
group_label = gtk.Label(_("Group"))
626
groupsList = gtk.ListStore(gobject.TYPE_STRING)
627
self.groupComboEntry = gtk.ComboBoxEntry(groupsList, 0)
628
groupCompletion = gtk.EntryCompletion()
629
groupCompletion.set_model(groupsList)
630
self.groupComboEntry.get_children()[0].set_completion(groupCompletion)
631
groupCompletion.set_text_column(0)
632
group_label_align = gtk.Alignment(0.0, 0.5)
633
group_label_align.add(group_label)
635
#fill groups combo box
636
for i in self.controller.groups.groups.keys():
637
groupsList.append([i])
639
labelsVBox = gtk.VBox()
640
labelsVBox.pack_start(labelAlias_align)
641
labelsVBox.pack_start(group_label_align)
643
entriesVBox = gtk.VBox()
644
entriesVBox.pack_start(self.entryAlias)
645
entriesVBox.pack_start(self.groupComboEntry)
647
attributesHBox = gtk.HBox()
648
attributesHBox.pack_start(labelsVBox)
649
attributesHBox.pack_start(entriesVBox, True, True)
651
profileHBox = gtk.HButtonBox()
652
profileHBox.set_layout(gtk.BUTTONBOX_END)
653
profileHBox.pack_start(self.profile)
655
rightSideVBox = gtk.VBox()
656
rightSideVBox.pack_start(attributesHBox)
657
rightSideVBox.pack_start(profileHBox)
659
self.detailsSection.pack_start(genericAvatar)
660
self.detailsSection.pack_start(rightSideVBox, True, True)
662
self.vbox.pack_end(self.detailsSection)
663
self.detailsSection.show_all()
665
self.buttonbox.remove(self.bDetails)
666
self.buttonbox.pack_start(self.bHideDetails)
667
self.profile.show_all()
668
self.bHideDetails.show_all()
669
self.showDetails = True
671
def cb_hide_details(self, button):
672
self.profile.get_parent().remove(self.profile)
673
self.vbox.remove(self.detailsSection)
674
self.detailsSection.destroy()
675
self.buttonbox.remove(self.bHideDetails)
676
self.buttonbox.pack_start(self.bDetails)
677
self.bDetails.show_all()
678
self.showDetails = False
599
680
def _buildpages(self):
600
681
'''Builds hboxpages, that is a bit complex to include in __init__'''
601
682
hboxpages = gtk.HBox()
603
arrowleft = Widgets.TinyArrow(gtk.ARROW_LEFT)
684
arrowleft = gtk.Arrow(gtk.ARROW_LEFT,gtk.SHADOW_NONE)
685
arrowleft.set_size_request(-1,15)
604
686
self.buttonleft = gtk.Button()
605
687
self.buttonleft.set_relief(gtk.RELIEF_NONE)
606
688
self.buttonleft.add(arrowleft)
607
689
self.buttonleft.connect('clicked', self.switchmail, -1)
609
arrowright = Widgets.TinyArrow(gtk.ARROW_RIGHT)
691
arrowright = gtk.Arrow(gtk.ARROW_RIGHT,gtk.SHADOW_NONE)
692
arrowright.set_size_request(-1,15)
610
693
self.buttonright = gtk.Button()
611
694
self.buttonright.set_relief(gtk.RELIEF_NONE)
612
695
self.buttonright.add(arrowright)
689
776
def cb_add(self, button):
690
777
'''Callback when the add button is clicked'''
691
778
mail, nick = self.mails[self.pointer]
692
self.controller.contacts.add(mail, '')
782
group = self.groupComboEntry.get_children()[0].get_text()
783
if group and not group in self.controller.groups.groups.keys():
784
self.controller.groups.add(group)
785
alias = self.entryAlias.get_text()
787
self.controller.contacts.add(mail, group, *(self.delayedAlias, mail, alias))
788
# self.controller.contacts.add(mail, group)
693
789
self.cb_cancel(None)
791
def delayedAlias(self, account, alias):
792
self.controller.contacts.set_alias(account, alias)
695
795
def cb_rej(self, button):
696
796
mail, nick = self.mails[self.pointer]
697
797
self.controller.contacts.remove_from_pending_list(mail)
820
928
self.response_cb(stock.CLOSE, '', '', '')
930
# Class that holds the iconview from the avatar chooser dialog
931
class IconView(gtk.HBox):
932
def __init__(self, label, path_list, avatar_chooser):
933
gtk.HBox.__init__(self)
935
self.chooser = avatar_chooser
936
self.model = gtk.ListStore(gtk.gdk.Pixbuf, str)
937
self.iconview = gtk.IconView(self.model)
938
self.iconview.enable_model_drag_dest([('text/uri-list', 0, 0)], gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_COPY)
939
self.iconview.connect("drag-data-received", self._drag_data_received)
940
self.iconview.set_pixbuf_column(0)
941
self.iconview.connect("item-activated", self._on_icon_activated)
942
self.iconview.connect("button_press_event", self.pop_up)
944
self.label = gtk.Label(label)
946
self.scroll = gtk.ScrolledWindow()
947
self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
948
self.scroll.set_shadow_type(gtk.SHADOW_IN)
949
self.scroll.add(self.iconview)
950
self.pack_start(self.scroll, True, True)
953
# Start a new thread to fill the iconview with images from path_list
954
thread.start_new_thread(self.fill, (path_list,))
956
def stop_and_clear(self):
960
def fill(self, path_list):
961
'''fill the IconView with avatars from the list of pictures'''
962
for search_path in path_list:
963
if os.path.exists(search_path):
964
for path in os.listdir(search_path):
965
name = os.path.splitext(path)[0]
968
if not name.endswith('_thumb') and not path.endswith('tmp') \
969
and not path.endswith('xml') and not path.endswith('db'):
970
gtk.gdk.threads_enter()
971
self.add_picture(os.path.join(search_path, path))
972
# make update the iconview
973
self.iconview.queue_draw()
974
gtk.gdk.threads_leave()
976
# give some time to the main thread (for GUI updates)
978
# Force Garbage Collector to tidy objects
979
# see http://faq.pygtk.org/index.py?req=show&file=faq08.004.htp
982
def pop_up(self, iconview, event):
983
if event.button == 3 and self.label.get_text() != _('System pictures'):
984
path = self.iconview.get_path_at_pos(event.x, event.y)
986
self.iconview.select_path(path)
987
remove_menu = gtk.Menu()
988
remove_item = gtk.ImageMenuItem(_('Delete'))
989
remove_item.set_image(gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU))
990
remove_item.connect('activate', self.chooser._on_remove)
991
remove_menu.append(remove_item)
992
remove_menu.popup(None, None, None, event.button, event.time)
993
remove_menu.show_all()
995
def _drag_data_received(self, treeview, context, x, y, selection, info, timestamp):
996
'''method called on an image dragged to the view'''
997
urls = selection.data.split('\n')
999
path = url.replace('file://', '')
1000
path = path.replace('\r', '')
1002
# the '\x00' value makes an error
1003
path = path.replace(chr(0), '')
1005
# this seems to be an error on ntpath but we take care :S
1007
if os.path.exists(path):
1008
self.add_picture(path)
1009
except TypeError, e:
1010
error(_("Could not add picture:\n %s") % (str(e),))
1012
def add_picture(self, path):
1013
'''Adds an avatar into the IconView'''
1015
if os.path.exists(path) and os.access(path, os.R_OK)\
1016
and not self.is_in_view(path):
1018
pixbuf = gtk.gdk.pixbuf_new_from_file(path)
1026
# On nt images are 128x128 (48x48 on xp)
1027
# On kde, images are 64x64
1028
if (self.label.get_text() == _('System pictures') or \
1029
self.label.get_text() == _('Contact pictures')) and \
1030
(pixbuf.get_width() != 96 or pixbuf.get_height() != 96):
1031
pixbuf = pixbuf.scale_simple(96, 96, gtk.gdk.INTERP_BILINEAR)
1033
if self.model != None and not self.STOP:
1034
self.model.append([pixbuf, path])
1035
# Esplicitely delete gtkpixbuf
1036
# see http://faq.pygtk.org/index.py?req=show&file=faq08.004.htp
1039
print path, 'not readable'
1040
except gobject.GError:
1041
print "image at %s could not be loaded" % path
1043
def is_in_view(self, filename):
1044
'''return True if filename already on the iconview'''
1045
if os.name != 'nt' and not self.STOP:
1046
for (pixbuf, path) in self.model:
1047
if os.path.samefile(filename, path):
1051
def _on_icon_activated(self, *args):
1052
'''method called when a picture is double clicked'''
1053
self.chooser._on_accept(None)
1055
def get_selected_items(self):
1056
return self.iconview.get_selected_items()
823
1058
class AvatarChooser(gtk.Window):
824
1059
'''A dialog to choose an avatar'''
836
1071
self.cache_path = cache_path
838
1073
self.set_title(_("Avatar chooser"))
839
self.set_default_size(602, 400)
1074
self.set_default_size(640, 400)
840
1075
self.set_border_width(4)
841
1076
self.set_position(gtk.WIN_POS_CENTER)
842
1077
self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
844
self.model = gtk.ListStore(gtk.gdk.Pixbuf, str)
846
self.view = gtk.IconView(self.model)
847
self.view.enable_model_drag_dest([('text/uri-list', 0, 0)], gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_COPY)
848
self.view.connect("drag-data-received", self._drag_data_received)
850
self.view.set_pixbuf_column(0)
851
self.view.connect("item-activated", self._on_icon_activated)
853
scroll = gtk.ScrolledWindow()
854
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
855
scroll.set_shadow_type(gtk.SHADOW_IN)
856
scroll.add(self.view)
1081
appDataFolder = os.path.split(os.environ['APPDATA'])[1]
1082
faces_path = os.path.join(os.environ['ALLUSERSPROFILE'], appDataFolder, \
1083
"Microsoft", "User Account Pictures", "Default Pictures")
1084
#little hack to fix problems with encoding
1085
unicodepath=u"%s" % faces_path
1086
faces_paths = [unicodepath]
1088
faces_paths = ['/usr/share/kde/apps/faces',\
1089
'/usr/share/kde4/apps/kdm/pics/users', \
1090
'/usr/share/pixmaps/faces']
1093
self.iconViews.append( IconView(_('Used'), [cache_path], self) )
1094
self.iconViews.append( IconView(_('System pictures'), faces_paths, self) )
1095
self.iconViews.append( IconView(_('Contact pictures'),[self.controller.config.getCachePath()], self) )
858
1097
vbox = gtk.VBox(spacing=4)
859
1098
side_vbox = gtk.VBox(spacing=4)
868
1107
vbbox.set_layout(gtk.BUTTONBOX_START)
870
1109
b_clear = gtk.Button(_("No picture"))
871
b_add = gtk.Button(stock=gtk.STOCK_ADD)
872
b_remove = gtk.Button(stock=gtk.STOCK_REMOVE)
873
b_remove_all = gtk.Button(_("Remove all"))
1110
self.b_add = gtk.Button(stock=gtk.STOCK_ADD)
1111
self.b_remove = gtk.Button(stock=gtk.STOCK_REMOVE)
1112
self.b_remove_all = gtk.Button(_("Remove all"))
874
1113
b_accept = gtk.Button(stock=gtk.STOCK_OK)
875
1114
b_cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
1115
b_accept.set_sensitive(False)
877
1117
b_clear.connect('clicked', self._on_clear)
878
b_add.connect('clicked', self._on_add)
879
b_remove.connect('clicked', self._on_remove)
880
b_remove_all.connect('clicked', self._on_remove_all)
1118
self.b_add.connect('clicked', self._on_add)
1119
self.b_remove.connect('clicked', self._on_remove)
1120
self.b_remove_all.connect('clicked', self._on_remove_all)
881
1121
b_accept.connect('clicked', self._on_accept)
882
1122
b_cancel.connect('clicked', self._on_cancel)
883
1123
self.connect('delete-event', self._on_close)
892
1132
hbbox.pack_start(b_cancel, False)
893
1133
hbbox.pack_start(b_accept, False)
895
vbbox.pack_start(b_add, False)
896
vbbox.pack_start(b_remove, False)
897
vbbox.pack_start(b_remove_all, False)
1135
vbbox.pack_start(self.b_add, False)
1136
vbbox.pack_start(self.b_remove, False)
1137
vbbox.pack_start(self.b_remove_all, False)
899
1139
side_vbox.pack_start(frame_current, False)
900
1140
side_vbox.pack_start(vbbox)
902
hbox.pack_start(scroll, True, True)
1142
self.notebook = gtk.Notebook()
1143
self.notebook.set_show_tabs(True)
1145
for view in self.iconViews:
1146
self.notebook.append_page(view, view.label)
1147
view.iconview.connect("selection-changed",\
1148
self.onSelectionChanged, b_accept)
1149
self.notebook.connect("switch-page", self._on_tab_changed, b_accept)
1151
#close dialog at logout
1152
self.controller.msn.connect('user-disconnected', self._on_close)
1153
self.controller.msn.connect('connection-problem', self._on_close)
1154
self.controller.msn.connect('connection-closed', self._on_close)
1155
self.controller.msn.connect('disconnected', self._on_close)
1156
self.controller.mainWindow.menu.disconnectItem.connect(\
1157
'button-release-event', self._on_close)
1158
if self.controller.trayIcon != None and \
1159
self.controller.trayDisconnect != None:
1160
self.controller.trayDisconnect.connect('activate', self._on_close)
1162
hbox.pack_start(self.notebook, True, True)
903
1163
hbox.pack_start(side_vbox, False, False)
905
1165
vbox.pack_start(hbox, True, True)
912
1171
self.set_current_picture(picture_path)
1173
def onSelectionChanged(self, iconView, accept_button):
1174
selected = iconView.get_selected_items()
1175
if len(selected) == 0:
1176
accept_button.set_sensitive(False)
1178
accept_button.set_sensitive(True)
1180
def stop_and_clear(self):
1181
for view in self.iconViews:
1182
view.stop_and_clear()
1183
# Force Garbage Collector to tidy objects
1184
# see http://faq.pygtk.org/index.py?req=show&file=faq08.004.htp
1187
def _on_tab_changed(self, notebook, page, page_num, b_accept):
1188
view = self.iconViews[page_num]
1189
if view.label.get_text() == _('System pictures'):
1190
self.b_add.set_sensitive(False)
1191
self.b_remove.set_sensitive(False)
1192
self.b_remove_all.set_sensitive(False)
1193
elif view.label.get_text() == _('Contact pictures'):
1194
self.b_add.set_sensitive(False)
1195
self.b_remove.set_sensitive(True)
1196
self.b_remove_all.set_sensitive(True)
1198
self.b_add.set_sensitive(True)
1199
self.b_remove.set_sensitive(True)
1200
self.b_remove_all.set_sensitive(True)
1201
self.onSelectionChanged(view.iconview, b_accept)
914
1203
def set_current_picture(self, path):
915
1204
'''set the current picture on the frame'''
916
1205
if os.path.exists(path):
955
1247
os.path.samefile(path1, path2)) or \
956
1248
(path1.lower() == path2.lower()))
959
'''fill the IconView with avatars from the list of pictures'''
961
for path in os.listdir(self.cache_path):
962
if not os.path.splitext(path)[0].endswith('_thumb'):
963
self.add_picture(os.path.join(self.cache_path, path))
965
def is_in_view(self, filename):
966
'''return True if filename already on the iconview'''
969
# nt doesn't include os.path.samefile
972
for (pixbuf, path) in self.model:
973
if os.path.samefile(filename, path):
979
def add_picture(self, path):
980
'''Adds an avatar into the IconView'''
982
if os.path.exists(path) and os.access(path, os.R_OK)\
983
and not self.is_in_view(path):
984
pixbuf = gtk.gdk.pixbuf_new_from_file(path)
986
self.model.append([pixbuf, path])
988
print path, 'not readable'
989
except gobject.GError:
990
print 'image at %s could not be loaded'
993
1250
def remove(self, path):
994
1251
'''remove the avatar in path'''
995
del self.model[self.get_iter_from_filename(path)]
1252
view = self.iconViews[self.notebook.get_current_page()]
1253
del view.model[self.get_iter_from_filename(path)]
1256
except Exception, e:
1257
print "could not remove", path
998
1260
parts = os.path.splitext(path)
999
1261
os.remove(parts[0] + "_thumb" + parts[1])
1000
1262
except Exception, e:
1001
print "could not remove", path
1003
1265
def remove_selected(self):
1004
1266
'''Removes avatar from a TreeIter'''
1011
1273
def remove_all(self):
1012
1274
'''remove all the items on the view'''
1013
for (pixbuf, path) in self.model:
1275
view = self.iconViews[self.notebook.get_current_page()]
1276
for (pixbuf, path) in view.model:
1014
1277
self.remove(path)
1018
def _drag_data_received(self, treeview, context, x, y, selection, info,
1020
'''method called on an image dragged to the view'''
1021
urls = selection.data.split('\n')
1023
path = url.replace('file://', '')
1024
path = path.replace('\r', '')
1026
# the '\x00' value makes an error
1027
path = path.replace(chr(0), '')
1029
# this seems to be an error on ntpath but we take care :S
1031
if os.path.exists(path):
1032
self.add_picture(path)
1033
except TypeError, e:
1034
error(_("Could not add picture:\n %s") % (str(e),))
1036
def _on_icon_activated(self, *args):
1037
'''method called when a picture is double clicked'''
1038
self._on_accept(None)
1040
1281
def _on_add(self, button):
1041
1282
'''called when the user select the add button'''
1042
1283
def _on_image_selected(response, path):
1043
1284
'''method called when an image is selected'''
1044
if response == stock.ACCEPT:
1285
if response == gtk.RESPONSE_ACCEPT:
1046
1287
av = Avatar.Avatar(self.controller, path, self.cache_path, resizeDialog=True)
1047
1288
filename = av.getImagePath()
1048
self.add_picture(av.getImagePath())
1289
view = self.iconViews[self.notebook.get_current_page()]
1290
view.add_picture(av.getImagePath())
1049
1291
except Exception, e:
1052
ImageChooser(paths.HOME_DIR, _on_image_selected).show()
1294
ImageChooser(paths.HOME_DIR, _on_image_selected).run()
1054
1296
def _on_remove(self, event):
1055
1297
'''Removes the selected avatar'''
1098
1340
if event.keyval == gtk.keysyms.Delete:
1099
1341
self.remove_selected()
1101
class ImageChooser(gtk.Window):
1343
class ImageChooser(gtk.FileChooserDialog):
1102
1344
'''a class to select images'''
1104
1346
def __init__(self, path, response_cb):
1105
1347
'''class constructor, path is the directory where the
1106
1348
dialog opens'''
1107
gtk.Window.__init__(self)
1349
gtk.FileChooserDialog.__init__(self, _("Image Chooser"), \
1350
parent=None, action=gtk.FILE_CHOOSER_ACTION_OPEN)
1109
1352
self.response_cb = response_cb
1111
self.set_title(_("Image Chooser"))
1112
1354
self.set_default_size(600, 400)
1113
1355
self.set_border_width(4)
1114
1356
self.set_position(gtk.WIN_POS_CENTER)
1115
self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
1117
self.vbox = gtk.VBox(spacing=4)
1119
self.file_chooser = gtk.FileChooserWidget()
1120
self.file_chooser.set_current_folder(path)
1358
self.vbox.set_spacing(4)
1360
self.set_current_folder(path)
1122
1362
hbbox = gtk.HButtonBox()
1123
1363
hbbox.set_spacing(4)
1195
1431
self.image.set_size_request(128, 128)
1196
1432
self.image.show()
1198
self.file_chooser.set_preview_widget(self.image)
1199
self.file_chooser.set_preview_widget_active(True)
1434
self.set_preview_widget(self.image)
1435
self.set_preview_widget_active(True)
1201
self.file_chooser.connect('update-preview', self._on_update_preview)
1437
self.connect('selection-changed', self._on_update_preview)
1203
1439
def _on_accept(self, button):
1204
1440
'''method called when the user clicks the button'''
1205
1441
filename = get_filename(self)
1206
1442
if os.path.isfile(filename):
1208
self.response_cb(stock.ACCEPT, filename)
1444
self.response_cb(gtk.RESPONSE_ACCEPT, filename)
1210
1446
error(_("No picture selected"))
1212
1448
def _on_cancel(self, button):
1213
1449
'''method called when the user clicks the button'''
1215
self.response_cb(stock.CANCEL, get_filename(self))
1451
self.response_cb(gtk.RESPONSE_CANCEL, get_filename(self))
1217
1453
def _on_close(self, window, event):
1218
1454
'''called when the user click on close'''
1220
self.response_cb(stock.CLOSE, get_filename(self))
1456
self.response_cb(gtk.RESPONSE_CLOSE, get_filename(self))
1222
1458
def _on_update_preview(self, filechooser):