root/branches/gajim_0.8.1/src/dialogs.py

Revision 3283, 43.1 kB (checked in by nk, 3 years ago)

fix expansion in edit groups dialog

Line 
1##      dialogs.py
2##
3## Gajim Team:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Vincent Hanquez <tab@snarc.org>
6##      - Nikos Kouremenos <kourem@gmail.com>
7##      - Dimitur Kirov <dkirov@gmail.com>
8##
9##      Copyright (C) 2003-2005 Gajim Team
10##
11## This program is free software; you can redistribute it and/or modify
12## it under the terms of the GNU General Public License as published
13## by the Free Software Foundation; version 2 only.
14##
15## This program is distributed in the hope that it will be useful,
16## but WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20
21import gtk
22import gtk.glade
23import gobject
24import os
25
26import gtkgui_helpers
27
28from vcard import VcardWindow
29from filetransfers_window import FileTransfersWindow
30from gajim_themes_window import GajimThemesWindow
31from advanced import AdvancedConfigurationWindow
32from gajim import Contact
33from common import gajim
34from common import helpers
35from common import i18n
36
37_ = i18n._
38APP = i18n.APP
39gtk.glade.bindtextdomain (APP, i18n.DIR)
40gtk.glade.textdomain (APP)
41
42GTKGUI_GLADE = 'gtkgui.glade'
43
44class EditGroupsDialog:
45        '''Class for the edit group dialog window'''
46        def __init__(self, user, account, plugin):
47                self.xml = gtk.glade.XML(GTKGUI_GLADE, 'edit_groups_dialog', APP)
48                self.dialog = self.xml.get_widget('edit_groups_dialog')
49                self.plugin = plugin
50                self.account = account
51                self.user = user
52                self.changes_made = False
53                self.list = self.xml.get_widget('groups_treeview')
54                self.xml.get_widget('nickname_label').set_markup(
55                        _("Contact's name: <i>%s</i>") % user.name)
56                self.xml.get_widget('jid_label').set_markup(
57                        _('JID: <i>%s</i>') % user.jid)
58               
59                self.xml.signal_autoconnect(self)
60                self.init_list()
61
62        def run(self):
63                self.dialog.show_all()
64                if self.changes_made:
65                        gajim.connections[self.account].update_contact(self.user.jid,
66                                self.user.name, self.user.groups)
67
68        def on_edit_groups_dialog_response(self, widget, response_id):
69                if response_id == gtk.RESPONSE_CLOSE:
70                        self.dialog.destroy()
71
72        def update_contact(self):
73                self.plugin.roster.remove_contact(self.user, self.account)
74                self.plugin.roster.add_contact_to_roster(self.user.jid, self.account)
75                gajim.connections[self.account].update_contact(self.user.jid,
76                        self.user.name, self.user.groups)
77
78        def on_add_button_clicked(self, widget):
79                group = self.xml.get_widget('group_entry').get_text().decode('utf-8')
80                if not group:
81                        return
82                # check if it already exists
83                model = self.list.get_model()
84                iter = model.get_iter_root()
85                while iter:
86                        if model.get_value(iter, 0).decode('utf-8') == group:
87                                return
88                        iter = model.iter_next(iter)
89                self.changes_made = True
90                model.append((group, True))
91                self.user.groups.append(group)
92                self.update_contact()
93
94        def group_toggled_cb(self, cell, path):
95                self.changes_made = True
96                model = self.list.get_model()
97                if model[path][1] and len(self.user.groups) == 1: # we try to remove
98                                                                                                                                                  # the last group
99                        ErrorDialog(_('Cannot remove last group'),
100                                        _('At least one contact group must be present.')).get_response()
101                        return
102                model[path][1] = not model[path][1]
103                if model[path][1]:
104                        self.user.groups.append(model[path][0].decode('utf-8'))
105                else:
106                        self.user.groups.remove(model[path][0].decode('utf-8'))
107                self.update_contact()
108
109        def init_list(self):
110                store = gtk.ListStore(str, bool)
111                self.list.set_model(store)
112                for g in gajim.groups[self.account].keys():
113                        if g in [_('Transports'), _('not in the roster')]:
114                                continue
115                        iter = store.append()
116                        store.set(iter, 0, g)
117                        if g in self.user.groups:
118                                store.set(iter, 1, True)
119                        else:
120                                store.set(iter, 1, False)
121                column = gtk.TreeViewColumn(_('Group'))
122                column.set_expand(True)
123                self.list.append_column(column)
124                renderer = gtk.CellRendererText()
125                column.pack_start(renderer)
126                column.set_attributes(renderer, text = 0)
127               
128                column = gtk.TreeViewColumn(_('In the group'))
129                column.set_expand(False)
130                self.list.append_column(column)
131                renderer = gtk.CellRendererToggle()
132                column.pack_start(renderer)
133                renderer.set_property('activatable', True)
134                renderer.connect('toggled', self.group_toggled_cb)
135                column.set_attributes(renderer, active = 1)
136
137class PassphraseDialog:
138        '''Class for Passphrase dialog'''
139        def run(self):
140                '''Wait for OK button to be pressed and return passphrase/password'''
141                rep = self.window.run()
142                if rep == gtk.RESPONSE_OK:
143                        passphrase = self.passphrase_entry.get_text().decode('utf-8')
144                else:
145                        passphrase = -1
146                save_passphrase_checkbutton = self.xml.\
147                        get_widget('save_passphrase_checkbutton')
148                self.window.destroy()
149                return passphrase, save_passphrase_checkbutton.get_active()
150
151        def __init__(self, titletext, labeltext, checkbuttontext):
152                self.xml = gtk.glade.XML(GTKGUI_GLADE, 'passphrase_dialog', APP)
153                self.window = self.xml.get_widget('passphrase_dialog')
154                self.passphrase_entry = self.xml.get_widget('passphrase_entry')
155                self.passphrase = -1
156                self.window.set_title(titletext)
157                self.xml.get_widget('message_label').set_text(labeltext)
158                self.xml.get_widget('save_passphrase_checkbutton').set_label(
159                        checkbuttontext)
160                self.xml.signal_autoconnect(self)
161                self.window.show_all()
162
163class ChooseGPGKeyDialog:
164        '''Class for GPG key dialog'''
165        def __init__(self, title_text, prompt_text, secret_keys, selected = None):
166                #list : {keyID: userName, ...}
167                xml = gtk.glade.XML(GTKGUI_GLADE, 'choose_gpg_key_dialog', APP)
168                self.window = xml.get_widget('choose_gpg_key_dialog')
169                self.window.set_title(title_text)
170                self.keys_treeview = xml.get_widget('keys_treeview')
171                prompt_label = xml.get_widget('prompt_label')
172                prompt_label.set_text(prompt_text)
173                model = gtk.ListStore(str, str)
174                self.keys_treeview.set_model(model)
175                #columns
176                renderer = gtk.CellRendererText()
177                self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
178                        renderer, text = 0)
179                renderer = gtk.CellRendererText()
180                self.keys_treeview.insert_column_with_attributes(-1, _('Contact name'),
181                        renderer, text = 1)
182                self.fill_tree(secret_keys, selected)
183                self.window.show_all()
184
185        def run(self):
186                rep = self.window.run()
187                if rep == gtk.RESPONSE_OK:
188                        selection = self.keys_treeview.get_selection()
189                        (model, iter) = selection.get_selected()
190                        keyID = [ model[iter][0].decode('utf-8'), model[iter][1] ]
191                else:
192                        keyID = None
193                self.window.destroy()
194                return keyID
195
196        def fill_tree(self, list, selected):
197                model = self.keys_treeview.get_model()
198                for keyID in list.keys():
199                        iter = model.append((keyID, list[keyID]))
200                        if keyID == selected:
201                                path = model.get_path(iter)
202                                self.keys_treeview.set_cursor(path)
203
204
205class ChangeStatusMessageDialog:
206        def __init__(self, plugin, show):
207                self.show = show
208                self.xml = gtk.glade.XML(GTKGUI_GLADE, 'change_status_message_dialog', APP)
209                self.window = self.xml.get_widget('change_status_message_dialog')
210                uf_show = helpers.get_uf_show(show)
211                self.window.set_title(_('%s Status Message') % uf_show)
212               
213                message_textview = self.xml.get_widget('message_textview')
214                self.message_buffer = message_textview.get_buffer()
215                msg = gajim.config.get('last_status_msg_' + show)
216                if not msg:
217                        msg = ''
218                msg = helpers.from_one_line(msg)
219                self.message_buffer.set_text(msg)
220                self.values = {'':''} # have an empty string selectable, so user can clear msg
221                for msg in gajim.config.get_per('statusmsg'):
222                        self.values[msg] = gajim.config.get_per('statusmsg', msg, 'message')
223                sorted_keys_list = helpers.get_sorted_keys(self.values)
224                liststore = gtk.ListStore(str, str)
225                message_comboboxentry = self.xml.get_widget('message_comboboxentry')
226                message_comboboxentry.set_model(liststore)
227                message_comboboxentry.set_text_column(0)
228                message_comboboxentry.child.set_property('editable', False)
229                for val in sorted_keys_list:
230                        message_comboboxentry.append_text(val)
231                self.xml.signal_autoconnect(self)
232                self.window.show_all()
233
234        def run(self):
235                '''Wait for OK button to be pressed and return status messsage'''
236                rep = self.window.run()
237                if rep == gtk.RESPONSE_OK:
238                        beg, end = self.message_buffer.get_bounds()
239                        message = self.message_buffer.get_text(beg, end, 0).decode('utf-8').strip()
240                        msg = helpers.to_one_line(message)
241                        gajim.config.set('last_status_msg_' + self.show, msg)
242                else:
243                        message = -1
244                self.window.destroy()
245                return message
246
247        def on_message_comboboxentry_changed(self, widget, data = None):
248                model = widget.get_model()
249                active = widget.get_active()
250                if active < 0:
251                        return None
252                name = model[active][0].decode('utf-8')
253                self.message_buffer.set_text(self.values[name])
254       
255        def on_change_status_message_dialog_key_press_event(self, widget, event):
256                if event.keyval == gtk.keysyms.Return or \
257                event.keyval == gtk.keysyms.KP_Enter:  # catch CTRL+ENTER
258                        if (event.state & gtk.gdk.CONTROL_MASK):
259                                self.window.response(gtk.RESPONSE_OK)
260
261class AddNewContactWindow:
262        '''Class for AddNewContactWindow'''
263        def __init__(self, plugin, account, jid = None):
264                self.plugin = plugin
265                self.account = account
266                self.xml = gtk.glade.XML(GTKGUI_GLADE, 'add_new_contact_window', APP)
267                self.window = self.xml.get_widget('add_new_contact_window')
268                self.uid_entry = self.xml.get_widget('uid_entry')
269                self.protocol_combobox = self.xml.get_widget('protocol_combobox')
270                self.jid_entry = self.xml.get_widget('jid_entry')
271                self.nickname_entry = self.xml.get_widget('nickname_entry')
272                if len(gajim.connections) >= 2:
273                        prompt_text =\
274_('Please fill in the data of the contact you want to add in account %s') %account
275                else:
276                        prompt_text = _('Please fill in the data of the contact you want to add')
277                self.xml.get_widget('prompt_label').set_text(prompt_text)
278                self.old_uid_value = ''
279                liststore = gtk.ListStore(str, str)
280                liststore.append(['Jabber', ''])
281                self.agents = ['Jabber']
282                jid_agents = []
283                for j in gajim.contacts[account]:
284                        user = gajim.contacts[account][j][0]
285                        if _('Transports') in user.groups and user.show != 'offline' and \
286                                        user.show != 'error':
287                                jid_agents.append(j)
288                for a in jid_agents:
289                        if a.find('aim') > -1:
290                                name = 'AIM'
291                        elif a.find('icq') > -1:
292                                name = 'ICQ'
293                        elif a.find('msn') > -1:
294                                name = 'MSN'
295                        elif a.find('yahoo') > -1:
296                                name = 'Yahoo!'
297                        else:
298                                name = a
299                        iter = liststore.append([name, a])
300                        self.agents.append(name)
301               
302                self.protocol_combobox.set_model(liststore)
303                self.protocol_combobox.set_active(0)
304                self.fill_jid()
305                if jid:
306                        self.jid_entry.set_text(jid)
307                        jid_splited = jid.split('@')
308                        if jid_splited[1] in jid_agents:
309                                uid = jid_splited[0].replace('%', '@')
310                                self.uid_entry.set_text(uid)
311                                self.protocol_combobox.set_active(jid_agents.index(jid_splited[1]) + 1)
312                        else:
313                                self.uid_entry.set_text(jid)
314                                self.protocol_combobox.set_active(0)
315                        self.set_nickname()
316
317                self.group_comboboxentry = self.xml.get_widget('group_comboboxentry')
318                liststore = gtk.ListStore(str)
319                self.group_comboboxentry.set_model(liststore)
320                for g in gajim.groups[account].keys():
321                        if g != _('not in the roster') and g != _('Transports'):
322                                self.group_comboboxentry.append_text(g)
323
324                if not jid_agents:
325                        # There are no transports, just hide the protocol combobox
326                        self.protocol_combobox.hide()
327                        self.protocol_combobox.set_no_show_all(True)
328                        protocol_label = self.xml.get_widget('protocol_label')
329                        protocol_label.hide()
330                        protocol_label.set_no_show_all(True)
331
332                self.xml.signal_autoconnect(self)
333                self.window.show_all()
334
335        def on_add_new_contact_window_key_press_event(self, widget, event):
336                if event.keyval == gtk.keysyms.Escape: # ESCAPE
337                        self.window.destroy()
338
339        def on_cancel_button_clicked(self, widget):
340                '''When Cancel button is clicked'''
341                self.window.destroy()
342
343        def on_subscribe_button_clicked(self, widget):
344                '''When Subscribe button is clicked'''
345                jid = self.jid_entry.get_text().decode('utf-8')
346                nickname = self.nickname_entry.get_text().decode('utf-8')
347                if not jid:
348                        return
349                if jid.find('@') < 0:
350                        ErrorDialog(_("Invalid user name"),
351_('Contact names must be of the form "user@servername".')).get_response()
352                        return
353                message_buffer = self.xml.get_widget('message_textview').get_buffer()
354                start_iter = message_buffer.get_start_iter()
355                end_iter = message_buffer.get_end_iter()
356                message = message_buffer.get_text(start_iter, end_iter, 0).decode('utf-8')
357                group = self.group_comboboxentry.child.get_text().decode('utf-8')
358                self.plugin.roster.req_sub(self, jid, message, self.account,
359                        group = group, pseudo = nickname)
360                if self.xml.get_widget('auto_authorize_checkbutton').get_active():
361                        gajim.connections[self.account].send_authorization(jid)
362                self.window.destroy()
363               
364        def fill_jid(self):
365                model = self.protocol_combobox.get_model()
366                index = self.protocol_combobox.get_active()
367                jid = self.uid_entry.get_text().decode('utf-8').strip()
368                if index > 0: # it's not jabber but a transport
369                        jid = jid.replace('@', '%')
370                agent = model[index][1].decode('utf-8')
371                if agent:
372                        jid += '@' + agent
373                self.jid_entry.set_text(jid)
374
375        def on_protocol_combobox_changed(self, widget):
376                self.fill_jid()
377
378        def guess_agent(self):
379                uid = self.uid_entry.get_text().decode('utf-8')
380                model = self.protocol_combobox.get_model()
381               
382                #If login contains only numbers, it's probably an ICQ number
383                if uid.isdigit():
384                        if 'ICQ' in self.agents:
385                                self.protocol_combobox.set_active(self.agents.index('ICQ'))
386                                return
387
388        def set_nickname(self):
389                uid = self.uid_entry.get_text().decode('utf-8')
390                nickname = self.nickname_entry.get_text().decode('utf-8')
391                if nickname == self.old_uid_value:
392                        self.nickname_entry.set_text(uid.split('@')[0])
393                       
394        def on_uid_entry_changed(self, widget):
395                uid = self.uid_entry.get_text().decode('utf-8')
396                self.guess_agent()
397                self.set_nickname()
398                self.fill_jid()
399                self.old_uid_value = uid.split('@')[0]
400
401class AboutDialog:
402        '''Class for about dialog'''
403        def __init__(self):
404                if gtk.pygtk_version < (2, 6, 0) or gtk.gtk_version < (2, 6, 0):
405                        InformationDialog(_('Gajim - a GTK+ Jabber client'),
406                                _('Version %s') % gajim.version)
407                        return
408
409                dlg = gtk.AboutDialog()
410                dlg.set_name('Gajim')
411                dlg.set_version(gajim.version)
412                s = u'Copyright \xa9 2003-2005 Gajim Team'
413                dlg.set_copyright(s)
414                text = open('../COPYING').read()
415                dlg.set_license(text)
416
417                dlg.set_comments(_('A GTK jabber client'))
418                dlg.set_website('http://www.gajim.org')
419
420                authors = ['Yann Le Boulanger <asterix@lagaule.org>', 'Vincent Hanquez <tab@snarc.org>', 'Nikos Kouremenos <kourem@gmail.com>', 'Dimitur Kirov <dkirov@gmail.com>', 'Gajim patchers']
421                dlg.set_authors(authors)
422
423                pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(gajim.DATA_DIR, 'pixmaps/gajim_about.png'))                 
424
425                dlg.set_logo(pixbuf)
426                dlg.set_translator_credits(_('translator-credits'))
427               
428                artists = ['Denis Craven', 'Membris Khan', 'Guillaume Morin']
429                dlg.set_artists(artists)
430
431                rep = dlg.run()
432                dlg.destroy()
433
434class Dialog(gtk.Dialog):
435        def __init__(self, parent, title, buttons, default = None):
436                gtk.Dialog.__init__(self, title, parent, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL | gtk.DIALOG_NO_SEPARATOR)
437
438                self.set_border_width(6)
439                self.vbox.set_spacing(12)
440                self.set_resizable(False)
441
442                for stock, response in buttons:
443                        self.add_button(stock, response)
444
445                if default is not None:
446                        self.set_default_response(default)
447                else:
448                        self.set_default_response(buttons[-1][1])
449
450        def get_button(self, index):
451                buttons = self.action_area.get_children()
452                return index < len(buttons) and buttons[index] or None
453
454
455class HigDialog(Dialog):
456        def __init__(self, parent, pritext, sectext, stockimage, buttons, default = None):
457                """GNOME higified version of the Dialog object. Inherit
458                from here if possible when you need a new dialog."""
459                Dialog.__init__(self, parent, "", buttons, default)
460
461                # hbox separating dialog image and contents
462                hbox = gtk.HBox()
463                hbox.set_spacing(12)
464                hbox.set_border_width(6)
465                self.vbox.pack_start(hbox)
466
467                # set up image
468                if stockimage is not None:
469                        image = gtk.Image()
470                        image.set_from_stock(stockimage, gtk.ICON_SIZE_DIALOG)
471                        image.set_alignment(0.5, 0)
472                        hbox.pack_start(image, False, False)
473
474                # set up main content area
475                self.contents = gtk.VBox()
476                self.contents.set_spacing(10)
477                hbox.pack_start(self.contents)
478
479                label = gtk.Label()
480                label.set_markup("<span size=\"larger\" weight=\"bold\">" + pritext + "</span>\n\n" + sectext)
481                label.set_line_wrap(True)
482                label.set_alignment(0, 0)
483                label.set_selectable(True)
484                self.contents.pack_start(label)
485
486        def get_response(self):
487                self.show_all()
488                response = gtk.Dialog.run(self)
489                self.destroy()
490                return response
491
492class ConfirmationDialog(HigDialog):
493        """HIG compliant confirmation dialog."""
494        def __init__(self, pritext, sectext=''):
495                HigDialog.__init__(self, None, pritext, sectext,
496                        gtk.STOCK_DIALOG_WARNING, [ [gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL],
497                        [ gtk.STOCK_OK, gtk.RESPONSE_OK ] ])
498                       
499class ConfirmationDialogCheck(ConfirmationDialog):
500        '''HIG compliant confirmation dialog with checkbutton.'''
501        def __init__(self, pritext, sectext='', checktext = ''):
502                HigDialog.__init__(self, None, pritext, sectext,
503                        gtk.STOCK_DIALOG_WARNING, [ [gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL] ])
504               
505                # add ok button manually, because we need to focus on it
506                ok_button = self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
507                self.checkbutton = gtk.CheckButton(checktext)
508                self.vbox.pack_start(self.checkbutton, expand = False, fill = True)
509                ok_button.grab_focus()
510       
511        def is_checked(self):
512                ''' Get active state of the checkbutton '''
513                return self.checkbutton.get_active()
514               
515class WarningDialog(HigDialog):
516        def __init__(self, pritext, sectext=''):
517                """HIG compliant warning dialog."""
518                HigDialog.__init__(
519                        self, None, pritext, sectext, gtk.STOCK_DIALOG_WARNING,
520                        [ [ gtk.STOCK_OK, gtk.RESPONSE_OK ] ]
521                )
522
523class InformationDialog(HigDialog):
524        def __init__(self, pritext, sectext=''):
525                """HIG compliant info dialog."""
526                HigDialog.__init__(
527                        self, None, pritext, sectext, gtk.STOCK_DIALOG_INFO,
528                        [ [ gtk.STOCK_OK, gtk.RESPONSE_OK ] ]
529                )
530                ok_button = self.action_area.get_children()[0]
531                ok_button.connect('clicked', self.on_ok_button_clicked)
532                self.show_all()
533
534        def on_ok_button_clicked(self, widget):
535                self.destroy()
536
537class InputDialog:
538        '''Class for Input dialog'''
539        def __init__(self, title, label_str, input_str = None, is_modal = True, ok_handler = None):
540                xml = gtk.glade.XML(GTKGUI_GLADE, 'input_dialog', APP)
541                self.dialog = xml.get_widget('input_dialog')
542                label = xml.get_widget('label')
543                self.input_entry = xml.get_widget('input_entry')
544                self.dialog.set_title(title)
545                label.set_text(label_str)
546                if input_str:
547                        self.input_entry.set_text(input_str)
548                        self.input_entry.select_region(0, -1) # select all
549               
550                self.is_modal = is_modal
551                if not is_modal and ok_handler is not None:
552                        self.ok_handler = ok_handler
553                        okbutton = xml.get_widget('okbutton')
554                        okbutton.connect('clicked', self.on_okbutton_clicked)
555                        cancelbutton = xml.get_widget('cancelbutton')
556                        cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
557                        self.