Skip to content
Snippets Groups Projects
Commit fc365a29 authored by Philipp Hörist's avatar Philipp Hörist
Browse files

Refactor Main

parent 2a05f702
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkPaned" id="paned">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="position">250</property>
<property name="position-set">True</property>
<child>
<!-- n-columns=1 n-rows=2 -->
<object class="GtkGrid" id="middle_grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<child>
<!-- n-columns=1 n-rows=3 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">3</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="workspace_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">Workspace Label</property>
<property name="ellipsize">end</property>
<style>
<class name="bold16"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="workspace_menu_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-left">6</property>
<property name="margin-right">6</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">12</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="primary-icon-name">edit-find-symbolic</property>
<property name="primary-icon-activatable">False</property>
<property name="primary-icon-sensitive">False</property>
<property name="placeholder-text" translatable="yes">Search…</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="start_chat_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Start Chat</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">list-add-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<style>
<class name="chatlist-top-bar"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="chat_list_scrolled">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<placeholder/>
</child>
<style>
<class name="no-border"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<style>
<class name="middle-grid"/>
</style>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<!-- n-columns=1 n-rows=1 -->
<object class="GtkGrid" id="right_grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkOverlay" id="right_grid_overlay">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
</interface>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<!-- Generated with glade 3.38.2 --> <!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.22"/> <requires lib="gtk+" version="3.22"/>
<!-- n-columns=3 n-rows=3 --> <!-- n-columns=2 n-rows=1 -->
<object class="GtkGrid" id="main_grid"> <object class="GtkGrid" id="main_grid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
...@@ -130,229 +130,6 @@ ...@@ -130,229 +130,6 @@
<property name="top-attach">0</property> <property name="top-attach">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkStack" id="main_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkPaned" id="paned">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="position">250</property>
<property name="position-set">True</property>
<child>
<!-- n-columns=1 n-rows=2 -->
<object class="GtkGrid" id="middle_grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<child>
<!-- n-columns=1 n-rows=3 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">3</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="workspace_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">Workspace Label</property>
<property name="ellipsize">end</property>
<style>
<class name="bold16"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="workspace_menu_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">12</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="primary-icon-name">edit-find-symbolic</property>
<property name="primary-icon-activatable">False</property>
<property name="primary-icon-sensitive">False</property>
<property name="placeholder-text" translatable="yes">Search…</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="start_chat_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Start Chat</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">list-add-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<style>
<class name="chatlist-top-bar"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="chat_list_scrolled">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<placeholder/>
</child>
<style>
<class name="no-border"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<style>
<class name="middle-grid"/>
</style>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<!-- n-columns=1 n-rows=1 -->
<object class="GtkGrid" id="right_grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkOverlay" id="right_grid_overlay">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
<packing>
<property name="name">chats</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child> <child>
<placeholder/> <placeholder/>
</child> </child>
......
...@@ -64,7 +64,6 @@ def __init__(self, account): ...@@ -64,7 +64,6 @@ def __init__(self, account):
self._ui.roster_menu_button.set_menu_model(roster_menu) self._ui.roster_menu_button.set_menu_model(roster_menu)
self._ui.connect_signals(self) self._ui.connect_signals(self)
self.show_all()
# pylint: disable=line-too-long # pylint: disable=line-too-long
self.register_events([ self.register_events([
...@@ -76,6 +75,7 @@ def __init__(self, account): ...@@ -76,6 +75,7 @@ def __init__(self, account):
# pylint: enable=line-too-long # pylint: enable=line-too-long
self.update() self.update()
self.show_all()
def _on_edit_profile(self, _button): def _on_edit_profile(self, _button):
open_window('ProfileWindow', account=self._account) open_window('ProfileWindow', account=self._account)
......
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import GLib
from gi.repository import Gtk from gi.repository import Gtk
from gajim.common.const import AvatarSize from gajim.common.const import AvatarSize
......
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
from gi.repository import Gdk from gi.repository import Gdk
......
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Gio
from gi.repository import GLib
from gajim.common import app from gajim.common import app
...@@ -26,6 +41,9 @@ class ChatListStack(Gtk.Stack): ...@@ -26,6 +41,9 @@ class ChatListStack(Gtk.Stack):
'chat-selected': (GObject.SignalFlags.RUN_LAST, 'chat-selected': (GObject.SignalFlags.RUN_LAST,
None, None,
(str, str, str)), (str, str, str)),
'chat-unselected': (GObject.SignalFlags.RUN_LAST,
None,
()),
} }
def __init__(self, main_window, ui, chat_stack): def __init__(self, main_window, ui, chat_stack):
...@@ -42,13 +60,29 @@ def __init__(self, main_window, ui, chat_stack): ...@@ -42,13 +60,29 @@ def __init__(self, main_window, ui, chat_stack):
self.add_named(Gtk.Box(), 'default') self.add_named(Gtk.Box(), 'default')
self.show_all()
self.connect('notify::visible-child-name', self._on_visible_child_name) self.connect('notify::visible-child-name', self._on_visible_child_name)
self._ui.search_entry.connect( self._ui.search_entry.connect(
'search-changed', self._on_search_changed) 'search-changed', self._on_search_changed)
main_window.connect('notify::is-active', self._on_window_active) main_window.connect('notify::is-active', self._on_window_active)
self._add_actions()
self.show_all()
def _add_actions(self):
actions = [
('toggle-chat-pinned', 'as', self._toggle_chat_pinned),
('move-chat-to-workspace', 'as', self._move_chat_to_workspace),
]
for action in actions:
action_name, variant, func = action
if variant is not None:
variant = GLib.VariantType.new(variant)
act = Gio.SimpleAction.new(action_name, variant)
act.connect('activate', func)
app.window.add_action(act)
def _on_window_active(self, window, _param): def _on_window_active(self, window, _param):
is_active = window.get_property('is-active') is_active = window.get_property('is-active')
if is_active: if is_active:
...@@ -108,10 +142,9 @@ def remove_chat_list(self, workspace_id): ...@@ -108,10 +142,9 @@ def remove_chat_list(self, workspace_id):
def _on_row_selected(self, _chat_list, row): def _on_row_selected(self, _chat_list, row):
if row is None: if row is None:
self._chat_stack.clear() self.emit('chat-unselected')
return return
self._chat_stack.show_chat(row.account, row.jid)
if row.is_active: if row.is_active:
row.reset_unread() row.reset_unread()
self.emit('chat-selected', row.workspace_id, row.account, row.jid) self.emit('chat-selected', row.workspace_id, row.account, row.jid)
...@@ -148,11 +181,24 @@ def store_open_chats(self, workspace_id): ...@@ -148,11 +181,24 @@ def store_open_chats(self, workspace_id):
app.settings.set_workspace_setting( app.settings.set_workspace_setting(
workspace_id, 'open_chats', open_chats) workspace_id, 'open_chats', open_chats)
def toggle_chat_pinned(self, workspace_id, account, jid): def _toggle_chat_pinned(self, _action, param):
workspace_id, account, jid = param.unpack()
chat_list = self._chat_lists[workspace_id] chat_list = self._chat_lists[workspace_id]
chat_list.toggle_chat_pinned(account, jid) chat_list.toggle_chat_pinned(account, jid)
self.store_open_chats(workspace_id) self.store_open_chats(workspace_id)
def _move_chat_to_workspace(self, _action, param):
new_workspace_id, account, jid = param.unpack()
current_chatlist = self.get_visible_child()
type_ = current_chatlist.get_chat_type(account, jid)
current_chatlist.remove_chat(account, jid)
new_chatlist = self.get_chatlist(new_workspace_id)
new_chatlist.add_chat(account, jid, type_)
self.store_open_chats(current_chatlist.workspace_id)
self.store_open_chats(new_workspace_id)
def remove_chat(self, workspace_id, account, jid): def remove_chat(self, workspace_id, account, jid):
chat_list = self._chat_lists[workspace_id] chat_list = self._chat_lists[workspace_id]
type_ = chat_list.get_chat_type(account, jid) type_ = chat_list.get_chat_type(account, jid)
......
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import GObject
from gajim.common import app
from gajim.common.i18n import _
from .util import get_builder
from .chat_list_stack import ChatListStack
from .chat_stack import ChatStack
from .search_view import SearchView
WORKSPACE_MENU_DICT = {
'edit': _('Edit…'),
'remove': _('Remove'),
}
class ChatPage(Gtk.Box):
__gsignals__ = {
'chat-selected': (GObject.SignalFlags.RUN_LAST,
None,
(str, str, str)),
}
def __init__(self):
Gtk.Box.__init__(self)
self._ui = get_builder('chat_paned.ui')
self.add(self._ui.paned)
self._chat_stack = ChatStack()
self._ui.right_grid_overlay.add(self._chat_stack)
self._search_view = SearchView()
self._search_view.connect('hide-search', self._on_search_hide)
self._search_revealer = Gtk.Revealer()
self._search_revealer.set_reveal_child(True)
self._search_revealer.set_halign(Gtk.Align.END)
self._search_revealer.set_no_show_all(True)
self._search_revealer.add(self._search_view)
self._ui.right_grid_overlay.add_overlay(self._search_revealer)
self._chat_list_stack = ChatListStack(self, self._ui, self._chat_stack)
self._chat_list_stack.connect('chat-selected', self._on_chat_selected)
self._chat_list_stack.connect('chat-unselected',
self._on_chat_unselected)
self._ui.chat_list_scrolled.add(self._chat_list_stack)
self._ui.start_chat_button.connect('clicked',
self._on_start_chat_clicked)
self._ui.paned.set_position(app.settings.get('chat_handle_position'))
self._ui.paned.connect('button-release-event', self._on_button_release)
workspace_menu = Gio.Menu()
for action, label in WORKSPACE_MENU_DICT.items():
workspace_menu.append(label, f'win.{action.lower()}-workspace')
self._ui.workspace_menu_button.set_menu_model(workspace_menu)
self._startup_finished = False
self._add_actions()
self.show_all()
def _add_actions(self):
actions = [
('remove-chat', 'as', self._remove_chat),
('search-history', None, self._on_search_history),
]
for action in actions:
action_name, variant, func = action
if variant is not None:
variant = GLib.VariantType.new(variant)
act = Gio.SimpleAction.new(action_name, variant)
act.connect('activate', func)
app.window.add_action(act)
def set_startup_finished(self):
self._startup_finished = True
def get_chat_list_stack(self):
return self._chat_list_stack
def get_chat_stack(self):
return self._chat_stack
@staticmethod
def _on_start_chat_clicked(_button):
app.app.activate_action('start-chat', GLib.Variant('s', ''))
@staticmethod
def _on_button_release(paned, event):
if event.window != paned.get_handle_window():
return
position = paned.get_position()
app.settings.set('chat_handle_position', position)
def _on_chat_selected(self, _chat_list_stack, workspace_id, account, jid):
self._chat_stack.show_chat(account, jid)
self._search_view.set_context(account, jid)
self.emit('chat-selected', workspace_id, account, jid)
def _on_chat_unselected(self, _chat_list_stack):
self._chat_stack.clear()
def _on_search_history(self, _action, _param):
control = self.get_active_control()
if control is not None:
self._search_view.set_context(control.account, control.contact.jid)
self._search_view.clear()
self._search_revealer.show()
self._search_view.set_focus()
def _on_search_hide(self, *args):
self._search_revealer.hide()
def process_event(self, event):
self._chat_stack.process_event(event)
self._chat_list_stack.process_event(event)
def add_chat_list(self, workspace_id):
self._chat_list_stack.add_chat_list(workspace_id)
def show_workspace_chats(self, workspace_id):
self._chat_list_stack.show_chat_list(workspace_id)
def update_workspace(self, workspace_id):
name = app.settings.get_workspace_setting(workspace_id, 'name')
self._ui.workspace_label.set_text(name)
def remove_chat_list(self, workspace_id):
self._chat_list_stack.remove_chat_list(workspace_id)
def chat_exists(self, account, jid):
return self._chat_list_stack.contains_chat(account, jid)
def chat_exists_for_workspace(self, workspace_id, account, jid):
return self._chat_list_stack.contains_chat(
account, jid, workspace_id=workspace_id)
def add_chat_for_workspace(self,
workspace_id,
account,
jid,
type_,
pinned=False,
select=False):
if self.chat_exists(account, jid):
if select:
self._chat_list_stack.select_chat(account, jid)
return
if type_ == 'groupchat':
self._chat_stack.add_group_chat(account, jid)
elif type_ == 'pm':
if not self._startup_finished:
# TODO: Currently we cant load private chats at start
# because the Contacts dont exist yet
return
self._chat_stack.add_private_chat(account, jid)
else:
self._chat_stack.add_chat(account, jid)
self._chat_list_stack.add_chat(workspace_id, account, jid, type_,
pinned)
if self._startup_finished:
if select:
self._chat_list_stack.select_chat(account, jid)
self._chat_list_stack.store_open_chats(workspace_id)
def load_workspace_chats(self, workspace_id):
open_chats = app.settings.get_workspace_setting(workspace_id,
'open_chats')
active_accounts = app.settings.get_active_accounts()
for account, jid, type_, pinned in open_chats:
if account not in active_accounts:
continue
self.add_chat_for_workspace(workspace_id,
account,
jid,
type_,
pinned=pinned)
def is_chat_active(self, account, jid):
return self._chat_list_stack.is_chat_active(account, jid)
def _remove_chat(self, _action, param):
account, jid = param.unpack()
self.remove_chat(account, jid)
def remove_chat(self, account, jid):
for workspace_id in app.settings.get_workspaces():
if self.chat_exists_for_workspace(workspace_id, account, jid):
self._chat_list_stack.remove_chat(workspace_id, account, jid)
return
def get_control(self, account, jid):
return self._chat_stack.get_control(account, jid)
def get_active_control(self):
chat = self._chat_list_stack.get_selected_chat()
if chat is None:
return None
return self.get_control(chat.account, chat.jid)
def get_controls(self, account=None):
return self._chat_stack.get_controls(account)
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
from gi.repository import Gtk from gi.repository import Gtk
from gajim.common import app
from gajim.chat_control import ChatControl from gajim.chat_control import ChatControl
from gajim.groupchat_control import GroupchatControl from gajim.groupchat_control import GroupchatControl
from gajim.privatechat_control import PrivateChatControl from gajim.privatechat_control import PrivateChatControl
from gajim.common.i18n import _
log = logging.getLogger('gajim.gui.chatstack') log = logging.getLogger('gajim.gui.chatstack')
......
...@@ -11,13 +11,10 @@ ...@@ -11,13 +11,10 @@
from gajim.common.i18n import _ from gajim.common.i18n import _
from gajim.common.nec import EventHelper from gajim.common.nec import EventHelper
from gajim.gui.account_page import AccountPage
from gajim.gui.adhoc import AdHocCommand from gajim.gui.adhoc import AdHocCommand
from gajim.gui.search_view import SearchView
from gajim.gui.chat_list_stack import ChatListStack
from gajim.gui.chat_stack import ChatStack
from gajim.gui.account_side_bar import AccountSideBar from gajim.gui.account_side_bar import AccountSideBar
from gajim.gui.workspace_side_bar import WorkspaceSideBar from gajim.gui.workspace_side_bar import WorkspaceSideBar
from gajim.gui.main_stack import MainStack
from gajim.gui.dialogs import DialogButton from gajim.gui.dialogs import DialogButton
from gajim.gui.dialogs import ConfirmationDialog from gajim.gui.dialogs import ConfirmationDialog
from gajim.gui.util import get_builder from gajim.gui.util import get_builder
...@@ -27,11 +24,6 @@ ...@@ -27,11 +24,6 @@
log = logging.getLogger('gajim.gui.main') log = logging.getLogger('gajim.gui.main')
WORKSPACE_MENU_DICT = {
'edit': _('Edit…'),
'remove': _('Remove'),
}
class MainWindow(Gtk.ApplicationWindow, EventHelper): class MainWindow(Gtk.ApplicationWindow, EventHelper):
def __init__(self): def __init__(self):
...@@ -46,8 +38,6 @@ def __init__(self): ...@@ -46,8 +38,6 @@ def __init__(self):
self.window = self self.window = self
app.window = self app.window = self
self._active_workspace = None
self._startup_finished = False self._startup_finished = False
self._ui = get_builder('main.ui') self._ui = get_builder('main.ui')
...@@ -57,44 +47,19 @@ def __init__(self): ...@@ -57,44 +47,19 @@ def __init__(self):
surface = load_icon('org.gajim.Gajim', self, 40) surface = load_icon('org.gajim.Gajim', self, 40)
self._ui.app_image.set_from_surface(surface) self._ui.app_image.set_from_surface(surface)
self._chat_stack = ChatStack() self._main_stack = MainStack()
self._ui.right_grid_overlay.add(self._chat_stack) self._ui.main_grid.add(self._main_stack)
self._search_view = SearchView()
self._search_view.connect('hide-search', self._on_search_hide)
self._search_revealer = Gtk.Revealer()
self._search_revealer.set_reveal_child(True)
self._search_revealer.set_halign(Gtk.Align.END)
self._search_revealer.set_no_show_all(True)
self._search_revealer.add(self._search_view)
self._ui.right_grid_overlay.add_overlay(self._search_revealer)
self._chat_list_stack = ChatListStack(self, self._ui, self._chat_stack) self._chat_page = self._main_stack.get_chat_page()
self._chat_list_stack.connect('chat-selected', self._on_chat_selected)
self._ui.chat_list_scrolled.add(self._chat_list_stack)
self._workspace_side_bar = WorkspaceSideBar(self._chat_list_stack) self._workspace_side_bar = WorkspaceSideBar(self._chat_page)
self._ui.workspace_scrolled.add(self._workspace_side_bar) self._ui.workspace_scrolled.add(self._workspace_side_bar)
self._account_side_bar = AccountSideBar() self._account_side_bar = AccountSideBar()
self._ui.account_box.add(self._account_side_bar) self._ui.account_box.add(self._account_side_bar)
self._account_pages = {}
workspace_menu = Gio.Menu()
for action, label in WORKSPACE_MENU_DICT.items():
workspace_menu.append(label, f'win.{action.lower()}-workspace')
self._ui.workspace_menu_button.set_menu_model(workspace_menu)
self._ui.start_chat_button.connect(
'clicked', self._on_start_chat_clicked)
self._ui.connect_signals(self) self._ui.connect_signals(self)
self._ui.paned.set_position(app.settings.get('chat_handle_position'))
self._ui.paned.connect('button-release-event', self._on_button_release)
self.register_events([ self.register_events([
('presence-received', ged.GUI1, self._on_event), ('presence-received', ged.GUI1, self._on_event),
('caps-update', ged.GUI1, self._on_event), ('caps-update', ged.GUI1, self._on_event),
...@@ -113,12 +78,10 @@ def __init__(self): ...@@ -113,12 +78,10 @@ def __init__(self):
('signed-in', ged.GUI1, self._on_signed_in), ('signed-in', ged.GUI1, self._on_signed_in),
]) ])
self.show_all()
self._load_chats() self._load_chats()
self._add_accounts()
self._add_actions() self._add_actions()
self._add_actions2() self._add_actions2()
self.show_all()
@staticmethod @staticmethod
def _on_our_show(event): def _on_our_show(event):
...@@ -131,13 +94,6 @@ def _on_signed_in(event): ...@@ -131,13 +94,6 @@ def _on_signed_in(event):
app.app.set_account_actions_state(event.account, True) app.app.set_account_actions_state(event.account, True)
app.app.update_app_actions_state() app.app.update_app_actions_state()
@staticmethod
def _on_button_release(paned, event):
if event.window != paned.get_handle_window():
return
position = paned.get_position()
app.settings.set('chat_handle_position', position)
def _add_actions(self): def _add_actions(self):
actions = [ actions = [
('add-workspace', 's', self._add_workspace), ('add-workspace', 's', self._add_workspace),
...@@ -146,11 +102,7 @@ def _add_actions(self): ...@@ -146,11 +102,7 @@ def _add_actions(self):
('activate-workspace', 's', self._activate_workspace), ('activate-workspace', 's', self._activate_workspace),
('add-chat', 'a{sv}', self._add_chat), ('add-chat', 'a{sv}', self._add_chat),
('add-group-chat', 'as', self._add_group_chat), ('add-group-chat', 'as', self._add_group_chat),
('remove-chat', 'as', self._remove_chat),
('toggle-chat-pinned', 'as', self._toggle_chat_pinned),
('move-chat-to-workspace', 'as', self._move_chat_to_workspace),
('add-to-roster', 'as', self._add_to_roster), ('add-to-roster', 'as', self._add_to_roster),
('search-history', None, self._on_search_history),
] ]
for action in actions: for action in actions:
...@@ -275,6 +227,10 @@ def _on_action(self, action, _param): ...@@ -275,6 +227,10 @@ def _on_action(self, action, _param):
# self.notebook.set_current_page(number - 1) # self.notebook.set_current_page(number - 1)
# return # return
def _set_startup_finished(self):
self._startup_finished = True
self._chat_page.set_startup_finished()
def get_widget(self, name): def get_widget(self, name):
return getattr(self._ui, name) return getattr(self._ui, name)
...@@ -290,15 +246,9 @@ def show_title(self, *args): ...@@ -290,15 +246,9 @@ def show_title(self, *args):
def get_active_jid(self, *args): def get_active_jid(self, *args):
pass pass
def get_account_page(self, account):
return self._account_pages[account]
def show_account_page(self, account): def show_account_page(self, account):
self._account_side_bar.activate_account_page(account) self._account_side_bar.activate_account_page(account)
self._ui.main_stack.set_visible_child_name(account) self._main_stack.show_account(account)
def get_workspace_bar(self):
return self._workspace_side_bar
def get_active_workspace(self): def get_active_workspace(self):
return self._workspace_side_bar.get_active_workspace() return self._workspace_side_bar.get_active_workspace()
...@@ -306,10 +256,7 @@ def get_active_workspace(self): ...@@ -306,10 +256,7 @@ def get_active_workspace(self):
def is_chat_active(self, account, jid): def is_chat_active(self, account, jid):
if not self.get_property('has-toplevel-focus'): if not self.get_property('has-toplevel-focus'):
return False return False
return self._chat_list_stack.is_chat_active(account, jid) return self._chat_page.is_chat_active(account, jid)
def show_chats(self):
self._ui.main_stack.set_visible_child_name('chats')
def _add_workspace(self, _action, param): def _add_workspace(self, _action, param):
workspace_id = param.get_string() workspace_id = param.get_string()
...@@ -317,20 +264,16 @@ def _add_workspace(self, _action, param): ...@@ -317,20 +264,16 @@ def _add_workspace(self, _action, param):
def add_workspace(self, workspace_id): def add_workspace(self, workspace_id):
self._workspace_side_bar.add_workspace(workspace_id) self._workspace_side_bar.add_workspace(workspace_id)
self._chat_list_stack.add_chat_list(workspace_id) self._chat_page.add_chat_list(workspace_id)
self._workspace_side_bar.activate_workspace(workspace_id)
self._chat_list_stack.show_chat_list(workspace_id)
if self._startup_finished: if self._startup_finished:
self.activate_workspace(workspace_id)
self._workspace_side_bar.store_workspace_order() self._workspace_side_bar.store_workspace_order()
def _edit_workspace(self, _action, _param): def _edit_workspace(self, _action, _param):
workspace_id = self.get_active_workspace() workspace_id = self.get_active_workspace()
if workspace_id is not None: if workspace_id is not None:
self.edit_workspace(workspace_id) open_window('WorkspaceDialog', workspace_id=workspace_id)
@staticmethod
def edit_workspace(workspace_id):
open_window('WorkspaceDialog', workspace_id=workspace_id)
def _remove_workspace(self, _action, _param): def _remove_workspace(self, _action, _param):
workspace_id = self.get_active_workspace() workspace_id = self.get_active_workspace()
...@@ -346,10 +289,9 @@ def remove_workspace(self, workspace_id): ...@@ -346,10 +289,9 @@ def remove_workspace(self, workspace_id):
if was_active: if was_active:
new_active_id = self._workspace_side_bar.get_first_workspace() new_active_id = self._workspace_side_bar.get_first_workspace()
self._workspace_side_bar.activate_workspace(new_active_id) self.activate_workspace(new_active_id)
self._chat_list_stack.show_chat_list(new_active_id)
self._chat_list_stack.remove_chat_list(workspace_id) self._chat_page.remove_chat_list(workspace_id)
app.settings.remove_workspace(workspace_id) app.settings.remove_workspace(workspace_id)
def _activate_workspace(self, _action, param): def _activate_workspace(self, _action, param):
...@@ -357,138 +299,59 @@ def _activate_workspace(self, _action, param): ...@@ -357,138 +299,59 @@ def _activate_workspace(self, _action, param):
self.activate_workspace(workspace_id) self.activate_workspace(workspace_id)
def activate_workspace(self, workspace_id): def activate_workspace(self, workspace_id):
self._ui.main_stack.set_visible_child_name('chats') self._main_stack.show_chats(workspace_id)
self._workspace_side_bar.activate_workspace(workspace_id) self._workspace_side_bar.activate_workspace(workspace_id)
self._chat_list_stack.show_chat_list(workspace_id)
def update_workspace(self, workspace_id): def update_workspace(self, workspace_id):
name = app.settings.get_workspace_setting(workspace_id, 'name') self._chat_page.update_workspace(workspace_id)
self._ui.workspace_label.set_text(name)
app.interface.avatar_storage.invalidate_cache(workspace_id)
self._workspace_side_bar.update_avatar(workspace_id) self._workspace_side_bar.update_avatar(workspace_id)
def _add_group_chat(self, _action, param): def _add_group_chat(self, _action, param):
account, jid = param.unpack() self.add_group_chat(**param.unpack())
self.add_group_chat(account, jid)
def add_group_chat(self, account, jid, select=False): def add_group_chat(self, account, jid, select=False):
workspace_id = self._workspace_side_bar.get_active_workspace() workspace_id = self.get_active_workspace()
self.add_chat_for_workspace(workspace_id, self._chat_page.add_chat_for_workspace(workspace_id,
account, account,
jid, jid,
'groupchat', 'groupchat',
select=select) select=select)
def _add_chat(self, _action, param): def _add_chat(self, _action, param):
self.add_chat(**param.unpack()) self.add_chat(**param.unpack())
def add_chat(self, account, jid, type_, select=False): def add_chat(self, account, jid, type_, select=False):
workspace_id = self._workspace_side_bar.get_active_workspace() workspace_id = self.get_active_workspace()
self.add_chat_for_workspace(workspace_id, self._chat_page.add_chat_for_workspace(workspace_id,
account, account,
jid, jid,
type_, type_,
select=select) select=select)
def add_private_chat(self, account, jid, select=False): def add_private_chat(self, account, jid, select=False):
workspace_id = self._workspace_side_bar.get_active_workspace() workspace_id = self.get_active_workspace()
self.add_chat_for_workspace(workspace_id, self._chat_page.add_chat_for_workspace(workspace_id,
account, account,
jid, jid,
'pm', 'pm',
select=select) select=select)
def add_chat_for_workspace(self,
workspace_id,
account,
jid,
type_,
pinned=False,
select=False):
if self.chat_exists(account, jid):
if select:
self._chat_list_stack.select_chat(account, jid)
self.show_chats()
return
if type_ == 'groupchat':
self._chat_stack.add_group_chat(account, jid)
elif type_ == 'pm':
if not self._startup_finished:
# TODO: Currently we cant load private chats at start
# because the Contacts dont exist yet
return
self._chat_stack.add_private_chat(account, jid)
else:
self._chat_stack.add_chat(account, jid)
self._chat_list_stack.add_chat(workspace_id, account, jid, type_,
pinned)
if self._startup_finished:
if select:
self._chat_list_stack.select_chat(account, jid)
self.show_chats()
self._chat_list_stack.store_open_chats(workspace_id)
def _toggle_chat_pinned(self, _action, param):
workspace_id, account, jid = param.unpack()
self.toggle_chat_pinned(workspace_id, account, jid)
def toggle_chat_pinned(self, workspace_id, account, jid):
self._chat_list_stack.toggle_chat_pinned(workspace_id, account, jid)
def _move_chat_to_workspace(self, _action, param):
new_workspace_id, account, jid = param.unpack()
self.move_chat_to_workspace(new_workspace_id, account, jid)
def move_chat_to_workspace(self, new_workspace_id, account, jid):
current_chatlist = self._chat_list_stack.get_visible_child()
type_ = current_chatlist.get_chat_type(account, jid)
current_chatlist.remove_chat(account, jid)
new_chatlist = self._chat_list_stack.get_chatlist(new_workspace_id)
new_chatlist.add_chat(account, jid, type_)
self._chat_list_stack.store_open_chats(current_chatlist.workspace_id)
self._chat_list_stack.store_open_chats(new_workspace_id)
def _remove_chat(self, _action, param):
account, jid = param.unpack()
self.remove_chat(account, jid)
def remove_chat(self, account, jid):
for workspace_id in app.settings.get_workspaces():
if self.chat_exists_for_workspace(workspace_id, account, jid):
self._chat_list_stack.remove_chat(workspace_id, account, jid)
return
@staticmethod @staticmethod
def _add_to_roster(_action, param): def _add_to_roster(_action, param):
_workspace, account, jid = param.unpack() _workspace, account, jid = param.unpack()
open_window('AddNewContactWindow', account=account, contact_jid=jid) open_window('AddNewContactWindow', account=account, contact_jid=jid)
def chat_exists(self, account, jid): def get_control(self, *args, **kwargs):
return self._chat_list_stack.contains_chat(account, jid) return self._chat_page.get_control(*args, **kwargs)
def chat_exists_for_workspace(self, workspace_id, account, jid): def get_controls(self, *args, **kwargs):
return self._chat_list_stack.contains_chat( return self._chat_page.get_controls(*args, **kwargs)
account, jid, workspace_id=workspace_id)
def get_control(self, account, jid): def get_active_control(self, *args, **kwargs):
return self._chat_stack.get_control(account, jid) return self._chat_page.get_active_control(*args, **kwargs)
def get_active_control(self): def chat_exists(self, *args, **kwargs):
chat = self._chat_list_stack.get_selected_chat() return self._chat_page.chat_exists(*args, **kwargs)
if chat is None:
return None
return self.get_control(chat.account, chat.jid)
def get_controls(self, account=None):
return self._chat_stack.get_controls(account)
def _add_accounts(self):
for account in list(app.connections.keys()):
account_page = AccountPage(account)
self._account_pages[account] = account_page
self._ui.main_stack.add_named(account_page, account)
@staticmethod @staticmethod
def contact_info(account, jid): def contact_info(account, jid):
...@@ -512,7 +375,7 @@ def block_contact(self, account, jid): ...@@ -512,7 +375,7 @@ def block_contact(self, account, jid):
# TODO: Keep "confirm_block" setting? # TODO: Keep "confirm_block" setting?
def _block_contact(report=None): def _block_contact(report=None):
client.get_module('Blocking').block([contact.jid], report) client.get_module('Blocking').block([contact.jid], report)
self.remove_chat(account, contact.jid) self._chat_page.remove_chat(account, contact.jid)
ConfirmationDialog( ConfirmationDialog(
_('Block Contact'), _('Block Contact'),
...@@ -533,7 +396,7 @@ def remove_contact(self, account, jid): ...@@ -533,7 +396,7 @@ def remove_contact(self, account, jid):
client = app.get_client(account) client = app.get_client(account)
def _remove_contact(): def _remove_contact():
self.remove_chat(account, jid) self._chat_page.remove_chat(account, jid)
client.get_module('Roster').delete_item(jid) client.get_module('Roster').delete_item(jid)
contact = client.get_module('Contacts').get_contact(jid) contact = client.get_module('Contacts').get_contact(jid)
...@@ -552,45 +415,13 @@ def _remove_contact(): ...@@ -552,45 +415,13 @@ def _remove_contact():
def _load_chats(self): def _load_chats(self):
for workspace_id in app.settings.get_workspaces(): for workspace_id in app.settings.get_workspaces():
self._workspace_side_bar.add_workspace(workspace_id) self.add_workspace(workspace_id)
self._chat_list_stack.add_chat_list(workspace_id) self._chat_page.load_workspace_chats(workspace_id)
open_chats = app.settings.get_workspace_setting(workspace_id,
'open_chats')
active_accounts = app.settings.get_active_accounts()
for account, jid, type_, pinned in open_chats:
if account not in active_accounts:
continue
self.add_chat_for_workspace(workspace_id, account, jid, type_,
pinned=pinned)
for workspace_id in app.settings.get_workspaces():
self._workspace_side_bar.activate_workspace(workspace_id)
self._chat_list_stack.show_chat_list(workspace_id)
break
self._startup_finished = True
@staticmethod
def _on_start_chat_clicked(_button):
app.app.activate_action('start-chat', GLib.Variant('s', ''))
def _on_chat_selected(self, _chat_list_stack, _workspace_id, *args):
control = self.get_active_control()
if control is not None:
self._search_view.set_context(control.account, control.contact.jid)
def _on_search_history(self, _action, _param): workspace_id = self._workspace_side_bar.get_first_workspace()
control = self.get_active_control() self.activate_workspace(workspace_id)
if control is not None:
self._search_view.set_context(control.account, control.contact.jid)
self._search_view.clear()
self._search_revealer.show()
self._search_view.set_focus()
def _on_search_hide(self, *args): self._set_startup_finished()
self._search_revealer.hide()
def _on_event(self, event): def _on_event(self, event):
if event.name == 'caps-update': if event.name == 'caps-update':
...@@ -600,9 +431,6 @@ def _on_event(self, event): ...@@ -600,9 +431,6 @@ def _on_event(self, event):
if event.name == 'update-roster-avatar': if event.name == 'update-roster-avatar':
return return
for page in self._account_pages.values():
page.process_event(event)
if not self.chat_exists(event.account, event.jid): if not self.chat_exists(event.account, event.jid):
if event.name == 'message-received': if event.name == 'message-received':
if event.properties.is_muc_pm: if event.properties.is_muc_pm:
...@@ -617,8 +445,7 @@ def _on_event(self, event): ...@@ -617,8 +445,7 @@ def _on_event(self, event):
# No chat is open, dont handle any gui events # No chat is open, dont handle any gui events
return return
self._chat_stack.process_event(event) self._main_stack.process_event(event)
self._chat_list_stack.process_event(event)
def quit(self): def quit(self):
accounts = list(app.connections.keys()) accounts = list(app.connections.keys())
......
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gtk
from gajim.common import app
from .chat_page import ChatPage
from .account_page import AccountPage
class MainStack(Gtk.Stack):
def __init__(self):
Gtk.Stack.__init__(self)
self._chat_page = ChatPage()
self._chat_page.connect('chat-selected', self._on_chat_selected)
self.add_named(self._chat_page, 'chats')
for account in list(app.connections.keys()):
account_page = AccountPage(account)
self.add_named(account_page, account)
def show_chats(self, workspace_id):
self._chat_page.show_workspace_chats(workspace_id)
self.set_visible_child_name('chats')
def show_account(self, account):
self.set_visible_child_name(account)
def get_account_page(self, account):
return self.get_child_by_name(account)
def get_chat_page(self):
return self.get_child_by_name('chats')
def process_event(self, event):
for page in self.get_children():
page.process_event(event)
def _on_chat_selected(self, *args):
self.set_visible_child_name('chats')
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gdk from gi.repository import Gdk
from gi.repository import Gtk from gi.repository import Gtk
...@@ -10,7 +23,7 @@ ...@@ -10,7 +23,7 @@
class WorkspaceSideBar(Gtk.ListBox): class WorkspaceSideBar(Gtk.ListBox):
def __init__(self, chat_list_stack): def __init__(self, chat_page):
Gtk.ListBox.__init__(self) Gtk.ListBox.__init__(self)
self.set_vexpand(True) self.set_vexpand(True)
self.set_valign(Gtk.Align.START) self.set_valign(Gtk.Align.START)
...@@ -38,6 +51,7 @@ def __init__(self, chat_list_stack): ...@@ -38,6 +51,7 @@ def __init__(self, chat_list_stack):
self.add(AddWorkspace('add')) self.add(AddWorkspace('add'))
chat_list_stack = chat_page.get_chat_list_stack()
chat_list_stack.connect('unread-count-changed', chat_list_stack.connect('unread-count-changed',
self._on_unread_count_changed) self._on_unread_count_changed)
chat_list_stack.connect('chat-selected', chat_list_stack.connect('chat-selected',
...@@ -286,6 +300,7 @@ def __init__(self, workspace_id): ...@@ -286,6 +300,7 @@ def __init__(self, workspace_id):
self.update() self.update()
def update(self): def update(self):
app.interface.avatar_storage.invalidate_cache(self._workspace_id)
scale = self.get_scale_factor() scale = self.get_scale_factor()
surface = app.interface.avatar_storage.get_workspace_surface( surface = app.interface.avatar_storage.get_workspace_surface(
self._workspace_id, AvatarSize.WORKSPACE, scale) self._workspace_id, AvatarSize.WORKSPACE, scale)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment