Daniel Brötzmann pushed to branch mainwindow at gajim / gajim

Commits:

2 changed files:

Changes:

  • gajim/gtk/conversation/plain_widget.py
    ... ... @@ -13,7 +13,6 @@
    13 13
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14 14
     
    
    15 15
     import logging
    
    16
    -from urllib.parse import quote
    
    17 16
     
    
    18 17
     from gi.repository import Gtk
    
    19 18
     from gi.repository import Pango
    
    ... ... @@ -21,14 +20,13 @@
    21 20
     from gi.repository import GLib
    
    22 21
     
    
    23 22
     from gajim.common import app
    
    24
    -from gajim.common import i18n
    
    25 23
     from gajim.common.const import StyleAttr
    
    26 24
     from gajim.common.helpers import open_uri
    
    27 25
     from gajim.common.helpers import reduce_chars_newlines
    
    28 26
     from gajim.common.helpers import parse_uri
    
    29
    -from gajim.common.i18n import _
    
    30 27
     
    
    31
    -from gajim.gui_menu_builder import get_conv_context_menu
    
    28
    +from gajim.gui_menu_builder import get_conv_action_context_menu
    
    29
    +from gajim.gui_menu_builder import get_conv_uri_context_menu
    
    32 30
     
    
    33 31
     from ..util import get_cursor
    
    34 32
     from ..util import make_pango_attribute
    
    ... ... @@ -72,6 +70,20 @@ def __init__(self, account, selectable):
    72 70
     
    
    73 71
             self.get_style_context().add_class('gajim-conversation-text')
    
    74 72
     
    
    73
    +        self.connect('populate-popup', self._on_populate_popup)
    
    74
    +
    
    75
    +    def _on_populate_popup(self, label, menu):
    
    76
    +        selected, start, end = label.get_selection_bounds()
    
    77
    +        if not selected:
    
    78
    +            menu.show_all()
    
    79
    +            return
    
    80
    +
    
    81
    +        selected_text = label.get_text()[start:end]
    
    82
    +        action_menu_item = get_conv_action_context_menu(
    
    83
    +            self._account, selected_text)
    
    84
    +        menu.prepend(action_menu_item)
    
    85
    +        menu.show_all()
    
    86
    +
    
    75 87
         def print_text_with_styling(self, block):
    
    76 88
             text = GLib.markup_escape_text(block.text.strip())
    
    77 89
             for uri in block.uris:
    
    ... ... @@ -261,69 +273,9 @@ def _on_populate_popup(self, _textview, menu):
    261 273
                 menu.show_all()
    
    262 274
                 return
    
    263 275
     
    
    264
    -        selected_text_short = reduce_chars_newlines(
    
    265
    -            self._selected_text, 25, 2)
    
    266
    -        item = Gtk.MenuItem.new_with_mnemonic(
    
    267
    -            _('_Actions for "%s"') % selected_text_short)
    
    268
    -        menu.prepend(item)
    
    269
    -        submenu = Gtk.Menu()
    
    270
    -        item.set_submenu(submenu)
    
    271
    -        uri_text = quote(self._selected_text.encode('utf-8'))
    
    272
    -
    
    273
    -        if app.settings.get('always_english_wikipedia'):
    
    274
    -            uri = (f'https://en.wikipedia.org/wiki/'
    
    275
    -                   f'Special:Search?search={uri_text}')
    
    276
    -        else:
    
    277
    -            uri = (f'https://{i18n.get_short_lang_code()}.wikipedia.org/'
    
    278
    -                   f'wiki/Special:Search?search={uri_text}')
    
    279
    -        item = Gtk.MenuItem.new_with_mnemonic(_('Read _Wikipedia Article'))
    
    280
    -        id_ = item.connect('activate', self._visit_uri, uri)
    
    281
    -        self._handlers[id_] = item
    
    282
    -        submenu.append(item)
    
    283
    -
    
    284
    -        item = Gtk.MenuItem.new_with_mnemonic(
    
    285
    -            _('Look it up in _Dictionary'))
    
    286
    -        dict_link = app.settings.get('dictionary_url')
    
    287
    -        if dict_link == 'WIKTIONARY':
    
    288
    -            # Default is wikitionary.org
    
    289
    -            if app.settings.get('always_english_wiktionary'):
    
    290
    -                uri = (f'https://en.wiktionary.org/wiki/'
    
    291
    -                       f'Special:Search?search={uri_text}')
    
    292
    -            else:
    
    293
    -                uri = (f'https://{i18n.get_short_lang_code()}.wiktionary.org/'
    
    294
    -                       f'wiki/Special:Search?search={uri_text}')
    
    295
    -            id_ = item.connect('activate', self._visit_uri, uri)
    
    296
    -            self._handlers[id_] = item
    
    297
    -        else:
    
    298
    -            if dict_link.find('%s') == -1:
    
    299
    -                # There has to be a '%s' in the url if it’s not WIKTIONARY
    
    300
    -                item = Gtk.MenuItem.new_with_label(
    
    301
    -                    _('Dictionary URL is missing a "%s"'))
    
    302
    -                item.set_sensitive(False)
    
    303
    -            else:
    
    304
    -                uri = dict_link % uri_text
    
    305
    -                id_ = item.connect('activate', self._visit_uri, uri)
    
    306
    -                self._handlers[id_] = item
    
    307
    -        submenu.append(item)
    
    308
    -
    
    309
    -        search_link = app.settings.get('search_engine')
    
    310
    -        if search_link.find('%s') == -1:
    
    311
    -            # There has to be a '%s' in the url
    
    312
    -            item = Gtk.MenuItem.new_with_label(
    
    313
    -                _('Web Search URL is missing a "%s"'))
    
    314
    -            item.set_sensitive(False)
    
    315
    -        else:
    
    316
    -            item = Gtk.MenuItem.new_with_mnemonic(_('Web _Search for it'))
    
    317
    -            uri = search_link % uri_text
    
    318
    -            id_ = item.connect('activate', self._visit_uri, uri)
    
    319
    -            self._handlers[id_] = item
    
    320
    -        submenu.append(item)
    
    321
    -
    
    322
    -        item = Gtk.MenuItem.new_with_mnemonic(_('Open as _Link'))
    
    323
    -        id_ = item.connect('activate', self._visit_uri, uri)
    
    324
    -        self._handlers[id_] = item
    
    325
    -        submenu.append(item)
    
    326
    -
    
    276
    +        action_menu_item = get_conv_action_context_menu(
    
    277
    +            self._account, self._selected_text)
    
    278
    +        menu.prepend(action_menu_item)
    
    327 279
             menu.show_all()
    
    328 280
     
    
    329 281
         def _on_uri_clicked(self, texttag, _widget, event, iter_, _kind):
    
    ... ... @@ -346,7 +298,7 @@ def _on_uri_clicked(self, texttag, _widget, event, iter_, _kind):
    346 298
     
    
    347 299
             uri = parse_uri(word)
    
    348 300
             if event.button.button == 3:  # right click
    
    349
    -            self._show_context_menu(uri)
    
    301
    +            self._show_uri_context_menu(uri)
    
    350 302
                 return Gdk.EVENT_STOP
    
    351 303
     
    
    352 304
             # TODO:
    
    ... ... @@ -359,8 +311,8 @@ def _on_uri_clicked(self, texttag, _widget, event, iter_, _kind):
    359 311
             open_uri(uri, account=self._account)
    
    360 312
             return Gdk.EVENT_STOP
    
    361 313
     
    
    362
    -    def _show_context_menu(self, uri):
    
    363
    -        menu = get_conv_context_menu(self._account, uri)
    
    314
    +    def _show_uri_context_menu(self, uri):
    
    315
    +        menu = get_conv_uri_context_menu(self._account, uri)
    
    364 316
             if menu is None:
    
    365 317
                 log.warning('No handler for URI type: %s', uri)
    
    366 318
                 return
    
    ... ... @@ -373,7 +325,3 @@ def destroy(menu, _pspec):
    373 325
             menu.attach_to_widget(self, None)
    
    374 326
             menu.connect('notify::visible', destroy)
    
    375 327
             menu.popup_at_pointer()
    376
    -
    
    377
    -    @staticmethod
    
    378
    -    def _visit_uri(_widget, uri):
    
    379
    -        open_uri(uri)

  • gajim/gui_menu_builder.py
    ... ... @@ -14,6 +14,9 @@
    14 14
     # You should have received a copy of the GNU General Public License
    
    15 15
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    16 16
     
    
    17
    +from typing import Optional
    
    18
    +from urllib.parse import quote
    
    19
    +
    
    17 20
     from gi.repository import Gtk
    
    18 21
     from gi.repository import Gio
    
    19 22
     from gi.repository import GLib
    
    ... ... @@ -22,9 +25,14 @@
    22 25
     from gajim.common.helpers import is_affiliation_change_allowed
    
    23 26
     from gajim.common.helpers import is_role_change_allowed
    
    24 27
     from gajim.common.helpers import jid_is_blocked
    
    28
    +from gajim.common.helpers import reduce_chars_newlines
    
    25 29
     from gajim.common.i18n import _
    
    30
    +from gajim.common.i18n import get_short_lang_code
    
    26 31
     from gajim.common.const import URIType
    
    27 32
     from gajim.common.const import URIAction
    
    33
    +from gajim.common.structs import URI
    
    34
    +
    
    35
    +from gajim.gui.const import ControlType
    
    28 36
     
    
    29 37
     
    
    30 38
     def get_singlechat_menu(control_id, account, jid, type_):
    
    ... ... @@ -65,7 +73,7 @@ def build_chatstate_menu():
    65 73
     
    
    66 74
             for entry in entries:
    
    67 75
                 label, setting = entry
    
    68
    -            action = 'win.send-chatstate-%s::%s' % (control_id, setting)
    
    76
    +            action = f'win.send-chatstate-{control_id}::{setting}'
    
    69 77
                 menu.append(label, action)
    
    70 78
             return menu
    
    71 79
     
    
    ... ... @@ -162,7 +170,7 @@ def build_menu(preset):
    162 170
         return build_menu(groupchat_menu)
    
    163 171
     
    
    164 172
     
    
    165
    -def get_account_menu(account):
    
    173
    +def get_account_menu(account: str) -> Gio.Menu:
    
    166 174
         '''
    
    167 175
         [(action, label/sub_menu)]
    
    168 176
             action: string
    
    ... ... @@ -196,7 +204,7 @@ def build_menu(preset):
    196 204
             for item in preset:
    
    197 205
                 if isinstance(item[1], str):
    
    198 206
                     action, label = item
    
    199
    -                action = 'app.{}{}'.format(account, action)
    
    207
    +                action = f'app.{account}{action}'
    
    200 208
                     menuitem = Gio.MenuItem.new(label, action)
    
    201 209
                     variant = GLib.Variant('s', account)
    
    202 210
                     menuitem.set_action_and_target_value(action, variant)
    
    ... ... @@ -250,11 +258,11 @@ def build_accounts_menu():
    250 258
             menubar.insert_submenu(menu_position, _('Accounts'), acc_menu)
    
    251 259
     
    
    252 260
     
    
    253
    -def get_encryption_menu(control_id, control_type, zeroconf=False):
    
    261
    +def get_encryption_menu(control_id: str, control_type: ControlType,
    
    262
    +                        zeroconf: Optional[bool] = False) -> Gio.Menu:
    
    254 263
         menu = Gio.Menu()
    
    255 264
         menu.append(
    
    256
    -        _('Disabled'),
    
    257
    -        'win.set-encryption-{}::{}'.format(control_id, 'disabled'))
    
    265
    +        _('Disabled'), f'win.set-encryption-{control_id}::disabled')
    
    258 266
         for name, plugin in app.plugin_manager.encryption_plugins.items():
    
    259 267
             if control_type.is_groupchat:
    
    260 268
                 if not hasattr(plugin, 'allow_groupchat'):
    
    ... ... @@ -265,15 +273,82 @@ def get_encryption_menu(control_id, control_type, zeroconf=False):
    265 273
             if zeroconf:
    
    266 274
                 if not hasattr(plugin, 'allow_zeroconf'):
    
    267 275
                     continue
    
    268
    -        menu_action = 'win.set-encryption-{}::{}'.format(
    
    269
    -            control_id, name)
    
    276
    +        menu_action = f'win.set-encryption-{control_id}::{name}'
    
    270 277
             menu.append(name, menu_action)
    
    271 278
         if menu.get_n_items() == 1:
    
    272 279
             return None
    
    273 280
         return menu
    
    274 281
     
    
    275 282
     
    
    276
    -def get_conv_context_menu(account, uri):
    
    283
    +def get_conv_action_context_menu(account: str, selected_text: str) -> Gtk.Menu:
    
    284
    +    selected_text_short = reduce_chars_newlines(selected_text, 10, 1)
    
    285
    +
    
    286
    +    action_menu_item = Gtk.MenuItem.new_with_mnemonic(
    
    287
    +        _('_Actions for "%s"') % selected_text_short)
    
    288
    +    submenu = Gtk.Menu()
    
    289
    +    action_menu_item.set_submenu(submenu)
    
    290
    +
    
    291
    +    uri_text = quote(selected_text.encode('utf-8'))
    
    292
    +
    
    293
    +    if app.settings.get('always_english_wikipedia'):
    
    294
    +        uri = (f'https://en.wikipedia.org/wiki/'
    
    295
    +               f'Special:Search?search={uri_text}')
    
    296
    +    else:
    
    297
    +        uri = (f'https://{get_short_lang_code()}.wikipedia.org/'
    
    298
    +               f'wiki/Special:Search?search={uri_text}')
    
    299
    +    item = Gtk.MenuItem.new_with_mnemonic(_('Read _Wikipedia Article'))
    
    300
    +    value = GLib.Variant.new_strv([account, uri])
    
    301
    +    item.set_action_target_value(value)
    
    302
    +    submenu.append(item)
    
    303
    +
    
    304
    +    item = Gtk.MenuItem.new_with_mnemonic(
    
    305
    +        _('Look it up in _Dictionary'))
    
    306
    +    dict_link = app.settings.get('dictionary_url')
    
    307
    +    if dict_link == 'WIKTIONARY':
    
    308
    +        # Default is wikitionary.org
    
    309
    +        if app.settings.get('always_english_wiktionary'):
    
    310
    +            uri = (f'https://en.wiktionary.org/wiki/'
    
    311
    +                   f'Special:Search?search={uri_text}')
    
    312
    +        else:
    
    313
    +            uri = (f'https://{get_short_lang_code()}.wiktionary.org/'
    
    314
    +                   f'wiki/Special:Search?search={uri_text}')
    
    315
    +    else:
    
    316
    +        if dict_link.find('%s') == -1:
    
    317
    +            # There has to be a '%s' in the url if it’s not WIKTIONARY
    
    318
    +            item = Gtk.MenuItem.new_with_label(
    
    319
    +                _('Dictionary URL is missing a "%s"'))
    
    320
    +            item.set_sensitive(False)
    
    321
    +        else:
    
    322
    +            uri = dict_link % uri_text
    
    323
    +    value = GLib.Variant.new_strv([account, uri])
    
    324
    +    item.set_action_target_value(value)
    
    325
    +    submenu.append(item)
    
    326
    +
    
    327
    +    search_link = app.settings.get('search_engine')
    
    328
    +    if search_link.find('%s') == -1:
    
    329
    +        # There has to be a '%s' in the url
    
    330
    +        item = Gtk.MenuItem.new_with_label(
    
    331
    +            _('Web Search URL is missing a "%s"'))
    
    332
    +        item.set_sensitive(False)
    
    333
    +    else:
    
    334
    +        item = Gtk.MenuItem.new_with_mnemonic(_('Web _Search for it'))
    
    335
    +        uri = search_link % uri_text
    
    336
    +    value = GLib.Variant.new_strv([account, uri])
    
    337
    +    item.set_action_target_value(value)
    
    338
    +    submenu.append(item)
    
    339
    +
    
    340
    +    item = Gtk.MenuItem.new_with_mnemonic(_('Open as _Link'))
    
    341
    +    value = GLib.Variant.new_strv([account, uri_text])
    
    342
    +    item.set_action_target_value(value)
    
    343
    +    submenu.append(item)
    
    344
    +
    
    345
    +    for item in submenu:
    
    346
    +        item.set_action_name('app.open-link')
    
    347
    +
    
    348
    +    return action_menu_item
    
    349
    +
    
    350
    +
    
    351
    +def get_conv_uri_context_menu(account: str, uri: URI) -> Gtk.Menu:
    
    277 352
         if uri.type == URIType.XMPP:
    
    278 353
             if uri.action == URIAction.JOIN:
    
    279 354
                 context_menu = [
    
    ... ... @@ -323,9 +398,9 @@ def get_conv_context_menu(account, uri):
    323 398
             menuitem.set_label(label)
    
    324 399
     
    
    325 400
             if action.startswith('-'):
    
    326
    -            action = 'app.%s%s' % (account, action)
    
    401
    +            action = f'app.{account}{action}'
    
    327 402
             else:
    
    328
    -            action = 'app.%s' % action
    
    403
    +            action = f'app.{action}'
    
    329 404
             menuitem.set_action_name(action)
    
    330 405
     
    
    331 406
             data = uri.data
    
    ... ... @@ -456,15 +531,15 @@ def build_workspaces_submenu(current_workspace_id, account, jid):
    456 531
     
    
    457 532
     def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    
    458 533
         menu = Gtk.Menu()
    
    534
    +    real_jid = contact.real_jid or ''
    
    459 535
     
    
    460 536
         item = Gtk.MenuItem(label=_('Information'))
    
    461
    -    action = 'win.contact-information-%s::%s' % (control_id, contact.name)
    
    537
    +    action = f'win.contact-information-{control_id}::{contact.name}'
    
    462 538
         item.set_detailed_action_name(action)
    
    463 539
         menu.append(item)
    
    464 540
     
    
    465 541
         item = Gtk.MenuItem(label=_('Add to Contact List…'))
    
    466
    -    action = 'app.{account}-add-contact(["{account}", "{jid}"])'.format(
    
    467
    -        account=account, jid=contact.real_jid or '')
    
    542
    +    action = f'app.{account}-add-contact(["{account}", "{real_jid}"])'
    
    468 543
         if contact.real_jid is None:
    
    469 544
             item.set_sensitive(False)
    
    470 545
         else:
    
    ... ... @@ -472,14 +547,14 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    472 547
         menu.append(item)
    
    473 548
     
    
    474 549
         item = Gtk.MenuItem(label=_('Execute Command…'))
    
    475
    -    action = 'win.execute-command-%s::%s' % (control_id, contact.name)
    
    550
    +    action = f'win.execute-command-{control_id}::{contact.name}'
    
    476 551
         item.set_detailed_action_name(action)
    
    477 552
         menu.append(item)
    
    478 553
     
    
    479 554
         menu.append(Gtk.SeparatorMenuItem())
    
    480 555
     
    
    481 556
         item = Gtk.MenuItem(label=_('Kick'))
    
    482
    -    action = 'win.kick-%s::%s' % (control_id, contact.name)
    
    557
    +    action = f'win.kick-{control_id}::{contact.name}'
    
    483 558
         if is_role_change_allowed(self_contact, contact):
    
    484 559
             item.set_detailed_action_name(action)
    
    485 560
         else:
    
    ... ... @@ -487,7 +562,7 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    487 562
         menu.append(item)
    
    488 563
     
    
    489 564
         item = Gtk.MenuItem(label=_('Ban'))
    
    490
    -    action = 'win.ban-%s::%s' % (control_id, contact.real_jid or '')
    
    565
    +    action = f'win.ban-{control_id}::{real_jid}'
    
    491 566
         if is_affiliation_change_allowed(self_contact, contact, 'outcast'):
    
    492 567
             item.set_detailed_action_name(action)
    
    493 568
         else:
    
    ... ... @@ -497,8 +572,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    497 572
         menu.append(Gtk.SeparatorMenuItem())
    
    498 573
     
    
    499 574
         item = Gtk.MenuItem(label=_('Make Owner'))
    
    500
    -    action = 'win.change-affiliation-%s(["%s", "owner"])' % (control_id,
    
    501
    -                                                             contact.real_jid)
    
    575
    +    action = (f'win.change-affiliation-{control_id}'
    
    576
    +              f'(["{contact.real_jid}", "owner"])')
    
    502 577
         if is_affiliation_change_allowed(self_contact, contact, 'owner'):
    
    503 578
             item.set_detailed_action_name(action)
    
    504 579
         else:
    
    ... ... @@ -506,8 +581,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    506 581
         menu.append(item)
    
    507 582
     
    
    508 583
         item = Gtk.MenuItem(label=_('Make Admin'))
    
    509
    -    action = 'win.change-affiliation-%s(["%s", "admin"])' % (control_id,
    
    510
    -                                                             contact.real_jid)
    
    584
    +    action = (f'win.change-affiliation-{control_id}'
    
    585
    +              f'(["{contact.real_jid}", "admin"])')
    
    511 586
         if is_affiliation_change_allowed(self_contact, contact, 'admin'):
    
    512 587
             item.set_detailed_action_name(action)
    
    513 588
         else:
    
    ... ... @@ -515,8 +590,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    515 590
         menu.append(item)
    
    516 591
     
    
    517 592
         item = Gtk.MenuItem(label=_('Make Member'))
    
    518
    -    action = 'win.change-affiliation-%s(["%s", "member"])' % (control_id,
    
    519
    -                                                              contact.real_jid)
    
    593
    +    action = (f'win.change-affiliation-{control_id}'
    
    594
    +              f'(["{contact.real_jid}", "member"])')
    
    520 595
         if is_affiliation_change_allowed(self_contact, contact, 'member'):
    
    521 596
             item.set_detailed_action_name(action)
    
    522 597
         else:
    
    ... ... @@ -524,8 +599,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    524 599
         menu.append(item)
    
    525 600
     
    
    526 601
         item = Gtk.MenuItem(label=_('Revoke Member'))
    
    527
    -    action = 'win.change-affiliation-%s(["%s", "none"])' % (control_id,
    
    528
    -                                                            contact.real_jid)
    
    602
    +    action = (f'win.change-affiliation-{control_id}'
    
    603
    +              f'(["{contact.real_jid}", "none"])')
    
    529 604
         if is_affiliation_change_allowed(self_contact, contact, 'none'):
    
    530 605
             item.set_detailed_action_name(action)
    
    531 606
         else:
    
    ... ... @@ -540,9 +615,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    540 615
             role = 'visitor'
    
    541 616
     
    
    542 617
         item = Gtk.MenuItem(label=label)
    
    543
    -    action = 'win.change-role-%s(["%s", "%s"])' % (control_id,
    
    544
    -                                                   contact.name,
    
    545
    -                                                   role)
    
    618
    +    action = (f'win.change-role-{control_id}'
    
    619
    +              f'(["{contact.name}", "{role}"])')
    
    546 620
         if is_role_change_allowed(self_contact, contact):
    
    547 621
             item.set_detailed_action_name(action)
    
    548 622
         else: