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

Revision 4786, 14.7 kB (checked in by asterix, 3 years ago)

fix color parser

  • Property svn:eol-style set to LF
Line 
1##      gtkgui_helpers.py
2##
3## Contributors for this file:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Nikos Kouremenos <kourem@gmail.com>
6##      - Dimitur Kirov <dkirov@gmail.com>
7##
8## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
9##                         Vincent Hanquez <tab@snarc.org>
10## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
11##                    Vincent Hanquez <tab@snarc.org>
12##                    Nikos Kouremenos <nkour@jabber.org>
13##                    Dimitur Kirov <dkirov@gmail.com>
14##                    Travis Shirk <travis@pobox.com>
15##                    Norman Rasmussen <norman@rasmussen.co.za>
16##
17## This program is free software; you can redistribute it and/or modify
18## it under the terms of the GNU General Public License as published
19## by the Free Software Foundation; version 2 only.
20##
21## This program is distributed in the hope that it will be useful,
22## but WITHOUT ANY WARRANTY; without even the implied warranty of
23## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24## GNU General Public License for more details.
25##
26
27import xml.sax.saxutils
28import gtk
29import gobject
30import pango
31import os
32import sys
33
34import vcard
35
36
37HAS_PYWIN32 = True
38if os.name == 'nt':
39        try:
40                import win32file
41                import win32con
42                import pywintypes
43        except ImportError:
44                HAS_PYWIN32 = False
45
46from common import i18n
47i18n.init()
48_ = i18n._
49from common import gajim
50from common import helpers
51
52screen_w = gtk.gdk.screen_width()
53screen_h = gtk.gdk.screen_height()
54
55def get_theme_font_for_option(theme, option):
56        '''return string description of the font, stored in
57        theme preferences'''
58        font_name = gajim.config.get_per('themes', theme, option)
59        font_desc = pango.FontDescription()
60        font_prop_str =  gajim.config.get_per('themes', theme, option + 'attrs')
61        if font_prop_str:
62                if font_prop_str.find('B') != -1:
63                        font_desc.set_weight(pango.WEIGHT_BOLD)
64                if font_prop_str.find('I') != -1:
65                        font_desc.set_style(pango.STYLE_ITALIC)
66        fd = pango.FontDescription(font_name)
67        fd.merge(font_desc, True)
68        return fd.to_string()
69       
70def get_default_font():
71        '''Get the desktop setting for application font
72        first check for GNOME, then XFCE and last KDE
73        it returns None on failure or else a string 'Font Size' '''
74       
75        try:
76                import gconf
77                # in try because daemon may not be there
78                client = gconf.client_get_default()
79
80                return helpers.ensure_unicode_string(
81                        client.get_string('/desktop/gnome/interface/font_name'))
82        except:
83                pass
84
85        # try to get xfce default font
86        # Xfce 4.2 adopts freedesktop.org's Base Directory Specification
87        # see http://www.xfce.org/~benny/xfce/file-locations.html
88        # and http://freedesktop.org/Standards/basedir-spec
89        xdg_config_home = os.environ.get('XDG_CONFIG_HOME', '')
90        if xdg_config_home == '':
91                xdg_config_home = os.path.expanduser('~/.config') # default     
92        xfce_config_file = os.path.join(xdg_config_home, 'xfce4/mcs_settings/gtk.xml')
93       
94        kde_config_file = os.path.expanduser('~/.kde/share/config/kdeglobals')
95       
96        if os.path.exists(xfce_config_file):
97                try:
98                        for line in file(xfce_config_file):
99                                if line.find('name="Gtk/FontName"') != -1:
100                                        start = line.find('value="') + 7
101                                        return helpers.ensure_unicode_string(
102                                                line[start:line.find('"', start)])
103                except:
104                        #we talk about file
105                        print >> sys.stderr, _('Error: cannot open %s for reading') % xfce_config_file
106       
107        elif os.path.exists(kde_config_file):
108                try:
109                        for line in file(kde_config_file):
110                                if line.find('font=') == 0: # font=Verdana,9,other_numbers
111                                        start = 5 # 5 is len('font=')
112                                        line = line[start:]
113                                        values = line.split(',')
114                                        font_name = values[0]
115                                        font_size = values[1]
116                                        font_string = '%s %s' % (font_name, font_size) # Verdana 9
117                                        return helpers.ensure_unicode_string(font_string)
118                except:
119                        #we talk about file
120                        print >> sys.stderr, _('Error: cannot open %s for reading') % kde_config_file
121       
122        return None
123       
124def reduce_chars_newlines(text, max_chars = 0, max_lines = 0, 
125        widget = None):
126        '''Cut the chars after 'max_chars' on each line
127        and show only the first 'max_lines'. If there is more text
128        to be shown, display the whole text in tooltip on 'widget'
129        If any of the params is not present (None or 0) the action
130        on it is not performed'''
131
132        def _cut_if_long(str):
133                if len(str) > max_chars:
134                        str = str[:max_chars - 3] + '...'
135                return str
136       
137        if max_lines == 0:
138                lines = text.split('\n')
139        else:
140                lines = text.split('\n', max_lines)[:max_lines]
141        if max_chars > 0:
142                if lines:
143                        lines = map(lambda e: _cut_if_long(e), lines)
144        if lines:
145                reduced_text = reduce(lambda e, e1: e + '\n' + e1, lines)
146        else:
147                reduced_text = ''
148        if reduced_text != text and widget is not None:
149                pass # FIXME show tooltip
150        return reduced_text
151
152def escape_for_pango_markup(string):
153        # escapes < > & ' "
154        # for pango markup not to break
155        if string is None:
156                return
157        if gtk.pygtk_version >= (2, 8, 0) and gtk.gtk_version >= (2, 8, 0):
158                escaped_str = gobject.markup_escape_text(string)
159        else:
160                escaped_str = xml.sax.saxutils.escape(string, {"'": '&apos;',
161                        '"': '&quot;'})
162       
163        return escaped_str
164
165def autodetect_browser_mailer():
166        # recognize the environment for appropriate browser/mailer
167        if os.path.isdir('/proc'):
168                # under Linux: checking if 'gnome-session' or
169                # 'startkde' programs were run before gajim, by
170                # checking /proc (if it exists)
171                #
172                # if something is unclear, read `man proc`;
173                # if /proc exists, directories that have only numbers
174                # in their names contain data about processes.
175                # /proc/[xxx]/exe is a symlink to executable started
176                # as process number [xxx].
177                # filter out everything that we are not interested in:
178                files = os.listdir('/proc')
179
180                # files that doesn't have only digits in names...
181                files = filter(str.isdigit, files)
182
183                # files that aren't directories...
184                files = filter(lambda f:os.path.isdir('/proc/' + f), files)
185
186                # processes owned by somebody not running gajim...
187                # (we check if we have access to that file)
188                files = filter(lambda f:os.access('/proc/' + f +'/exe', os.F_OK), files)
189
190                # be sure that /proc/[number]/exe is really a symlink
191                # to avoid TBs in incorrectly configured systems
192                files = filter(lambda f:os.path.islink('/proc/' + f + '/exe'), files)
193
194                # list of processes
195                processes = [os.path.basename(os.readlink('/proc/' + f +'/exe')) for f in files]
196                if 'gnome-session' in processes:
197                        gajim.config.set('openwith', 'gnome-open')
198                elif 'startkde' in processes:
199                        gajim.config.set('openwith', 'kfmclient exec')
200                else:
201                        gajim.config.set('openwith', 'custom')
202
203def move_window(window, x, y):
204        '''moves the window but also checks if out of screen'''
205        if x < 0:
206                x = 0
207        if y < 0:
208                y = 0
209        window.move(x, y)
210
211def resize_window(window, w, h):
212        '''resizes window but also checks if huge window or negative values'''
213        if w > screen_w:
214                w = screen_w
215        if h > screen_h:
216                h = screen_h
217        window.resize(abs(w), abs(h))
218
219def one_window_opened(typ):
220        for account in gajim.connections:
221                if not gajim.interface.instances[account].has_key(typ):
222                        continue
223                if len(gajim.interface.instances[account][typ]):
224                        return True
225        return False
226
227class TagInfoHandler(xml.sax.ContentHandler):
228        def __init__(self, tagname1, tagname2):
229                xml.sax.ContentHandler.__init__(self)
230                self.tagname1 = tagname1
231                self.tagname2 = tagname2
232                self.servers = []
233
234        def startElement(self, name, attributes):
235                if name == self.tagname1:
236                        for attribute in attributes.getNames():
237                                if attribute == 'jid':
238                                        jid = attributes.getValue(attribute)
239                                        # we will get the port next time so we just set it 0 here
240                                        self.servers.append([jid, 0])
241                elif name == self.tagname2:
242                        for attribute in attributes.getNames():
243                                if attribute == 'port':
244                                        port = attributes.getValue(attribute)
245                                        # we received the jid last time, so we now assign the port
246                                        # number to the last jid in the list
247                                        self.servers[-1][1] = port
248
249        def endElement(self, name):
250                pass
251
252def parse_server_xml(path_to_file):
253        try:
254                handler = TagInfoHandler('item', 'active')
255                xml.sax.parse(path_to_file, handler)
256                return handler.servers
257        # handle exception if unable to open file
258        except IOError, message:
259                print >> sys.stderr, _('Error reading file:'), message
260        # handle exception parsing file
261        except xml.sax.SAXParseException, message:
262                print >> sys.stderr, _('Error parsing file:'), message
263
264def set_unset_urgency_hint(window, unread_messages_no):
265        '''sets/unsets urgency hint in window argument
266        depending if we have unread messages or not'''
267        if gtk.gtk_version >= (2, 8, 0) and gtk.pygtk_version >= (2, 8, 0):
268                if unread_messages_no > 0:
269                        window.props.urgency_hint = True
270                else:
271                        window.props.urgency_hint = False
272
273def get_abspath_for_script(scriptname, want_type = False):
274        '''checks if we are svn or normal user and returns abspath to asked script
275        if want_type is True we return 'svn' or 'install' '''
276        if os.path.isdir('.svn'): # we are svn user
277                type = 'svn'
278                cwd = os.getcwd() # it's always ending with src
279
280                if scriptname == 'gajim-remote':
281                        path_to_script = cwd + '/gajim-remote.py'
282               
283                elif scriptname == 'gajim':
284                        script = '#!/bin/sh\n' # the script we may create
285                        script += 'cd %s' % cwd
286                        path_to_script = cwd + '/../scripts/gajim_sm_script'
287                               
288                        try:
289                                if os.path.exists(path_to_script):
290                                        os.remove(path_to_script)
291
292                                f = open(path_to_script, 'w')
293                                script += '\nexec python -OOt gajim.py $0 $@\n'
294                                f.write(script)
295                                f.close()
296                                os.chmod(path_to_script, 0700)
297                        except OSError: # do not traceback (could be a permission problem)
298                                #we talk about a file here
299                                s = _('Could not write to %s. Session Management support will not work') % path_to_script
300                                print >> sys.stderr, s
301
302        else: # normal user (not svn user)
303                type = 'install'
304                # always make it like '/usr/local/bin/gajim'
305                path_to_script = helpers.is_in_path(scriptname, True)
306               
307       
308        if want_type:
309                return path_to_script, type
310        else:
311                return path_to_script
312
313def get_pixbuf_from_data(file_data):
314        '''Gets image data and returns gtk.gdk.Pixbuf'''
315        pixbufloader = gtk.gdk.PixbufLoader()
316        try:
317                pixbufloader.write(file_data)
318                pixbufloader.close()
319                pixbuf = pixbufloader.get_pixbuf()
320        except gobject.GError: # 'unknown image format'
321                pixbuf = None
322
323        return pixbuf
324
325def get_invisible_cursor():
326        pixmap = gtk.gdk.Pixmap(None, 1, 1, 1)
327        color = gtk.gdk.Color()
328        cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0)
329        return cursor
330
331def get_current_desktop(window):
332        '''returns the current virtual desktop for given window
333        NOTE: window is GDK window'''
334        prop = window.property_get('_NET_CURRENT_DESKTOP')
335        if prop is None: # it means it's normal window (not root window)
336                # so we look for it's current virtual desktop in another property
337                prop = window.property_get('_NET_WM_DESKTOP')
338
339        if prop is not None:
340                # f.e. prop is ('CARDINAL', 32, [0]) we want 0 or 1.. from [0]
341                current_virtual_desktop_no = prop[2][0]
342                return current_virtual_desktop_no
343
344def possibly_move_window_in_current_desktop(window):
345        '''moves GTK window to current virtual desktop if it is not in the
346        current virtual desktop
347        window is GTK window'''
348        if os.name == 'nt':
349                return
350
351        root_window = gtk.gdk.screen_get_default().get_root_window()
352        # current user's vd
353        current_virtual_desktop_no = get_current_desktop(root_window)
354       
355        # vd roster window is in
356        window_virtual_desktop = get_current_desktop(window.window)
357
358        # if one of those is None, something went wrong and we cannot know
359        # VD info, just hide it (default action) and not show it afterwards
360        if None not in (window_virtual_desktop, current_virtual_desktop_no):
361                if current_virtual_desktop_no != window_virtual_desktop:
362                        # we are in another VD that the window was
363                        # so show it in current VD
364                        window.show()
365
366def file_is_locked(path_to_file):
367        '''returns True if file is locked (WINDOWS ONLY)'''
368        if os.name != 'nt': # just in case
369                return
370       
371        if not HAS_PYWIN32:
372                return
373       
374        secur_att = pywintypes.SECURITY_ATTRIBUTES()
375        secur_att.Initialize()
376       
377        try:
378                # try make a handle for READING the file
379                hfile = win32file.CreateFile(
380                        path_to_file,                                   # path to file
381                        win32con.GENERIC_READ,                  # open for reading
382                        0,                                                              # do not share with other proc
383                        secur_att,
384                        win32con.OPEN_EXISTING,                 # existing file only
385                        win32con.FILE_ATTRIBUTE_NORMAL, # normal file
386                        0                                                               # no attr. template
387                )
388        except pywintypes.error, e:
389                return True
390        else: # in case all went ok, close file handle (go to hell WinAPI)
391                hfile.Close()
392                return False
393
394def _get_fade_color(treeview, selected, focused):
395        '''get a gdk color that is between foreground and background in 0.3
396        0.7 respectively colors of the cell for the given treeview'''
397        style = treeview.style
398        if selected:
399                if focused: # is the window focused?
400                        state = gtk.STATE_SELECTED
401                else: # is it not? NOTE: many gtk themes change bg on this
402                        state = gtk.STATE_ACTIVE
403        else:
404                state = gtk.STATE_NORMAL
405        bg = style.base[state]
406        fg = style.text[state]
407
408        p = 0.3 # background
409        q = 0.7 # foreground # p + q should do 1.0
410        return gtk.gdk.Color(int(bg.red*p + fg.red*q),
411                              int(bg.green*p + fg.green*q),
412                              int(bg.blue*p + fg.blue*q))
413
414def get_scaled_pixbuf(pixbuf, type):
415        '''returns scaled pixbuf, keeping ratio etc
416        type is either "chat" or "roster"'''
417       
418        # resize to a width / height for the avatar not to have distortion
419        # (keep aspect ratio)
420
421        # don't make avatars bigger than they are
422        if pixbuf.get_width() < gajim.config.get(type + '_avatar_width') and \
423                pixbuf.get_height() < gajim.config.get(type + '_avatar_height'):
424                return pixbuf # we don't want to make avatar bigger
425
426        ratio = float(pixbuf.get_width()) / float(pixbuf.get_height())
427        if ratio > 1:
428                w = gajim.config.get(type + '_avatar_width')
429                h = int(w / ratio)
430        else:
431                h = gajim.config.get(type + '_avatar_height')
432                w = int(h * ratio)
433        scaled_buf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_HYPER)
434        return scaled_buf
435
436def get_avatar_pixbuf_from_cache(jid):
437        '''checks if jid has cached avatar and if that avatar is valid image
438        (can be shown)
439        returns None if there is no image in vcard
440        returns 'ask' if cached vcard should not be used (user changed his vcard,
441        so we have new sha) or if we don't have the vcard'''
442
443        if gajim.config.get('hide_avatar_of_transport') and\
444                gajim.jid_is_transport(jid):
445                # don't show avatar for the transport itself
446                return None
447       
448        if jid not in os.listdir(gajim.VCARDPATH):
449                return 'ask'
450
451        vcard_dict = gajim.connections.values()[0].get_cached_vcard(jid)
452        if not vcard_dict: # This can happen if cached vcard is too old
453                return 'ask'
454        if not vcard_dict.has_key('PHOTO'):
455                return None
456        pixbuf = vcard.get_avatar_pixbuf_encoded_mime(vcard_dict['PHOTO'])[0]
457        return pixbuf
458
459def make_gtk_month_python_month(month):
460        '''gtk start counting months from 0, so January is 0
461        but python's time start from 1, so align to python
462        month MUST be integer'''
463        return month + 1
464
465def make_python_month_gtk_month(month):
466        return month - 1
467
468def make_color_string(color):
469        '''create #aabbcc color string from gtk color'''
470        return '#' + hex(color.red)[-2:] + hex(color.green)[-2:] + \
471                hex(color.blue)[-2:]
Note: See TracBrowser for help on using the browser.