1 /*
2  * %kadu copyright begin%
3  * Copyright 2009, 2010, 2011 Piotr Galiszewski (piotr.galiszewski@kadu.im)
4  * Copyright 2012 Wojciech Treter (juzefwt@gmail.com)
5  * Copyright 2010 Tomasz Rostański (rozteck@interia.pl)
6  * Copyright 2011 Piotr Dąbrowski (ultr@ultr.pl)
7  * Copyright 2009 Michał Podsiadlik (michal@kadu.net)
8  * Copyright 2009 Bartłomiej Zimoń (uzi18@o2.pl)
9  * Copyright 2010, 2011, 2012, 2013, 2014 Bartosz Brachaczek (b.brachaczek@gmail.com)
10  * Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
11  * %kadu copyright end%
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License as
15  * published by the Free Software Foundation; either version 2 of
16  * the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program. If not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 #include <QtGui/QResizeEvent>
28 #include <QtWidgets/QFileDialog>
29 #include <QtXml/QDomElement>
30 
31 
32 #include "accounts/account-details.h"
33 #include "buddies/buddy-set.h"
34 #include "chat/chat.h"
35 #include "configuration/configuration-api.h"
36 #include "configuration/configuration.h"
37 #include "configuration/configuration.h"
38 #include "configuration/deprecated-configuration-api.h"
39 #include "contacts/contact-set.h"
40 #include "contacts/contact.h"
41 #include "core/injected-factory.h"
42 #include "gui/actions/action.h"
43 #include "gui/actions/base-action-context.h"
44 #include "gui/configuration/chat-configuration-holder.h"
45 #include "gui/widgets/chat-edit-box-size-manager.h"
46 #include "gui/widgets/chat-widget/chat-widget-actions.h"
47 #include "gui/widgets/chat-widget/chat-widget.h"
48 #include "gui/widgets/color-selector.h"
49 #include "gui/widgets/custom-input.h"
50 #include "gui/widgets/talkable-tree-view.h"
51 #include "gui/widgets/toolbar.h"
52 #include "gui/windows/message-dialog.h"
53 #include "icons/icons-manager.h"
54 #include "identities/identity.h"
55 #include "misc/change-notifier-lock.h"
56 #include "misc/error.h"
57 #include "protocols/protocol.h"
58 #include "protocols/services/chat-image-service.h"
59 #include "status/status-configuration-holder.h"
60 #include "status/status-container-manager.h"
61 #include "debug.h"
62 
63 #include "chat-edit-box.h"
64 
65 QList<ChatEditBox *> chatEditBoxes;
66 
ChatEditBox(const Chat & chat,QWidget * parent)67 ChatEditBox::ChatEditBox(const Chat &chat, QWidget *parent) :
68 		MainWindow(new BaseActionContext(this), "chat", parent), CurrentChat(chat)
69 {
70 }
71 
~ChatEditBox()72 ChatEditBox::~ChatEditBox()
73 {
74 // 	disconnect(m_chatWidgetActions->colorSelector(), 0, this, 0);
75 	disconnect(InputBox, 0, this, 0);
76 
77 	chatEditBoxes.removeAll(this);
78 }
79 
setChatConfigurationHolder(ChatConfigurationHolder * chatConfigurationHolder)80 void ChatEditBox::setChatConfigurationHolder(ChatConfigurationHolder *chatConfigurationHolder)
81 {
82 	m_chatConfigurationHolder = chatConfigurationHolder;
83 }
84 
setChatWidgetActions(ChatWidgetActions * chatWidgetActions)85 void ChatEditBox::setChatWidgetActions(ChatWidgetActions *chatWidgetActions)
86 {
87 	m_chatWidgetActions = chatWidgetActions;
88 }
89 
setIconsManager(IconsManager * iconsManager)90 void ChatEditBox::setIconsManager(IconsManager *iconsManager)
91 {
92 	m_iconsManager = iconsManager;
93 }
94 
setStatusConfigurationHolder(StatusConfigurationHolder * statusConfigurationHolder)95 void ChatEditBox::setStatusConfigurationHolder(StatusConfigurationHolder *statusConfigurationHolder)
96 {
97 	m_statusConfigurationHolder = statusConfigurationHolder;
98 }
99 
setStatusContainerManager(StatusContainerManager * statusContainerManager)100 void ChatEditBox::setStatusContainerManager(StatusContainerManager *statusContainerManager)
101 {
102 	m_statusContainerManager = statusContainerManager;
103 }
104 
init()105 void ChatEditBox::init()
106 {
107 	chatEditBoxes.append(this);
108 
109 	Context = static_cast<BaseActionContext *>(actionContext());
110 
111 	ChangeNotifierLock lock(Context->changeNotifier());
112 
113 	RoleSet roles;
114 	if (CurrentChat.contacts().size() > 1)
115 		roles.insert(ChatRole);
116 	else
117 		roles.insert(BuddyRole);
118 	Context->setRoles(roles);
119 
120 	Context->setChat(CurrentChat);
121 	Context->setContacts(CurrentChat.contacts());
122 	Context->setBuddies(CurrentChat.contacts().toBuddySet());
123 	updateContext();
124 
125 	connect(m_statusConfigurationHolder, SIGNAL(setStatusModeChanged()), this, SLOT(updateContext()));
126 
127 	InputBox = injectedFactory()->makeInjected<CustomInput>(CurrentChat, this);
128 	InputBox->setWordWrapMode(QTextOption::WordWrap);
129 
130 	setCentralWidget(InputBox);
131 
132 	bool old_top = loadOldToolBarsFromConfig("chatTopDockArea", Qt::TopToolBarArea);
133 	bool old_middle = loadOldToolBarsFromConfig("chatMiddleDockArea", Qt::TopToolBarArea);
134 	bool old_bottom = loadOldToolBarsFromConfig("chatBottomDockArea", Qt::BottomToolBarArea);
135 	bool old_left = loadOldToolBarsFromConfig("chatLeftDockArea", Qt::LeftToolBarArea);
136 	bool old_right = loadOldToolBarsFromConfig("chatRightDockArea", Qt::RightToolBarArea);
137 
138 	if (old_top || old_middle || old_bottom || old_left || old_right)
139 		writeToolBarsToConfig(); // port old config
140 	else
141 		loadToolBarsFromConfig(); // load new config
142 
143 // 	connect(m_chatWidgetActions->colorSelector(), SIGNAL(actionCreated(Action *)),
144 // 			this, SLOT(colorSelectorActionCreated(Action *)));
145 	connect(InputBox, SIGNAL(keyPressed(QKeyEvent *,CustomInput *, bool &)),
146 			this, SIGNAL(keyPressed(QKeyEvent *,CustomInput *,bool &)));
147 	connect(InputBox, SIGNAL(fontChanged(QFont)), this, SLOT(fontChanged(QFont)));
148 	connect(InputBox, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
149 
150 	connect(m_chatConfigurationHolder, SIGNAL(chatConfigurationUpdated()), this, SLOT(configurationUpdated()));
151 
152 	configurationUpdated();
153 }
154 
fontChanged(QFont font)155 void ChatEditBox::fontChanged(QFont font)
156 {
157 	if (m_chatWidgetActions->bold()->action(actionContext()))
158 		m_chatWidgetActions->bold()->action(actionContext())->setChecked(font.bold());
159 	if (m_chatWidgetActions->italic()->action(actionContext()))
160 		m_chatWidgetActions->italic()->action(actionContext())->setChecked(font.italic());
161 	if (m_chatWidgetActions->underline()->action(actionContext()))
162 		m_chatWidgetActions->underline()->action(actionContext())->setChecked(font.underline());
163 }
164 
colorSelectorActionCreated(Action * action)165 void ChatEditBox::colorSelectorActionCreated(Action *action)
166 {
167 	if (action->parent() == this)
168 		setColorFromCurrentText(true);
169 }
170 
cursorPositionChanged()171 void ChatEditBox::cursorPositionChanged()
172 {
173 	setColorFromCurrentText(false);
174 }
175 
configurationUpdated()176 void ChatEditBox::configurationUpdated()
177 {
178 	setColorFromCurrentText(true);
179 
180 	InputBox->setAutoSend(m_chatConfigurationHolder->autoSend());
181 }
182 
setAutoSend(bool autoSend)183 void ChatEditBox::setAutoSend(bool autoSend)
184 {
185 	InputBox->setAutoSend(autoSend);
186 }
187 
inputBox()188 CustomInput * ChatEditBox::inputBox()
189 {
190 	return InputBox;
191 }
192 
supportsActionType(ActionDescription::ActionType type)193 bool ChatEditBox::supportsActionType(ActionDescription::ActionType type)
194 {
195 	return (type == ActionDescription::TypeGlobal || type == ActionDescription::TypeChat || type == ActionDescription::TypeUser);
196 }
197 
talkableProxyModel()198 TalkableProxyModel * ChatEditBox::talkableProxyModel()
199 {
200 	ChatWidget *cw = chatWidget();
201 	if (cw && cw->chat().contacts().count() > 1)
202 		return cw->talkableProxyModel();
203 
204 	return 0;
205 }
206 
updateContext()207 void ChatEditBox::updateContext()
208 {
209 	if (m_statusConfigurationHolder->isSetStatusPerIdentity())
210 		Context->setStatusContainer(CurrentChat.chatAccount().accountIdentity().statusContainer());
211 	else if (m_statusConfigurationHolder->isSetStatusPerAccount())
212 		Context->setStatusContainer(CurrentChat.chatAccount().statusContainer());
213 	else
214 		Context->setStatusContainer(m_statusContainerManager);
215 }
216 
chatWidget()217 ChatWidget * ChatEditBox::chatWidget()
218 {
219 	ChatWidget *result = qobject_cast<ChatWidget *>(parentWidget());
220 	if (result)
221 		return result;
222 
223 	result = qobject_cast<ChatWidget *>(parent()->parent());
224 	if (result)
225 		return result;
226 
227 	return 0;
228 }
229 
createDefaultToolbars(Configuration * configuration,QDomElement toolbarsConfig)230 void ChatEditBox::createDefaultToolbars(Configuration *configuration, QDomElement toolbarsConfig)
231 {
232 	QDomElement dockAreaConfig = getDockAreaConfigElement(configuration, toolbarsConfig, "chat_topDockArea");
233 	QDomElement toolbarConfig = configuration->api()->createElement(dockAreaConfig, "ToolBar");
234 
235 	addToolButton(configuration, toolbarConfig, "autoSendAction");
236 	addToolButton(configuration, toolbarConfig, "clearChatAction");
237 	addToolButton(configuration, toolbarConfig, "insertEmoticonAction", Qt::ToolButtonTextBesideIcon);
238 	addToolButton(configuration, toolbarConfig, "insertImageAction");
239 	addToolButton(configuration, toolbarConfig, "showHistoryAction");
240 	addToolButton(configuration, toolbarConfig, "encryptionAction");
241 	addToolButton(configuration, toolbarConfig, "editUserAction");
242 	addToolButton(configuration, toolbarConfig, "__spacer1", Qt::ToolButtonTextBesideIcon);
243 	addToolButton(configuration, toolbarConfig, "sendAction", Qt::ToolButtonTextBesideIcon);
244 }
245 
openColorSelector(const QWidget * activatingWidget)246 void ChatEditBox::openColorSelector(const QWidget *activatingWidget)
247 {
248 	//sytuacja podobna jak w przypadku emoticon_selectora
249 	ColorSelector *colorSelector = new ColorSelector(InputBox->palette().foreground().color(), activatingWidget, this);
250 	connect(colorSelector, SIGNAL(colorSelect(const QColor &)), this, SLOT(changeColor(const QColor &)));
251 	colorSelector->show();
252 }
253 
openInsertImageDialog()254 void ChatEditBox::openInsertImageDialog()
255 {
256 	ChatImageService *chatImageService = CurrentChat.chatAccount().protocolHandler()->chatImageService();
257 	if (!chatImageService)
258 		return;
259 
260 	// QTBUG-849
261 	QString selectedFile = QFileDialog::getOpenFileName(this, tr("Insert image"), configuration()->deprecatedApi()->readEntry("Chat", "LastImagePath"),
262 							tr("Images (*.png *.PNG *.jpg *.JPG *.jpeg *.JPEG *.gif *.GIF *.bmp *.BMP);;All Files (*)"));
263 	if (!selectedFile.isEmpty())
264 	{
265 		QFileInfo f(selectedFile);
266 
267 		configuration()->deprecatedApi()->writeEntry("Chat", "LastImagePath", f.absolutePath());
268 
269 		if (!f.isReadable())
270 		{
271 			MessageDialog::show(m_iconsManager->iconByPath(KaduIcon("dialog-warning")), tr("Kadu"), tr("This file is not readable"), QMessageBox::Ok, this);
272 			return;
273 		}
274 
275 		Error imageSizeError = chatImageService->checkImageSize(f.size());
276 		if (!imageSizeError.message().isEmpty())
277 		{
278 			MessageDialog *dialog = MessageDialog::create(m_iconsManager->iconByPath(KaduIcon("dialog-warning")), tr("Kadu"), imageSizeError.message(), this);
279 			dialog->addButton(QMessageBox::Yes, tr("Send anyway"));
280 			dialog->addButton(QMessageBox::No, tr("Cancel"));
281 
282 			switch (imageSizeError.severity())
283 			{
284 				case NoError:
285 					break;
286 				case ErrorLow:
287 					if (dialog->ask())
288 						return;
289 					break;
290 				case ErrorHigh:
291 					MessageDialog::show(m_iconsManager->iconByPath(KaduIcon("dialog-error")), tr("Kadu"), imageSizeError.message(), QMessageBox::Ok, this);
292 					return;
293 				default:
294 					break;
295 			}
296 		}
297 
298 		int tooBigCounter = 0;
299 		int disconnectedCounter = 0;
300 
301 		foreach (const Contact &contact, CurrentChat.contacts())
302 		{
303 			if (contact.currentStatus().isDisconnected())
304 				disconnectedCounter++;
305 			else if (contact.maximumImageSize() == 0 || contact.maximumImageSize() * 1024 < f.size())
306 				tooBigCounter++;
307 		}
308 
309 		QString message;
310 		if (1 == CurrentChat.contacts().count())
311 		{
312 			Contact contact = *CurrentChat.contacts().constBegin();
313 			if (tooBigCounter > 0)
314 				message = tr("This image has %1 KiB and may be too big for %2.")
315 						.arg((f.size() + 1023) / 1024).arg(contact.display(true)) + '\n';
316 			else if (disconnectedCounter > 0)
317 				message = tr("%1 appears to be offline and may not receive images.").arg(contact.display(true)) + '\n';
318 		}
319 		else
320 		{
321 			if (tooBigCounter > 0)
322 				message = tr("This image has %1 KiB and may be too big for %2 of %3 contacts in this conference.")
323 						.arg((f.size() + 1023) / 1024).arg(tooBigCounter).arg(CurrentChat.contacts().count()) + '\n';
324 			if (disconnectedCounter > 0)
325 				message += tr("%1 of %2 contacts appear to be offline and may not receive images.").arg(disconnectedCounter).arg(CurrentChat.contacts().count()) + '\n';
326 		}
327 		if (tooBigCounter > 0 || disconnectedCounter > 0)
328 			message += tr("Do you really want to send this image?");
329 
330 		MessageDialog *dialog = MessageDialog::create(m_iconsManager->iconByPath(KaduIcon("dialog-question")), tr("Kadu"), message, this);
331 		dialog->addButton(QMessageBox::Yes, tr("Send anyway"));
332 		dialog->addButton(QMessageBox::No, tr("Cancel"));
333 
334 		if (!message.isEmpty() && !dialog->ask())
335 			return;
336 
337 		InputBox->insertHtml(QString("<img src='%1' />").arg(selectedFile));
338 	}
339 }
340 
changeColor(const QColor & newColor)341 void ChatEditBox::changeColor(const QColor &newColor)
342 {
343 	CurrentColor = newColor;
344 
345 	QPixmap p(12, 12);
346 	p.fill(CurrentColor);
347 
348 // 	Action *action = m_chatWidgetActions->colorSelector()->action(this);
349 // 	if (action)
350 // 		action->setIcon(p);
351 
352 	InputBox->setTextColor(CurrentColor);
353 }
354 
setColorFromCurrentText(bool force)355 void ChatEditBox::setColorFromCurrentText(bool force)
356 {
357 	Q_UNUSED(force);
358 
359 /*
360 	Action *action = m_chatWidgetActions->colorSelector()->action(this);
361 	if (!action || (!force && (InputBox->textColor() == CurrentColor)))
362 		return;
363 
364 	int i;
365 	for (i = 0; i < 16; ++i)
366 		if (InputBox->textColor() == QColor(colors[i]))
367 			break;
368 
369 	QPixmap p(12, 12);
370 	if (i >= 16)
371 		CurrentColor = InputBox->palette().foreground().color();
372 	else
373 		CurrentColor = colors[i];
374 
375 	p.fill(CurrentColor);
376 
377 	action->QAction::setIcon(p);
378 */
379 }
380 
insertPlainText(const QString & plainText)381 void ChatEditBox::insertPlainText(const QString &plainText)
382 {
383 	InputBox->insertPlainText( plainText);
384 }
385 
386 #include "moc_chat-edit-box.cpp"
387