1 /*
2     kopetechatwindow.cpp - Chat Window
3 
4     Copyright (c) 2008      by Benson Tsai           <btsai@vrwarp.com>
5     Copyright (c) 2007      by Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
6     Copyright (c) 2002-2006 by Olivier Goffart       <ogoffart@kde.org>
7     Copyright (c) 2003-2004 by Richard Smith         <kde@metafoo.co.uk>
8     Copyright (C) 2002      by James Grant
9     Copyright (c) 2002      by Stefan Gehn           <metz@gehn.net>
10     Copyright (c) 2002-2004 by Martijn Klingens      <klingens@kde.org>
11 
12     Kopete    (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
13 
14     *************************************************************************
15     *                                                                       *
16     * This program is free software; you can redistribute it and/or modify  *
17     * it under the terms of the GNU General Public License as published by  *
18     * the Free Software Foundation; either version 2 of the License, or     *
19     * (at your option) any later version.                                   *
20     *                                                                       *
21     *************************************************************************
22 */
23 
24 #include "kopetechatwindow.h"
25 
26 #include <QMenu>
27 #include <QIcon>
28 #include <QTimer>
29 #include <QFrame>
30 #include <QLabel>
31 #include <QPixmap>
32 #include <QLayout>
33 #include <QDockWidget>
34 #include <QTextStream>
35 #include <QCloseEvent>
36 #include <QVBoxLayout>
37 
38 #ifdef CHRONO
39 #include <QTime>
40 #endif
41 
42 #include <kactioncollection.h>
43 #include <kcursor.h>
44 #include <KLocalizedString>
45 #include <QMenuBar>
46 #include <kconfig.h>
47 #include <kiconloader.h>
48 #include "kopetechatwindow_debug.h"
49 #include <kwindowsystem.h>
50 #include <ktemporaryfile.h>
51 #include <kedittoolbar.h>
52 #include <QPushButton>
53 #include <ktabwidget.h>
54 #include <kdialog.h>
55 #include <kstringhandler.h>
56 #include <ksqueezedtextlabel.h>
57 #include <kstandardshortcut.h>
58 #include <kglobalsettings.h>
59 #include <kcolorscheme.h>
60 #include <khbox.h>
61 #include <kvbox.h>
62 #include <ktoolbar.h>
63 #include <KShortcut>
64 #include <kstandardaction.h>
65 #include <ktoggleaction.h>
66 #include <kactionmenu.h>
67 #include <ktoolbarspaceraction.h>
68 #include <QStatusBar>
69 
70 #include "chatmessagepart.h"
71 #include "chattexteditpart.h"
72 #include "chatview.h"
73 #include "kopeteapplication.h"
74 #include "kopetebehaviorsettings.h"
75 #include "kopeteemoticonaction.h"
76 #include "kopetegroup.h"
77 #include "kopetechatsession.h"
78 #include "kopetemetacontact.h"
79 #include "kopetepluginmanager.h"
80 #include "kopeteprotocol.h"
81 #include "kopetestdaction.h"
82 #include "kopeteviewmanager.h"
83 #include "chatmemberslistview.h"
84 #include "chatsessionmemberslistmodel.h"
85 
86 #include <qtoolbutton.h>
87 #include <kxmlguifactory.h>
88 #include <KTabBar>
89 #include <KSharedConfig>
90 
91 typedef QMap<Kopete::Account *, KopeteChatWindow *> AccountMap;
92 typedef QMap<Kopete::Group *, KopeteChatWindow *> GroupMap;
93 typedef QMap<Kopete::MetaContact *, KopeteChatWindow *> MetaContactMap;
94 typedef QList<KopeteChatWindow *> WindowList;
95 
96 using Kopete::ChatSessionMembersListModel;
97 
98 namespace {
99 AccountMap accountMap;
100 GroupMap groupMap;
101 MetaContactMap mcMap;
102 WindowList windows;
103 }
104 
window(Kopete::ChatSession * manager)105 KopeteChatWindow *KopeteChatWindow::window(Kopete::ChatSession *manager)
106 {
107     bool windowCreated = false;
108     KopeteChatWindow *myWindow = 0;
109 
110     //Take the first and the first? What else?
111     Kopete::Group *group = nullptr;
112     Kopete::ContactPtrList members = manager->members();
113     Kopete::MetaContact *metaContact = members.first()->metaContact();
114 
115     if (metaContact) {
116         Kopete::GroupList gList = metaContact->groups();
117         group = gList.first();
118     }
119 
120     switch (Kopete::BehaviorSettings::self()->chatWindowGroupPolicy()) {
121     //Open chats from the same protocol in the same window
122     case Kopete::BehaviorSettings::EnumChatWindowGroupPolicy::GroupByAccount:
123         if (accountMap.contains(manager->account())) {
124             myWindow = accountMap[ manager->account() ];
125         } else {
126             windowCreated = true;
127         }
128         break;
129 
130     //Open chats from the same group in the same window
131     case Kopete::BehaviorSettings::EnumChatWindowGroupPolicy::GroupByGroup:
132         if (group && groupMap.contains(group)) {
133             myWindow = groupMap[ group ];
134         } else {
135             windowCreated = true;
136         }
137         break;
138 
139     //Open chats from the same metacontact in the same window
140     case Kopete::BehaviorSettings::EnumChatWindowGroupPolicy::GroupByMetaContact:
141         if (mcMap.contains(metaContact)) {
142             myWindow = mcMap[ metaContact ];
143         } else {
144             windowCreated = true;
145         }
146         break;
147 
148     //Open all chats in the same window
149     case Kopete::BehaviorSettings::EnumChatWindowGroupPolicy::GroupAll:
150         if (windows.isEmpty()) {
151             windowCreated = true;
152         } else {
153             //Here we are finding the window with the most tabs and
154             //putting it there. Need this for the cases where config changes
155             //midstream
156 
157             int viewCount = -1;
158             WindowList::iterator it;
159             for (it = windows.begin(); it != windows.end(); ++it) {
160                 if ((*it)->chatViewCount() > viewCount) {
161                     myWindow = (*it);
162                     viewCount = (*it)->chatViewCount();
163                 }
164             }
165         }
166         break;
167 
168     //Open every chat in a new window
169     case Kopete::BehaviorSettings::EnumChatWindowGroupPolicy::OpenNewWindow:
170     default:
171         windowCreated = true;
172         break;
173     }
174 
175     if (windowCreated) {
176         myWindow = new KopeteChatWindow(manager->form());
177 
178         if (!accountMap.contains(manager->account())) {
179             accountMap.insert(manager->account(), myWindow);
180         }
181 
182         if (!mcMap.contains(metaContact)) {
183             mcMap.insert(metaContact, myWindow);
184         }
185 
186         if (group && !groupMap.contains(group)) {
187             groupMap.insert(group, myWindow);
188         }
189     }
190 
191 //	kDebug( 14010 ) << "Open Windows: " << windows.count();
192 
193     return myWindow;
194 }
195 
KopeteChatWindow(Kopete::ChatSession::Form form,QWidget * parent)196 KopeteChatWindow::KopeteChatWindow(Kopete::ChatSession::Form form, QWidget *parent)
197     : KXmlGuiWindow(parent)
198     , initialForm(form)
199 {
200 #ifdef CHRONO
201     QTime chrono;
202     chrono.start();
203 #endif
204     m_activeView = nullptr;
205     m_popupView = nullptr;
206     backgroundFile = nullptr;
207     updateBg = true;
208     m_tabBar = nullptr;
209 
210     m_participantsWidget = new QDockWidget(i18n("Participants"), this);
211     m_participantsWidget->setAllowedAreas(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea);
212     m_participantsWidget->setFeatures(QDockWidget::DockWidgetClosable);
213     m_participantsWidget->setTitleBarWidget(nullptr);
214     m_participantsWidget->setObjectName(QStringLiteral("Participants")); //object name is required for automatic position and settings save.
215 
216     ChatSessionMembersListModel *members_model = new ChatSessionMembersListModel(this);
217 
218     connect(this, SIGNAL(chatSessionChanged(Kopete::ChatSession*)), members_model, SLOT(setChatSession(Kopete::ChatSession*)));
219 
220     ChatMembersListView *chatmembers = new ChatMembersListView(m_participantsWidget);
221     chatmembers->setModel(members_model);
222     chatmembers->setWordWrap(true);
223     m_participantsWidget->setWidget(chatmembers);
224     initActions();
225 
226     addDockWidget(Qt::RightDockWidgetArea, m_participantsWidget);
227 
228     KVBox *vBox = new KVBox(this);
229     vBox->setLineWidth(0);
230     vBox->setSpacing(0);
231     vBox->setFrameStyle(QFrame::NoFrame);
232     // set default window size.  This could be removed by fixing the size hints of the contents
233     if (initialForm == Kopete::ChatSession::Chatroom) {
234         resize(650, 400);
235     } else {
236         m_participantsWidget->hide();
237         resize(400, 400);
238     }
239     setCentralWidget(vBox);
240 
241     mainArea = new QFrame(vBox);
242     mainArea->setLineWidth(0);
243     mainArea->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
244     mainLayout = new QVBoxLayout(mainArea);
245     mainLayout->setContentsMargins(0, 4, 0, 0);
246 
247     if (Kopete::BehaviorSettings::self()->chatWindowShowSendButton()) {
248         //Send Button
249         m_button_send = new QPushButton(i18nc("@action:button", "Send"), statusBar());
250         m_button_send->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
251         m_button_send->setEnabled(false);
252         m_button_send->setFont(statusBar()->font());
253         m_button_send->setFixedHeight(statusBar()->sizeHint().height());
254         connect(m_button_send, SIGNAL(clicked()), this, SLOT(slotSendMessage()));
255         statusBar()->addPermanentWidget(m_button_send, 0);
256     } else {
257         m_button_send = nullptr;
258     }
259 
260     m_status_text = new KSqueezedTextLabel(i18nc("@info:status", "Ready."), statusBar());
261     m_status_text->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
262     m_status_text->setFont(statusBar()->font());
263     m_status_text->setFixedHeight(statusBar()->sizeHint().height());
264     statusBar()->addWidget(m_status_text, 1);
265 
266     windows.append(this);
267     windowListChanged();
268 
269     m_alwaysShowTabs = KSharedConfig::openConfig()->group("ChatWindowSettings").
270                        readEntry(QStringLiteral("AlwaysShowTabs"), false);
271 //	kDebug( 14010 ) << "Open Windows: " << windows.count();
272 
273     setupGUI(static_cast<StandardWindowOptions>(ToolBar | Keys | StatusBar | Save | Create), QStringLiteral("kopetechatwindow.rc"));
274 
275     //has to be done after the setupGUI, in order to have the toolbar set up to restore window settings.
276     readOptions();
277 #ifdef CHRONO
278     qDebug()<<"TIME: "<<chrono.elapsed();
279 #endif
280 }
281 
~KopeteChatWindow()282 KopeteChatWindow::~KopeteChatWindow()
283 {
284     kDebug(14010);
285 
286     emit(closing(this));
287 
288     for (AccountMap::Iterator it = accountMap.begin(); it != accountMap.end();) {
289         if (it.value() == this) {
290             it = accountMap.erase(it);
291         } else {
292             ++it;
293         }
294     }
295 
296     for (GroupMap::Iterator it = groupMap.begin(); it != groupMap.end();) {
297         if (it.value() == this) {
298             it = groupMap.erase(it);
299         } else {
300             ++it;
301         }
302     }
303 
304     for (MetaContactMap::Iterator it = mcMap.begin(); it != mcMap.end();) {
305         if (it.value() == this) {
306             it = mcMap.erase(it);
307         } else {
308             ++it;
309         }
310     }
311 
312     windows.removeAt(windows.indexOf(this));
313     windowListChanged();
314 
315 //	kDebug( 14010 ) << "Open Windows: " << windows.count();
316 
317     saveOptions();
318 
319     delete backgroundFile;
320     delete anim;
321     delete animIcon;
322 }
323 
windowListChanged()324 void KopeteChatWindow::windowListChanged()
325 {
326     // update all windows' Move Tab to Window action
327     for (WindowList::iterator it = windows.begin(); it != windows.end(); ++it) {
328         (*it)->checkDetachEnable();
329     }
330 }
331 
slotTabContextMenu(QWidget * tab,const QPoint & pos)332 void KopeteChatWindow::slotTabContextMenu(QWidget *tab, const QPoint &pos)
333 {
334     m_popupView = static_cast<ChatView *>(tab);
335 
336     QMenu popup;
337     popup.addSection(KStringHandler::rsqueeze(m_popupView->caption()));
338     popup.addAction(actionContactMenu);
339     popup.addSeparator();
340     popup.addAction(actionTabPlacementMenu);
341     popup.addAction(tabDetach);
342     popup.addAction(actionDetachMenu);
343     popup.addAction(tabCloseAllOthers);
344     popup.addAction(tabClose);
345     popup.exec(pos);
346 
347     m_popupView = 0;
348 }
349 
activeView()350 ChatView *KopeteChatWindow::activeView()
351 {
352     return m_activeView;
353 }
354 
updateSendKeySequence()355 void KopeteChatWindow::updateSendKeySequence()
356 {
357     if (!sendMessage || !m_activeView) {
358         return;
359     }
360 
361     m_activeView->editPart()->textEdit()->setSendKeySequenceList(sendMessage->shortcuts());
362 }
363 
initActions(void)364 void KopeteChatWindow::initActions(void)
365 {
366     KActionCollection *coll = actionCollection();
367 
368     createStandardStatusBarAction();
369 
370     chatSend = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("&Send Message"), coll);
371     //Recuperate the qAction for later
372     sendMessage = coll->addAction(QStringLiteral("chat_send"), chatSend);
373     //Set up change signal in case the user changer the shortcut later
374     connect(sendMessage, SIGNAL(changed()), SLOT(updateSendKeySequence()));
375 
376     connect(chatSend, SIGNAL(triggered(bool)), SLOT(slotSendMessage()));
377     //Default to 'Return' and 'Enter' for sending messages
378     //'Return' is the key in the main part of the keyboard
379     //'Enter' is on the Numpad
380     QList<QKeySequence> chatSendShortcuts;
381     chatSendShortcuts << QKeySequence(Qt::Key_Return) << QKeySequence(Qt::Key_Enter);
382     coll->setDefaultShortcuts(chatSend, chatSendShortcuts);
383     chatSend->setEnabled(false);
384 
385     chatSendFile = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Send File..."), coll);
386     coll->addAction(QStringLiteral("chat_send_file"), chatSendFile);
387     connect(chatSendFile, SIGNAL(triggered(bool)), SLOT(slotSendFile()));
388     chatSendFile->setEnabled(false);
389 
390     KStandardAction::save(this, SLOT(slotChatSave()), coll);
391     KStandardAction::print(this, SLOT(slotChatPrint()), coll);
392     QAction *quitAction = KStandardAction::quit(this, SLOT(close()), coll);
393     quitAction->setText(i18n("Close All Chats"));
394 
395     tabClose = KStandardAction::close(this, SLOT(slotChatClosed()), coll);
396     coll->addAction(QStringLiteral("tabs_close"), tabClose);
397 
398     tabActive = new QAction(i18n("&Activate Next Active Tab"), coll);
399     coll->addAction(QStringLiteral("tabs_active"), tabActive);
400 //  tabActive->setShortcut( KStandardShortcut::tabNext() );
401     tabActive->setEnabled(false);
402     connect(tabActive, SIGNAL(triggered(bool)), this, SLOT(slotNextActiveTab()));
403 
404     tabRight = new QAction(i18n("&Activate Next Tab"), coll);
405     coll->addAction(QStringLiteral("tabs_right"), tabRight);
406     coll->setDefaultShortcuts(tabRight, KStandardShortcut::tabNext());
407     tabRight->setEnabled(false);
408     connect(tabRight, SIGNAL(triggered(bool)), this, SLOT(slotNextTab()));
409 
410     tabLeft = new QAction(i18n("&Activate Previous Tab"), coll);
411     coll->addAction(QStringLiteral("tabs_left"), tabLeft);
412     coll->setDefaultShortcuts(tabLeft, KStandardShortcut::tabPrev());
413     tabLeft->setEnabled(false);
414     connect(tabLeft, SIGNAL(triggered(bool)), this, SLOT(slotPreviousTab()));
415 
416     // This action exists mostly so that the shortcut is configurable.
417     // The actual "slot" is the eventFilter.
418     nickComplete = new QAction(i18n("Nic&k Completion"), coll);
419     coll->addAction(QStringLiteral("nick_complete"), nickComplete);
420     coll->setDefaultShortcut(nickComplete, QKeySequence(Qt::Key_Tab));
421 
422     tabDetach = new QAction(QIcon::fromTheme(QStringLiteral("tab-detach")), i18n("&Detach Chat"), coll);
423     coll->addAction(QStringLiteral("tabs_detach"), tabDetach);
424     tabDetach->setEnabled(false);
425     connect(tabDetach, SIGNAL(triggered(bool)), this, SLOT(slotDetachChat()));
426 
427     tabCloseAllOthers = new QAction(QIcon::fromTheme(QStringLiteral("tab-close")), i18n("Close &All But This Tab"), coll);
428     coll->addAction(QStringLiteral("tabs_close_others"), tabCloseAllOthers);
429     tabCloseAllOthers->setEnabled(true);
430     connect(tabCloseAllOthers, SIGNAL(triggered(bool)), this, SLOT(slotCloseAllOtherTabs()));
431 
432     actionDetachMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("tab-detach")), i18n("&Move Tab to Window"), coll);
433     coll->addAction(QStringLiteral("tabs_detachmove"), actionDetachMenu);
434     actionDetachMenu->setDelayed(false);
435 
436     connect(actionDetachMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotPrepareDetachMenu()));
437     connect(actionDetachMenu->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotDetachChat(QAction*)));
438 
439     actionTabPlacementMenu = new KActionMenu(i18n("&Tab Placement"), coll);
440     coll->addAction(QStringLiteral("tabs_placement"), actionTabPlacementMenu);
441     connect(actionTabPlacementMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotPreparePlacementMenu()));
442     connect(actionTabPlacementMenu->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotPlaceTabs(QAction*)));
443 
444     coll->setDefaultShortcut(tabDetach, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B));
445 
446     KStandardAction::cut(this, SLOT(slotCut()), coll);
447     KStandardAction::copy(this, SLOT(slotCopy()), coll);
448     KStandardAction::paste(this, SLOT(slotPaste()), coll);
449 
450     QAction *action;
451 
452     historyUp = new QAction(i18n("Previous History"), coll);
453     coll->addAction(QStringLiteral("history_up"), historyUp);
454     coll->setDefaultShortcut(historyUp, QKeySequence(Qt::CTRL + Qt::Key_Up));
455     connect(historyUp, SIGNAL(triggered(bool)), this, SLOT(slotHistoryUp()));
456 
457     historyDown = new QAction(i18n("Next History"), coll);
458     coll->addAction(QStringLiteral("history_down"), historyDown);
459     coll->setDefaultShortcut(historyDown, QKeySequence(Qt::CTRL + Qt::Key_Down));
460     connect(historyDown, SIGNAL(triggered(bool)), this, SLOT(slotHistoryDown()));
461 
462     action = KStandardAction::prior(this, SLOT(slotPageUp()), coll);
463     coll->addAction(QStringLiteral("scroll_up"), action);
464     action = KStandardAction::next(this, SLOT(slotPageDown()), coll);
465     coll->addAction(QStringLiteral("scroll_down"), action);
466 
467     KStandardAction::showMenubar(menuBar(), SLOT(setVisible(bool)), coll);
468 
469     toggleAutoSpellCheck = new KToggleAction(i18n("Automatic Spell Checking"), coll);
470     coll->addAction(QStringLiteral("enable_auto_spell_check"), toggleAutoSpellCheck);
471     toggleAutoSpellCheck->setChecked(true);
472     connect(toggleAutoSpellCheck, SIGNAL(triggered(bool)), this, SLOT(toggleAutoSpellChecking()));
473 
474     QAction *toggleParticipantsAction = m_participantsWidget->toggleViewAction();
475     toggleParticipantsAction->setText(i18n("Show Participants"));
476     toggleParticipantsAction->setIconText(i18n("Participants"));
477     toggleParticipantsAction->setIcon(QIcon::fromTheme(QStringLiteral("system-users")));
478     coll->addAction(QStringLiteral("show_participants_widget"), toggleParticipantsAction);
479 
480     actionSmileyMenu = new KopeteEmoticonAction(coll);
481     coll->addAction(QStringLiteral("format_smiley"), actionSmileyMenu);
482     actionSmileyMenu->setDelayed(false);
483     connect(actionSmileyMenu, SIGNAL(activated(QString)), this, SLOT(slotSmileyActivated(QString)));
484 
485     actionContactMenu = new KActionMenu(i18n("Co&ntacts"), coll);
486     coll->addAction(QStringLiteral("contacts_menu"), actionContactMenu);
487     actionContactMenu->setDelayed(false);
488     connect(actionContactMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotPrepareContactMenu()));
489 
490     KopeteStdAction::preferences(coll, "settings_prefs");
491 
492     KToolBarSpacerAction *spacer = new KToolBarSpacerAction(coll);
493     coll->addAction(QStringLiteral("spacer"), spacer);
494 
495     //The Sending movie
496     normalIcon = QPixmap(BarIcon(QStringLiteral("kopete")));
497 
498     // we can't set the tool bar as parent, if we do, it will be deleted when we configure toolbars
499     anim = new QLabel(QString(), 0L);
500     anim->setObjectName(QStringLiteral("kde toolbar widget"));
501     anim->setMargin(5);
502     anim->setPixmap(normalIcon);
503 
504     animIcon = KIconLoader::global()->loadMovie(QStringLiteral("newmessage"), KIconLoader::Toolbar);
505     if (animIcon) {
506         animIcon->setPaused(true);
507     }
508 
509     QWidgetAction *animAction = new QWidgetAction(coll);
510     animAction->setText(i18n("Toolbar Animation"));
511     animAction->setDefaultWidget(anim);
512     coll->addAction(QStringLiteral("toolbar_animation"), animAction);
513 
514     //toolBar()->insertWidget( 99, anim->width(), anim );
515     //toolBar()->alignItemRight( 99 );
516 }
517 
518 /*
519 const QString KopeteChatWindow::fileContents( const QString &path ) const
520 {
521     QString contents;
522     QFile file( path );
523     if ( file.open( QIODevice::ReadOnly ) )
524     {
525         QTextStream stream( &file );
526         contents = stream.readAll();
527         file.close();
528     }
529 
530     return contents;
531 }
532 */
slotStopAnimation(ChatView * view)533 void KopeteChatWindow::slotStopAnimation(ChatView *view)
534 {
535     if (view == m_activeView) {
536         anim->setPixmap(normalIcon);
537         if (animIcon && animIcon->state() == QMovie::Running) {
538             animIcon->setPaused(true);
539         }
540     }
541 }
542 
slotUpdateSendEnabled()543 void KopeteChatWindow::slotUpdateSendEnabled()
544 {
545     if (!m_activeView) {
546         return;
547     }
548 
549     bool enabled = m_activeView->canSend();
550     chatSend->setEnabled(enabled);
551     if (m_button_send) {
552         m_button_send->setEnabled(enabled);
553     }
554 }
555 
updateChatSendFileAction()556 void KopeteChatWindow::updateChatSendFileAction()
557 {
558     if (!m_activeView) {
559         return;
560     }
561 
562     chatSendFile->setEnabled(m_activeView->canSendFile());
563 }
564 
toggleAutoSpellChecking()565 void KopeteChatWindow::toggleAutoSpellChecking()
566 {
567     if (!m_activeView) {
568         return;
569     }
570 
571     bool currentSetting = m_activeView->editPart()->checkSpellingEnabled();
572     m_activeView->editPart()->setCheckSpellingEnabled(!currentSetting);
573     updateSpellCheckAction();
574 }
575 
updateSpellCheckAction()576 void KopeteChatWindow::updateSpellCheckAction()
577 {
578     if (!m_activeView) {
579         return;
580     }
581 
582     bool currentSetting = m_activeView->editPart()->checkSpellingEnabled();
583     toggleAutoSpellCheck->setChecked(currentSetting);
584 }
585 
enableSpellCheckAction(bool enable)586 void KopeteChatWindow::enableSpellCheckAction(bool enable)
587 {
588     toggleAutoSpellCheck->setChecked(enable);
589 }
590 
updateActions()591 void KopeteChatWindow::updateActions()
592 {
593     updateSpellCheckAction();
594     updateChatSendFileAction();
595 }
596 
slotHistoryUp()597 void KopeteChatWindow::slotHistoryUp()
598 {
599     if (m_activeView) {
600         m_activeView->editPart()->historyUp();
601     }
602 }
603 
slotHistoryDown()604 void KopeteChatWindow::slotHistoryDown()
605 {
606     if (m_activeView) {
607         m_activeView->editPart()->historyDown();
608     }
609 }
610 
slotPageUp()611 void KopeteChatWindow::slotPageUp()
612 {
613     if (m_activeView) {
614         m_activeView->messagePart()->pageUp();
615     }
616 }
617 
slotPageDown()618 void KopeteChatWindow::slotPageDown()
619 {
620     if (m_activeView) {
621         m_activeView->messagePart()->pageDown();
622     }
623 }
624 
slotCut()625 void KopeteChatWindow::slotCut()
626 {
627     m_activeView->cut();
628 }
629 
slotCopy()630 void KopeteChatWindow::slotCopy()
631 {
632     m_activeView->copy();
633 }
634 
slotPaste()635 void KopeteChatWindow::slotPaste()
636 {
637     m_activeView->paste();
638 }
639 
slotResetFontAndColor()640 void KopeteChatWindow::slotResetFontAndColor()
641 {
642     m_activeView->resetFontAndColor();
643 }
644 
setStatus(const QString & text)645 void KopeteChatWindow::setStatus(const QString &text)
646 {
647     m_status_text->setText(text);
648 }
649 
testCanDecode(const QDragMoveEvent * event,bool & accept)650 void KopeteChatWindow::testCanDecode(const QDragMoveEvent *event, bool &accept)
651 {
652     if (m_tabBar && qobject_cast<KTabBar *>(m_tabBar->childAt(event->pos()))
653         && chatViewList[static_cast<KTabBar *>(m_tabBar->childAt(event->pos()))->selectTab(event->pos())]->isDragEventAccepted(event)) {
654         accept = true;
655     } else {
656         accept = false;
657     }
658 }
659 
receivedDropEvent(QWidget * w,QDropEvent * e)660 void KopeteChatWindow::receivedDropEvent(QWidget *w, QDropEvent *e)
661 {
662     m_tabBar->setCurrentWidget(w);
663     activeView()->dropEvent(e);
664 }
665 
createTabBar()666 void KopeteChatWindow::createTabBar()
667 {
668     if (!m_tabBar) {
669         KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("ChatWindowSettings"));
670 
671         m_tabBar = new KTabWidget(mainArea);
672         m_tabBar->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
673         m_tabBar->setTabsClosable(cg.readEntry(QStringLiteral("HoverClose"), true));
674         m_tabBar->setMovable(true);
675         m_tabBar->setAutomaticResizeTabs(true);
676         connect(m_tabBar, SIGNAL(closeRequest(QWidget*)), this, SLOT(slotCloseChat(QWidget*)));
677 
678         m_UpdateChatLabel = cg.readEntry(QStringLiteral("ShowContactName"), true);
679 
680         QToolButton *m_rightWidget = new QToolButton(m_tabBar);
681         connect(m_rightWidget, SIGNAL(clicked()), this, SLOT(slotChatClosed()));
682         m_rightWidget->setIcon(SmallIcon("tab-close"));
683         m_rightWidget->adjustSize();
684         m_rightWidget->setToolTip(i18nc("@info:tooltip", "Close the current tab"));
685         m_tabBar->setCornerWidget(m_rightWidget, Qt::TopRightCorner);
686 
687         mainLayout->addWidget(m_tabBar);
688         m_tabBar->show();
689 
690         for (ChatViewList::iterator it = chatViewList.begin(); it != chatViewList.end(); ++it) {
691             addTab(*it);
692         }
693 
694         connect(m_tabBar, SIGNAL(testCanDecode(const QDragMoveEvent*,bool&)), this, SLOT(testCanDecode(const QDragMoveEvent*,bool&)));
695         connect(m_tabBar, SIGNAL(receivedDropEvent(QWidget*,QDropEvent*)), this, SLOT(receivedDropEvent(QWidget*,QDropEvent*)));
696         connect(m_tabBar, SIGNAL(currentChanged(QWidget*)), this, SLOT(setActiveView(QWidget*)));
697         connect(m_tabBar, SIGNAL(contextMenu(QWidget*,QPoint)), this, SLOT(slotTabContextMenu(QWidget*,QPoint)));
698 
699         if (m_activeView) {
700             m_tabBar->setCurrentWidget(m_activeView);
701         } else {
702             setActiveView(chatViewList.first());
703         }
704 
705         int tabPosition = cg.readEntry(QStringLiteral("Tab Placement"), 0);
706 
707         QAction action(this);
708         action.setData(tabPosition);
709         slotPlaceTabs(&action);
710     }
711 }
712 
slotCloseChat(QWidget * chatView)713 void KopeteChatWindow::slotCloseChat(QWidget *chatView)
714 {
715     static_cast<ChatView *>(chatView)->closeView();
716     //FIXME: check if we need to remove from the chatViewList
717 }
718 
addTab(ChatView * view)719 void KopeteChatWindow::addTab(ChatView *view)
720 {
721     QList<Kopete::Contact *> chatMembers = view->msgManager()->members();
722     Kopete::Contact *c = nullptr;
723     foreach (Kopete::Contact *contact, chatMembers) {
724         if (!c || c->onlineStatus() < contact->onlineStatus()) {
725             c = contact;
726         }
727     }
728     QIcon pluginIcon = c ? view->msgManager()->contactOnlineStatus(c).iconFor(c)
729                        : QIcon::fromTheme(view->msgManager()->protocol()->pluginIcon());
730 
731     view->setParent(m_tabBar);
732     view->setWindowFlags(0);
733     view->move(QPoint());
734     view->show();
735 
736     m_tabBar->addTab(view, pluginIcon, QLatin1String(""));
737     view->setVisible(view == m_activeView);
738     connect(view, SIGNAL(updateStatusIcon(ChatView*)), this, SLOT(slotUpdateCaptionIcons(ChatView*)));
739 
740     if (m_UpdateChatLabel) {
741         connect(view, SIGNAL(captionChanged(bool)), this, SLOT(updateChatLabel()));
742         view->setCaption(view->caption(), false);
743     }
744 }
745 
setPrimaryChatView(ChatView * view)746 void KopeteChatWindow::setPrimaryChatView(ChatView *view)
747 {
748     //TODO figure out what else we have to save here besides the font
749     //reparent clears a lot of stuff out
750     view->setParent(mainArea);
751     view->setWindowFlags(0);
752     view->move(QPoint());
753     view->show();
754 
755     mainLayout->addWidget(view);
756     setActiveView(view);
757 }
758 
deleteTabBar()759 void KopeteChatWindow::deleteTabBar()
760 {
761     if (m_tabBar) {
762         disconnect(m_tabBar, SIGNAL(currentChanged(QWidget*)), this, SLOT(setActiveView(QWidget*)));
763         disconnect(m_tabBar, SIGNAL(contextMenu(QWidget*,QPoint)), this, SLOT(slotTabContextMenu(QWidget*,QPoint)));
764 
765         if (!chatViewList.isEmpty()) {
766             setPrimaryChatView(chatViewList.first());
767         }
768 
769         m_tabBar->deleteLater();
770         m_tabBar = nullptr;
771     }
772 }
773 
attachChatView(ChatView * newView)774 void KopeteChatWindow::attachChatView(ChatView *newView)
775 {
776     chatViewList.append(newView);
777 
778     if (!m_alwaysShowTabs && chatViewList.count() == 1) {
779         setPrimaryChatView(newView);
780     } else {
781         if (!m_tabBar) {
782             createTabBar();
783         } else {
784             addTab(newView);
785         }
786         newView->setActive(false);
787     }
788 
789     newView->setMainWindow(this);
790     newView->editWidget()->installEventFilter(this);
791 
792     KCursor::setAutoHideCursor(newView->editWidget(), true, true);
793     connect(newView, SIGNAL(captionChanged(bool)), this, SLOT(slotSetCaption(bool)));
794     connect(newView, SIGNAL(messageSuccess(ChatView*)), this, SLOT(slotStopAnimation(ChatView*)));
795     connect(newView, SIGNAL(updateStatusIcon(ChatView*)), this, SLOT(slotUpdateCaptionIcons(ChatView*)));
796 
797     if (m_UpdateChatLabel) {
798         connect(newView, SIGNAL(updateChatState(ChatView*,int)), this, SLOT(updateChatState(ChatView*,int)));
799     }
800 
801     updateActions();
802     checkDetachEnable();
803     connect(newView, SIGNAL(autoSpellCheckEnabled(ChatView*,bool)),
804             this, SLOT(slotAutoSpellCheckEnabled(ChatView*,bool)));
805 }
806 
checkDetachEnable()807 void KopeteChatWindow::checkDetachEnable()
808 {
809     bool haveTabs = (chatViewList.count() > 1);
810     tabCloseAllOthers->setEnabled(haveTabs);
811     tabDetach->setEnabled(haveTabs);
812     tabLeft->setEnabled(haveTabs);
813     tabRight->setEnabled(haveTabs);
814     tabActive->setEnabled(haveTabs);
815     actionTabPlacementMenu->setEnabled(m_tabBar != 0);
816 
817     bool otherWindows = (windows.count() > 1);
818     actionDetachMenu->setEnabled(otherWindows);
819 }
820 
detachChatView(ChatView * view)821 void KopeteChatWindow::detachChatView(ChatView *view)
822 {
823     chatViewList.removeAt(chatViewList.indexOf(view));
824 
825     disconnect(view, SIGNAL(captionChanged(bool)), this, SLOT(slotSetCaption(bool)));
826     disconnect(view, SIGNAL(updateStatusIcon(ChatView*)), this, SLOT(slotUpdateCaptionIcons(ChatView*)));
827     disconnect(view, SIGNAL(updateChatState(ChatView*,int)), this, SLOT(updateChatState(ChatView*,int)));
828     view->editWidget()->removeEventFilter(this);
829 
830     if (m_tabBar) {
831         int curPage = m_tabBar->currentIndex();
832         QWidget *page = m_tabBar->currentWidget();
833 
834         // if the current view is to be detached, switch to a different one
835         if (page == view) {
836             if (curPage > 0) {
837                 m_tabBar->setCurrentIndex(curPage - 1);
838             } else {
839                 m_tabBar->setCurrentIndex(curPage + 1);
840             }
841         }
842 
843         m_tabBar->removePage(view);
844 
845         if (m_tabBar->currentWidget()) {
846             setActiveView(static_cast<ChatView *>(m_tabBar->currentWidget()));
847         }
848     }
849 
850     if (m_activeView == view) {
851         m_activeView = 0;
852     }
853 
854     if (chatViewList.isEmpty()) {
855         close();
856     } else if (!m_alwaysShowTabs && chatViewList.count() == 1) {
857         deleteTabBar();
858     }
859 
860     checkDetachEnable();
861 }
862 
slotDetachChat(QAction * action)863 void KopeteChatWindow::slotDetachChat(QAction *action)
864 {
865     KopeteChatWindow *newWindow = nullptr;
866     ChatView *detachedView;
867 
868     if (m_popupView) {
869         detachedView = m_popupView;
870     } else {
871         detachedView = m_activeView;
872     }
873 
874     if (!detachedView) {
875         return;
876     }
877 
878     //if we don't do this, we might crash
879     createGUI(nullptr);
880     guiFactory()->removeClient(detachedView->msgManager());
881 
882     if (!action) {
883         newWindow = new KopeteChatWindow(detachedView->msgManager()->form());
884         newWindow->setObjectName(QStringLiteral("KopeteChatWindow"));
885     } else {
886         newWindow = windows.at(action->data().toInt());
887     }
888 
889     newWindow->show();
890     newWindow->raise();
891 
892     detachChatView(detachedView);
893     newWindow->attachChatView(detachedView);
894 }
895 
slotCloseAllOtherTabs()896 void KopeteChatWindow::slotCloseAllOtherTabs()
897 {
898     ChatView *detachedView;
899 
900     if (m_popupView) {
901         detachedView = m_popupView;
902     } else {
903         detachedView = m_activeView;
904     }
905 
906     foreach (ChatView *view, chatViewList) {
907         if (view != detachedView) {
908             view->closeView();
909         }
910     }
911 }
912 
slotPreviousTab()913 void KopeteChatWindow::slotPreviousTab()
914 {
915     int curPage = m_tabBar->currentIndex();
916     if (curPage > 0) {
917         m_tabBar->setCurrentIndex(curPage - 1);
918     } else {
919         m_tabBar->setCurrentIndex(m_tabBar->count() - 1);
920     }
921 }
922 
slotNextTab()923 void KopeteChatWindow::slotNextTab()
924 {
925     int curPage = m_tabBar->currentIndex();
926     if (curPage == (m_tabBar->count() - 1)) {
927         m_tabBar->setCurrentIndex(0);
928     } else {
929         m_tabBar->setCurrentIndex(curPage + 1);
930     }
931 }
932 
slotNextActiveTab()933 void KopeteChatWindow::slotNextActiveTab()
934 {
935     int curPage = m_tabBar->currentIndex();
936     for (int i = (curPage+1) % m_tabBar->count(); i != curPage; i = (i+1) % m_tabBar->count()) {
937         ChatView *v = static_cast<ChatView *>(m_tabBar->widget(i)); //We assume we only have ChatView's
938         if (v->tabState() == ChatView::Highlighted || v->tabState() == ChatView::Message) {
939             m_tabBar->setCurrentIndex(i);
940             break;
941         }
942     }
943 }
944 
slotSetCaption(bool active)945 void KopeteChatWindow::slotSetCaption(bool active)
946 {
947     if (active && m_activeView) {
948         setCaption(m_activeView->caption(), false);
949     }
950 }
951 
updateBackground(const QPixmap & pm)952 void KopeteChatWindow::updateBackground(const QPixmap &pm)
953 {
954     if (updateBg) {
955         updateBg = false;
956         delete backgroundFile;
957 
958         backgroundFile = new KTemporaryFile();
959         backgroundFile->setSuffix(QStringLiteral(".bmp"));
960         backgroundFile->open();
961         pm.save(backgroundFile, "BMP");
962         QTimer::singleShot(100, this, SLOT(slotEnableUpdateBg()));
963     }
964 }
965 
setActiveView(QWidget * widget)966 void KopeteChatWindow::setActiveView(QWidget *widget)
967 {
968     ChatView *view = static_cast<ChatView *>(widget);
969 
970     if (m_activeView == view) {
971         return;
972     }
973 
974     if (m_activeView) {
975         disconnect(m_activeView->editWidget(), SIGNAL(checkSpellingChanged(bool)), this, SLOT(enableSpellCheckAction(bool)));
976         disconnect(m_activeView, SIGNAL(canSendChanged(bool)), this, SLOT(slotUpdateSendEnabled()));
977         disconnect(m_activeView, SIGNAL(canAcceptFilesChanged()), this, SLOT(updateChatSendFileAction()));
978         guiFactory()->removeClient(m_activeView->msgManager());
979         m_activeView->saveChatSettings();
980     }
981 
982     if (view != 0) {
983         guiFactory()->addClient(view->msgManager());
984     }
985 //  createGUI( view->editPart() );
986 
987     if (m_activeView) {
988         m_activeView->setActive(false);
989     }
990 
991     m_activeView = view;
992 
993     if (view == 0) {
994         return;
995     }
996 
997     if (chatViewList.indexOf(view) == -1) {
998         attachChatView(view);
999     }
1000 
1001     connect(m_activeView->editWidget(), SIGNAL(checkSpellingChanged(bool)), this, SLOT(enableSpellCheckAction(bool)));
1002     connect(m_activeView, SIGNAL(canSendChanged(bool)), this, SLOT(slotUpdateSendEnabled()));
1003     connect(m_activeView, SIGNAL(canAcceptFilesChanged()), this, SLOT(updateChatSendFileAction()));
1004 
1005     //Tell it it is active
1006     m_activeView->setActive(true);
1007 
1008     //Update icons to match
1009     slotUpdateCaptionIcons(m_activeView);
1010 
1011     if (m_activeView->sendInProgress() && animIcon) {
1012         anim->setMovie(animIcon);
1013         animIcon->setPaused(false);
1014     } else {
1015         anim->setPixmap(normalIcon);
1016         if (animIcon) {
1017             animIcon->setPaused(true);
1018         }
1019     }
1020 
1021     if (m_alwaysShowTabs || chatViewList.count() > 1) {
1022         if (!m_tabBar) {
1023             createTabBar();
1024         }
1025 
1026         m_tabBar->setCurrentWidget(m_activeView);
1027     }
1028 
1029     setCaption(m_activeView->caption());
1030     setStatus(m_activeView->statusText());
1031     m_activeView->setFocus();
1032     updateActions();
1033     slotUpdateSendEnabled();
1034     m_activeView->loadChatSettings();
1035     updateSendKeySequence();
1036 
1037     emit chatSessionChanged(m_activeView->msgManager());
1038 }
1039 
slotUpdateCaptionIcons(ChatView * view)1040 void KopeteChatWindow::slotUpdateCaptionIcons(ChatView *view)
1041 {
1042     if (!view) {
1043         return; //(pas de charité)
1044     }
1045     QList<Kopete::Contact *> chatMembers = view->msgManager()->members();
1046     Kopete::Contact *c = nullptr;
1047     foreach (Kopete::Contact *contact, chatMembers) {
1048         if (!c || c->onlineStatus() < contact->onlineStatus()) {
1049             c = contact;
1050         }
1051     }
1052 
1053     if (view == m_activeView) {
1054         setWindowIcon(c ? view->msgManager()->contactOnlineStatus(c).iconFor(c)
1055                       : QIcon::fromTheme(view->msgManager()->protocol()->pluginIcon()));
1056     }
1057 
1058     if (m_tabBar) {
1059         m_tabBar->setTabIcon(m_tabBar->indexOf(view), c ? view->msgManager()->contactOnlineStatus(c).iconFor(c)
1060                              : QIcon::fromTheme(view->msgManager()->protocol()->pluginIcon()));
1061     }
1062 }
1063 
slotChatClosed()1064 void KopeteChatWindow::slotChatClosed()
1065 {
1066     if (m_popupView) {
1067         m_popupView->closeView();
1068     } else {
1069         m_activeView->closeView();
1070     }
1071 }
1072 
slotPrepareDetachMenu(void)1073 void KopeteChatWindow::slotPrepareDetachMenu(void)
1074 {
1075     QMenu *detachMenu = actionDetachMenu->menu();
1076     detachMenu->clear();
1077 
1078     QAction *action;
1079     for (int id = 0; id < windows.count(); id++) {
1080         KopeteChatWindow *win = windows.at(id);
1081         if (win != this) {
1082             action = detachMenu->addAction(win->windowIcon(), win->windowTitle());
1083             action->setData(id);
1084         }
1085     }
1086 }
1087 
slotSendMessage()1088 void KopeteChatWindow::slotSendMessage()
1089 {
1090     if (m_activeView && m_activeView->canSend()) {
1091         if (animIcon) {
1092             anim->setMovie(animIcon);
1093             animIcon->setPaused(false);
1094         }
1095         m_activeView->sendMessage();
1096     }
1097 }
1098 
slotSendFile()1099 void KopeteChatWindow::slotSendFile()
1100 {
1101     if (m_activeView) {
1102         m_activeView->sendFile();
1103     }
1104 }
1105 
slotPrepareContactMenu(void)1106 void KopeteChatWindow::slotPrepareContactMenu(void)
1107 {
1108     QMenu *contactsMenu = actionContactMenu->menu();
1109     contactsMenu->clear();
1110 
1111     Kopete::ContactPtrList m_them;
1112 
1113     if (m_popupView) {
1114         m_them = m_popupView->msgManager()->members();
1115     } else {
1116         m_them = m_activeView->msgManager()->members();
1117     }
1118 
1119     //TODO: don't display a menu with one contact in it, display that
1120     // contact's menu instead. Will require changing text and icon of
1121     // 'Contacts' action, or something cleverer.
1122     uint contactCount = 0;
1123 
1124     foreach (Kopete::Contact *contact, m_them) {
1125         QMenu *p = contact->popupMenu();
1126         connect(actionContactMenu->menu(), SIGNAL(aboutToHide()),
1127                 p, SLOT(deleteLater()));
1128 
1129         p->setIcon(contact->onlineStatus().iconFor(contact));
1130         if (contact->metaContact()) {
1131             p->setTitle(contact->metaContact()->displayName());
1132         } else {
1133             p->setTitle(contact->contactId());
1134         }
1135 
1136         contactsMenu->addMenu(p);
1137 
1138         //FIXME: This number should be a config option
1139         if (++contactCount == 15 && contact != m_them.last()) {
1140             KActionMenu *moreMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("More..."), this);
1141             connect(actionContactMenu->menu(), SIGNAL(aboutToHide()),
1142                     moreMenu, SLOT(deleteLater()));
1143             contactsMenu->addAction(moreMenu);
1144             contactsMenu = moreMenu->menu();
1145             contactCount = 0;
1146         }
1147     }
1148 }
1149 
slotPreparePlacementMenu()1150 void KopeteChatWindow::slotPreparePlacementMenu()
1151 {
1152     QMenu *placementMenu = actionTabPlacementMenu->menu();
1153     placementMenu->clear();
1154 
1155     QAction *action;
1156     action = placementMenu->addAction(i18n("Top"));
1157     action->setData(0);
1158 
1159     action = placementMenu->addAction(i18n("Bottom"));
1160     action->setData(1);
1161 
1162     action = placementMenu->addAction(i18n("Left"));
1163     action->setData(2);
1164 
1165     action = placementMenu->addAction(i18n("Right"));
1166     action->setData(3);
1167 }
1168 
slotPlaceTabs(QAction * action)1169 void KopeteChatWindow::slotPlaceTabs(QAction *action)
1170 {
1171     int placement = action->data().toInt();
1172 
1173     if (m_tabBar) {
1174         switch (placement) {
1175         case 1:
1176             m_tabBar->setTabPosition(QTabWidget::South);
1177             break;
1178         case 2:
1179             m_tabBar->setTabPosition(QTabWidget::West);
1180             break;
1181         case 3:
1182             m_tabBar->setTabPosition(QTabWidget::East);
1183             break;
1184         default:
1185             m_tabBar->setTabPosition(QTabWidget::North);
1186         }
1187         saveOptions();
1188     }
1189 }
1190 
readOptions()1191 void KopeteChatWindow::readOptions()
1192 {
1193     // load and apply config file settings affecting the appearance of the UI
1194 //	kDebug(14010) ;
1195     applyMainWindowSettings(KSharedConfig::openConfig()->group((initialForm
1196                                                                 == Kopete::ChatSession::Chatroom ? QStringLiteral("KopeteChatWindowGroupMode") : QStringLiteral("KopeteChatWindowIndividualMode"))));
1197     //config->setGroup( QLatin1String("ChatWindowSettings") );
1198 }
1199 
saveOptions()1200 void KopeteChatWindow::saveOptions()
1201 {
1202 //	kDebug(14010) ;
1203 
1204     KConfigGroup kopeteChatWindowMainWinSettings(KSharedConfig::openConfig(), (initialForm == Kopete::ChatSession::Chatroom ? QStringLiteral("KopeteChatWindowGroupMode") : QStringLiteral(
1205                                                                                    "KopeteChatWindowIndividualMode")));
1206 
1207     // saves menubar,toolbar and statusbar setting
1208     saveMainWindowSettings(kopeteChatWindowMainWinSettings);
1209     if (m_tabBar) {
1210         KConfigGroup chatWindowSettings(KSharedConfig::openConfig(), QStringLiteral("ChatWindowSettings"));
1211         chatWindowSettings.writeEntry(QStringLiteral("Tab Placement"), (int)m_tabBar->tabPosition());
1212         chatWindowSettings.sync();
1213     }
1214     kopeteChatWindowMainWinSettings.sync();
1215 }
1216 
slotChatSave()1217 void KopeteChatWindow::slotChatSave()
1218 {
1219 //	kDebug(14010) << "KopeteChatWindow::slotChatSave()";
1220     if (isActiveWindow() && m_activeView) {
1221         m_activeView->messagePart()->save();
1222     }
1223 }
1224 
changeEvent(QEvent * e)1225 void KopeteChatWindow::changeEvent(QEvent *e)
1226 {
1227     if (e->type() == QEvent::ActivationChange && isActiveWindow() && m_activeView) {
1228         m_activeView->setActive(true);
1229     }
1230 }
1231 
slotChatPrint()1232 void KopeteChatWindow::slotChatPrint()
1233 {
1234     m_activeView->messagePart()->print();
1235 }
1236 
slotSmileyActivated(const QString & sm)1237 void KopeteChatWindow::slotSmileyActivated(const QString &sm)
1238 {
1239     if (!sm.isNull()) {
1240         m_activeView->addText(' ' + sm + ' ');
1241     }
1242     //we are adding space around the emoticon becasue our parser only display emoticons not in a word.
1243 }
1244 
slotAutoSpellCheckEnabled(ChatView * view,bool isEnabled)1245 void KopeteChatWindow::slotAutoSpellCheckEnabled(ChatView *view, bool isEnabled)
1246 {
1247     if (view != m_activeView) {
1248         return;
1249     }
1250 
1251     toggleAutoSpellCheck->setChecked(isEnabled);
1252     m_activeView->editPart()->setCheckSpellingEnabled(isEnabled);
1253 }
1254 
queryClose()1255 bool KopeteChatWindow::queryClose()
1256 {
1257 #ifdef CHRONO
1258     QTime chrono;
1259     chrono.start();
1260 #endif
1261     bool canClose = true;
1262 
1263 //	kDebug( 14010 ) << " Windows left open:";
1264 //	for( QPtrListIterator<ChatView> it( chatViewList ); it; ++it)
1265 //		kDebug( 14010 ) << "  " << *it << " (" << (*it)->caption() << ")";
1266     setUpdatesEnabled(false);//hide the crazyness from users
1267     while (!chatViewList.isEmpty())
1268     {
1269         ChatView *view = chatViewList.takeFirst();
1270 
1271         // FIXME: This should only check if it *can* close
1272         // and not start closing if the close can be aborted halfway, it would
1273         // leave us with half the chats open and half of them closed. - Martijn
1274 
1275         // if the view is closed, it is removed from chatViewList for us
1276         if (!view->closeView()) {
1277             qDebug() << "Closing view failed!";
1278             canClose = false;
1279         }
1280     }
1281     setUpdatesEnabled(true);
1282 #ifdef CHRONO
1283     qDebug()<<"TIME: "<<chrono.elapsed();
1284 #endif
1285     return canClose;
1286 }
1287 
queryExit()1288 bool KopeteChatWindow::queryExit()
1289 {
1290     KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
1291     if (app->isSavingSession()
1292         || app->isShuttingDown() /* only set if KopeteApplication::quitKopete() or
1293                                     KopeteApplication::commitData() called */
1294         || !Kopete::BehaviorSettings::self()->showSystemTray() /* also close if our tray icon is hidden! */
1295         || isHidden()) {
1296         Kopete::PluginManager::self()->shutdown();
1297         return true;
1298     } else {
1299         return false;
1300     }
1301 }
1302 
closeEvent(QCloseEvent * e)1303 void KopeteChatWindow::closeEvent(QCloseEvent *e)
1304 {
1305     // if there's a system tray applet and we are not shutting down then just do what needs to be done if a
1306     // window is closed.
1307     KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
1308     if (Kopete::BehaviorSettings::self()->showSystemTray() && !app->isShuttingDown() && !app->isSavingSession()) {
1309 //		hide();
1310         // BEGIN of code borrowed from KMainWindow::closeEvent
1311         // Save settings if auto-save is enabled, and settings have changed
1312         if (settingsDirty() && autoSaveSettings()) {
1313             saveAutoSaveSettings();
1314         }
1315 
1316         if (queryClose()) {
1317             e->accept();
1318         } else {
1319             e->ignore();
1320         }
1321         // END of code borrowed from KMainWindow::closeEvent
1322     } else {
1323         KXmlGuiWindow::closeEvent(e);
1324     }
1325 }
1326 
updateChatState(ChatView * cv,int newState)1327 void KopeteChatWindow::updateChatState(ChatView *cv, int newState)
1328 {
1329     Q_UNUSED(cv);
1330 
1331     if (m_tabBar) {
1332         KColorScheme scheme(QPalette::Active, KColorScheme::Window);
1333         switch (newState) {
1334         case ChatView::Highlighted:
1335             m_tabBar->setTabTextColor(m_tabBar->indexOf(cv), scheme.foreground(KColorScheme::LinkText).color());
1336             break;
1337         case ChatView::Message:
1338             m_tabBar->setTabTextColor(m_tabBar->indexOf(cv), scheme.foreground(KColorScheme::ActiveText).color());
1339             break;
1340         case ChatView::Changed:
1341             m_tabBar->setTabTextColor(m_tabBar->indexOf(cv), scheme.foreground(KColorScheme::NeutralText).color());
1342             break;
1343         case ChatView::Typing:
1344             m_tabBar->setTabTextColor(m_tabBar->indexOf(cv), scheme.foreground(KColorScheme::PositiveText).color());
1345             break;
1346         case ChatView::Normal:
1347         default:
1348             m_tabBar->setTabTextColor(m_tabBar->indexOf(cv), scheme.foreground(KColorScheme::NormalText).color());
1349             break;
1350         }
1351     }
1352 }
1353 
updateChatTooltip(ChatView * cv)1354 void KopeteChatWindow::updateChatTooltip(ChatView *cv)
1355 {
1356     if (m_tabBar) {
1357         m_tabBar->setTabToolTip(m_tabBar->indexOf(cv), QStringLiteral("<qt>%1</qt>").arg(cv->caption()));
1358     }
1359 }
1360 
updateChatLabel()1361 void KopeteChatWindow::updateChatLabel()
1362 {
1363     ChatView *chat = dynamic_cast<ChatView *>(sender());
1364     if (!chat || !m_tabBar) {
1365         return;
1366     }
1367 
1368     if (m_tabBar) {
1369         m_tabBar->setTabText(m_tabBar->indexOf(chat), chat->caption());
1370         if (m_tabBar->count() < 2 || m_tabBar->currentWidget() == chat) {
1371             setCaption(chat->caption());
1372         }
1373     }
1374 }
1375 
resizeEvent(QResizeEvent * e)1376 void KopeteChatWindow::resizeEvent(QResizeEvent *e)
1377 {
1378     KXmlGuiWindow::resizeEvent(e);
1379     if (m_activeView && m_activeView->messagePart()) {
1380         m_activeView->messagePart()->keepScrolledDown();
1381     }
1382 }
1383 
eventFilter(QObject * obj,QEvent * event)1384 bool KopeteChatWindow::eventFilter(QObject *obj, QEvent *event)
1385 {
1386     if (m_activeView && obj == m_activeView->editWidget() && event->type() == QEvent::KeyPress) {
1387         KShortcut *eventFilterShortcut = new KShortcut(nickComplete->shortcut());
1388         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
1389         if (eventFilterShortcut->primary() == QKeySequence(keyEvent->key())) {
1390             m_activeView->nickComplete();
1391             return true;
1392         }
1393     }
1394     return KXmlGuiWindow::eventFilter(obj, event);
1395 }
1396 
1397 // vim: set noet ts=4 sts=4 sw=4:
1398