root/branches/gajim_0.11/src/gtkgui_helpers.py

Revision 7940, 23.5 kB (checked in by asterix, 20 months ago)

merge diff from trunk

  • Property svn:eol-style set to LF
Line 
1##      gtkgui_helpers.py
2##
3## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
4## Copyright (C) 2004-2005 Vincent Hanquez <tab@snarc.org>
5## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
6## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
7## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
8## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
9##
10## This program is free software; you can redistribute it and/or modify
11## it under the terms of the GNU General Public License as published
12## by the Free Software Foundation; version 2 only.
13##
14## This program is distributed in the hope that it will be useful,
15## but WITHOUT ANY WARRANTY; without even the implied warranty of
16## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17## GNU General Public License for more details.
18##
19
20import xml.sax.saxutils
21import gtk
22import gtk.glade
23import gobject
24import pango
25import os
26import sys
27
28import vcard
29import dialogs
30
31
32HAS_PYWIN32 = True
33if os.name == 'nt':
34        try:
35                import win32file
36                import win32con
37                import pywintypes
38        except ImportError:
39                HAS_PYWIN32 = False
40
41from common import i18n
42from common import gajim
43from common import helpers
44
45gtk.glade.bindtextdomain(i18n.APP, i18n.DIR)
46gtk.glade.textdomain(i18n.APP)
47
48screen_w = gtk.gdk.screen_width()
49screen_h = gtk.gdk.screen_height()
50
51GLADE_DIR = os.path.join('..', 'data', 'glade')
52def get_glade(file_name, root = None):
53        file_path = os.path.join(GLADE_DIR, file_name)
54        return gtk.glade.XML(file_path, root=root, domain=i18n.APP)
55
56def get_completion_liststore(entry):
57        ''' create a completion model for entry widget
58        completion list consists of (Pixbuf, Text) rows'''
59        completion = gtk.EntryCompletion()
60        liststore = gtk.ListStore(gtk.gdk.Pixbuf, str)
61       
62        render_pixbuf = gtk.CellRendererPixbuf()
63        completion.pack_start(render_pixbuf, expand = False)
64        completion.add_attribute(render_pixbuf, 'pixbuf', 0)
65       
66        render_text = gtk.CellRendererText()
67        completion.pack_start(render_text, expand = True)
68        completion.add_attribute(render_text, 'text', 1)
69        completion.set_property('text_column', 1)
70        completion.set_model(liststore)
71        entry.set_completion(completion)
72        return liststore
73       
74       
75def popup_emoticons_under_button(menu, button, parent_win):
76        ''' pops emoticons menu under button, which is in parent_win'''
77        window_x1, window_y1 = parent_win.get_origin()
78        def position_menu_under_button(menu):
79                # inline function, which will not keep refs, when used as CB
80                button_x, button_y = button.allocation.x, button.allocation.y
81               
82                # now convert them to X11-relative
83                window_x, window_y = window_x1, window_y1
84                x = window_x + button_x
85                y = window_y + button_y
86
87                menu_width, menu_height = menu.size_request()
88
89                ## should we pop down or up?
90                if (y + button.allocation.height + menu_height
91                        < gtk.gdk.screen_height()):
92                        # now move the menu below the button
93                        y += button.allocation.height
94                else:
95                        # now move the menu above the button
96                        y -= menu_height
97
98                # push_in is True so all the menuitems are always inside screen
99                push_in = True
100                return (x, y, push_in)
101
102        menu.popup(None, None, position_menu_under_button, 1, 0)
103       
104def get_theme_font_for_option(theme, option):
105        '''return string description of the font, stored in
106        theme preferences'''
107        font_name = gajim.config.get_per('themes', theme, option)
108        font_desc = pango.FontDescription()
109        font_prop_str =  gajim.config.get_per('themes', theme, option + 'attrs')
110        if font_prop_str:
111                if font_prop_str.find('B') != -1:
112                        font_desc.set_weight(pango.WEIGHT_BOLD)
113                if font_prop_str.find('I') != -1:
114                        font_desc.set_style(pango.STYLE_ITALIC)
115        fd = pango.FontDescription(font_name)
116        fd.merge(font_desc, True)
117        return fd.to_string()
118       
119def get_default_font():
120        '''Get the desktop setting for application font
121        first check for GNOME, then XFCE and last KDE
122        it returns None on failure or else a string 'Font Size' '''
123       
124        try:
125                import gconf
126                # in try because daemon may not be there
127                client = gconf.client_get_default()
128
129                return client.get_string('/desktop/gnome/interface/font_name'
130                        ).decode('utf-8')
131        except:
132                pass
133
134        # try to get xfce default font
135        # Xfce 4.2 adopts freedesktop.org's Base Directory Specification
136        # see http://www.xfce.org/~benny/xfce/file-locations.html
137        # and http://freedesktop.org/Standards/basedir-spec
138        xdg_config_home = os.environ.get('XDG_CONFIG_HOME', '')
139        if xdg_config_home == '':
140                xdg_config_home = os.path.expanduser('~/.config') # default     
141        xfce_config_file = os.path.join(xdg_config_home, 'xfce4/mcs_settings/gtk.xml')
142       
143        kde_config_file = os.path.expanduser('~/.kde/share/config/kdeglobals')
144       
145        if os.path.exists(xfce_config_file):
146                try:
147                        for line in file(xfce_config_file):
148                                if line.find('name="Gtk/FontName"') != -1:
149                                        start = line.find('value="') + 7
150                                        return line[start:line.find('"', start)].decode('utf-8')
151                except:
152                        #we talk about file
153                        print >> sys.stderr, _('Error: cannot open %s for reading') % xfce_config_file
154       
155        elif os.path.exists(kde_config_file):
156                try:
157                        for line in file(kde_config_file):
158                                if line.find('font=') == 0: # font=Verdana,9,other_numbers
159                                        start = 5 # 5 is len('font=')
160                                        line = line[start:]
161                                        values = line.split(',')
162                                        font_name = values[0]
163                                        font_size = values[1]
164                                        font_string = '%s %s' % (font_name, font_size) # Verdana 9
165                                        return font_string.decode('utf-8')
166                except:
167                        #we talk about file
168                        print >> sys.stderr, _('Error: cannot open %s for reading') % kde_config_file
169       
170        return None
171       
172def escape_for_pango_markup(string):
173        # escapes < > & ' "
174        # for pango markup not to break
175        if string is None:
176                return
177        if gtk.pygtk_version >= (2, 8, 0) and gtk.gtk_version >= (2, 8, 0):
178                escaped_str = gobject.markup_escape_text(string)
179        else:
180                escaped_str = xml.sax.saxutils.escape(string, {"'": '&apos;',
181                        '"': '&quot;'})
182       
183        return escaped_str
184
185def autodetect_browser_mailer():
186        # recognize the environment and set appropriate browser/mailer
187        if user_runs_gnome():
188                gajim.config.set('openwith', 'gnome-open')
189        elif user_runs_kde():
190                gajim.config.set('openwith', 'kfmclient exec')
191        elif user_runs_xfce():
192                gajim.config.set('openwith', 'exo-open')
193        else:
194                gajim.config.set('openwith', 'custom')
195
196def user_runs_gnome():
197        return 'gnome-session' in get_running_processes()
198
199def user_runs_kde():
200        return 'startkde' in get_running_processes()
201
202def user_runs_xfce():
203        procs = get_running_processes()
204        if 'startxfce4' in procs or 'xfce4-session' in procs:
205                return True
206        return False
207
208def get_running_processes():
209        '''returns running processes or None (if not /proc exists)'''
210        if os.path.isdir('/proc'):
211                # under Linux: checking if 'gnome-session' or
212                # 'startkde' programs were run before gajim, by
213                # checking /proc (if it exists)
214                #
215                # if something is unclear, read `man proc`;
216                # if /proc exists, directories that have only numbers
217                # in their names contain data about processes.
218                # /proc/[xxx]/exe is a symlink to executable started
219                # as process number [xxx].
220                # filter out everything that we are not interested in:
221                files = os.listdir('/proc')
222
223                # files that doesn't have only digits in names...
224                files = filter(str.isdigit, files)
225
226                # files that aren't directories...
227                files = filter(lambda f:os.path.isdir('/proc/' + f), files)
228
229                # processes owned by somebody not running gajim...
230                # (we check if we have access to that file)
231                files = filter(lambda f:os.access('/proc/' + f +'/exe', os.F_OK), files)
232
233                # be sure that /proc/[number]/exe is really a symlink
234                # to avoid TBs in incorrectly configured systems
235                files = filter(lambda f:os.path.islink('/proc/' + f + '/exe'), files)
236
237                # list of processes
238                processes = [os.path.basename(os.readlink('/proc/' + f +'/exe')) for f in files]
239               
240                return processes
241        return []
242
243def move_window(window, x, y):
244        '''moves the window but also checks if out of screen'''
245        if x < 0:
246                x = 0
247        if y < 0:
248                y = 0
249        window.move(x, y)
250
251def resize_window(window, w, h):
252        '''resizes window but also checks if huge window or negative values'''
253        if not w or not h:
254                return
255        if w > screen_w:
256                w = screen_w
257        if h > screen_h:
258                h = screen_h
259        window.resize(abs(w), abs(h))
260
261class TagInfoHandler(xml.sax.ContentHandler):
262        def __init__(self, tagname1, tagname2):
263                xml.sax.ContentHandler.__init__(self)
264                self.tagname1 = tagname1
265                self.tagname2 = tagname2
266                self.servers = []
267
268        def startElement(self, name, attributes):
269                if name == self.tagname1:
270                        for attribute in attributes.getNames():
271                                if attribute == 'jid':
272                                        jid = attributes.getValue(attribute)
273                                        # we will get the port next time so we just set it 0 here
274                                        self.servers.append([jid, 0])
275                elif name == self.tagname2:
276                        for attribute in attributes.getNames():
277                                if attribute == 'port':
278                                        port = attributes.getValue(attribute)
279                                        # we received the jid last time, so we now assign the port
280                                        # number to the last jid in the list
281                                        self.servers[-1][1] = port
282
283        def endElement(self, name):
284                pass
285
286def parse_server_xml(path_to_file):
287        try:
288                handler = TagInfoHandler('item', 'active')
289                xml.sax.parse(path_to_file, handler)
290                return handler.servers
291        # handle exception if unable to open file
292        except IOError, message:
293                print >> sys.stderr, _('Error reading file:'), message
294        # handle exception parsing file
295        except xml.sax.SAXParseException, message:
296                print >> sys.stderr, _('Error parsing file:'), message
297
298def set_unset_urgency_hint(window, unread_messages_no):
299        '''sets/unsets urgency hint in window argument
300        depending if we have unread messages or not'''
301        if gtk.gtk_version >= (2, 8, 0) and gtk.pygtk_version >= (2, 8, 0) and \
302                gajim.config.get('use_urgency_hint'):
303                if unread_messages_no > 0:
304                        window.props.urgency_hint = True
305                else:
306                        window.props.urgency_hint = False
307
308def get_abspath_for_script(scriptname, want_type = False):
309        '''checks if we are svn or normal user and returns abspath to asked script
310        if want_type is True we return 'svn' or 'install' '''
311        if os.path.isdir('.svn'): # we are svn user
312                type = 'svn'
313                cwd = os.getcwd() # it's always ending with src
314
315                if scriptname == 'gajim-remote':
316                        path_to_script = cwd + '/gajim-remote.py'
317               
318                elif scriptname == 'gajim':
319                        script = '#!/bin/sh\n' # the script we may create
320                        script += 'cd %s' % cwd
321                        path_to_script = cwd + '/../scripts/gajim_sm_script'
322                               
323                        try:
324                                if os.path.exists(path_to_script):
325                                        os.remove(path_to_script)
326
327                                f = open(path_to_script, 'w')
328                                script += '\nexec python -OOt gajim.py $0 $@\n'
329                                f.write(script)
330                                f.close()
331                                os.chmod(path_to_script, 0700)
332                        except OSError: # do not traceback (could be a permission problem)
333                                #we talk about a file here
334                                s = _('Could not write to %s. Session Management support will not work') % path_to_script
335                                print >> sys.stderr, s
336
337        else: # normal user (not svn user)
338                type = 'install'
339                # always make it like '/usr/local/bin/gajim'
340                path_to_script = helpers.is_in_path(scriptname, True)
341               
342       
343        if want_type:
344                return path_to_script, type
345        else:
346                return path_to_script
347
348def get_pixbuf_from_data(file_data, want_type = False):
349        '''Gets image data and returns gtk.gdk.Pixbuf
350        if want_type is True it also returns 'jpeg', 'png' etc'''
351        pixbufloader = gtk.gdk.PixbufLoader()
352        try:
353                pixbufloader.write(file_data)
354                pixbufloader.close()
355                pixbuf = pixbufloader.get_pixbuf()
356        except gobject.GError: # 'unknown image format'
357                pixbufloader.close()
358                pixbuf = None
359                if want_type:
360                        return None, None
361                else:
362                        return None
363
364        if want_type:
365                typ = pixbufloader.get_format()['name']
366                return pixbuf, typ
367        else:
368                return pixbuf
369
370def get_invisible_cursor():
371        pixmap = gtk.gdk.Pixmap(None, 1, 1, 1)
372        color = gtk.gdk.Color()
373        cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0)
374        return cursor
375
376def get_current_desktop(window):
377        '''returns the current virtual desktop for given window
378        NOTE: window is GDK window'''
379        prop = window.property_get('_NET_CURRENT_DESKTOP')
380        if prop is None: # it means it's normal window (not root window)
381                # so we look for it's current virtual desktop in another property
382                prop = window.property_get('_NET_WM_DESKTOP')
383
384        if prop is not None:
385                # f.e. prop is ('CARDINAL', 32, [0]) we want 0 or 1.. from [0]
386                current_virtual_desktop_no = prop[2][0]
387                return current_virtual_desktop_no
388
389def possibly_move_window_in_current_desktop(window):
390        '''moves GTK window to current virtual desktop if it is not in the
391        current virtual desktop
392        window is GTK window'''
393        if os.name == 'nt':
394                return False
395
396        root_window = gtk.gdk.screen_get_default().get_root_window()
397        # current user's vd
398        current_virtual_desktop_no = get_current_desktop(root_window)
399       
400        # vd roster window is in
401        window_virtual_desktop = get_current_desktop(window.window)
402
403        # if one of those is None, something went wrong and we cannot know
404        # VD info, just hide it (default action) and not show it afterwards
405        if None not in (window_virtual_desktop, current_virtual_desktop_no):
406                if current_virtual_desktop_no != window_virtual_desktop:
407                        # we are in another VD that the window was
408                        # so show it in current VD
409                        window.present()
410                        return True
411        return False
412
413def file_is_locked(path_to_file):
414        '''returns True if file is locked (WINDOWS ONLY)'''
415        if os.name != 'nt': # just in case
416                return
417       
418        if not HAS_PYWIN32:
419                return
420       
421        secur_att = pywintypes.SECURITY_ATTRIBUTES()
422        secur_att.Initialize()
423       
424        try:
425                # try make a handle for READING the file
426                hfile = win32file.CreateFile(
427                        path_to_file,                                   # path to file
428                        win32con.GENERIC_READ,                  # open for reading
429                        0,                                                              # do not share with other proc
430                        secur_att,
431                        win32con.OPEN_EXISTING,                 # existing file only
432                        win32con.FILE_ATTRIBUTE_NORMAL, # normal file
433                        0                                                               # no attr. template
434                )
435        except pywintypes.error, e:
436                return True
437        else: # in case all went ok, close file handle (go to hell WinAPI)
438                hfile.Close()
439                return False
440
441def _get_fade_color(treeview, selected, focused):
442        '''get a gdk color that is between foreground and background in 0.3
443        0.7 respectively colors of the cell for the given treeview'''
444        style = treeview.style
445        if selected:
446                if focused: # is the window focused?
447                        state = gtk.STATE_SELECTED
448                else: # is it not? NOTE: many gtk themes change bg on this
449                        state = gtk.STATE_ACTIVE
450        else:
451                state = gtk.STATE_NORMAL
452        bg = style.base[state]
453        fg = style.text[state]
454
455        p = 0.3 # background
456        q = 0.7 # foreground # p + q should do 1.0
457        return gtk.gdk.Color(int(bg.red*p + fg.red*q),
458                                        int(bg.green*p + fg.green*q),
459                                        int(bg.blue*p + fg.blue*q))
460
461def get_scaled_pixbuf(pixbuf, kind):
462        '''returns scaled pixbuf, keeping ratio etc or None
463        kind is either "chat", "roster", "notification", "tooltip", "vcard"'''
464       
465        # resize to a width / height for the avatar not to have distortion
466        # (keep aspect ratio)
467        width = gajim.config.get(kind + '_avatar_width')
468        height = gajim.config.get(kind + '_avatar_height')
469        if width < 1 or height < 1:
470                return None
471
472        # Pixbuf size
473        pix_width = pixbuf.get_width()
474        pix_height = pixbuf.get_height()
475        # don't make avatars bigger than they are
476        if pix_width < width and pix_height < height:
477                return pixbuf # we don't want to make avatar bigger
478
479        ratio = float(pix_width) / float(pix_height)
480        if ratio > 1:
481                w = width
482                h = int(w / ratio)
483        else:
484                h = height
485                w = int(h * ratio)
486        scaled_buf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_HYPER)
487        return scaled_buf
488
489def get_avatar_pixbuf_from_cache(fjid, is_fake_jid = False):
490        '''checks if jid has cached avatar and if that avatar is valid image
491        (can be shown)
492        returns None if there is no image in vcard
493        returns 'ask' if cached vcard should not be used (user changed his vcard,
494        so we have new sha) or if we don't have the vcard'''
495
496        jid, nick = gajim.get_room_and_nick_from_fjid(fjid)
497        if gajim.config.get('hide_avatar_of_transport') and\
498                gajim.jid_is_transport(jid):
499                # don't show avatar for the transport itself
500                return None
501
502        puny_jid = helpers.sanitize_filename(jid)
503        if is_fake_jid:
504                puny_nick = helpers.sanitize_filename(nick)
505                path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
506        else:
507                path = os.path.join(gajim.VCARD_PATH, puny_jid)
508        if not os.path.isfile(path):
509                return 'ask'
510
511        vcard_dict = gajim.connections.values()[0].get_cached_vcard(fjid,
512                is_fake_jid)
513        if not vcard_dict: # This can happen if cached vcard is too old
514                return 'ask'
515        if not vcard_dict.has_key('PHOTO'):
516                return None
517        pixbuf = vcard.get_avatar_pixbuf_encoded_mime(vcard_dict['PHOTO'])[0]
518        return pixbuf
519
520def make_gtk_month_python_month(month):
521        '''gtk start counting months from 0, so January is 0
522        but python's time start from 1, so align to python
523        month MUST be integer'''
524        return month + 1
525
526def make_python_month_gtk_month(month):
527        return month - 1
528
529def make_color_string(color):
530        '''create #aabbcc color string from gtk color'''
531        col = '#'
532        for i in (color.red, color.green, color.blue):
533                # GTK sometime return a value > 256
534                h = hex(i%256)[2:4]
535                if len(h) == 1:
536                        h = '0' + h
537                col += h
538        return col
539
540def make_pixbuf_grayscale(pixbuf):
541        pixbuf2 = pixbuf.copy()
542        pixbuf.saturate_and_pixelate(pixbuf2, 0.0, False)
543        return pixbuf2
544
545def get_path_to_generic_or_avatar(generic, jid = None, suffix = None):
546        '''Chooses between avatar image and default image.
547        Returns full path to the avatar image if it exists,
548        otherwise returns full path to the image.'''
549        if jid:
550                puny_jid = helpers.sanitize_filename(jid)
551                path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + suffix
552                if os.path.exists(path_to_file):
553                        return path_to_file
554        return os.path.abspath(generic)
555
556def decode_filechooser_file_paths(file_paths):
557        '''decode as UTF-8 under Windows and
558        ask sys.getfilesystemencoding() in POSIX
559        file_paths MUST be LIST'''
560        file_paths_list = list()
561       
562        if os.name == 'nt': # decode as UTF-8 under Windows
563                for file_path in file_paths:
564                        file_path = file_path.decode('utf8')
565                        file_paths_list.append(file_path)
566        else:
567                for file_path in file_paths:
568                        try:
569                                file_path = file_path.decode(sys.getfilesystemencoding())
570                        except:
571                                try:
572                                        file_path = file_path.decode('utf-8')
573                                except:
574                                        pass
575                        file_paths_list.append(file_path)
576       
577        return file_paths_list
578
579def possibly_set_gajim_as_xmpp_handler():
580        '''registers (by default only the first time) xmmp: to Gajim.'''
581        path_to_dot_kde = os.path.expanduser('~/.kde')
582        if os.path.exists(path_to_dot_kde):
583                path_to_kde_file = os.path.join(path_to_dot_kde,
584                        'share/services/xmpp.protocol')
585        else:
586                path_to_kde_file = None
587
588        def set_gajim_as_xmpp_handler(widget = None):
589                if widget:
590                        # come from confirmation dialog
591                        gajim.config.set('check_if_gajim_is_default',
592                                dlg.checkbutton.get_active())
593                        dlg.destroy()
594                path_to_gajim_script, typ = get_abspath_for_script('gajim-remote', True)
595                if path_to_gajim_script:
596                        if typ == 'svn':
597                                command = path_to_gajim_script + ' handle_uri %s'
598                        else: # 'installed'
599                                command = 'gajim-remote handle_uri %s'
600
601                        # setting for GNOME/Gconf
602                        client.set_bool('/desktop/gnome/url-handlers/xmpp/enabled', True)
603                        client.set_string('/desktop/gnome/url-handlers/xmpp/command', command)
604                        client.set_bool('/desktop/gnome/url-handlers/xmpp/needs_terminal', False)
605
606                        # setting for KDE
607                        if path_to_kde_file is not None: # user has run kde at least once
608                                try:
609                                        f = open(path_to_kde_file, 'a')
610                                        f.write('''\
611[Protocol]
612exec=%s "%%u"
613protocol=xmpp
614input=none
615output=none
616helper=true
617listing=false
618reading=false
619writing=false
620makedir=false
621deleting=false
622icon=gajim
623Description=xmpp
624''' % command)
625                                        f.close()
626                                except IOError:
627                                        pass
628
629        try:
630                import gconf
631                # in try because daemon may not be there
632                client = gconf.client_get_default()
633        except:
634                return
635
636        old_command = client.get_string('/desktop/gnome/url-handlers/xmpp/command')
637        if not old_command or old_command.endswith(' open_chat %s'):
638                # first time (GNOME/GCONF) or old Gajim version
639                we_set = True
640        elif path_to_kde_file is not None and not os.path.exists(path_to_kde_file):
641                # only the first time (KDE)
642                we_set = True
643        else:
644                we_set = False
645
646        if we_set:
647                set_gajim_as_xmpp_handler()
648        elif old_command and not old_command.endswith(' handle_uri %s'):
649                # xmpp: is currently handled by another program, so ask the user
650                pritext = _('Gajim is not the default Jabber client')
651                sectext = _('Would you like to make Gajim the default Jabber client?')
652                checktext = _('Always check to see if Gajim is the default Jabber client '
653                        'on startup')
654                def on_cancel(widget):
655                        gajim.config.set('check_if_gajim_is_default',
656                                dlg.checkbutton.get_active())
657                        dlg.destroy()
658                dlg = dialogs.ConfirmationDialogCheck(pritext, sectext, checktext,
659                        set_gajim_as_xmpp_handler, on_cancel)
660                if gajim.config.get('check_if_gajim_is_default'):
661                        dlg.checkbutton.set_active(True)
662
663def escape_underscore(s):
664        '''Escape underlines to prevent them from being interpreted
665        as keyboard accelerators'''
666        return s.replace('_', '__')
667
668def get_state_image_from_file_path_show(file_path, show):
669        state_file = show.replace(' ', '_')
670        files = []
671        files.append(os.path.join(file_path, state_file + '.png'))
672        files.append(os.path.join(file_path, state_file + '.gif'))
673        image = gtk.Image()
674        image.set_from_pixbuf(None)
675        for file_ in files:
676                if os.path.exists(file_):
677                        image.set_from_file(file_)
678                        break
679
680        return image
681
682def get_possible_button_event(event):
683        '''mouse or keyboard caused the event?'''
684        if event.type == gtk.gdk.KEY_PRESS:
685                return 0 # no event.button so pass 0
686        # BUTTON_PRESS event, so pass event.button
687        return event.button
688
689def destroy_widget(widget):
690        widget.destroy()
691
692def on_avatar_save_as_menuitem_activate(widget, jid, account,
693default_name = ''):
694        def on_ok(widget):
695                def on_ok2(widget, file_path, pixbuf):
696                        pixbuf.save(file_path, 'jpeg')
697                        dialog2.destroy()
698                        dialog.destroy()
699
700                file_path = dialog.get_filename()
701                file_path = decode_filechooser_file_paths((file_path,))[0]
702                if os.path.exists(file_path):
703                        # check if we have write permissions
704                        if not os.access(file_path, os.W_OK):
705                                file_name = os.path.basename(file_path)
706                                dialogs.ErrorDialog(_('Cannot overwrite existing file "%s"' %
707                                        file_name),
708                                _('A file with this name already exists and you do not have '
709                                'permission to overwrite it.'))
710                                return
711                        dialog2 = dialogs.FTOverwriteConfirmationDialog(
712                                _('This file already exists'), _('What do you want to do?'),
713                                False)
714                        dialog2.set_transient_for(dialog)
715                        dialog2.set_destroy_with_parent(True)
716                        response = dialog2.get_response()
717                        if response < 0:
718                                return
719                else:
720                        dirname = os.path.dirname(file_path)
721                        if not os.access(dirname, os.W_OK):
722                                dialogs.ErrorDialog(_('Directory "%s" is not writable') % \
723                                dirname, _('You do not have permission to create files in this'
724                                ' directory.'))
725                                return
726
727                # Get pixbuf
728                pixbuf = None
729                is_fake = False
730                if account and gajim.contacts.is_pm_from_jid(account, jid):
731                        is_fake = True
732                pixbuf = get_avatar_pixbuf_from_cache(jid, is_fake)
733                ext = file_path.split('.')[-1]
734                type_ = ''
735                if not ext:
736                        # Silently save as Jpeg image
737                        file_path += '.jpeg'
738                        type_ = 'jpeg'
739                elif ext == 'jpg':
740                        type_ = 'jpeg'
741                else:
742                        type_ = ext
743
744                # Save image
745                try:
746                        pixbuf.save(file_path, type_)
747                except:
748                        if os.path.exists(file_path):
749                                os.remove(file_path)
750                        new_file_path = '.'.join(file_path.split('.')[:-1]) + '.jpeg'
751                        dialog2 = dialogs.ConfirmationDialog(_('Extension not supported'),
752                                _('Image cannot be saved in %(type)s format. Save as %(new_filename)s?') % {'type': type_, 'new_filename': new_file_path},
753                                on_response_ok = (on_ok2, new_file_path, pixbuf))
754                else:
755                        dialog.destroy()
756
757        def on_cancel(widget):
758                dialog.destroy()
759
760        dialog = dialogs.FileChooserDialog(
761                title_text = _('Save Image as...'),
762                action = gtk.FILE_CHOOSER_ACTION_SAVE,
763                buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
764                gtk.STOCK_SAVE, gtk.RESPONSE_OK),
765                default_response = gtk.RESPONSE_OK,
766                current_folder = gajim.config.get('last_save_dir'),
767                on_response_ok = on_ok,
768                on_response_cancel = on_cancel)
769
770        dialog.set_current_name(default_name)
771        dialog.connect('delete-event', lambda widget, event:
772                on_cancel(widget))
773
774def on_bm_header_changed_state(widget, event):
775        widget.set_state(gtk.STATE_NORMAL) #do not allow selected_state
Note: See TracBrowser for help on using the browser.