| ... |
... |
@@ -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:
|