From c5d3e5663250cf4fee13b35b40e45a16c5ed0a37 Mon Sep 17 00:00:00 2001 From: Nikos Kouremenos Date: Wed, 9 Mar 2005 21:29:20 +0000 Subject: [PATCH] url detection is ready! --- plugins/gtkgui/dialogs.py | 47 ++++-- plugins/gtkgui/gtkgui.glade | 4 +- plugins/gtkgui/gtkgui.py | 318 ++++++++++++++++++------------------ 3 files changed, 193 insertions(+), 176 deletions(-) diff --git a/plugins/gtkgui/dialogs.py b/plugins/gtkgui/dialogs.py index db3f9d293..6639d661f 100644 --- a/plugins/gtkgui/dialogs.py +++ b/plugins/gtkgui/dialogs.py @@ -452,26 +452,51 @@ class add_contact_window: agent_combobox.set_active(jid_agents.index(jid_splited[1])+1) self.xml.signal_autoconnect(self) -class about_window: #FIXME REWRITE: (nk) pygtk2.6 has a built-in window for that - """Class for about window""" +class About_dialog: + """Class for about dialog""" def delete_event(self, widget): """close window""" - del self.plugin.windows['about'] # remove us from open windows + #del self.plugin.windows['about'] # remove us from open windows + pass def on_close(self, widget): """When Close button is clicked""" - widget.get_toplevel().destroy() + #widget.get_toplevel().destroy() + pass def __init__(self, plugin): - xml = gtk.glade.XML(GTKGUI_GLADE, 'about_window', APP) - self.window = xml.get_widget('about_window') - xml.get_widget('logo_image').set_from_file('plugins/gtkgui/pixmaps/logo.png') - self.plugin = plugin - xml.signal_connect('gtk_widget_destroy', self.delete_event) - xml.signal_connect('on_close_clicked', self.on_close) + #xml.get_widget('logo_image').set_from_file('plugins/gtkgui/pixmaps/logo.png') + dlg = gtk.AboutDialog() + dlg.set_name('Gajim') + dlg.set_version('0.6') + dlg.set_copyright('Copyright (C) 2003-2005 Gajim Team') + dlg.set_license('''This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; version 2 only. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details.''') + + dlg.set_comments('The best GTK jabber-only client') + dlg.set_website('http://www.gajim.org') + dlg.set_authors('Yann') +# dlg.set_authors('Yann Le Boulanger') + + dlg.set_logo(gtk.gdk.pixbuf_new_from_file('plugins/gtkgui/pixmaps/logo.png')) + + dlg.run() + #dlg.show_all() + ''' + , version='0.6', 'Nikos Kouremenos', 'The best GTK jabber-only client', 'http://www.gajim.org', 'Official Page', 'GPL', 'Yann') + dlg.run() + ''' + #xml.signal_connect('gtk_widget_destroy', self.delete_event) + #xml.signal_connect('on_close_clicked', self.on_close) - self.plugin.windows['about'] = self # add us to open windows + #self.plugin.windows['about'] = self # add us to open windows class Confirmation_dialog: """Class for confirmation dialog""" diff --git a/plugins/gtkgui/gtkgui.glade b/plugins/gtkgui/gtkgui.glade index bebfe5306..e63071ac6 100644 --- a/plugins/gtkgui/gtkgui.glade +++ b/plugins/gtkgui/gtkgui.glade @@ -353,7 +353,7 @@ True True True - True + False False True @@ -4060,7 +4060,7 @@ on the server as a vCard True True - On every lines + On every line True GTK_RELIEF_NORMAL True diff --git a/plugins/gtkgui/gtkgui.py b/plugins/gtkgui/gtkgui.py index 2899905c1..1a5a954f0 100644 --- a/plugins/gtkgui/gtkgui.py +++ b/plugins/gtkgui/gtkgui.py @@ -18,39 +18,6 @@ ## GNU General Public License for more details. ## -def usage(): - #TODO: use i18n - print "usage :", sys.argv[0], ' [OPTION]' - print " -p\tport on which the sock plugin listen" - print " -h, --help\tdisplay this help and exit" - -if __name__ == "__main__": - import getopt, pickle, sys, socket - try: - opts, args = getopt.getopt(sys.argv[1:], "p:h", ["help"]) - except getopt.GetoptError: - # print help information and exit: - usage() - sys.exit(2) - port = 8255 - for o, a in opts: - if o == '-p': - port = a - if o in ("-h", "--help"): - usage() - sys.exit() - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect(('', 8255)) - except: - #TODO: use i18n - print "unable to connect to localhost on port ", port - else: - evp = pickle.dumps(('EXEC_PLUGIN', '', 'gtkgui')) - sock.send('<'+evp+'>') - sock.close() - sys.exit() - import pygtk pygtk.require('2.0') import gtk @@ -69,12 +36,41 @@ from common import i18n _ = i18n._ APP = i18n.APP -gtk.glade.bindtextdomain (APP, i18n.DIR) -gtk.glade.textdomain (APP) +gtk.glade.bindtextdomain(APP, i18n.DIR) +gtk.glade.textdomain(APP) from dialogs import * from config import * +def usage(): + #TODO: use i18n + print "usage :", sys.argv[0], ' [OPTION]' + print " -p\tport on which the sock plugin listen" + print " -h, --help\tdisplay this help and exit" + +def launch_browser_mailer(self, kind, url): + #kind = 'url' or 'mail' + if self.plugin.config['openwith'] == 'gnome-open': + app = 'gnome-open' + args = ['gnome-open'] + args.append(url) + elif self.plugin.config['openwith'] == 'kfmclient exec': + app = 'kfmclient' + args = ['kfmclient', 'exec'] + elif self.plugin.config['openwith'] == 'custom': + if kind == 'url': + conf = self.plugin.config['custombrowser'] + if kind == 'mail': + conf = self.plugin.config['custommailapp'] + if conf == '': # if no app is configured + return + args = conf.split() + app = args[0] + args.append(url) + os.spawnvp(os.P_NOWAIT, app, args) + + + GTKGUI_GLADE='plugins/gtkgui/gtkgui.glade' class ImageCellRenderer(gtk.GenericCellRenderer): @@ -228,27 +224,6 @@ class tabbed_chat_window: self.on_tabbed_chat_window_key_press_event) self.xml.signal_connect('on_chat_notebook_switch_page', \ self.on_chat_notebook_switch_page) - - def run_application(self, type, url): - #type = 'url' or 'mail' - if self.plugin.config['openwith'] == 'gnome-open': - app = 'gnome-open' - args = ['gnome-open'] - args.append(url) - elif self.plugin.config['openwith'] == 'kfmclient exec': - app = 'kfmclient' - args = ['kfmclient', 'exec'] - elif self.plugin.config['openwith'] == 'custom': - if type == 'url': - conf = self.plugin.config['custombrowser'] - if type == 'mail': - conf = self.plugin.config['custommailapp'] - if conf == '': - return - args = conf.split() - app = args[0] - args.append(url) - os.spawnvp(os.P_NOWAIT, app, args) def update_tags(self): for jid in self.tagIn: @@ -353,8 +328,8 @@ class tabbed_chat_window: jid = self.get_active_jid() conversation_buffer = self.xmls[jid].get_widget('conversation_textview').\ get_buffer() - deb, end = conversation_buffer.get_bounds() - conversation_buffer.delete(deb, end) + start, end = conversation_buffer.get_bounds() + conversation_buffer.delete(start, end) def on_close_button_clicked(self, button): """When close button is pressed : @@ -462,6 +437,7 @@ class tabbed_chat_window: tag.set_property('foreground', '#9e9e9e') tag.set_property('scale', pango.SCALE_SMALL) tag.set_property('justification', gtk.JUSTIFY_CENTER) + for way in ['status', 'incoming', 'outgoing']: for special in ['mail', 'italic', 'underline', 'bold', 'url']: tag = conversation_buffer.create_tag(way + '_' + special) @@ -487,6 +463,7 @@ class tabbed_chat_window: elif special == 'bold': tag.set_property('weight', pango.WEIGHT_BOLD) link_tag = conversation_buffer.create_tag('hyperlink', foreground='blue') + self.xmls[user.jid].signal_autoconnect(self) conversation_scrolledwindow = self.xmls[user.jid].\ get_widget('conversation_scrolledwindow') @@ -652,7 +629,7 @@ class tabbed_chat_window: def print_special_word(self, word, jid, contact = ''): conversation_textview = self.xmls[jid].get_widget('conversation_textview') conversation_buffer = conversation_textview.get_buffer() - end_iter = conversation_buffer.get_end_iter() + if contact == 'status': tag = 'status' else: @@ -662,13 +639,15 @@ class tabbed_chat_window: tag = 'incoming' if word in self.plugin.emoticons.keys(): #it's a smiley + end_iter = conversation_buffer.get_end_iter() conversation_buffer.insert_pixbuf(end_iter, self.plugin.emoticons[word]) return - elif word.startswith('mailto'): + elif word.startswith('mailto:'): #it's a mail tag += '_mail' - #TODO: search for sth@sth.sth + elif self.plugin.sth_at_sth_dot_sth_re.match(word): # returns match object or None #it's a mail too + tag += '_mail' elif word.startswith('/') and word.endswith('/'): #it's an italic text tag += '_italic' @@ -682,7 +661,8 @@ class tabbed_chat_window: #it's an url tag += '_url' - conversation_buffer.insert_with_tags_by_name(end_iter, text, tag) + end_iter = conversation_buffer.get_end_iter() + conversation_buffer.insert_with_tags_by_name(end_iter, word, tag) def print_conversation(self, text, jid, contact = '', tim = None): """Print a line in the conversation : @@ -723,76 +703,38 @@ class tabbed_chat_window: conversation_buffer.insert_with_tags_by_name(end_iter, ttext, tag) - # regexp meta characters are: . ^ $ * + ? { [ ] \ | ( ) - # one escapes the metachars with \ - # \S matches anything but ' ' '\t' '\n' '\r' '\f' and '\v' - # \s matches any whitespace character - # \w any alphanumeric character - # * means 0 or more times - # + means 1 or more times - # | means or - # [^*] anything but * (inside [] you don't have to escape metachars) - # url_pattern is one string literal. I've put spaces to make the regexp look better - url_pattern = r'http://\S*|' 'https://\S*|' 'news://\S*|' 'ftp://\S*|' 'mailto:\S*|' 'ed2k://\S*|' 'www\.\S*|' 'ftp\.\S*|' '\*\w+[^*]*\w+\*|' '/\w+[^/]*\w+/|' '_\w+[^_]*\w+_|' '\w+[^\s]*@\w+\.\w+' - start = 0 end = 0 index = 0 - - if len(otext) > 0: # is it necessary? maybe that should be above? - - if self.plugin.config['useemoticons']: - emoticons_pattern = '' - for emoticon in self.plugin.roster.emoticons: # travel tru emoticons list - emoticon_escaped = sre.escape(emoticon) # espace regexp metachars - emoticons_pattern += emoticon_escaped + '|'# or is | in regexp - emoticons_pattern = emoticons_pattern[0:-1] # remove the last | - - emoticons_re = sre.compile(emoticons_pattern, sre.IGNORECASE) - iterator = emoticons_re.finditer(otext) - for match in iterator: - emot_start, emot_end = match.span() - emot_ascii = otext[emot_start:emot_end] - #print 'detected', emot_ascii, 'in (start, end)', '(', emot_start, ',', emot_end, ')' - #print 'adding text before emoticon', index, emot_start - if emot_start != 0: - conversation_buffer.insert(end_iter, otext[index:emot_start]) - conversation_buffer.insert_pixbuf(end_iter, \ - self.plugin.roster.emoticons[emot_ascii]) - index = emot_end # update index - - url_re = sre.compile(url_pattern, sre.IGNORECASE) - iterator = url_re.finditer(otext) + + if self.plugin.config['useemoticons']: # search for emoticons & urls + my_re = sre.compile(self.plugin.emot_and_url_pattern, sre.IGNORECASE) + iterator = my_re.finditer(otext) for match in iterator: - url_start, url_end = match.span() - url_text = otext[url_start:url_end] - #print 'detected', url_text, 'in (start, end)', '(', url_start, ',', url_end, ')' - conversation_buffer.insert_with_tags_by_name(end_iter,\ - url_text, 'hyperlink') - index = url_end # update index + start, end = match.span() + special_word = otext[start:end] + if start != 0: + text_before_special_word = otext[index:start] + end_iter = conversation_buffer.get_end_iter() + conversation_buffer.insert(end_iter, text_before_special_word) + self.print_special_word(special_word, jid, contact) + index = end # update index + else: # search for just urls + my_re = sre.compile(self.plugin.url_pattern, sre.IGNORECASE) + iterator = my_re.finditer(otext) + for match in iterator: + start, end = match.span() + special_word = otext[start:end] + if start != 0: + text_before_special_word = otext[index:start] + end_iter = conversation_buffer.get_end_iter() + conversation_buffer.insert(end_iter, text_before_special_word) + self.print_special_word(special_word, jid, contact) + index = end # update index - #conversation_buffer.insert(end_iter, otext[end:]) - #print 'adding the rest of the text in', index, 'and after' - conversation_buffer.insert(end_iter, otext[index:]) - - ''' - if len(otext) > 0: - beg = 0 - if self.plugin.config['useemoticons']: - index = 0 - while index < len(otext): - for s in self.plugin.roster.emoticons: - l = len(s) - if s == otext[index:index+l]: - conversation_buffer.insert(end_iter, otext[beg:index]) - conversation_buffer.insert_pixbuf(end_iter, \ - self.plugin.roster.emoticons[s]) - index+=l - beg = index - index+=1 - - conversation_buffer.insert(end_iter, otext[beg:]) - ''' + #add the rest in the index and after + end_iter = conversation_buffer.get_end_iter() + conversation_buffer.insert(end_iter, otext[index:]) #scroll to the end of the textview end_rect = conversation_textview.get_iter_location(end_iter) @@ -2433,7 +2375,7 @@ class roster_window: def on_about_menuitem_activate(self, widget): """When about is selected : call the about class""" - about_window(self.plugin) + About_dialog(self.plugin) def on_accounts_menuitem_activate(self, widget): """When accounts is seleted : @@ -2583,34 +2525,6 @@ class roster_window: self.plugin.windows[account]['browser'] = \ agent_browser_window(self.plugin, account) - def image_is_ok(self, image): - if not os.path.exists(image): - return 0 - img = gtk.Image() - try: - img.set_from_file(image) - except: - return 0 - if img.get_storage_type() == gtk.IMAGE_PIXBUF: - pix = img.get_pixbuf() - else: - return 0 - if pix.get_width() > 24 or pix.get_height() > 24: - return 0 - return 1 - - def mkemoticons(self): - """initialize emoticons dictionary""" - self.emoticons = dict() - split_line = self.plugin.config['emoticons'].split('\t') - for i in range(0, len(split_line)/2): - # (nk) lost you here. if you remember add some comments about the idea of the algo - emot_file = split_line[2*i+1] - if not self.image_is_ok(emot_file): - continue - pix = gtk.gdk.pixbuf_new_from_file(emot_file) - self.emoticons[split_line[2*i]] = pix - def mkpixbufs(self): """initialise pixbufs array""" iconstyle = self.plugin.config['iconstyle'] @@ -2803,8 +2717,6 @@ class roster_window: model.set_sort_column_id(1, gtk.SORT_ASCENDING) self.tree.set_model(model) self.mkpixbufs() - if self.plugin.config['useemoticons']: - self.mkemoticons() liststore = gtk.ListStore(gobject.TYPE_STRING, gtk.Image) self.cb = gtk.ComboBox() @@ -3485,9 +3397,27 @@ class plugin: def hide_systray(self): self.systray.hide_icon() self.systray_visible = 0 + + def image_is_ok(self, image): + if not os.path.exists(image): + return False + img = gtk.Image() + try: + img.set_from_file(image) + except: + return True + if img.get_storage_type() == gtk.IMAGE_PIXBUF: + pix = img.get_pixbuf() + else: + return False + if pix.get_width() > 24 or pix.get_height() > 24: + return False + return True def __init__(self, quIN, quOUT): gtk.gdk.threads_init() + gtk.about_dialog_set_email_hook(launch_browser_mailer, 'mail') + gtk.about_dialog_set_url_hook(launch_browser_mailer, 'url') self.queueIN = quIN self.queueOUT = quOUT self.send('REG_MESSAGE', 'gtkgui', ['ROSTER', 'WARNING', 'STATUS', \ @@ -3577,15 +3507,15 @@ class plugin: path = 'plugins/gtkgui/icons/' + iconstyle + '/' files = [path + 'online.gif', path + 'online.png', path + 'online.xpm'] pix = None - for file in files: - if os.path.exists(file): - pix = gtk.gdk.pixbuf_new_from_file(file) + for f in files: + if os.path.exists(f): + pix = gtk.gdk.pixbuf_new_from_file(f) break if pix: gtk.window_set_default_icon(pix) self.roster = roster_window(self) - gtk.timeout_add(100, self.read_queue) - gtk.timeout_add(100, self.read_sleepy) + gobject.timeout_add(100, self.read_queue) + gobject.timeout_add(100, self.read_sleepy) self.sleeper = common.sleepy.Sleepy( \ self.config['autoawaytime']*60, \ self.config['autoxatime']*60) @@ -3601,9 +3531,71 @@ class plugin: self.systray = systray(self) if self.config['trayicon']: self.show_systray() + + if self.config['useemoticons']: + """initialize emoticons dictionary""" + self.emoticons = dict() + split_line = self.config['emoticons'].split('\t') + for i in range(0, len(split_line)/2): + emot_file = split_line[2*i+1] + if not self.image_is_ok(emot_file): + continue + pix = gtk.gdk.pixbuf_new_from_file(emot_file) + self.emoticons[split_line[2*i]] = pix + + # regexp meta characters are: . ^ $ * + ? { } [ ] \ | ( ) + # one escapes the metachars with \ + # \S matches anything but ' ' '\t' '\n' '\r' '\f' and '\v' + # \s matches any whitespace character + # \w any alphanumeric character + # * means 0 or more times + # + means 1 or more times + # | means or + # [^*] anything but * (inside [] you don't have to escape metachars) + # url_pattern is one string literal. I've put spaces to make the regexp look better + self.url_pattern = r'http://\S*|' 'https://\S*|' 'news://\S*|' 'ftp://\S*|' 'mailto:\S*|' 'ed2k://\S*|' 'www\.\S*|' 'ftp\.\S*|' '\*\w+[^*]*\w+\*|' '/\w+[^/]*\w+/|' '_\w+[^_]*\w+_|' '\w+[^\s]*@\w+\.\w+' + + # at least one letter in 3 parts (before @, after @, after .) + self.sth_at_sth_dot_sth_re = sre.compile(r'\w+[^\s]*@\w+\.\w+') + + emoticons_pattern = '' + for emoticon in self.emoticons: # travel tru emoticons list + emoticon_escaped = sre.escape(emoticon) # espace regexp metachars + emoticons_pattern += emoticon_escaped + '|'# or is | in regexp + #self.emoticons_pattern = self.emoticons_pattern[0:-1] # remove the last | + + self.emot_and_url_pattern = emoticons_pattern + self.url_pattern + gtk.gdk.threads_enter() self.autoconnect() gtk.main() gtk.gdk.threads_leave() +if __name__ == "__main__": + import getopt, pickle, sys, socket + try: + opts, args = getopt.getopt(sys.argv[1:], "p:h", ["help"]) + except getopt.GetoptError: + # print help information and exit: + usage() + sys.exit(2) + port = 8255 + for o, a in opts: + if o == '-p': + port = a + if o in ("-h", "--help"): + usage() + sys.exit() + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect(('', 8255)) + except: + #TODO: use i18n + print "unable to connect to localhost on port ", port + else: + evp = pickle.dumps(('EXEC_PLUGIN', '', 'gtkgui')) + sock.send('<'+evp+'>') + sock.close() + sys.exit() + print _("plugin gtkgui loaded")