1 /*
2  * %kadu copyright begin%
3  * Copyright 2014 Bartosz Brachaczek (b.brachaczek@gmail.com)
4  * Copyright 2013, 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
5  * %kadu copyright end%
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <QtWidgets/QMenu>
22 
23 #include "accounts/account-manager.h"
24 #include "accounts/account.h"
25 #include "chat/chat-manager.h"
26 #include "configuration/configuration.h"
27 #include "configuration/deprecated-configuration-api.h"
28 #include "contacts/contact-set.h"
29 #include "contacts/contact.h"
30 #include "core/myself.h"
31 #include "core/injected-factory.h"
32 #include "gui/actions/action.h"
33 #include "gui/actions/actions.h"
34 #include "gui/actions/chat/leave-chat-action.h"
35 #include "gui/actions/edit-talkable-action.h"
36 #include "gui/configuration/chat-configuration-holder.h"
37 #include "gui/menu/menu-inventory.h"
38 #include "gui/widgets/chat-edit-box.h"
39 #include "gui/widgets/chat-widget/chat-widget-manager.h"
40 #include "gui/widgets/chat-widget/chat-widget.h"
41 #include "gui/widgets/custom-input.h"
42 #include "gui/widgets/toolbar.h"
43 #include "gui/widgets/webkit-messages-view/webkit-messages-view.h"
44 #include "gui/windows/kadu-window-actions.h"
45 #include "gui/windows/message-dialog.h"
46 #include "gui/windows/open-chat-with/open-chat-with-service.h"
47 #include "gui/windows/open-chat-with/open-chat-with.h"
48 #include "model/roles.h"
49 #include "debug.h"
50 
51 #include "chat-widget-actions.h"
52 
disableEmptyTextBoxOrNotConnected(Action * action)53 static void disableEmptyTextBoxOrNotConnected(Action *action)
54 {
55 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(action->parent());
56 	if (!chatEditBox)
57 	{
58 		action->setEnabled(false);
59 		return;
60 	}
61 
62 	action->setEnabled(chatEditBox->chatWidget()->chat().isConnected() && !chatEditBox->inputBox()->toPlainText().isEmpty());
63 }
64 
disableEmptyMessages(Action * action)65 static void disableEmptyMessages(Action *action)
66 {
67 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(action->parent());
68 	if (!chatEditBox)
69 	{
70 		action->setEnabled(false);
71 		return;
72 	}
73 
74 	action->setEnabled(0 != chatEditBox->chatWidget()->chatMessagesView()->countMessages());
75 }
76 
disableNoChatImageService(Action * action)77 static void disableNoChatImageService(Action *action)
78 {
79 	action->setEnabled(false);
80 
81 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(action->parent());
82 	if (!chatEditBox)
83 		return;
84 
85 	Account account = action->context()->chat().chatAccount();
86 	if (!account)
87 		return;
88 
89 	Protocol *protocol = account.protocolHandler();
90 	if (!protocol)
91 		return;
92 
93 	action->setEnabled(protocol->chatImageService());
94 }
95 
checkBlocking(Myself * myself,Action * action)96 static void checkBlocking(Myself *myself, Action *action)
97 {
98 	BuddySet buddies = action->context()->buddies();
99 
100 	if (!buddies.count() || buddies.contains(myself->buddy()))
101 	{
102 		action->setEnabled(false);
103 		return;
104 	}
105 
106 	if (action->context()->buddies().isAnyTemporary())
107 	{
108 		action->setEnabled(false);
109 		return;
110 	}
111 
112 	action->setEnabled(!action->context()->roles().contains(ContactRole));
113 
114 	bool on = false;
115 	foreach (const Buddy &buddy, buddies)
116 		if (buddy.isBlocked())
117 		{
118 			on = true;
119 			break;
120 		}
121 	action->setChecked(on);
122 }
123 
124 // TODO: quickhack
disableNoGadu(Action * action)125 static void disableNoGadu(Action *action)
126 {
127 	action->setEnabled(false);
128 
129 	Chat chat = action->context()->chat();
130 	if (!chat)
131 		return;
132 
133 	Protocol *protocol = chat.chatAccount().protocolHandler();
134 	if (!protocol)
135 		return;
136 
137 	if (!protocol->protocolFactory())
138 		return;
139 
140 	action->setEnabled(protocol->protocolFactory()->name() == "gadu");
141 }
142 
ChatWidgetActions(QObject * parent)143 ChatWidgetActions::ChatWidgetActions(QObject *parent) : QObject(parent)
144 {
145 }
146 
~ChatWidgetActions()147 ChatWidgetActions::~ChatWidgetActions()
148 {
149 }
150 
setActions(Actions * actions)151 void ChatWidgetActions::setActions(Actions *actions)
152 {
153 	m_actions = actions;
154 }
155 
setChatConfigurationHolder(ChatConfigurationHolder * chatConfigurationHolder)156 void ChatWidgetActions::setChatConfigurationHolder(ChatConfigurationHolder *chatConfigurationHolder)
157 {
158 	m_chatConfigurationHolder = chatConfigurationHolder;
159 }
160 
setChatWidgetManager(ChatWidgetManager * chatWidgetManager)161 void ChatWidgetActions::setChatWidgetManager(ChatWidgetManager *chatWidgetManager)
162 {
163 	m_chatWidgetManager = chatWidgetManager;
164 }
165 
setConfiguration(Configuration * configuration)166 void ChatWidgetActions::setConfiguration(Configuration *configuration)
167 {
168 	m_configuration = configuration;
169 }
170 
setInjectedFactory(InjectedFactory * injectedFactory)171 void ChatWidgetActions::setInjectedFactory(InjectedFactory *injectedFactory)
172 {
173 	m_injectedFactory = injectedFactory;
174 }
175 
setMenuInventory(MenuInventory * menuInventory)176 void ChatWidgetActions::setMenuInventory(MenuInventory *menuInventory)
177 {
178 	m_menuInventory = menuInventory;
179 }
180 
setMyself(Myself * myself)181 void ChatWidgetActions::setMyself(Myself *myself)
182 {
183 	m_myself = myself;
184 }
185 
setOpenChatWithService(OpenChatWithService * openChatWithService)186 void ChatWidgetActions::setOpenChatWithService(OpenChatWithService *openChatWithService)
187 {
188 	m_openChatWithService = openChatWithService;
189 }
190 
init()191 void ChatWidgetActions::init()
192 {
193 	m_actions->blockSignals();
194 
195 	MoreActions = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
196 		ActionDescription::TypeChat, "moreActionsAction",
197 		this, SLOT(moreActionsActionActivated(QAction *, bool)),
198 		KaduIcon(), tr("More..."), false
199 	);
200 
201 	AutoSend = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
202 		ActionDescription::TypeChat, "autoSendAction",
203 		this, SLOT(autoSendActionActivated(QAction *, bool)),
204 		KaduIcon("kadu_icons/enter"), tr("Return Sends Message"), true
205 	);
206 	connect(AutoSend, SIGNAL(actionCreated(Action *)), this, SLOT(autoSendActionCreated(Action *)));
207 
208 	ClearChat = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
209 		ActionDescription::TypeChat, "clearChatAction",
210 		this, SLOT(clearActionActivated(QAction *, bool)),
211 		KaduIcon("edit-clear"), tr("Clear Messages in Chat Window"), false,
212 		disableEmptyMessages
213 	);
214 	connect(ClearChat, SIGNAL(actionCreated(Action *)), this, SLOT(clearChatActionCreated(Action *)));
215 
216 	InsertImage = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
217 		ActionDescription::TypeChat, "insertImageAction",
218 		this, SLOT(insertImageActionActivated(QAction *, bool)),
219 		KaduIcon("insert-image"), tr("Insert Image"), false,
220 		disableNoChatImageService
221 	);
222 
223 	Bold = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
224 		ActionDescription::TypeChat, "boldAction",
225 		this, SLOT(boldActionActivated(QAction *, bool)),
226 		KaduIcon("format-text-bold"), tr("Bold"), true,
227 		disableNoGadu
228 	);
229 
230 	Italic = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
231 		ActionDescription::TypeChat, "italicAction",
232 		this, SLOT(italicActionActivated(QAction *, bool)),
233 		KaduIcon("format-text-italic"), tr("Italic"), true,
234 		disableNoGadu
235 	);
236 
237 	Underline = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
238 		ActionDescription::TypeChat, "underlineAction",
239 		this, SLOT(underlineActionActivated(QAction *, bool)),
240 		KaduIcon("format-text-underline"), tr("Underline"), true,
241 		disableNoGadu
242 	);
243 
244 	Send = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
245 		ActionDescription::TypeChat, "sendAction",
246 		this, SLOT(sendActionActivated(QAction *, bool)),
247 		KaduIcon("go-next"), tr("&Send"), false,
248 		disableEmptyTextBoxOrNotConnected
249 	);
250 	connect(Send, SIGNAL(actionCreated(Action *)), this, SLOT(sendActionCreated(Action *)));
251 
252 	BlockUser = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
253 		ActionDescription::TypeUser, "blockUserAction",
254 		this, SLOT(blockUserActionActivated(QAction *, bool)),
255 		KaduIcon("kadu_icons/block-buddy"), tr("Block Buddy"), true,
256 		[this](Action *action){ return checkBlocking(m_myself, action); }
257 	);
258 
259 	// The last ActionDescription of each type will send actionLoaded() signal.
260 	m_actions->unblockSignals();
261 
262 	OpenChat = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
263 		ActionDescription::TypeUser, "chatAction",
264 		this, SLOT(openChatActionActivated(QAction *, bool)),
265 		KaduIcon("internet-group-chat"), tr("&Chat"), false,
266 		disableNoChat
267 	);
268 
269 	m_menuInventory
270 		->menu("buddy-list")
271 		->addAction(OpenChat, KaduMenu::SectionChat, 1000);
272 
273 	OpenWith = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
274 		ActionDescription::TypeGlobal, "openChatWithAction",
275 		this, SLOT(openChatWithActionActivated(QAction *, bool)),
276 		KaduIcon("internet-group-chat"), tr("Open Chat with...")
277 	);
278 	OpenWith->setShortcut("kadu_openchatwith", Qt::ApplicationShortcut);
279 /*
280 	ColorSelector = m_injectedFactory->makeInjected<ActionDescription>(nullptr,
281 		ActionDescription::TypeChat, "colorAction",
282 		this, SLOT(colorSelectorActionActivated(QAction *, bool)),
283 		KaduIcon("kadu_icons/change-color"), tr("Change Color")
284 	);*/
285 
286 	EditTalkable = m_injectedFactory->makeInjected<EditTalkableAction>(this);
287 	m_actions->insert(EditTalkable);
288 
289 	LeaveChat = m_injectedFactory->makeInjected<LeaveChatAction>(this);
290 	m_actions->insert(LeaveChat);
291 }
292 
done()293 void ChatWidgetActions::done()
294 {
295 	delete MoreActions;
296 	delete AutoSend;
297 	delete ClearChat;
298 	delete InsertImage;
299 	delete Bold;
300 	delete Italic;
301 	delete Underline;
302 	delete Send;
303 	delete BlockUser;
304 	delete OpenChat;
305 	delete OpenWith;
306 	delete EditTalkable;
307 	delete LeaveChat;
308 }
309 
configurationUpdated()310 void ChatWidgetActions::configurationUpdated()
311 {
312 	autoSendActionCheck();
313 }
314 
autoSendActionCreated(Action * action)315 void ChatWidgetActions::autoSendActionCreated(Action *action)
316 {
317 	action->setChecked(m_configuration->deprecatedApi()->readBoolEntry("Chat", "AutoSend"));
318 }
319 
clearChatActionCreated(Action * action)320 void ChatWidgetActions::clearChatActionCreated(Action *action)
321 {
322 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(action->parent());
323 	if (!chatEditBox)
324 		return;
325 
326 	connect(chatEditBox->chatWidget()->chatMessagesView(), SIGNAL(messagesUpdated()), action, SLOT(checkState()));
327 }
328 
sendActionCreated(Action * action)329 void ChatWidgetActions::sendActionCreated(Action *action)
330 {
331 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(action->parent());
332 	if (!chatEditBox)
333 		return;
334 
335 	connect(chatEditBox->inputBox(), SIGNAL(textChanged()), action, SLOT(checkState()));
336 	connect(chatEditBox->chatWidget()->chat(), SIGNAL(connected()), action, SLOT(checkState()));
337 	connect(chatEditBox->chatWidget()->chat(), SIGNAL(disconnected()), action, SLOT(checkState()));
338 
339 	ChatWidget *chatWidget = chatEditBox->chatWidget();
340 	if (!chatWidget)
341 		return;
342 }
343 
autoSendActionCheck()344 void ChatWidgetActions::autoSendActionCheck()
345 {
346  	bool check = m_configuration->deprecatedApi()->readBoolEntry("Chat", "AutoSend");
347  	foreach (Action *action, AutoSend->actions())
348  		action->setChecked(check);
349 }
350 
autoSendActionActivated(QAction * sender,bool toggled)351 void ChatWidgetActions::autoSendActionActivated(QAction *sender, bool toggled)
352 {
353 	kdebugf();
354 
355 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
356 	if (!chatEditBox)
357 		return;
358 
359 	m_configuration->deprecatedApi()->writeEntry("Chat", "AutoSend", toggled);
360 	autoSendActionCheck();
361 
362 	m_chatConfigurationHolder->configurationUpdated();
363 }
364 
moreActionsActionActivated(QAction * sender,bool toggled)365 void ChatWidgetActions::moreActionsActionActivated(QAction *sender, bool toggled)
366 {
367 	Q_UNUSED(toggled)
368 	Action *action = qobject_cast<Action *>(sender);
369 	if (!action)
370 		return;
371 
372 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
373 	if (!chatEditBox)
374 		return;
375 
376 	ChatWidget *chatWidget = chatEditBox->chatWidget();
377 	if (!chatWidget)
378 		return;
379 
380 	QList<QWidget *> widgets = sender->associatedWidgets();
381 	if (widgets.isEmpty())
382 		return;
383 
384 	QWidget *widget = widgets.at(widgets.size() - 1);
385 
386 	QWidget *parent = widget->parentWidget();
387 	while (0 != parent && 0 == qobject_cast<ToolBar *>(parent))
388 		parent = parent->parentWidget();
389 	ToolBar *toolbar = qobject_cast<ToolBar *>(parent);
390 
391 	QMenu menu;
392 	QMenu *subMenu = new QMenu(tr("More"), &menu);
393 
394 	foreach (const QString &actionName, m_actions->keys())
395 	{
396 		if (toolbar && toolbar->windowHasAction(actionName, false))
397 			continue;
398 
399 		ActionDescription *actionDescription = m_actions->value(actionName);
400 		if (ActionDescription::TypeChat == actionDescription->type())
401 			menu.addAction(m_actions->createAction(actionName, chatEditBox->actionContext(), chatEditBox));
402 		else if (ActionDescription::TypeUser == actionDescription->type())
403 			subMenu->addAction(m_actions->createAction(actionName, chatEditBox->actionContext(), chatEditBox));
404 	}
405 
406 	menu.addSeparator();
407 	menu.addMenu(subMenu);
408 	menu.exec(widget->mapToGlobal(QPoint(0, widget->height())));
409 }
410 
clearActionActivated(QAction * sender,bool toggled)411 void ChatWidgetActions::clearActionActivated(QAction *sender, bool toggled)
412 {
413 	Q_UNUSED(toggled)
414 
415 	kdebugf();
416 
417 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
418 	if (!chatEditBox)
419 		return;
420 
421 	ChatWidget *chatWidget = chatEditBox->chatWidget();
422 	if (chatWidget)
423 		chatWidget->clearChatWindow();
424 
425 	kdebugf2();
426 }
427 
insertImageActionActivated(QAction * sender,bool toggled)428 void ChatWidgetActions::insertImageActionActivated(QAction *sender, bool toggled)
429 {
430 	Q_UNUSED(toggled)
431 
432 	kdebugf();
433 
434 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
435 	if (!chatEditBox)
436 		return;
437 
438 	chatEditBox->openInsertImageDialog();
439 }
440 
boldActionActivated(QAction * sender,bool toggled)441 void ChatWidgetActions::boldActionActivated(QAction *sender, bool toggled)
442 {
443 	Q_UNUSED(toggled)
444 
445 	kdebugf();
446 
447 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
448 	if (!chatEditBox)
449 		return;
450 
451 	chatEditBox->inputBox()->setFontWeight(toggled ? QFont::Bold : QFont::Normal);
452 
453 	kdebugf2();
454 }
455 
italicActionActivated(QAction * sender,bool toggled)456 void ChatWidgetActions::italicActionActivated(QAction *sender, bool toggled)
457 {
458 	Q_UNUSED(toggled)
459 
460 	kdebugf();
461 
462 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
463 	if (!chatEditBox)
464 		return;
465 
466 	chatEditBox->inputBox()->setFontItalic(toggled);
467 
468 	kdebugf2();
469 }
470 
underlineActionActivated(QAction * sender,bool toggled)471 void ChatWidgetActions::underlineActionActivated(QAction *sender, bool toggled)
472 {
473 	Q_UNUSED(toggled)
474 
475 	kdebugf();
476 
477 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
478 	if (!chatEditBox)
479 		return;
480 
481 	chatEditBox->inputBox()->setFontUnderline(toggled);
482 
483 	kdebugf2();
484 }
485 
sendActionActivated(QAction * sender,bool toggled)486 void ChatWidgetActions::sendActionActivated(QAction *sender, bool toggled)
487 {
488 	Q_UNUSED(toggled)
489 
490 	kdebugf();
491 
492 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
493 	if (!chatEditBox)
494 		return;
495 
496 	ChatWidget *chatWidget = chatEditBox->chatWidget();
497 	if (chatWidget)
498 		chatWidget->sendMessage();
499 
500 	kdebugf2();
501 }
502 
blockUserActionActivated(QAction * sender,bool toggled)503 void ChatWidgetActions::blockUserActionActivated(QAction *sender, bool toggled)
504 {
505 	Q_UNUSED(toggled)
506 
507 	kdebugf();
508 
509 	Action *action = qobject_cast<Action *>(sender);
510 	if (!action)
511 		return;
512 
513 	BuddySet buddies = action->context()->buddies();
514 	if (buddies.isEmpty())
515 		return;
516 
517 	bool on = false;
518 
519 	foreach (const Buddy &buddy, buddies)
520 		if (!buddy.isBlocked())
521 		{
522 			on = true;
523 			break;
524 		}
525 
526 	foreach (Buddy buddy, buddies)
527 	{
528 		buddy.setBlocked(on);
529 		// update actions
530 		updateBlockingActions(buddy);
531 	}
532 
533 	kdebugf2();
534 }
535 
updateBlockingActions(Buddy buddy)536 void ChatWidgetActions::updateBlockingActions(Buddy buddy)
537 {
538 	QList<Contact> buddyContacts = buddy.contacts();
539 
540 	foreach (Action *action, BlockUser->actions())
541 	{
542 		Contact contact = action->context()->contacts().toContact();
543 		if (contact)
544 			if (buddyContacts.contains(contact))
545 				action->setChecked(buddy.isBlocked());
546 	}
547 }
548 
openChatActionActivated(QAction * sender,bool toggled)549 void ChatWidgetActions::openChatActionActivated(QAction *sender, bool toggled)
550 {
551 	Q_UNUSED(toggled)
552 
553 	kdebugf();
554 
555 	auto action = qobject_cast<Action *>(sender);
556 	if (!action)
557 		return;
558 
559 	m_chatWidgetManager->openChat(action->context()->chat(), OpenChatActivation::Activate);
560 
561 	kdebugf2();
562 }
563 
openChatWithActionActivated(QAction * sender,bool toggled)564 void ChatWidgetActions::openChatWithActionActivated(QAction *sender, bool toggled)
565 {
566 	Q_UNUSED(toggled)
567 
568 	kdebugf();
569 
570 	auto action = qobject_cast<Action *>(sender);
571 	if (!action)
572 		return;
573 
574 	m_openChatWithService->show();
575 
576 	kdebugf2();
577 }
578 
colorSelectorActionActivated(QAction * sender,bool toggled)579 void ChatWidgetActions::colorSelectorActionActivated(QAction *sender, bool toggled)
580 {
581 	Q_UNUSED(toggled)
582 
583 	ChatEditBox *chatEditBox = qobject_cast<ChatEditBox *>(sender->parent());
584 	if (!chatEditBox)
585 		return;
586 
587 	QList<QWidget *> widgets = sender->associatedWidgets();
588 	if (widgets.isEmpty())
589 		return;
590 
591 	chatEditBox->openColorSelector(widgets.at(widgets.size() - 1));
592 }
593 
594 #include "moc_chat-widget-actions.cpp"
595