Daniel Brötzmann pushed to branch mainwindow at gajim / gajim
Commits:
-
ca496e71
by wurstsalat at 2021-03-04T20:46:36+01:00
6 changed files:
- gajim/chat_control.py
- gajim/gtk/account_page.py
- gajim/gtk/chat_list.py
- gajim/gtk/main.py
- gajim/gtk/roster.py
- gajim/gui_menu_builder.py
Changes:
| ... | ... | @@ -397,34 +397,10 @@ def _on_add_to_roster(self, _action, _param): |
| 397 | 397 |
AddNewContactWindow(self.account, self.contact.jid)
|
| 398 | 398 |
|
| 399 | 399 |
def _on_block_contact(self, _action, _param):
|
| 400 |
- def _block_contact(report=None):
|
|
| 401 |
- con = app.connections[self.account]
|
|
| 402 |
- con.get_module('Blocking').block([self.contact.jid], report=report)
|
|
| 403 |
- |
|
| 404 |
- self.parent_win.remove_tab(self, None, force=True)
|
|
| 405 |
- if _('Not in contact list') in self.contact.get_shown_groups():
|
|
| 406 |
- app.interface.roster.remove_contact(
|
|
| 407 |
- self.contact.jid, self.account, force=True, backend=True)
|
|
| 408 |
- return
|
|
| 409 |
- app.interface.roster.draw_contact(self.contact.jid, self.account)
|
|
| 410 |
- |
|
| 411 |
- ConfirmationDialog(
|
|
| 412 |
- _('Block Contact'),
|
|
| 413 |
- _('Really block this contact?'),
|
|
| 414 |
- _('You will appear offline for this contact and you will '
|
|
| 415 |
- 'not receive further messages.'),
|
|
| 416 |
- [DialogButton.make('Cancel'),
|
|
| 417 |
- DialogButton.make('OK',
|
|
| 418 |
- text=_('_Report Spam'),
|
|
| 419 |
- callback=_block_contact,
|
|
| 420 |
- kwargs={'report': 'spam'}),
|
|
| 421 |
- DialogButton.make('Remove',
|
|
| 422 |
- text=_('_Block'),
|
|
| 423 |
- callback=_block_contact)],
|
|
| 424 |
- modal=False).show()
|
|
| 400 |
+ app.window.block_contact(self.account, self.contact.jid)
|
|
| 425 | 401 |
|
| 426 | 402 |
def _on_information(self, _action, _param):
|
| 427 |
- app.interface.roster.on_info(None, self.contact, self.account)
|
|
| 403 |
+ app.window.contact_info(self.account, self.contact.jid)
|
|
| 428 | 404 |
|
| 429 | 405 |
def _on_invite_contacts(self, _action, _param):
|
| 430 | 406 |
"""
|
| ... | ... | @@ -37,6 +37,9 @@ def __init__(self, account): |
| 37 | 37 |
self._roster = Roster(account)
|
| 38 | 38 |
self._ui.roster_box.add(self._roster)
|
| 39 | 39 |
|
| 40 |
+ self._ui.paned.set_position(app.settings.get('chat_handle_position'))
|
|
| 41 |
+ self._ui.paned.connect('button-release-event', self._on_button_release)
|
|
| 42 |
+ |
|
| 40 | 43 |
self._ui.connect_signals(self)
|
| 41 | 44 |
self.show_all()
|
| 42 | 45 |
|
| ... | ... | @@ -46,6 +49,16 @@ def _on_account_settings(self, _button): |
| 46 | 49 |
window = open_window('AccountsWindow')
|
| 47 | 50 |
window.select_account(self._account)
|
| 48 | 51 |
|
| 52 |
+ @staticmethod
|
|
| 53 |
+ def _on_button_release(paned, event):
|
|
| 54 |
+ if event.window != paned.get_handle_window():
|
|
| 55 |
+ return
|
|
| 56 |
+ position = paned.get_position()
|
|
| 57 |
+ app.settings.set('chat_handle_position', position)
|
|
| 58 |
+ |
|
| 59 |
+ def get_roster(self):
|
|
| 60 |
+ return self._roster
|
|
| 61 |
+ |
|
| 49 | 62 |
def update(self):
|
| 50 | 63 |
account_label = app.settings.get_account_setting(
|
| 51 | 64 |
self._account, 'account_label')
|
| ... | ... | @@ -418,7 +418,9 @@ def _on_state_flags_changed(self, _listboxrow, *args): |
| 418 | 418 |
self._ui.revealer.set_reveal_child(False)
|
| 419 | 419 |
|
| 420 | 420 |
def _on_close_button_clicked(self, _button):
|
| 421 |
- app.window.remove_chat(self.workspace_id, self.account, self.jid)
|
|
| 421 |
+ app.window.activate_action(
|
|
| 422 |
+ 'remove-chat',
|
|
| 423 |
+ GLib.Variant('as', [self.account, self.jid]))
|
|
| 422 | 424 |
|
| 423 | 425 |
def set_timestamp(self, timestamp):
|
| 424 | 426 |
self._timestamp = timestamp
|
| ... | ... | @@ -7,20 +7,23 @@ |
| 7 | 7 |
|
| 8 | 8 |
from gajim.common import app
|
| 9 | 9 |
from gajim.common import ged
|
| 10 |
+from gajim.common.helpers import ask_for_status_message
|
|
| 11 |
+from gajim.common.helpers import jid_is_blocked
|
|
| 10 | 12 |
from gajim.common.i18n import _
|
| 11 |
-from gajim.gui.util import get_builder
|
|
| 12 |
-from gajim.gui.util import load_icon
|
|
| 13 |
+from gajim.common.nec import EventHelper
|
|
| 14 |
+ |
|
| 13 | 15 |
from gajim.gui.account_page import AccountPage
|
| 16 |
+from gajim.gui.adhoc import AdHocCommand
|
|
| 14 | 17 |
from gajim.gui.chat_list_stack import ChatListStack
|
| 15 | 18 |
from gajim.gui.chat_stack import ChatStack
|
| 16 | 19 |
from gajim.gui.account_side_bar import AccountSideBar
|
| 17 | 20 |
from gajim.gui.workspace_side_bar import WorkspaceSideBar
|
| 18 |
- |
|
| 19 |
-from gajim.common.helpers import ask_for_status_message
|
|
| 20 | 21 |
from gajim.gui.dialogs import DialogButton
|
| 21 | 22 |
from gajim.gui.dialogs import ConfirmationDialog
|
| 23 |
+from gajim.gui.util import get_builder
|
|
| 24 |
+from gajim.gui.util import load_icon
|
|
| 22 | 25 |
|
| 23 |
-from gajim.common.nec import EventHelper
|
|
| 26 |
+from gajim.vcard import VcardWindow
|
|
| 24 | 27 |
|
| 25 | 28 |
from .util import open_window
|
| 26 | 29 |
|
| ... | ... | @@ -425,11 +428,14 @@ def move_chat_to_workspace(self, new_workspace_id, account, jid): |
| 425 | 428 |
self._chat_list_stack.store_open_chats(new_workspace_id)
|
| 426 | 429 |
|
| 427 | 430 |
def _remove_chat(self, _action, param):
|
| 428 |
- workspace_id, account, jid = param.unpack()
|
|
| 429 |
- self.remove_chat(workspace_id, account, jid)
|
|
| 431 |
+ account, jid = param.unpack()
|
|
| 432 |
+ self.remove_chat(account, jid)
|
|
| 430 | 433 |
|
| 431 |
- def remove_chat(self, workspace_id, account, jid):
|
|
| 432 |
- self._chat_list_stack.remove_chat(workspace_id, account, jid)
|
|
| 434 |
+ def remove_chat(self, account, jid):
|
|
| 435 |
+ for workspace_id in app.settings.get_workspaces():
|
|
| 436 |
+ if self.chat_exists_for_workspace(workspace_id, account, jid):
|
|
| 437 |
+ self._chat_list_stack.remove_chat(workspace_id, account, jid)
|
|
| 438 |
+ return
|
|
| 433 | 439 |
|
| 434 | 440 |
def chat_exists(self, account, jid):
|
| 435 | 441 |
return self._chat_list_stack.contains_chat(account, jid)
|
| ... | ... | @@ -447,14 +453,91 @@ def get_active_control(self): |
| 447 | 453 |
return None
|
| 448 | 454 |
return self.get_control(chat.account, chat.jid)
|
| 449 | 455 |
|
| 456 |
+ def get_controls(self, account=None):
|
|
| 457 |
+ return self._chat_stack.get_controls(account)
|
|
| 458 |
+ |
|
| 450 | 459 |
def _add_accounts(self):
|
| 451 | 460 |
for account in list(app.connections.keys()):
|
| 452 | 461 |
account_page = AccountPage(account)
|
| 453 | 462 |
self._account_pages[account] = account_page
|
| 454 | 463 |
self._ui.main_stack.add_named(account_page, account)
|
| 455 | 464 |
|
| 456 |
- def get_controls(self, account=None):
|
|
| 457 |
- return self._chat_stack.get_controls(account)
|
|
| 465 |
+ @staticmethod
|
|
| 466 |
+ def contact_info(account, jid):
|
|
| 467 |
+ client = app.get_client(account)
|
|
| 468 |
+ contact = client.get_module('Contacts').get_contact(jid)
|
|
| 469 |
+ |
|
| 470 |
+ # TODO: VcardWindow does not work with new type Contacts
|
|
| 471 |
+ info = app.interface.instances[account]['infos']
|
|
| 472 |
+ if contact.jid in info:
|
|
| 473 |
+ info[contact.jid].window.present()
|
|
| 474 |
+ else:
|
|
| 475 |
+ info[contact.jid] = VcardWindow(contact, account)
|
|
| 476 |
+ |
|
| 477 |
+ @staticmethod
|
|
| 478 |
+ def execute_command(account, jid):
|
|
| 479 |
+ # TODO: Resource?
|
|
| 480 |
+ AdHocCommand(account, jid)
|
|
| 481 |
+ |
|
| 482 |
+ def block_contact(self, account, jid):
|
|
| 483 |
+ client = app.get_client(account)
|
|
| 484 |
+ |
|
| 485 |
+ if jid_is_blocked(account, jid):
|
|
| 486 |
+ client.get_module('Blocking').unblock([jid])
|
|
| 487 |
+ roster = self._account_pages[account].get_roster()
|
|
| 488 |
+ roster.draw_contact(jid)
|
|
| 489 |
+ return
|
|
| 490 |
+ |
|
| 491 |
+ # TODO: Keep "confirm_block" setting?
|
|
| 492 |
+ def _block_contact(report=None):
|
|
| 493 |
+ app.events.remove_events(account, jid)
|
|
| 494 |
+ |
|
| 495 |
+ contact = client.get_module('Contacts').get_contact(jid)
|
|
| 496 |
+ client.get_module('Blocking').block([jid], report)
|
|
| 497 |
+ if not contact.is_in_roster:
|
|
| 498 |
+ self.remove_chat(account, jid)
|
|
| 499 |
+ return
|
|
| 500 |
+ |
|
| 501 |
+ roster = self._account_pages[account].get_roster()
|
|
| 502 |
+ roster.draw_contact(jid)
|
|
| 503 |
+ # TODO: draw_contact draws too early (not blocked yet)
|
|
| 504 |
+ # TODO: Draw ChatList row differently?
|
|
| 505 |
+ |
|
| 506 |
+ ConfirmationDialog(
|
|
| 507 |
+ _('Block Contact'),
|
|
| 508 |
+ _('Really block this contact?'),
|
|
| 509 |
+ _('You will appear offline for this contact and you '
|
|
| 510 |
+ 'will not receive further messages.'),
|
|
| 511 |
+ [DialogButton.make('Cancel'),
|
|
| 512 |
+ DialogButton.make('OK',
|
|
| 513 |
+ text=_('_Report Spam'),
|
|
| 514 |
+ callback=_block_contact,
|
|
| 515 |
+ kwargs={'report': 'spam'}),
|
|
| 516 |
+ DialogButton.make('Remove',
|
|
| 517 |
+ text=_('_Block'),
|
|
| 518 |
+ callback=_block_contact)],
|
|
| 519 |
+ modal=False).show()
|
|
| 520 |
+ |
|
| 521 |
+ def remove_contact(self, account, jid):
|
|
| 522 |
+ def _remove_contact():
|
|
| 523 |
+ self.remove_chat(account, jid)
|
|
| 524 |
+ roster = self._account_pages[account].get_roster()
|
|
| 525 |
+ roster.remove_contact(jid)
|
|
| 526 |
+ |
|
| 527 |
+ client = app.get_client(account)
|
|
| 528 |
+ contact = client.get_module('Contacts').get_contact(jid)
|
|
| 529 |
+ sec_text = _('You are about to remove %(name)s (%(jid)s) from '
|
|
| 530 |
+ 'your contact list.\n') % {
|
|
| 531 |
+ 'name': contact.name,
|
|
| 532 |
+ 'jid': jid}
|
|
| 533 |
+ # TODO: ConfirmationCheckDialog for subscription decision?
|
|
| 534 |
+ ConfirmationDialog(
|
|
| 535 |
+ _('Remove Contact'),
|
|
| 536 |
+ _('Remove contact from contact list'),
|
|
| 537 |
+ sec_text,
|
|
| 538 |
+ [DialogButton.make('Cancel'),
|
|
| 539 |
+ DialogButton.make('Remove',
|
|
| 540 |
+ callback=_remove_contact)]).show()
|
|
| 458 | 541 |
|
| 459 | 542 |
def _load_chats(self):
|
| 460 | 543 |
for workspace_id in app.settings.get_workspaces():
|
| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 |
from enum import IntEnum
|
| 5 | 5 |
|
| 6 | 6 |
from gi.repository import Gdk
|
| 7 |
+from gi.repository import Gio
|
|
| 7 | 8 |
from gi.repository import GLib
|
| 8 | 9 |
from gi.repository import Gtk
|
| 9 | 10 |
from gi.repository import GObject
|
| ... | ... | @@ -12,6 +13,7 @@ |
| 12 | 13 |
from gajim.common import ged
|
| 13 | 14 |
from gajim.common.const import AvatarSize
|
| 14 | 15 |
from gajim.common.const import StyleAttr
|
| 16 |
+from gajim.common.helpers import jid_is_blocked
|
|
| 15 | 17 |
from gajim.common.i18n import _
|
| 16 | 18 |
|
| 17 | 19 |
from gajim.gui_menu_builder import get_roster_menu
|
| ... | ... | @@ -73,11 +75,70 @@ def __init__(self, account): |
| 73 | 75 |
self._ui.connect_signals(self)
|
| 74 | 76 |
|
| 75 | 77 |
self.register_events([
|
| 78 |
+ ('account-connected', ged.CORE, self._on_account_state),
|
|
| 79 |
+ ('account-disconnected', ged.CORE, self._on_account_state),
|
|
| 76 | 80 |
('theme-update', ged.GUI2, self._on_theme_update),
|
| 77 | 81 |
])
|
| 78 | 82 |
|
| 83 |
+ self._add_actions()
|
|
| 79 | 84 |
self._initial_draw()
|
| 80 | 85 |
|
| 86 |
+ def _add_actions(self):
|
|
| 87 |
+ actions = [
|
|
| 88 |
+ ('contact-info', self._contact_info),
|
|
| 89 |
+ ('execute-command', self._execute_command),
|
|
| 90 |
+ ('block-contact', self._block_contact),
|
|
| 91 |
+ ('remove-contact', self._remove_contact),
|
|
| 92 |
+ ]
|
|
| 93 |
+ for action in actions:
|
|
| 94 |
+ action_name, func = action
|
|
| 95 |
+ act = Gio.SimpleAction.new(
|
|
| 96 |
+ f'{action_name}-{self._account}', GLib.VariantType.new('s'))
|
|
| 97 |
+ act.connect('activate', func)
|
|
| 98 |
+ app.window.add_action(act)
|
|
| 99 |
+ |
|
| 100 |
+ def update_actions(self):
|
|
| 101 |
+ online = app.account_is_connected(self._account)
|
|
| 102 |
+ blocking_support = self._client.get_module('Blocking').supported
|
|
| 103 |
+ |
|
| 104 |
+ app.window.lookup_action(
|
|
| 105 |
+ f'contact-info-{self._account}').set_enabled(online)
|
|
| 106 |
+ app.window.lookup_action(
|
|
| 107 |
+ f'execute-command-{self._account}').set_enabled(online)
|
|
| 108 |
+ app.window.lookup_action(
|
|
| 109 |
+ f'block-contact-{self._account}').set_enabled(
|
|
| 110 |
+ online and blocking_support)
|
|
| 111 |
+ app.window.lookup_action(
|
|
| 112 |
+ f'remove-contact-{self._account}').set_enabled(online)
|
|
| 113 |
+ |
|
| 114 |
+ def _remove_actions(self):
|
|
| 115 |
+ actions = [
|
|
| 116 |
+ 'contact-info',
|
|
| 117 |
+ 'execute-command',
|
|
| 118 |
+ 'block-contact',
|
|
| 119 |
+ 'remove-contact',
|
|
| 120 |
+ ]
|
|
| 121 |
+ for action in actions:
|
|
| 122 |
+ app.window.remove_action(f'{action}-{self._account}')
|
|
| 123 |
+ |
|
| 124 |
+ def _on_account_state(self, _event):
|
|
| 125 |
+ self.update_actions()
|
|
| 126 |
+ |
|
| 127 |
+ def _on_theme_update(self, _event):
|
|
| 128 |
+ self.redraw()
|
|
| 129 |
+ |
|
| 130 |
+ def _contact_info(self, _action, param):
|
|
| 131 |
+ app.window.contact_info(self._account, param.get_string())
|
|
| 132 |
+ |
|
| 133 |
+ def _execute_command(self, _action, param):
|
|
| 134 |
+ app.window.execute_command(self._account, param.get_string())
|
|
| 135 |
+ |
|
| 136 |
+ def _block_contact(self, _action, param):
|
|
| 137 |
+ app.window.block_contact(self._account, param.get_string())
|
|
| 138 |
+ |
|
| 139 |
+ def _remove_contact(self, _action, param):
|
|
| 140 |
+ app.window.remove_contact(self._account, param.get_string())
|
|
| 141 |
+ |
|
| 81 | 142 |
def _on_roster_row_activated(self, _treeview, path, _column):
|
| 82 | 143 |
iter_ = self._store.get_iter(path)
|
| 83 | 144 |
if self._store.iter_parent(iter_) is None:
|
| ... | ... | @@ -113,9 +174,6 @@ def _on_roster_button_press_event(self, treeview, event): |
| 113 | 174 |
def _on_focus_out(treeview, _param):
|
| 114 | 175 |
treeview.get_selection().unselect_all()
|
| 115 | 176 |
|
| 116 |
- def _on_theme_update(self, _event):
|
|
| 117 |
- self.redraw()
|
|
| 118 |
- |
|
| 119 | 177 |
def _show_contact_menu(self, jid, treeview, event):
|
| 120 | 178 |
menu = get_roster_menu(self._account, jid)
|
| 121 | 179 |
|
| ... | ... | @@ -131,6 +189,7 @@ def _show_contact_menu(self, jid, treeview, event): |
| 131 | 189 |
popover.popup()
|
| 132 | 190 |
|
| 133 | 191 |
def _on_destroy(self, _roster):
|
| 192 |
+ self._remove_actions()
|
|
| 134 | 193 |
self._contact_refs = {}
|
| 135 | 194 |
self._group_refs = {}
|
| 136 | 195 |
self._roster.set_model(None)
|
| ... | ... | @@ -163,8 +222,7 @@ def invalidate_sort(self): |
| 163 | 222 |
self.enable_sort(True)
|
| 164 | 223 |
|
| 165 | 224 |
def _initial_draw(self):
|
| 166 |
- client = app.get_client(self._account)
|
|
| 167 |
- for contact in client.get_module('Roster').iter_contacts():
|
|
| 225 |
+ for contact in self._client.get_module('Roster').iter_contacts():
|
|
| 168 | 226 |
contact.connect('presence-update', self._on_presence_update)
|
| 169 | 227 |
contact.connect('avatar-update', self._on_avatar_update)
|
| 170 | 228 |
self._add_contact(contact)
|
| ... | ... | @@ -217,6 +275,27 @@ def _add_contact(self, contact): |
| 217 | 275 |
self._roster.get_model() is not None):
|
| 218 | 276 |
self._roster.expand_row(group_path, False)
|
| 219 | 277 |
|
| 278 |
+ def remove_contact(self, jid):
|
|
| 279 |
+ # TODO: How to delete a roster item?
|
|
| 280 |
+ #self._client.get_module('Roster').delete_item(jid)
|
|
| 281 |
+ |
|
| 282 |
+ iter_ = self._get_contact_iter(jid)
|
|
| 283 |
+ if not iter_:
|
|
| 284 |
+ return
|
|
| 285 |
+ |
|
| 286 |
+ group_iter = self._store.iter_parent(iter_)
|
|
| 287 |
+ if group_iter is None:
|
|
| 288 |
+ raise ValueError('Trying to remove non-chil')
|
|
| 289 |
+ |
|
| 290 |
+ self._store.remove(iter_)
|
|
| 291 |
+ del self._contact_refs[jid]
|
|
| 292 |
+ if not self._store.iter_has_child(group_iter):
|
|
| 293 |
+ group = self._store[group_iter][Column.JID_OR_GROUP]
|
|
| 294 |
+ del self._group_refs[group]
|
|
| 295 |
+ self._store.remove(group_iter)
|
|
| 296 |
+ |
|
| 297 |
+ self.redraw()
|
|
| 298 |
+ |
|
| 220 | 299 |
def draw_groups(self):
|
| 221 | 300 |
for group in self._group_refs:
|
| 222 | 301 |
self.draw_group(group)
|
| ... | ... | @@ -242,9 +321,12 @@ def draw_contact(self, jid): |
| 242 | 321 |
return
|
| 243 | 322 |
|
| 244 | 323 |
contact = self._client.get_module('Contacts').get_contact(jid)
|
| 324 |
+ name = GLib.markup_escape_text(contact.name)
|
|
| 325 |
+ if jid_is_blocked(self._account, jid):
|
|
| 326 |
+ name = f'<span strikethrough="true">{name}</span>'
|
|
| 245 | 327 |
|
| 246 | 328 |
self.draw_avatar(contact)
|
| 247 |
- self._store[iter_][Column.TEXT] = GLib.markup_escape_text(contact.name)
|
|
| 329 |
+ self._store[iter_][Column.TEXT] = name
|
|
| 248 | 330 |
|
| 249 | 331 |
def draw_avatar(self, contact):
|
| 250 | 332 |
iter_ = self._get_contact_iter(str(contact.jid))
|
| ... | ... | @@ -797,17 +797,24 @@ def get_conv_context_menu(account, uri): |
| 797 | 797 |
|
| 798 | 798 |
|
| 799 | 799 |
def get_roster_menu(account, jid):
|
| 800 |
+ if helpers.jid_is_blocked(account, jid):
|
|
| 801 |
+ block_label = _('Unblock')
|
|
| 802 |
+ else:
|
|
| 803 |
+ block_label = _('Block…')
|
|
| 800 | 804 |
menu_items = [
|
| 801 |
- ('show-contact-info', _('Contact Info')),
|
|
| 805 |
+ ('contact-info', _('Information')),
|
|
| 806 |
+ ('execute-command', _('Execute Command…')),
|
|
| 807 |
+ ('block-contact', block_label),
|
|
| 808 |
+ ('remove-contact', _('Remove…')),
|
|
| 802 | 809 |
]
|
| 810 |
+ |
|
| 803 | 811 |
menu = Gio.Menu()
|
| 804 | 812 |
for item in menu_items:
|
| 805 | 813 |
action, label = item
|
| 806 |
- action = f'win.{action}'
|
|
| 814 |
+ action = f'win.{action}-{account}'
|
|
| 807 | 815 |
menuitem = Gio.MenuItem.new(label, action)
|
| 808 |
- variant_list = GLib.Variant(
|
|
| 809 |
- 'as', [account, str(jid)])
|
|
| 810 |
- menuitem.set_action_and_target_value(action, variant_list)
|
|
| 816 |
+ variant = GLib.Variant('s', str(jid))
|
|
| 817 |
+ menuitem.set_action_and_target_value(action, variant)
|
|
| 811 | 818 |
menu.append_item(menuitem)
|
| 812 | 819 |
|
| 813 | 820 |
return menu
|