| 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 | |
|---|
| 27 | import xml.sax.saxutils |
|---|
| 28 | import gtk |
|---|
| 29 | import gobject |
|---|
| 30 | import pango |
|---|
| 31 | import os |
|---|
| 32 | import sys |
|---|
| 33 | |
|---|
| 34 | import vcard |
|---|
| 35 | |
|---|
| 36 | |
|---|
| 37 | HAS_PYWIN32 = True |
|---|
| 38 | if os.name == 'nt': |
|---|
| 39 | try: |
|---|
| 40 | import win32file |
|---|
| 41 | import win32con |
|---|
| 42 | import pywintypes |
|---|
| 43 | except ImportError: |
|---|
| 44 | HAS_PYWIN32 = False |
|---|
| 45 | |
|---|
| 46 | from common import i18n |
|---|
| 47 | i18n.init() |
|---|
| 48 | _ = i18n._ |
|---|
| 49 | from common import gajim |
|---|
| 50 | from common import helpers |
|---|
| 51 | |
|---|
| 52 | screen_w = gtk.gdk.screen_width() |
|---|
| 53 | screen_h = gtk.gdk.screen_height() |
|---|
| 54 | |
|---|
| 55 | def 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 | |
|---|
| 70 | def 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 | |
|---|
| 124 | def 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 | |
|---|
| 152 | def 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, {"'": ''', |
|---|
| 161 | '"': '"'}) |
|---|
| 162 | |
|---|
| 163 | return escaped_str |
|---|
| 164 | |
|---|
| 165 | def 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 | |
|---|
| 203 | def 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 | |
|---|
| 211 | def 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 | |
|---|
| 219 | def 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 | |
|---|
| 227 | class 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 | |
|---|
| 252 | def 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 | |
|---|
| 264 | def 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 | |
|---|
| 273 | def 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 | |
|---|
| 313 | def 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 | |
|---|
| 325 | def 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 | |
|---|
| 331 | def 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 | |
|---|
| 344 | def 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 | |
|---|
| 366 | def 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 | |
|---|
| 394 | def _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 | |
|---|
| 414 | def 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 | |
|---|
| 436 | def 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 | |
|---|
| 459 | def 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 | |
|---|
| 465 | def make_python_month_gtk_month(month): |
|---|
| 466 | return month - 1 |
|---|
| 467 | |
|---|
| 468 | def 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:] |
|---|