diff --git a/gajim/app_actions.py b/gajim/app_actions.py
index 4471084ae..1e76a72d0 100644
--- a/gajim/app_actions.py
+++ b/gajim/app_actions.py
@@ -14,6 +14,9 @@
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see .
+from gi.repository import Gtk
+from gi.repository import Gdk
+
from gajim.common import app
from gajim.common import helpers
from gajim.common.app import interface
@@ -130,24 +133,29 @@ def on_service_disco(action, param):
pass
-def on_join_gc(action, param):
- account = None
+def on_join_gc(_action, param):
+ account, jid = None, None
if param is None:
if not app.get_connected_accounts():
return
else:
- account = param.get_string()
+ account, jid = param.get_strv()
+ if not jid:
+ jid = None
window = app.get_app_window(JoinGroupchatWindow)
if window is None:
- JoinGroupchatWindow(account, None)
+ JoinGroupchatWindow(account, jid)
else:
window.present()
-def on_add_contact(action, param):
- window = app.get_app_window(AddNewContactWindow)
+def on_add_contact(_action, param):
+ account, jid = param.get_strv()
+ if not jid:
+ jid = None
+ window = app.get_app_window(AddNewContactWindow, account)
if window is None:
- AddNewContactWindow(param.get_string())
+ AddNewContactWindow(account, jid)
else:
window.present()
@@ -337,3 +345,25 @@ def show_next_pending_event(action, param):
if not event:
return
app.interface.handle_event(account, jid, event.type_)
+
+
+def open_link(_action, param):
+ kind, link = param.get_strv()
+ helpers.launch_browser_mailer(kind, link)
+
+
+def copy_link(_action, param):
+ text = param.get_string()
+ clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
+ clip.set_text(text, -1)
+
+
+def start_chat(_action, param):
+ account, jid = param.get_strv()
+ app.interface.new_chat_from_jid(account, jid)
+
+
+def join_groupchat(_action, param):
+ account, jid = param.get_strv()
+ room_jid = jid.split('?')[0]
+ app.interface.join_gc_minimal(account, room_jid)
diff --git a/gajim/application.py b/gajim/application.py
index 7ab32c38b..0ea34f025 100644
--- a/gajim/application.py
+++ b/gajim/application.py
@@ -363,6 +363,14 @@ class GajimApplication(Gtk.Application):
act.connect("activate", app_actions.on_add_contact_jid)
self.add_action(act)
+ act = Gio.SimpleAction.new('copy-link', GLib.VariantType.new('s'))
+ act.connect("activate", app_actions.copy_link)
+ self.add_action(act)
+
+ act = Gio.SimpleAction.new('open-link', GLib.VariantType.new('as'))
+ act.connect("activate", app_actions.open_link)
+ self.add_action(act)
+
for action in general_actions:
action_name, func = action
act = Gio.SimpleAction.new(action_name, None)
@@ -388,8 +396,9 @@ class GajimApplication(Gtk.Application):
return [
('-start-single-chat', a.on_single_message, 'online', 's'),
- ('-join-groupchat', a.on_join_gc, 'online', 's'),
- ('-add-contact', a.on_add_contact, 'online', 's'),
+ ('-start-chat', a.start_chat, 'online', 'as'),
+ ('-join-groupchat', a.on_join_gc, 'online', 'as'),
+ ('-add-contact', a.on_add_contact, 'online', 'as'),
('-services', a.on_service_disco, 'online', 's'),
('-profile', a.on_profile, 'feature', 's'),
('-xml-console', a.on_xml_console, 'always', 's'),
diff --git a/gajim/conversation_textview.py b/gajim/conversation_textview.py
index 7e1c2717b..8e5799e22 100644
--- a/gajim/conversation_textview.py
+++ b/gajim/conversation_textview.py
@@ -23,21 +23,20 @@
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see .
+import time
+import os
+import queue
+import urllib
+import logging
+from calendar import timegm
+
from gi.repository import Gtk
-from gi.repository import Gdk
from gi.repository import Pango
from gi.repository import GObject
from gi.repository import GLib
-import time
-import os
-import queue
-import urllib
-
-from gajim.gtk import AddNewContactWindow
from gajim.gtk import util
from gajim.gtk.util import load_icon
-from gajim.gtk.util import get_builder
from gajim.gtk.util import get_cursor
from gajim.gtk.emoji_data import emoji_pixbufs
from gajim.gtk.emoji_data import is_emoji
@@ -45,7 +44,7 @@ from gajim.gtk.emoji_data import get_emoji_pixbuf
from gajim.common import app
from gajim.common import helpers
from gajim.common import i18n
-from calendar import timegm
+
from gajim.common.fuzzyclock import FuzzyClock
from gajim.common.const import StyleAttr
@@ -55,7 +54,6 @@ NOT_SHOWN = 0
ALREADY_RECEIVED = 1
SHOWN = 2
-import logging
log = logging.getLogger('gajim.conversation_textview')
def is_selection_modified(mark):
@@ -172,10 +170,7 @@ class ConversationTextview(GObject.GObject):
self.fc = FuzzyClock()
# no need to inherit TextView, use it as atrribute is safer
- self.tv = HtmlTextView()
- # we have to override HtmlTextView Event handlers
- # because we don't inherit
- self.tv.hyperlink_handler = self.hyperlink_handler
+ self.tv = HtmlTextView(account)
self.tv.connect_tooltip(self.query_tooltip)
# set properties
@@ -658,146 +653,6 @@ class ConversationTextview(GObject.GObject):
finish_sel.forward_word_end()
self.selected_phrase = buffer_.get_text(start_sel, finish_sel, True)
- def on_open_link_activate(self, widget, kind, text):
- helpers.launch_browser_mailer(kind, text)
-
- def on_copy_link_activate(self, widget, text):
- clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
- clip.set_text(text, -1)
-
- def on_start_chat_activate(self, widget, jid):
- app.interface.new_chat_from_jid(self.account, jid)
-
- def on_join_group_chat_menuitem_activate(self, widget, room_jid):
- # Remove ?join
- room_jid = room_jid.split('?')[0]
- app.interface.join_gc_minimal(self.account, room_jid)
-
- def on_add_to_roster_activate(self, widget, jid):
- AddNewContactWindow(self.account, jid)
-
- def make_link_menu(self, event, kind, text):
- xml = get_builder('chat_context_menu.ui')
- menu = xml.get_object('chat_context_menu')
- childs = menu.get_children()
- if kind == 'url':
- id_ = childs[0].connect('activate', self.on_copy_link_activate, text)
- self.handlers[id_] = childs[0]
- id_ = childs[1].connect('activate', self.on_open_link_activate, kind,
- text)
- self.handlers[id_] = childs[1]
- childs[2].hide() # copy mail/jid address
- childs[3].hide() # open mail composer
- childs[4].hide() # jid section separator
- childs[5].hide() # start chat
- childs[6].hide() # join group chat
- childs[7].hide() # add to roster
- else: # It's a mail or a JID
- text = text.lower()
- if text.startswith('xmpp:'):
- text = text[5:]
- id_ = childs[2].connect('activate', self.on_copy_link_activate, text)
- self.handlers[id_] = childs[2]
- id_ = childs[3].connect('activate', self.on_open_link_activate, kind,
- text)
- self.handlers[id_] = childs[3]
- id_ = childs[5].connect('activate', self.on_start_chat_activate, text)
- self.handlers[id_] = childs[5]
- id_ = childs[6].connect('activate',
- self.on_join_group_chat_menuitem_activate, text)
- self.handlers[id_] = childs[6]
-
- if self.account and app.connections[self.account].\
- roster_supported:
- id_ = childs[7].connect('activate',
- self.on_add_to_roster_activate, text)
- self.handlers[id_] = childs[7]
- childs[7].show() # show add to roster menuitem
- else:
- childs[7].hide() # hide add to roster menuitem
-
- if kind == 'xmpp':
- id_ = childs[0].connect('activate', self.on_copy_link_activate,
- 'xmpp:' + text)
- self.handlers[id_] = childs[0]
- childs[0].set_label(_('Copy JID'))
- childs[2].hide() # copy mail/jid address
- childs[3].hide() # open mail composer
- childs[4].hide() # jid section separator
- elif kind == 'mail':
- childs[2].set_label(_('Copy Email Address'))
- childs[4].hide() # jid section separator
- childs[5].hide() # start chat
- childs[6].hide() # join group chat
- childs[7].hide() # add to roster
-
- if kind != 'xmpp':
- childs[0].hide() # copy link location
- childs[1].hide() # open link in browser
-
- menu.attach_to_widget(self.tv, None)
- menu.popup(None, None, None, None, event.button.button, event.time)
-
- def hyperlink_handler(self, texttag, widget, event, iter_, kind):
- if event.type == Gdk.EventType.BUTTON_PRESS:
- begin_iter = iter_.copy()
- # we get the beginning of the tag
- while not begin_iter.begins_tag(texttag):
- begin_iter.backward_char()
- end_iter = iter_.copy()
- # we get the end of the tag
- while not end_iter.ends_tag(texttag):
- end_iter.forward_char()
-
- # Detect XHTML-IM link
- word = getattr(texttag, 'href', None)
- if word:
- if word.startswith('xmpp'):
- kind = 'xmpp'
- elif word.startswith('mailto:'):
- kind = 'mail'
- elif app.interface.sth_at_sth_dot_sth_re.match(word):
- # it's a JID or mail
- kind = 'sth_at_sth'
- else:
- word = self.tv.get_buffer().get_text(begin_iter, end_iter, True)
-
- if event.button.button == 3: # right click
- self.make_link_menu(event, kind, word)
- return True
-
- self.plugin_modified = False
- app.plugin_manager.extension_point(
- 'hyperlink_handler', word, kind, self,
- self.tv.get_toplevel())
- if self.plugin_modified:
- return
-
- # we launch the correct application
- if kind == 'xmpp':
- word = word[5:]
- if '?' in word:
- (jid, action) = word.split('?')
- if action == 'join':
- app.interface.join_gc_minimal(self.account, jid)
- else:
- self.on_start_chat_activate(None, jid)
- else:
- self.on_start_chat_activate(None, word)
- # handle geo:-URIs
- elif word[:4] == 'geo:':
- location = word[4:]
- lat, _, lon = location.partition(',')
- if lon == '':
- return
- uri = 'https://www.openstreetmap.org/?' \
- 'mlat=%(lat)s&mlon=%(lon)s&zoom=16' % \
- {'lat': lat, 'lon': lon}
- helpers.launch_browser_mailer(kind, uri)
- # other URIs
- else:
- helpers.launch_browser_mailer(kind, word)
-
def detect_and_print_special_text(self, otext, other_tags, graphics=True,
iter_=None, additional_data=None):
"""
diff --git a/gajim/data/gui/chat_context_menu.ui b/gajim/data/gui/chat_context_menu.ui
deleted file mode 100644
index c5ef3ce7e..000000000
--- a/gajim/data/gui/chat_context_menu.ui
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
diff --git a/gajim/gui_menu_builder.py b/gajim/gui_menu_builder.py
index a849ad7ce..e71f51bef 100644
--- a/gajim/gui_menu_builder.py
+++ b/gajim/gui_menu_builder.py
@@ -675,7 +675,7 @@ def get_bookmarks_menu(account, rebuild=False):
# Build Join Groupchat
action = 'app.{}-join-groupchat'.format(account)
menuitem = Gio.MenuItem.new(_('Join Group Chat'), action)
- variant = GLib.Variant('s', account)
+ variant = GLib.Variant('as', [account, ''])
menuitem.set_action_and_target_value(action, variant)
menu.append_item(menuitem)
@@ -749,7 +749,10 @@ def get_account_menu(account):
continue
action = 'app.{}{}'.format(account, action)
menuitem = Gio.MenuItem.new(label, action)
- variant = GLib.Variant('s', account)
+ if 'add_contact' in action:
+ variant = GLib.Variant('as', [account, ''])
+ else:
+ variant = GLib.Variant('s', account)
menuitem.set_action_and_target_value(action, variant)
menu.append_item(menuitem)
else:
@@ -840,6 +843,75 @@ def get_encryption_menu(control_id, type_id, zeroconf=False):
return menu
+def get_conv_context_menu(account, kind, text):
+ if kind == 'xmpp':
+ if '?join' in text:
+ context_menu = [
+ ('copy-link', _('Copy JID')),
+ ('-join-groupchat', _('Join Groupchat')),
+ ]
+ else:
+ context_menu = [
+ ('copy-link', _('Copy JID')),
+ ('-start-chat', _('Start Chat')),
+ ('-add-contact', _('Add to Roster…')),
+ ]
+
+ elif kind == 'url':
+ context_menu = [
+ ('copy-link', _('Copy Link Location')),
+ ('open-link', _('Open Link in Browser')),
+ ]
+
+ elif kind == 'mail':
+ context_menu = [
+ ('copy-link', _('Copy Email Address')),
+ ('open-link', _('Open Email Composer')),
+ ]
+
+ elif kind == 'sth_at_sth':
+ context_menu = [
+ ('copy-link', _('Copy JID/Email')),
+ ('open-link', _('Open Email Composer')),
+ ('-start-chat', _('Start Chat')),
+ ('-join-groupchat', _('Join Groupchat')),
+ ('-add-contact', _('Add to Roster…')),
+ ]
+ else:
+ return
+
+ menu = Gtk.Menu()
+ for item in context_menu:
+ action, label = item
+ menuitem = Gtk.MenuItem()
+ menuitem.set_label(label)
+
+ if action.startswith('-'):
+ action = 'app.%s%s' % (account, action)
+ else:
+ action = 'app.%s' % action
+ menuitem.set_action_name(action)
+
+ if 'join-groupchat' in action:
+ text = text.replace('xmpp:', '')
+ text = text.split('?')[0]
+
+ if 'add-contact' in action:
+ text = text.replace('xmpp:', '')
+ text = text.split('?')[0]
+
+ if action == 'app.open-link':
+ value = GLib.Variant.new_strv([kind, text])
+ elif action == 'app.copy-link':
+ value = GLib.Variant.new_string(text)
+ else:
+ value = GLib.Variant.new_strv([account, text])
+ menuitem.set_action_target_value(value)
+ menuitem.show()
+ menu.append(menuitem)
+ return menu
+
+
def escape_mnemonic(label):
if label is None:
return
diff --git a/gajim/htmltextview.py b/gajim/htmltextview.py
index ffed884af..ac8ce38ba 100644
--- a/gajim/htmltextview.py
+++ b/gajim/htmltextview.py
@@ -43,16 +43,16 @@ from gi.repository import Pango
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
+from gi.repository import GLib
from gajim.common import app
from gajim.common import helpers
from gajim.common.i18n import _
from gajim.common.const import StyleAttr
-from gajim.gtk import JoinGroupchatWindow
-from gajim.gtk import AddNewContactWindow
from gajim.gtk.util import load_icon
from gajim.gtk.util import get_cursor
-from gajim.gtk.util import get_builder
+
+from gajim.gui_menu_builder import get_conv_context_menu
log = logging.getLogger('gajim.htmlview')
@@ -825,7 +825,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
class HtmlTextView(Gtk.TextView):
- def __init__(self):
+ def __init__(self, account=None):
Gtk.TextView.__init__(self)
self.set_wrap_mode(Gtk.WrapMode.CHAR)
self.set_editable(False)
@@ -835,11 +835,12 @@ class HtmlTextView(Gtk.TextView):
self.connect('unrealize', self.on_html_text_view_unrealized)
self.connect('copy-clipboard', self.on_html_text_view_copy_clipboard)
self.id_ = self.connect('button-release-event',
- self.on_left_mouse_button_release)
+ self.on_left_mouse_button_release)
self.get_buffer().eol_tag = self.get_buffer().create_tag('eol')
self.config = app.config
self.interface = app.interface
- # end big hack
+ self.account = account
+ self.plugin_modified = False
def connect_tooltip(self, func=None):
self.connect('query-tooltip', func or self.__query_tooltip)
@@ -892,73 +893,21 @@ class HtmlTextView(Gtk.TextView):
self._changed_cursor = False
return False
- def on_open_link_activate(self, widget, kind, text):
- helpers.launch_browser_mailer(kind, text)
+ def show_context_menu(self, _event, kind, text):
+ menu = get_conv_context_menu(self.account, kind, text)
+ if menu is None:
+ return
- def on_copy_link_activate(self, widget, text):
- clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
- clip.set_text(text, -1)
+ def destroy(menu, pspec):
+ visible = menu.get_property('visible')
+ if not visible:
+ GLib.idle_add(menu.destroy)
-# def on_start_chat_activate(self, widget, jid):
-# app.interface.new_chat_from_jid(self.account, jid)
+ menu.attach_to_widget(self, None)
+ menu.connect('notify::visible', destroy)
+ menu.popup_at_pointer()
- def on_join_group_chat_menuitem_activate(self, widget, room_jid):
- JoinGroupchatWindow(None, room_jid)
-
- def on_add_to_roster_activate(self, widget, jid):
- AddNewContactWindow(self.account, jid)
-
- def make_link_menu(self, event, kind, text):
- ui = get_builder('chat_context_menu.ui')
- menu = ui.get_object('chat_context_menu')
- childs = menu.get_children()
- if kind == 'url':
- childs[0].connect('activate', self.on_copy_link_activate, text)
- childs[1].connect('activate', self.on_open_link_activate, kind,
- text)
- childs[2].hide() # copy mail address
- childs[3].hide() # open mail composer
- childs[4].hide() # jid section separator
- childs[5].hide() # start chat
- childs[6].hide() # join group chat
- childs[7].hide() # add to roster
- else: # It's a mail or a JID
- text = text.lower()
- if text.startswith('xmpp:'):
- text = text[5:]
- childs[2].connect('activate', self.on_copy_link_activate, text)
- childs[3].connect('activate', self.on_open_link_activate, kind,
- text)
-# childs[5].connect('activate', self.on_start_chat_activate, text)
- childs[6].connect('activate',
- self.on_join_group_chat_menuitem_activate, text)
-
-# if self.account and app.connections[self.account].\
-# roster_supported:
-# childs[7].connect('activate',
-# self.on_add_to_roster_activate, text)
-# childs[7].show() # show add to roster menuitem
-# else:
-# childs[7].hide() # hide add to roster menuitem
-
- if kind == 'xmpp':
- childs[0].connect('activate', self.on_copy_link_activate,
- 'xmpp:' + text)
- childs[2].hide() # copy mail address
- childs[3].hide() # open mail composer
- elif kind == 'mail':
- childs[6].hide() # join group chat
-
- if kind != 'xmpp':
- childs[0].hide() # copy link location
- childs[1].hide() # open link in browser
- childs[4].hide() # jid section separator
- childs[5].hide() # start chat
- childs[7].hide() # add to roster
-
- menu.popup(None, None, None, event.button, event.time)
-
- def hyperlink_handler(self, texttag, widget, event, iter_, kind):
+ def hyperlink_handler(self, texttag, _widget, event, iter_, kind):
if event.type == Gdk.EventType.BUTTON_PRESS:
begin_iter = iter_.copy()
# we get the beginning of the tag
@@ -980,31 +929,45 @@ class HtmlTextView(Gtk.TextView):
# it's a JID or mail
kind = 'sth_at_sth'
else:
- word = self.textview.get_buffer().get_text(begin_iter,
- end_iter)
+ word = self.get_buffer().get_text(begin_iter, end_iter, True)
- if event.button == 3: # right click
- self.make_link_menu(event, kind, word)
+ if event.button.button == 3: # right click
+ self.show_context_menu(event, kind, word)
return True
+ self.plugin_modified = False
+ app.plugin_manager.extension_point(
+ 'hyperlink_handler', word, kind, self,
+ self.get_toplevel())
+ if self.plugin_modified:
+ return
+
# we launch the correct application
if kind == 'xmpp':
word = word[5:]
if '?' in word:
(jid, action) = word.split('?')
if action == 'join':
- self.on_join_group_chat_menuitem_activate(None, jid)
+ app.interface.join_gc_minimal(self.account, jid)
else:
- self.on_start_chat_activate(None, jid)
+ app.interface.new_chat_from_jid(self.account, jid)
else:
- self.on_start_chat_activate(None, word)
+ app.interface.new_chat_from_jid(self.account, word)
+
+ # handle geo:-URIs
+ elif word[:4] == 'geo:':
+ location = word[4:]
+ lat, _, lon = location.partition(',')
+ if lon == '':
+ return
+ uri = 'https://www.openstreetmap.org/?' \
+ 'mlat=%(lat)s&mlon=%(lon)s&zoom=16' % \
+ {'lat': lat, 'lon': lon}
+ helpers.launch_browser_mailer(kind, uri)
+ # other URIs
else:
helpers.launch_browser_mailer(kind, word)
- def _hyperlink_handler(self, texttag, widget, event, iter_, kind):
- # self.hyperlink_handler can be overwritten, so call it when needed
- self.hyperlink_handler(texttag, widget, event, iter_, kind)
-
def display_html(self, html, textview, conv_textview, iter_=None):
buffer_ = self.get_buffer()
if iter_:
diff --git a/gajim/roster_window.py b/gajim/roster_window.py
index 29fb4839c..42bd1d732 100644
--- a/gajim/roster_window.py
+++ b/gajim/roster_window.py
@@ -3679,7 +3679,7 @@ class RosterWindow:
When the join gc menuitem is clicked, show the join gc window
"""
app.app.activate_action('%s-join-groupchat' % account,
- GLib.Variant('s', account))
+ GLib.Variant('as', [account, '']))
def on_show_transports_action(self, action, param):
app.config.set('show_transports_group', param.get_boolean())