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