1 /*
2    Drawpile - a collaborative drawing program.
3 
4    Copyright (C) 2019 Calle Laakkonen
5 
6    Drawpile is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10 
11    Drawpile is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with Drawpile.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "chatbox.h"
21 #include "chatwidget.h"
22 #include "chatwindow.h"
23 #include "useritemdelegate.h"
24 #include "document.h"
25 #include "canvas/canvasmodel.h"
26 #include "canvas/userlist.h"
27 #include "net/client.h"
28 
29 #include <QResizeEvent>
30 #include <QSplitter>
31 #include <QListView>
32 #include <QVBoxLayout>
33 #include <QMetaObject>
34 
35 namespace widgets {
36 
ChatBox(Document * doc,QWidget * parent)37 ChatBox::ChatBox(Document *doc, QWidget *parent)
38 	: QWidget(parent)
39 {
40 	QSplitter *chatsplitter = new QSplitter(Qt::Horizontal, this);
41 	chatsplitter->setChildrenCollapsible(false);
42 	m_chatWidget = new ChatWidget(this);
43 	chatsplitter->addWidget(m_chatWidget);
44 
45 	m_userList = new QListView(this);
46 	m_userList->setSelectionMode(QListView::NoSelection);
47 	m_userItemDelegate = new UserItemDelegate(this);
48 	m_userItemDelegate->setDocument(doc);
49 	m_userList->setItemDelegate(m_userItemDelegate);
50 	chatsplitter->addWidget(m_userList);
51 
52 	chatsplitter->setStretchFactor(0, 5);
53 	chatsplitter->setStretchFactor(1, 1);
54 
55 	auto *layout = new QVBoxLayout;
56 	layout->addWidget(chatsplitter);
57 	layout->setMargin(0);
58 	setLayout(layout);
59 
60 	connect(m_chatWidget, &ChatWidget::message, this, &ChatBox::message);
61 	connect(m_chatWidget, &ChatWidget::detachRequested, this, &ChatBox::detachFromParent);
62 
63 	connect(doc, &Document::canvasChanged, this, &ChatBox::onCanvasChanged);
64 	connect(doc, &Document::serverLoggedIn, this, &ChatBox::onServerLogin);
65 
66 	connect(doc, &Document::sessionPreserveChatChanged, m_chatWidget, &ChatWidget::setPreserveMode);
67 	connect(doc->client(), &net::Client::serverMessage, m_chatWidget, &ChatWidget::systemMessage);
68 	connect(doc->client(), &net::Client::youWereKicked, m_chatWidget, &ChatWidget::kicked);
69 
70 	connect(m_userItemDelegate, &widgets::UserItemDelegate::opCommand, doc->client(), &net::Client::sendMessage);
71 	connect(m_userItemDelegate, &widgets::UserItemDelegate::requestPrivateChat, m_chatWidget, &ChatWidget::openPrivateChat);
72 }
73 
onCanvasChanged(canvas::CanvasModel * canvas)74 void ChatBox::onCanvasChanged(canvas::CanvasModel *canvas)
75 {
76 	m_userList->setModel(canvas->userlist()->onlineUsers());
77 	m_chatWidget->setUserList(canvas->userlist());
78 
79 	connect(canvas, &canvas::CanvasModel::chatMessageReceived, m_chatWidget, &ChatWidget::receiveMessage);
80 	connect(canvas, &canvas::CanvasModel::markerMessageReceived, m_chatWidget, &ChatWidget::receiveMarker);
81 	connect(canvas, &canvas::CanvasModel::userJoined, m_chatWidget, &ChatWidget::userJoined);
82 	connect(canvas, &canvas::CanvasModel::userLeft, m_chatWidget, &ChatWidget::userParted);
83 }
84 
onServerLogin()85 void ChatBox::onServerLogin()
86 {
87 	m_chatWidget->loggedIn(static_cast<Document*>(sender())->client()->myId());
88 }
89 
focusInput()90 void ChatBox::focusInput()
91 {
92 	m_chatWidget->focusInput();
93 }
94 
detachFromParent()95 void ChatBox::detachFromParent()
96 {
97 	if(!parent() || qobject_cast<ChatWindow*>(parent()))
98 		return;
99 
100 	m_state = State::Detached;
101 
102 	const auto siz = size();
103 
104 	QObject *oldParent = parent();
105 
106 	m_chatWidget->setAttached(false);
107 
108 	auto *window = new ChatWindow(this);
109 	connect(window, &ChatWindow::closing, this, &ChatBox::reattachNowPlease);
110 	connect(window, &ChatWindow::closing, this, &ChatBox::reattachToParent);
111 	connect(oldParent, &QObject::destroyed, window, &QObject::deleteLater);
112 
113 	window->show();
114 	window->resize(siz);
115 }
116 
reattachToParent()117 void ChatBox::reattachToParent()
118 {
119 	m_state = State::Expanded;
120 	m_chatWidget->setAttached(true);
121 }
122 
resizeEvent(QResizeEvent * event)123 void ChatBox::resizeEvent(QResizeEvent *event)
124 {
125 	QWidget::resizeEvent(event);
126 	if(event->size().height() == 0) {
127 		if(m_state == State::Expanded) {
128 			emit expandedChanged(false);
129 			m_state = State::Collapsed;
130 		}
131 
132 	} else if(m_state == State::Collapsed) {
133 		m_state = State::Expanded;
134 		emit expandedChanged(true);
135 	}
136 }
137 
138 }
139