1/*
2 kopetewindow.cpp - Kopete Main Window
3
4 Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
5 Copyright (c) 2001-2002 by Stefan Gehn <metz AT gehn.net>
6 Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
7 Copyright (c) 2002-2009 by Olivier Goffart <ogoffart at kde.org>
8 Copyright (c) 2005-2006 by Will Stephenson <wstephenson@kde.org>
9 Copyright (c) 2008 by Roman Jarosz <kedgedev@centrum.cz>
10
11 Kopete (c) 2002-2008 by the Kopete developers <kopete-devel@kde.org>
12
13 *************************************************************************
14 * *
15 * This program is free software; you can redistribute it and/or modify *
16 * it under the terms of the GNU General Public License as published by *
17 * the Free Software Foundation; either version 2 of the License, or *
18 * (at your option) any later version. *
19 * *
20 *************************************************************************
21*/
22
23#include "kopetewindow.h"
24
25#include <QCursor>
26#include <QLayout>
27
28#include <QTimer>
29#include <QPixmap>
30#include <QCloseEvent>
31#include <QMouseEvent>
32#include <QEvent>
33#include <QLabel>
34#include <QShowEvent>
35#include <QLineEdit>
36#include <QSignalMapper>
37#include <QTextEdit>
38
39#include <khbox.h>
40#include <kvbox.h>
41#include <kaction.h>
42#include <kactionmenu.h>
43#include <kactioncollection.h>
44#include <ktoggleaction.h>
45#include <kconfig.h>
46#include <kdebug.h>
47#include <kglobalaccel.h>
48#include <klocale.h>
49#include <kicon.h>
50#include <kiconloader.h>
51#include <kmessagebox.h>
52#include <knotifyconfigwidget.h>
53#include <kmenu.h>
54#include <kshortcutsdialog.h>
55#include <kedittoolbar.h>
56#include <kmenubar.h>
57#include <kstatusbar.h>
58#include <kwindowsystem.h>
59#include <kdeversion.h>
60#include <kinputdialog.h>
61#include <kplugininfo.h>
62#include <ksqueezedtextlabel.h>
63#include <kstringhandler.h>
64#include <kurl.h>
65#include <kxmlguifactory.h>
66#include <ktoolbar.h>
67#include <kdialog.h>
68#include <kstandardaction.h>
69#include <solid/networking.h>
70#include <kstatusbarofflineindicator.h>
71#include <kfilterproxysearchline.h>
72
73#include "addcontactpage.h"
74#include "addressbooklinkwidget.h"
75#include "ui_groupkabcselectorwidget.h"
76#include "kabcexport.h"
77#include "kopeteappearancesettings.h"
78#include "kopeteapplication.h"
79#include "kopeteaccount.h"
80#include "kopeteaccountmanager.h"
81#include "kopeteaccountstatusbaricon.h"
82#include "kopeteidentitystatusbaricon.h"
83#include "kopetebehaviorsettings.h"
84#include "kopetecontact.h"
85#include "kopetecontactlist.h"
86#include "kopetegroup.h"
87#include "kopeteidentity.h"
88#include "kopeteidentitymanager.h"
89#include "kopetelistviewsearchline.h"
90#include "kopetechatsessionmanager.h"
91#include "kopetepluginmanager.h"
92#include "kopeteprotocol.h"
93#include "kopetestdaction.h"
94#include "kopeteuiglobal.h"
95#include "systemtray.h"
96#include "kopeteonlinestatusmanager.h"
97#include "identitystatuswidget.h"
98#include "kopetestatusmanager.h"
99#include "kopetestatusrootaction.h"
100#include "kopetestatuseditaction.h"
101#include "kopeteemoticons.h"
102#include "kopeteinfoeventmanager.h"
103#include "infoeventwidget.h"
104#include "contactlisttreemodel.h"
105#include "contactlistplainmodel.h"
106#include "contactlistproxymodel.h"
107#include "kopeteitemdelegate.h"
108#include "kopetemetacontact.h"
109#include "kopetecontactlistview.h"
110#include "kopetestatusitems.h"
111
112
113//BEGIN GlobalStatusMessageIconLabel
114GlobalStatusMessageIconLabel::GlobalStatusMessageIconLabel ( QWidget *parent )
115 : QLabel ( parent )
116{
117 setCursor ( QCursor ( Qt::PointingHandCursor ) );
118 setFixedSize ( 16, 16 );
119 setPixmap ( SmallIcon ( "im-status-message-edit" ) );
120 setToolTip ( i18n ( "Global status message" ) );
121}
122
123void GlobalStatusMessageIconLabel::mouseReleaseEvent ( QMouseEvent *event )
124{
125 if ( event->button() == Qt::LeftButton || event->button() == Qt::RightButton )
126 {
127 emit iconClicked ( event->globalPos() );
128 event->accept();
129 }
130}
131//END GlobalStatusMessageIconLabel
132
133//BEGIN InfoEventIconLabel
134InfoEventIconLabel::InfoEventIconLabel( QWidget *parent )
135: QLabel( parent )
136{
137 setCursor( QCursor( Qt::PointingHandCursor ) );
138 setFixedSize( 16, 16 );
139 setPixmap( SmallIcon( "flag-black" ) );
140 setToolTip( i18n( "Service messages" ) );
141
142 connect( Kopete::InfoEventManager::self(), SIGNAL(changed()), this, SLOT(updateIcon()) );
143}
144
145void InfoEventIconLabel::mouseReleaseEvent( QMouseEvent *event )
146{
147 if ( event->button() == Qt::LeftButton || event->button() == Qt::RightButton )
148 {
149 emit clicked();
150 event->accept();
151 }
152}
153
154void InfoEventIconLabel::updateIcon()
155{
156 if ( Kopete::InfoEventManager::self()->eventCount() > 0 )
157 setPixmap( SmallIcon( "flag-green" ) );
158 else {
159 setPixmap( SmallIcon( "flag-black" ) );
160 emit clicked();
161 }
162}
163//END InfoEventIconLabel
164
165class KopeteWindow::Private
166{
167 public:
168 Private()
169 : contactlist ( 0 ), model(0), proxyModel(0), identitywidget ( 0 ), infoEventWidget ( 0 ), actionAddContact ( 0 ), actionDisconnect ( 0 ),
170 actionExportContacts ( 0 ), actionStatusMenu ( 0 ), actionDockMenu ( 0 ), actionSetAway ( 0 ),
171 actionSetBusy ( 0 ), actionSetAvailable ( 0 ), actionSetInvisible ( 0 ), actionPrefs ( 0 ),
172 actionQuit ( 0 ), actionSave ( 0 ), menubarAction ( 0 ), statusbarAction ( 0 ),
173 actionShowOfflineUsers ( 0 ), actionShowEmptyGroups ( 0 ), docked ( 0 ), deskRight ( 0 ),
174 statusBarWidget ( 0 ), tray ( 0 ), hidden ( false ), autoHide ( false ),
175 autoHideTimeout ( 0 ), autoHideTimer ( 0 ), addContactMapper ( 0 ),
176 showIdentityIcons( Kopete::AppearanceSettings::self()->showIdentityIcons() ),
177 globalStatusMessage ( 0 )
178 {}
179
180 ~Private()
181 {}
182
183 KopeteContactListView *contactlist;
184 Kopete::UI::ContactListModel* model;
185 Kopete::UI::ContactListProxyModel* proxyModel;
186
187 IdentityStatusWidget *identitywidget;
188 InfoEventWidget *infoEventWidget;
189
190 // Some Actions
191 KActionMenu *actionAddContact;
192
193 KAction *actionDisconnect;
194 KAction *actionExportContacts;
195
196 KActionMenu *actionStatusMenu;
197 KActionMenu *actionDockMenu;
198 KAction *actionSetAway;
199 KAction *actionSetBusy;
200 KAction *actionSetAvailable;
201 KAction *actionSetInvisible;
202
203
204 KAction *actionPrefs;
205 KAction *actionQuit;
206 KAction *actionSave;
207 KToggleAction *menubarAction;
208 KToggleAction *statusbarAction;
209 KToggleAction *actionShowAllOfflineEmpty;
210 KToggleAction *actionShowOfflineUsers;
211 KToggleAction *actionShowEmptyGroups;
212
213 int docked;
214 int deskRight;
215 QPoint position;
216 KHBox *statusBarWidget;
217 KopeteSystemTray *tray;
218 bool appDestroyed;
219 bool hidden;
220 bool autoHide;
221 unsigned int autoHideTimeout;
222 QTimer *autoHideTimer;
223 QTimer *autoResizeTimer;
224 QSignalMapper *addContactMapper;
225
226 bool showIdentityIcons;
227 QHash<const Kopete::Identity*, KopeteIdentityStatusBarIcon*> identityStatusBarIcons;
228 QHash<const Kopete::Account*, KopeteAccountStatusBarIcon*> accountStatusBarIcons;
229 KSqueezedTextLabel *globalStatusMessage;
230};
231
232/* KMainWindow is very broken from our point of view - it deref()'s the app
233 * when the last visible KMainWindow is destroyed. But when our main window is
234 * hidden when it's in the tray,closing the last chatwindow would cause the app
235 * to quit. - Richard
236 *
237 * Fortunately KMainWindow checks queryExit before deref()ing the Kapplication.
238 * KopeteWindow reimplements queryExit() and only returns true if it is shutting down
239 * (either because the user quit Kopete, or the session manager did).
240 *
241 * KopeteWindow and ChatWindows are closed by session management.
242 * App shutdown is not performed by the KopeteWindow but by KopeteApplication:
243 * 1) user quit - KopeteWindow::slotQuit() was called, calls KopeteApplication::quitKopete(),
244 * which closes all chatwindows and the KopeteWindow. The last window to close
245 * shuts down the PluginManager in queryExit(). When the PluginManager has completed its
246 * shutdown, the app is finally deref()ed, and the contact list and accountmanager
247 * are saved.
248 * and calling KApplication::quit()
249 * 2) session - KopeteWindow and all chatwindows are closed by KApplication session management.
250 * quit Then the shutdown proceeds as above.
251 *
252 * queryClose() is honoured so groupchats and chats receiving recent messages can interrupt
253 * (session) quit.
254 */
255
256KopeteWindow::KopeteWindow ( QWidget *parent )
257 : KXmlGuiWindow ( parent ), d ( new Private )
258{
259 d->appDestroyed = false;
260 connect ( kapp, SIGNAL (destroyed()), this, SLOT (slotAppDestroyed()) );
261 setAttribute ( Qt::WA_DeleteOnClose, false );
262 setAttribute ( Qt::WA_QuitOnClose, false );
263 // Applications should ensure that their StatusBar exists before calling createGUI()
264 // so that the StatusBar is always correctly positioned when KDE is configured to use
265 // a MacOS-style MenuBar.
266 // This fixes a "statusbar drawn over the top of the toolbar" bug
267 // e.g. it can happen when you switch desktops on Kopete startup
268 d->statusBarWidget = new KHBox ( statusBar() );
269 d->statusBarWidget->setMargin ( 2 );
270 d->statusBarWidget->setSpacing ( 1 );
271 window()->setAttribute( Qt::WA_AlwaysShowToolTips );
272 statusBar()->addPermanentWidget ( d->statusBarWidget, 0 );
273 QWidget *statusBarMessage = new QWidget ( statusBar() );
274 QHBoxLayout *statusBarMessageLayout = new QHBoxLayout ( statusBarMessage );
275 statusBarMessageLayout->setMargin ( 2 );
276
277 KStatusBarOfflineIndicator * indicator = new KStatusBarOfflineIndicator ( this );
278 statusBar()->addPermanentWidget ( indicator, 0 );
279
280 GlobalStatusMessageIconLabel *label = new GlobalStatusMessageIconLabel ( statusBarMessage );
281 connect ( label, SIGNAL (iconClicked(QPoint)),
282 this, SLOT (slotGlobalStatusMessageIconClicked(QPoint)) );
283 statusBarMessageLayout->addWidget ( label );
284 statusBarMessageLayout->addSpacing ( 1 );
285
286 InfoEventIconLabel *infoLabel = new InfoEventIconLabel ( statusBarMessage );
287 connect ( infoLabel, SIGNAL(clicked()), this, SLOT(slotInfoIconClicked()) );
288 statusBarMessageLayout->addWidget ( infoLabel );
289 statusBarMessageLayout->addSpacing ( 1 );
290 connect( Kopete::InfoEventManager::self(), SIGNAL(eventAdded(Kopete::InfoEvent*)), this, SLOT(slotNewInfoEvent()) );
291
292 d->globalStatusMessage = new KSqueezedTextLabel ( statusBarMessage );
293 connect ( Kopete::StatusManager::self(), SIGNAL (globalStatusChanged()),
294 this, SLOT (globalStatusChanged()) );
295 statusBarMessageLayout->addWidget ( d->globalStatusMessage );
296 statusBar()->addWidget ( statusBarMessage, 1 );
297
298 d->autoHideTimer = new QTimer ( this );
299 d->autoResizeTimer = new QTimer ( this );
300 d->autoResizeTimer->setSingleShot ( true );
301
302 // --------------------------------------------------------------------------------
303 initView();
304 initActions();
305 d->contactlist->initActions ( actionCollection() );
306 initSystray();
307 // --------------------------------------------------------------------------------
308
309 // Trap all loaded plugins, so we can add their status bar icons accordingly , also used to add XMLGUIClient
310 connect ( Kopete::PluginManager::self(), SIGNAL (pluginLoaded(Kopete::Plugin*)),
311 this, SLOT (slotPluginLoaded(Kopete::Plugin*)) );
312 connect ( Kopete::PluginManager::self(), SIGNAL (allPluginsLoaded()),
313 this, SLOT (slotAllPluginsLoaded()) );
314
315 // Connect all identity signals
316 connect ( Kopete::IdentityManager::self(), SIGNAL (identityRegistered(Kopete::Identity*)),
317 this, SLOT (slotIdentityRegistered(Kopete::Identity*)) );
318 connect ( Kopete::IdentityManager::self(), SIGNAL (identityUnregistered(const Kopete::Identity*)),
319 this, SLOT (slotIdentityUnregistered(const Kopete::Identity*)) );
320
321 connect ( d->autoHideTimer, SIGNAL (timeout()), this, SLOT (slotAutoHide()) );
322 connect ( d->contactlist, SIGNAL(visibleContentHeightChanged()), this, SLOT (slotStartAutoResizeTimer()) );
323 connect ( d->autoResizeTimer, SIGNAL (timeout()), this, SLOT (slotUpdateSize()) );
324 connect ( Kopete::AppearanceSettings::self(), SIGNAL (contactListAppearanceChanged()),
325 this, SLOT (slotContactListAppearanceChanged()) );
326 createGUI ( QLatin1String ( "kopeteui.rc" ) );
327
328 // call this _after_ createGUI(), otherwise menubar is not set up correctly
329 loadOptions();
330
331 // If some plugins are already loaded, merge the GUI
332 Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins();
333 foreach ( Kopete::Plugin *plug, plugins )
334 slotPluginLoaded ( plug );
335
336 // If some identity already registered, build the status icon
337 Kopete::Identity::List identityList = Kopete::IdentityManager::self()->identities();
338 foreach ( Kopete::Identity *i, identityList )
339 slotIdentityRegistered ( i );
340
341 //install an event filter for the quick search toolbar so we can
342 //catch the hide events
343 toolBar ( "quickSearchBar" )->installEventFilter ( this );
344}
345
346void KopeteWindow::slotAppDestroyed()
347{
348 d->appDestroyed = true;
349}
350
351void KopeteWindow::initView()
352{
353 QWidget *w = new QWidget ( this );
354 QVBoxLayout *l = new QVBoxLayout ( w );
355 d->contactlist = new KopeteContactListView ( w );
356
357 if ( Kopete::AppearanceSettings::self()->groupContactByGroup() )
358 d->model = new Kopete::UI::ContactListTreeModel( this );
359 else
360 d->model = new Kopete::UI::ContactListPlainModel( this );
361
362 d->model->init();
363 d->proxyModel = new Kopete::UI::ContactListProxyModel( this );
364 d->proxyModel->setSourceModel( d->model );
365 d->contactlist->setModel( d->proxyModel );
366 l->addWidget ( d->contactlist );
367 l->setSpacing ( 0 );
368 l->setContentsMargins ( 0,0,0,0 );
369 d->identitywidget = new IdentityStatusWidget ( 0, w );
370 d->identitywidget->setSizePolicy ( QSizePolicy ( QSizePolicy::Preferred, QSizePolicy::Minimum ) );
371 d->identitywidget->setVisible ( false );
372 l->addWidget ( d->identitywidget );
373 d->infoEventWidget = new InfoEventWidget ( w );
374 d->infoEventWidget->setSizePolicy ( QSizePolicy ( QSizePolicy::Preferred, QSizePolicy::Minimum ) );
375 d->infoEventWidget->setVisible ( false );
376 connect ( d->infoEventWidget, SIGNAL(showRequest()), this, SLOT(slotShowInfoEventWidget()) );
377 l->addWidget ( d->infoEventWidget );
378
379 setCentralWidget ( w );
380 d->contactlist->setFocus();
381}
382
383void KopeteWindow::initActions()
384{
385 // this action menu contains one action per account and is updated when accounts are registered/unregistered
386 d->actionAddContact = new KActionMenu ( KIcon ( "list-add-user" ), i18n ( "&Add Contact" ), this );
387 d->actionAddContact->setIconText ( i18n ( "Add" ) );
388 actionCollection()->addAction ( "AddContact", d->actionAddContact );
389 d->actionAddContact->setDelayed ( false );
390 // this signal mapper is needed to call slotAddContact with the correct arguments
391 d->addContactMapper = new QSignalMapper ( this );
392 connect ( d->addContactMapper, SIGNAL (mapped(QString)),
393 this, SLOT (slotAddContactDialogInternal(QString)) );
394
395 d->actionDisconnect = new KAction ( KIcon ( "user-offline" ), i18n ( "Offline" ), this );
396 actionCollection()->addAction ( "DisconnectAll", d->actionDisconnect );
397 connect ( d->actionDisconnect, SIGNAL (triggered(bool)), this, SLOT (slotDisconnectAll()) );
398 d->actionDisconnect->setEnabled ( false );
399
400 d->actionExportContacts = new KAction ( i18n ( "&Export Contacts..." ), this );
401 d->actionExportContacts->setIcon ( KIcon ( "document-export" ) );
402 actionCollection()->addAction ( "ExportContacts", d->actionExportContacts );
403 connect ( d->actionExportContacts, SIGNAL (triggered(bool)), this, SLOT (showExportDialog()) );
404
405 d->actionSetAway = new KAction ( KIcon ( "user-identity", 0, QStringList() << QString() << "user-away" ), i18n ( "&Away" ), this );
406 actionCollection()->addAction ( "SetAwayAll", d->actionSetAway );
407 connect ( d->actionSetAway, SIGNAL (triggered(bool)), this, SLOT (slotGlobalAway()) );
408
409 d->actionSetBusy = new KAction ( KIcon ( "user-identity", 0, QStringList() << QString() << "user-busy" ), i18n ( "&Busy" ), this );
410 actionCollection()->addAction ( "SetBusyAll", d->actionSetBusy );
411 connect ( d->actionSetBusy, SIGNAL (triggered(bool)), this, SLOT (slotGlobalBusy()) );
412
413
414 d->actionSetInvisible = new KAction ( KIcon ( "user-identity", 0, QStringList() << QString() << "user-invisible" ), i18n ( "&Invisible" ), this );
415 actionCollection()->addAction ( "SetInvisibleAll", d->actionSetInvisible );
416 connect ( d->actionSetInvisible, SIGNAL (triggered(bool)), this, SLOT (slotSetInvisibleAll()) );
417
418 d->actionSetAvailable = new KAction ( KIcon ( "user-identity", 0, QStringList() << QString() << "user-online" ), i18n ( "&Online" ), this );
419 actionCollection()->addAction ( "SetAvailableAll", d->actionSetAvailable );
420 connect ( d->actionSetAvailable, SIGNAL (triggered(bool)), this, SLOT (slotGlobalAvailable()) );
421
422 d->actionStatusMenu = new KActionMenu ( KIcon ( "user-identity", 0, QStringList() << QString() << "user-online" ), i18n ( "&Set Status" ), this );
423 d->actionStatusMenu->setIconText ( i18n ( "Status" ) );
424 actionCollection()->addAction ( "Status", d->actionStatusMenu );
425 d->actionStatusMenu->setDelayed ( false );
426
427 // Will be automatically deleted when the actionStatusMenu is deleted.
428 Kopete::StatusRootAction* statusAction = new Kopete::StatusRootAction ( d->actionStatusMenu );
429
430 connect ( statusAction, SIGNAL (changeStatus(uint,Kopete::StatusMessage)),
431 this, SLOT (setOnlineStatus(uint,Kopete::StatusMessage)) );
432 connect ( statusAction, SIGNAL (updateMessage(Kopete::StatusRootAction*)),
433 this, SLOT (updateStatusMenuMessage(Kopete::StatusRootAction*)) );
434 connect ( statusAction, SIGNAL (changeMessage(Kopete::StatusMessage)),
435 this, SLOT (setStatusMessage(Kopete::StatusMessage)) );
436
437 d->actionPrefs = KopeteStdAction::preferences ( actionCollection(), "settings_prefs" );
438
439 KStandardAction::quit ( this, SLOT (slotQuit()), actionCollection() );
440
441 setStandardToolBarMenuEnabled ( true );
442 d->menubarAction = KStandardAction::showMenubar ( menuBar(), SLOT (setVisible(bool)), actionCollection() );
443 actionCollection()->addAction ( "settings_showmenubar", d->menubarAction );
444 d->statusbarAction = KStandardAction::showStatusbar ( statusBar(), SLOT (setVisible(bool)), actionCollection() );
445 actionCollection()->addAction ( "settings_showstatusbar", d->statusbarAction );
446
447 KAction* act = KStandardAction::keyBindings ( guiFactory(), SLOT (configureShortcuts()), actionCollection() );
448 actionCollection()->addAction ( "settings_keys", act );
449
450 KAction *configureGlobalShortcutsAction = new KAction ( KIcon ( "configure-shortcuts" ), i18n ( "Configure &Global Shortcuts..." ), this );
451 configureGlobalShortcutsAction->setMenuRole( QAction::NoRole ); //OS X: prevent Qt heuristics to move action to app menu->"Preferences"
452 actionCollection()->addAction ( "settings_global", configureGlobalShortcutsAction );
453 connect ( configureGlobalShortcutsAction, SIGNAL (triggered(bool)), this, SLOT (slotConfGlobalKeys()) );
454
455 KStandardAction::configureToolbars ( this, SLOT (slotConfToolbar()), actionCollection() );
456 act = KStandardAction::configureNotifications ( this, SLOT (slotConfNotifications()), actionCollection() );
457 actionCollection()->addAction ( "settings_notifications", act );
458
459 d->actionShowAllOfflineEmpty = new KToggleAction ( KIcon ( "view-user-offline-kopete" ), i18n ( "Show &All" ), this );
460 actionCollection()->addAction ( "settings_show_all_offline_empty", d->actionShowAllOfflineEmpty );
461 d->actionShowAllOfflineEmpty->setShortcut ( KShortcut ( Qt::CTRL + Qt::Key_U ) );
462 connect ( d->actionShowAllOfflineEmpty, SIGNAL (triggered(bool)), this, SLOT (slotToggleShowAllOfflineEmpty(bool)) );
463
464 d->actionShowOfflineUsers = new KToggleAction ( KIcon ( "view-user-offline-kopete" ), i18n ( "Show Offline &Users" ), this );
465 actionCollection()->addAction ( "settings_show_offliners", d->actionShowOfflineUsers );
466 connect ( d->actionShowOfflineUsers, SIGNAL (triggered(bool)), this, SLOT (slotToggleShowOfflineUsers()) );
467
468 d->actionShowEmptyGroups = new KToggleAction ( KIcon ( "folder-grey" ), i18n ( "Show Empty &Groups" ), this );
469 actionCollection()->addAction ( "settings_show_empty_groups", d->actionShowEmptyGroups );
470 d->actionShowEmptyGroups->setShortcut ( KShortcut ( Qt::CTRL + Qt::Key_G ) );
471 connect ( d->actionShowEmptyGroups, SIGNAL (triggered(bool)), this, SLOT (slotToggleShowEmptyGroups()) );
472
473 /* The following are highly misleading together with the checkbox, consider removing them - ahartmetz
474 d->actionShowAllOfflineEmpty->setCheckedState ( KGuiItem ( i18n ( "Hide O&ffline" ) ) );
475 d->actionShowOfflineUsers->setCheckedState ( KGuiItem ( i18n ( "Hide Offline &Users" ) ) );
476 d->actionShowEmptyGroups->setCheckedState ( KGuiItem ( i18n ( "Hide Empty &Groups" ) ) );
477 */
478
479 KFilterProxySearchLine* searchLine = new KFilterProxySearchLine ( this );
480 searchLine->setProxy( d->proxyModel );
481 KAction *quickSearch = new KAction ( i18n ( "Quick Search Bar" ), this );
482 actionCollection()->addAction ( "quicksearch_bar", quickSearch );
483 quickSearch->setDefaultWidget ( searchLine );
484
485 // sync actions, config and prefs-dialog
486 connect ( Kopete::AppearanceSettings::self(), SIGNAL (configChanged()), this, SLOT (slotConfigChanged()) );
487 slotConfigChanged();
488
489 // Global actions
490 KAction *globalReadMessage = new KAction ( i18n ( "Read Message" ), this );
491 actionCollection()->addAction ( "ReadMessage", globalReadMessage );
492 connect ( globalReadMessage, SIGNAL (triggered(bool)), Kopete::ChatSessionManager::self(), SLOT (slotReadMessage()) );
493 globalReadMessage->setGlobalShortcut ( KShortcut ( Qt::CTRL + Qt::SHIFT + Qt::Key_I ) );
494 globalReadMessage->setWhatsThis ( i18n ( "Read the next pending message" ) );
495
496 KAction *globalShowContactList = new KAction ( i18n ( "Show/Hide Contact List" ), this );
497 actionCollection()->addAction ( "ShowContactList", globalShowContactList );
498 connect ( globalShowContactList, SIGNAL (triggered(bool)), this, SLOT (slotShowHide()) );
499 globalShowContactList->setGlobalShortcut ( KShortcut ( Qt::CTRL + Qt::ALT + Qt::Key_T ) );
500 globalShowContactList->setWhatsThis ( i18n ( "Show or hide the contact list" ) );
501
502 KAction *globalSetAway = new KAction ( i18n ( "Set Away/Back" ), this );
503 actionCollection()->addAction ( "Set_Away_Back", globalSetAway );
504 connect ( globalSetAway, SIGNAL (triggered(bool)), this, SLOT (slotToggleAway()) );
505 globalSetAway->setGlobalShortcut ( KShortcut ( Qt::CTRL + Qt::SHIFT + Qt::Key_W ) );
506}
507
508void KopeteWindow::slotShowHide()
509{
510 if ( isActiveWindow() )
511 {
512 d->autoHideTimer->stop(); //no timeouts if active
513 hide();
514 }
515 else
516 {
517 show();
518#ifdef Q_WS_X11
519 if ( !KWindowSystem::windowInfo ( winId(),NET::WMDesktop ).onAllDesktops() )
520 KWindowSystem::setOnDesktop ( winId(), KWindowSystem::currentDesktop() );
521#endif
522 raise();
523 KWindowSystem::forceActiveWindow( winId() );
524 }
525}
526
527void KopeteWindow::slotToggleAway()
528{
529 kDebug ( 14000 );
530 Kopete::StatusManager * statusManager = Kopete::StatusManager::self();
531 const Kopete::Status::StatusItem * item = 0;
532 bool away = Kopete::StatusManager::self()->globalAway();
533
534 foreach (const Kopete::Status::StatusItem *i, statusManager->getRootGroup()->childList()) {
535 if (i->title() == QLatin1String("Online") && away ) {
536 item = i;
537 break;
538 } else if (i->title() == QLatin1String("Away") && !away) {
539 item = i;
540 break;
541 }
542 }
543
544 const Kopete::Status::Status * status = qobject_cast<const Kopete::Status::Status*>(item);
545 if (status) {
546 statusManager->setGlobalStatusMessage(Kopete::StatusMessage(status->title(), status->message()));
547 }
548
549 if ( away )
550 slotGlobalAvailable();
551 else
552 slotGlobalAway();
553}
554
555void KopeteWindow::initSystray()
556{
557 if ( Kopete::BehaviorSettings::self()->showSystemTray() ) {
558 d->tray = KopeteSystemTray::systemTray ( this );
559
560 QObject::connect ( d->tray, SIGNAL (aboutToShowMenu(KMenu*)),
561 this, SLOT (slotTrayAboutToShowMenu(KMenu*)) );
562 // :FIXME: The signal quitSelected does not exist on KopeteSystemTray
563 // QObject::connect ( d->tray, SIGNAL (quitSelected()), this, SLOT (slotQuit()) );
564 }
565}
566
567KopeteWindow::~KopeteWindow()
568{
569 delete d;
570}
571
572bool KopeteWindow::eventFilter ( QObject* target, QEvent* event )
573{
574 KToolBar *toolBar = dynamic_cast<KToolBar*> ( target );
575 QAction *resetAction = actionCollection()->action ( "quicksearch_reset" );
576
577 if ( toolBar && resetAction && resetAction->associatedWidgets().contains ( toolBar ) )
578 {
579
580 if ( event->type() == QEvent::Hide )
581 {
582 resetAction->trigger();
583 return true;
584 }
585 return KXmlGuiWindow::eventFilter ( target, event );
586 }
587
588 return KXmlGuiWindow::eventFilter ( target, event );
589}
590
591void KopeteWindow::loadOptions()
592{
593 KSharedConfig::Ptr config = KGlobal::config();
594
595 toolBar ( "mainToolBar" )->applySettings ( config->group ( "ToolBar Settings" ) );
596 toolBar ( "quickSearchBar" )->applySettings ( config->group ( "QuickSearchBar Settings" ) );
597
598 applyMainWindowSettings ( config->group ( "General Options" ) );
599 KConfigGroup cg ( config, "General Options" );
600 QPoint pos = cg.readEntry ( "Position", QPoint(-1, -1) );
601 if ( pos.x() != -1 || pos.y() != -1 )
602 move ( pos );
603
604 QSize size = cg.readEntry ( "Geometry", QSize() );
605 if ( size.isEmpty() ) // Default size
606 resize ( QSize ( 272, 400 ) );
607 else
608 resize ( size );
609
610 d->autoHide = Kopete::AppearanceSettings::self()->contactListAutoHide();
611 d->autoHideTimeout = Kopete::AppearanceSettings::self()->contactListAutoHideTimeout();
612
613
614 QString tmp = cg.readEntry ( "WindowState", "Shown" );
615 if ( tmp == "Minimized" && Kopete::BehaviorSettings::self()->showSystemTray() )
616 {
617 showMinimized();
618 }
619 else if ( tmp == "Hidden" && Kopete::BehaviorSettings::self()->showSystemTray() )
620 {
621 hide();
622 }
623 else if ( !Kopete::BehaviorSettings::self()->startDocked() || !Kopete::BehaviorSettings::self()->showSystemTray() )
624 show();
625
626 d->menubarAction->setChecked ( !menuBar()->isHidden() );
627 d->statusbarAction->setChecked ( !statusBar()->isHidden() );
628}
629
630void KopeteWindow::saveOptions()
631{
632 KConfigGroup mainToolbarGroup ( KGlobal::config(), "ToolBar Settings" );
633 toolBar ( "mainToolBar" )->saveSettings ( mainToolbarGroup );
634 KConfigGroup qsbGroup ( KGlobal::config(), "QuickSearchBar Settings" );
635 toolBar ( "quickSearchBar" )->saveSettings ( qsbGroup );
636
637 KConfigGroup cg ( KGlobal::config(), "General Options" );
638 saveMainWindowSettings ( cg );
639
640 cg.writeEntry ( "Position", pos() );
641 cg.writeEntry ( "Geometry", size() );
642
643 if ( isMinimized() )
644 {
645 cg.writeEntry ( "WindowState", "Minimized" );
646 }
647 else if ( isHidden() )
648 {
649 cg.writeEntry ( "WindowState", "Hidden" );
650 }
651 else
652 {
653 cg.writeEntry ( "WindowState", "Shown" );
654 }
655
656 Kopete::Identity *identity = d->identitywidget->identity();
657 if ( identity )
658 cg.writeEntry ( "ShownIdentityId", identity->id() );
659 else
660 cg.writeEntry ( "ShownIdentityId", QString() );
661
662 cg.sync();
663}
664
665void KopeteWindow::slotToggleShowAllOfflineEmpty ( bool toggled )
666{
667 d->actionShowOfflineUsers->setChecked ( toggled );
668 d->actionShowEmptyGroups->setChecked ( toggled );
669 Kopete::AppearanceSettings::self()->setShowOfflineUsers ( toggled );
670 Kopete::AppearanceSettings::self()->setShowEmptyGroups ( toggled );
671 Kopete::AppearanceSettings::self()->writeConfig();
672}
673
674void KopeteWindow::slotToggleShowOfflineUsers()
675{
676 Kopete::AppearanceSettings::self()->setShowOfflineUsers ( d->actionShowOfflineUsers->isChecked() );
677 Kopete::AppearanceSettings::self()->writeConfig();
678}
679
680void KopeteWindow::slotToggleShowEmptyGroups()
681{
682 Kopete::AppearanceSettings::self()->setShowEmptyGroups ( d->actionShowEmptyGroups->isChecked() );
683 Kopete::AppearanceSettings::self()->writeConfig();
684}
685
686static bool compareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b);
687static bool invertedCompareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b);
688
689void KopeteWindow::slotConfigChanged()
690{
691 bool groupContactByGroupModel = qobject_cast<Kopete::UI::ContactListTreeModel*>( d->model );
692 if ( groupContactByGroupModel != Kopete::AppearanceSettings::self()->groupContactByGroup() )
693 {
694 Kopete::UI::ContactListModel* oldModel = d->model;
695 if ( Kopete::AppearanceSettings::self()->groupContactByGroup() )
696 d->model = new Kopete::UI::ContactListTreeModel( this );
697 else
698 d->model = new Kopete::UI::ContactListPlainModel( this );
699
700 d->model->init();
701 d->proxyModel->setSourceModel( d->model );
702 oldModel->deleteLater();
703 }
704
705 if ( isHidden() && !Kopete::BehaviorSettings::self()->showSystemTray() ) // user disabled systray while kopete is hidden, show it!
706 show();
707
708 d->actionShowAllOfflineEmpty->setChecked ( Kopete::AppearanceSettings::self()->showOfflineUsers() && Kopete::AppearanceSettings::self()->showEmptyGroups() );
709 d->actionShowOfflineUsers->setChecked ( Kopete::AppearanceSettings::self()->showOfflineUsers() );
710 d->actionShowEmptyGroups->setChecked ( Kopete::AppearanceSettings::self()->showEmptyGroups() );
711
712 if ( d->showIdentityIcons != Kopete::AppearanceSettings::self()->showIdentityIcons() )
713 {
714 // Delete status bar icons
715 if ( d->showIdentityIcons )
716 {
717 if ( d->identitywidget->isVisible() )
718 {
719 d->identitywidget->setIdentity( 0 );
720 d->identitywidget->setVisible( false );
721 }
722
723 qDeleteAll( d->identityStatusBarIcons );
724 d->identityStatusBarIcons.clear();
725 }
726 else
727 {
728 qDeleteAll( d->accountStatusBarIcons );
729 d->accountStatusBarIcons.clear();
730 }
731
732 // Add new status bar icons
733 d->showIdentityIcons = Kopete::AppearanceSettings::self()->showIdentityIcons();
734 if ( d->showIdentityIcons )
735 {
736 Kopete::Identity::List identityList = Kopete::IdentityManager::self()->identities();
737 foreach ( Kopete::Identity *identity, identityList )
738 {
739 KopeteIdentityStatusBarIcon *sbIcon = new KopeteIdentityStatusBarIcon ( identity, d->statusBarWidget );
740 connect ( sbIcon, SIGNAL(leftClicked(Kopete::Identity*,QPoint)), this,
741 SLOT(slotIdentityStatusIconLeftClicked(Kopete::Identity*,QPoint)) );
742
743 d->identityStatusBarIcons.insert ( identity, sbIcon );
744 slotIdentityStatusIconChanged ( identity );
745 slotIdentityToolTipChanged ( identity );
746 }
747 }
748 else
749 {
750 QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
751 qSort(accountList.begin(), accountList.end(), invertedCompareOnlineStatus);
752 foreach ( Kopete::Account *account, accountList )
753 {
754 KopeteAccountStatusBarIcon *sbIcon = new KopeteAccountStatusBarIcon ( account, d->statusBarWidget );
755 d->accountStatusBarIcons.insert ( account, sbIcon );
756 }
757 }
758 }
759}
760
761void KopeteWindow::slotContactListAppearanceChanged()
762{
763 d->autoHide = Kopete::AppearanceSettings::self()->contactListAutoHide();
764 d->autoHideTimeout = Kopete::AppearanceSettings::self()->contactListAutoHideTimeout();
765
766 startAutoHideTimer();
767}
768
769void KopeteWindow::slotConfNotifications()
770{
771 KNotifyConfigWidget::configure ( this );
772}
773
774void KopeteWindow::slotConfGlobalKeys()
775{
776 KShortcutsDialog::configure ( actionCollection() );
777}
778
779void KopeteWindow::slotConfToolbar()
780{
781 KConfigGroup cg ( KGlobal::config(), "General Options" );
782 saveMainWindowSettings ( cg );
783 KEditToolBar *dlg = new KEditToolBar ( factory() );
784 connect ( dlg, SIGNAL (newToolBarConfig()), this, SLOT (slotUpdateToolbar()) );
785 connect ( dlg, SIGNAL (finished()) , dlg, SLOT (deleteLater()) );
786 dlg->show();
787}
788
789void KopeteWindow::slotUpdateToolbar()
790{
791 applyMainWindowSettings ( KGlobal::config()->group ( "General Options" ) );
792}
793
794void KopeteWindow::slotGlobalAway()
795{
796 Kopete::AccountManager::self()->setOnlineStatus ( Kopete::OnlineStatusManager::Away,
797 Kopete::StatusManager::self()->globalStatusMessage() );
798}
799
800void KopeteWindow::slotGlobalBusy()
801{
802 Kopete::AccountManager::self()->setOnlineStatus ( Kopete::OnlineStatusManager::Busy,
803 Kopete::StatusManager::self()->globalStatusMessage() );
804}
805
806void KopeteWindow::slotGlobalAvailable()
807{
808 Kopete::AccountManager::self()->setOnlineStatus ( Kopete::OnlineStatusManager::Online,
809 Kopete::StatusManager::self()->globalStatusMessage() );
810}
811
812void KopeteWindow::slotSetInvisibleAll()
813{
814 Kopete::AccountManager::self()->setOnlineStatus ( Kopete::OnlineStatusManager::Invisible,
815 Kopete::StatusManager::self()->globalStatusMessage() );
816}
817
818void KopeteWindow::slotDisconnectAll()
819{
820 Kopete::AccountManager::self()->setOnlineStatus ( Kopete::OnlineStatusManager::Offline,
821 Kopete::StatusManager::self()->globalStatusMessage() );
822}
823
824bool KopeteWindow::queryClose()
825{
826 KopeteApplication *app = static_cast<KopeteApplication *> ( kapp );
827 if ( app->sessionSaving() || app->isShuttingDown() ) {
828 // we are shutting down, don't show any message
829 return true;
830 }
831
832 Kopete::PluginList list = Kopete::PluginManager::self()->loadedPlugins();
833 foreach ( Kopete::Plugin *plugin, list ) {
834 bool shown = false;
835 QMetaObject::invokeMethod(plugin, "showCloseWindowMessage", Qt::DirectConnection, Q_RETURN_ARG(bool, shown));
836 if ( shown ) {
837 // A message box has just been shown. Stop now, we do not want
838 // to spam the user with multiple message boxes.
839 return true;
840 }
841 }
842
843 if ( Kopete::BehaviorSettings::self()->showSystemTray()
844 && !isHidden() )
845 // I would make this a KMessageBox::queuedMessageBox but there doesn't seem to be don'tShowAgain support for those
846 KMessageBox::information ( this,
847 i18n ( "<qt>Closing the main window will keep Kopete running in the "
848 "system tray. Use 'Quit' from the 'File' menu to quit the application.</qt>" ),
849 i18n ( "Docking in System Tray" ), "hideOnCloseInfo" );
850// else // we are shutting down either user initiated or session management
851// Kopete::PluginManager::self()->shutdown();
852
853 return true;
854}
855
856bool KopeteWindow::shouldExitOnClose() const
857{
858 Kopete::PluginList list = Kopete::PluginManager::self()->loadedPlugins();
859 foreach ( Kopete::Plugin *plugin, list ) {
860 bool ok = true;
861 QMetaObject::invokeMethod(plugin, "shouldExitOnClose", Qt::DirectConnection, Q_RETURN_ARG(bool, ok));
862 if ( !ok ) {
863 kDebug ( 14000 ) << "plugin" << plugin->displayName() << "does not want to exit";
864 return false;
865 }
866 }
867 // If all plugins are OK, consider ourself OK only if there is no tray icon
868 return !Kopete::BehaviorSettings::self()->showSystemTray();
869}
870
871bool KopeteWindow::queryExit()
872{
873 KopeteApplication *app = static_cast<KopeteApplication *> ( kapp );
874 if ( app->sessionSaving()
875 || app->isShuttingDown() /* only set if KopeteApplication::quitKopete() or
876 KopeteApplication::commitData() called */
877 || shouldExitOnClose()
878 || isHidden() )
879 {
880 saveOptions();
881 kDebug ( 14000 ) << " shutting down plugin manager";
882 Kopete::PluginList list = Kopete::PluginManager::self()->loadedPlugins();
883 foreach ( Kopete::Plugin *plugin, list ) {
884 guiFactory()->removeClient(plugin);
885 }
886 Kopete::PluginManager::self()->shutdown();
887 return true;
888 }
889 else
890 return false;
891}
892
893void KopeteWindow::closeEvent ( QCloseEvent *e )
894{
895 // if we are not ok to exit on close and we are not shutting down then just do what needs to be done if a
896 // window is closed.
897 KopeteApplication *app = static_cast<KopeteApplication *> ( kapp );
898 if ( !shouldExitOnClose() && !app->isShuttingDown() && !app->sessionSaving() ) {
899 // BEGIN of code borrowed from KMainWindow::closeEvent
900 // Save settings if auto-save is enabled, and settings have changed
901 if ( settingsDirty() && autoSaveSettings() )
902 saveAutoSaveSettings();
903
904 if ( queryClose() ) {
905 e->accept();
906 }
907 // END of code borrowed from KMainWindow::closeEvent
908 kDebug ( 14000 ) << "just closing because we have a system tray icon";
909 }
910 else
911 {
912 kDebug ( 14000 ) << "delegating to KXmlGuiWindow::closeEvent()";
913 KXmlGuiWindow::closeEvent ( e );
914 }
915}
916
917void KopeteWindow::slotQuit()
918{
919 KopeteApplication *app = static_cast<KopeteApplication *> ( kapp );
920 app->quitKopete();
921
922 if ( d->tray && app->isShuttingDown() )
923 {
924 d->tray->deleteLater();
925 d->tray = 0;
926 }
927}
928
929void KopeteWindow::slotPluginLoaded ( Kopete::Plugin * p )
930{
931 guiFactory()->addClient ( p );
932}
933
934void KopeteWindow::slotAllPluginsLoaded()
935{
936// actionConnect->setEnabled(true);
937 d->actionDisconnect->setEnabled ( true );
938
939 KConfigGroup cg( KGlobal::config(), "General Options" );
940
941 // If some account already loaded, build the status icon
942 QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
943 qSort(accountList.begin(), accountList.end(), invertedCompareOnlineStatus);
944 foreach ( Kopete::Account *a, accountList )
945 slotAccountRegistered ( a );
946
947 //Connect the appropriate account signals
948 /* Please note that I tried to put this in the slotAllPluginsLoaded() function
949 * but it seemed to break the account icons in the statusbar --Matt */
950 connect ( Kopete::AccountManager::self(), SIGNAL (accountRegistered(Kopete::Account*)),
951 this, SLOT (slotAccountRegistered(Kopete::Account*)) );
952 connect ( Kopete::AccountManager::self(), SIGNAL (accountUnregistered(const Kopete::Account*)),
953 this, SLOT (slotAccountUnregistered(const Kopete::Account*)) );
954
955 if ( d->showIdentityIcons )
956 {
957 QString identityId = cg.readEntry( "ShownIdentityId", Kopete::IdentityManager::self()->defaultIdentity()->id() );
958 if ( !identityId.isEmpty() )
959 {
960 Kopete::Identity* identity = Kopete::IdentityManager::self()->findIdentity( identityId );
961 if ( identity )
962 slotIdentityStatusIconLeftClicked( identity, QPoint() );
963 }
964 }
965}
966
967void KopeteWindow::slotIdentityRegistered ( Kopete::Identity *identity )
968{
969 if ( !identity )
970 return;
971
972 connect ( identity, SIGNAL(onlineStatusChanged(Kopete::Identity*)),
973 this, SLOT(slotIdentityStatusIconChanged(Kopete::Identity*)) );
974 connect ( identity, SIGNAL(identityChanged(Kopete::Identity*)),
975 this, SLOT(slotIdentityStatusIconChanged(Kopete::Identity*)) );
976 connect ( identity, SIGNAL(toolTipChanged(Kopete::Identity*)),
977 this, SLOT(slotIdentityToolTipChanged(Kopete::Identity*)) );
978
979 if ( d->showIdentityIcons )
980 {
981 KopeteIdentityStatusBarIcon *sbIcon = new KopeteIdentityStatusBarIcon ( identity, d->statusBarWidget );
982 connect ( sbIcon, SIGNAL (leftClicked(Kopete::Identity*,QPoint)),
983 SLOT (slotIdentityStatusIconLeftClicked(Kopete::Identity*,QPoint)) );
984
985 d->identityStatusBarIcons.insert ( identity, sbIcon );
986 }
987
988 slotIdentityStatusIconChanged ( identity );
989 slotIdentityToolTipChanged( identity );
990}
991
992void KopeteWindow::slotIdentityUnregistered ( const Kopete::Identity *identity )
993{
994 kDebug ( 14000 ) ;
995
996 if ( d->showIdentityIcons )
997 {
998 KopeteIdentityStatusBarIcon *sbIcon = d->identityStatusBarIcons.value ( identity, 0 );
999 if ( sbIcon )
1000 {
1001 d->identityStatusBarIcons.remove ( identity );
1002 delete sbIcon;
1003 }
1004 }
1005
1006 makeTrayToolTip();
1007
1008}
1009
1010void KopeteWindow::slotIdentityToolTipChanged ( Kopete::Identity *identity )
1011{
1012 if ( d->appDestroyed )
1013 return;
1014
1015 KopeteApplication *app = static_cast<KopeteApplication *> ( kapp );
1016 if ( app->sessionSaving() || app->isShuttingDown() )
1017 return;
1018
1019 // Adds tooltip for each status icon, useful in case you have many accounts
1020 // over one protocol
1021 KopeteIdentityStatusBarIcon *i = d->identityStatusBarIcons.value ( identity, 0 );
1022 if ( i )
1023 i->setToolTip ( identity->toolTip() );
1024
1025 makeTrayToolTip();
1026}
1027
1028void KopeteWindow::slotIdentityStatusIconChanged ( Kopete::Identity *identity )
1029{
1030 kDebug ( 14000 ) << identity->property ( Kopete::Global::Properties::self()->statusMessage() ).value();
1031 // update the global status label if the change doesn't
1032// QString newAwayMessage = contact->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
1033// if ( status.status() != Kopete::OnlineStatus::Connecting )
1034// {
1035// QString globalMessage = m_globalStatusMessage->text();
1036// if ( newAwayMessage != globalMessage )
1037// m_globalStatusMessage->setText( ""i18n("status message to show when different accounts have different status messages", "(multiple)" );
1038// }
1039// kDebug(14000) << "Icons: '" <<
1040// status.overlayIcons() << "'" << endl;
1041
1042 if ( d->appDestroyed )
1043 return;
1044
1045 KopeteApplication *app = static_cast<KopeteApplication *> ( kapp );
1046 if ( app->sessionSaving() || app->isShuttingDown() )
1047 return;
1048
1049 if ( identity->onlineStatus() != Kopete::OnlineStatus::Connecting )
1050 {
1051 // FIXME: It's not global status so don't save it
1052 //Kopete::StatusManager::self()->setGlobalStatusMessage( identity->property( Kopete::Global::Properties::self()->statusMessage() ).value().toString() );
1053 }
1054
1055 KopeteIdentityStatusBarIcon *i = d->identityStatusBarIcons.value ( identity, 0 );
1056 if ( !i )
1057 return;
1058
1059 QPixmap pm;
1060 switch ( identity->onlineStatus() ) {
1061 case Kopete::OnlineStatus::Offline:
1062 case Kopete::OnlineStatus::Connecting:
1063 pm = SmallIcon ( "user-identity", 0, KIconLoader::DefaultState,
1064 QStringList() << QString() << "user-offline" );
1065 break;
1066 case Kopete::OnlineStatus::Invisible:
1067 pm = SmallIcon ( "user-identity", 0, KIconLoader::DefaultState,
1068 QStringList() << QString() << "user-invisible" );
1069 break;
1070 case Kopete::OnlineStatus::Away:
1071 pm = SmallIcon ( "user-identity", 0, KIconLoader::DefaultState,
1072 QStringList() << QString() << "user-away" );
1073 break;
1074 case Kopete::OnlineStatus::Busy:
1075 pm = SmallIcon ( "user-identity", 0, KIconLoader::DefaultState,
1076 QStringList() << QString() << "user-busy" );
1077 break;
1078 case Kopete::OnlineStatus::Online:
1079 pm = SmallIcon ( "user-identity", 0, KIconLoader::DefaultState,
1080 QStringList() << QString() << "user-online" );
1081 break;
1082 case Kopete::OnlineStatus::Unknown:
1083 pm = SmallIcon ( "user-identity" );
1084 break;
1085 }
1086
1087 // No Pixmap found, fallback to Unknown
1088 if ( pm.isNull() )
1089 i->setPixmap ( SmallIcon ( "user-identity" ) );
1090 else
1091 i->setPixmap ( pm );
1092}
1093
1094static bool compareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b)
1095{
1096 int c = 0;
1097
1098 if (a->identity() && b->identity()) {
1099 c = QString::localeAwareCompare(a->identity()->label(), b->identity()->label());
1100 }
1101
1102 if (c == 0) {
1103 c = a->myself()->onlineStatus().status() - b->myself()->onlineStatus().status();
1104
1105 if (c == 0) {
1106 return (QString::localeAwareCompare(a->protocol()->displayName(), b->protocol()->displayName()) < 0);
1107 }
1108 return (c > 0);
1109 }
1110 return (c < 0);
1111}
1112
1113static bool invertedCompareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b)
1114{
1115 return !compareOnlineStatus(a, b);
1116}
1117
1118void KopeteWindow::makeTrayToolTip()
1119{
1120 //FIXME: maybe use identities here?
1121 //the tool-tip of the systemtray.
1122 if ( d->tray )
1123 {
1124 QString tt = QLatin1String ( "<qt>" );
1125 QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
1126 qSort(accountList.begin(), accountList.end(), compareOnlineStatus);
1127 foreach ( Kopete::Account *a, accountList )
1128 {
1129 Kopete::Contact *self = a->myself();
1130 /*tt += i18nc ( "Account tooltip information: <nobr>ICON <b>PROTOCOL:</b> NAME (<i>STATUS</i>)</nobr><br />",
1131 "<nobr><img src=\"kopete-account-icon:%3:%4\" /> <b>%1:</b> %2 (<i>%5</i>)</nobr><br />",
1132 a->protocol()->displayName(), a->accountLabel(), QString ( QUrl::toPercentEncoding ( a->protocol()->pluginId() ) ),
1133 QString ( QUrl::toPercentEncoding ( a->accountId() ) ), self->onlineStatus().description() );*/
1134 tt += i18nc ( "Account tooltip information: <nobr>ICON <b>PROTOCOL:</b> NAME (<i>STATUS</i>)</nobr><br />",
1135 "<nobr><img src=\"%3\" width=\"16\" height=\"16\" /> <b>%1:</b> %2 (<i>%4</i>)</nobr><br />",
1136 a->protocol()->displayName(), a->accountLabel(),
1137 a->accountIconPath(KIconLoader::Small), self->onlineStatus().description() );
1138 }
1139 tt += QLatin1String ( "</qt>" );
1140 d->tray->setToolTip ( "kopete", i18n("Kopete"), tt );
1141 }
1142}
1143
1144void KopeteWindow::slotIdentityStatusIconLeftClicked ( Kopete::Identity *identity, const QPoint &p )
1145{
1146 Q_UNUSED ( p )
1147 if ( d->identitywidget->isVisible() && d->identitywidget->identity() == identity )
1148 {
1149 d->identitywidget->setIdentity ( 0 );
1150 d->identitywidget->setVisible ( false );
1151 return;
1152 }
1153
1154 if ( d->infoEventWidget->isVisible() )
1155 d->infoEventWidget->setVisible ( false );
1156
1157 d->identitywidget->setIdentity ( identity );
1158 d->identitywidget->setVisible ( true );
1159}
1160
1161void KopeteWindow::slotShowInfoEventWidget()
1162{
1163 if ( d->identitywidget->isVisible() )
1164 {
1165 d->identitywidget->setIdentity( 0 );
1166 d->identitywidget->setVisible( false );
1167 }
1168
1169 if ( !d->infoEventWidget->isVisible() )
1170 d->infoEventWidget->setVisible( true );
1171
1172 if ( !isActiveWindow() )
1173 slotShowHide();
1174}
1175
1176void KopeteWindow::slotInfoIconClicked()
1177{
1178 if ( d->infoEventWidget->isVisible() )
1179 {
1180 d->infoEventWidget->setVisible( false );
1181 }
1182 else
1183 {
1184 if ( d->identitywidget->isVisible() )
1185 {
1186 d->identitywidget->setIdentity( 0 );
1187 d->identitywidget->setVisible( false );
1188 }
1189 d->infoEventWidget->setVisible( true );
1190 }
1191}
1192
1193void KopeteWindow::slotAccountRegistered ( Kopete::Account *account )
1194{
1195
1196 //enable the connect all toolbar button
1197// actionConnect->setEnabled(true);
1198 d->actionDisconnect->setEnabled ( true );
1199
1200 // add an item for this account to the add contact actionmenu
1201 QString s = QString ( "actionAdd%1Contact" ).arg ( account->accountId() );
1202 KAction *action = new KAction ( KIcon ( account->accountIcon() ), account->accountLabel(), this );
1203 actionCollection()->addAction ( s, action );
1204 connect ( action, SIGNAL (triggered(bool)), d->addContactMapper, SLOT (map()) );
1205 connect ( account, SIGNAL(colorChanged(QColor)), this, SLOT(slotAccountColorChanged()) );
1206
1207 d->addContactMapper->setMapping ( action, account->protocol()->pluginId() + QChar ( 0xE000 ) + account->accountId() );
1208 d->actionAddContact->addAction ( action );
1209
1210 if ( !d->showIdentityIcons )
1211 {
1212 KopeteAccountStatusBarIcon *sbIcon = new KopeteAccountStatusBarIcon ( account, d->statusBarWidget );
1213 d->accountStatusBarIcons.insert ( account, sbIcon );
1214 }
1215}
1216
1217void KopeteWindow::slotAccountColorChanged()
1218{
1219 Kopete::Account* account = qobject_cast<Kopete::Account*>(sender());
1220 Q_ASSERT(account);
1221
1222 // update add contact actionmenu
1223 QString s = QString( "actionAdd%1Contact" ).arg( account->accountId() );
1224 QAction *action = actionCollection()->action ( s );
1225 if ( action )
1226 action->setIcon( KIcon( account->accountIcon() ) );
1227}
1228
1229void KopeteWindow::slotAccountUnregistered ( const Kopete::Account *account )
1230{
1231 QList<Kopete::Account *> accounts = Kopete::AccountManager::self()->accounts();
1232 if ( accounts.isEmpty() )
1233 {
1234// actionConnect->setEnabled(false);
1235 d->actionDisconnect->setEnabled ( false );
1236 }
1237
1238 disconnect ( account, SIGNAL(colorChanged(QColor)), this, SLOT(slotAccountColorChanged()) );
1239
1240 // update add contact actionmenu
1241 QString s = QString ( "actionAdd%1Contact" ).arg ( account->accountId() );
1242 QAction *action = actionCollection()->action ( s );
1243 if ( action )
1244 {
1245 kDebug ( 14000 ) << " found KAction " << action << " with name: " << action->objectName();
1246 d->addContactMapper->removeMappings ( action );
1247 d->actionAddContact->removeAction ( action );
1248 }
1249
1250 if ( !d->showIdentityIcons )
1251 {
1252 KopeteAccountStatusBarIcon *sbIcon = d->accountStatusBarIcons.value ( account, 0 );
1253 if ( sbIcon )
1254 {
1255 d->accountStatusBarIcons.remove ( account );
1256 delete sbIcon;
1257 }
1258 }
1259}
1260
1261void KopeteWindow::slotTrayAboutToShowMenu ( KMenu * popup )
1262{
1263 KActionCollection *actionCollection = d->tray->actionCollection();
1264
1265 popup->clear();
1266 popup->addTitle ( qApp->windowIcon(), KGlobal::caption() );
1267
1268 QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
1269 qSort(accountList.begin(), accountList.end(), invertedCompareOnlineStatus);
1270 foreach ( Kopete::Account *account, accountList )
1271 {
1272 KActionMenu *menu = new KActionMenu ( account->accountId(), account );
1273 menu->setIcon( account->myself()->onlineStatus().iconFor( account ) );
1274
1275 if ( !account->hasCustomStatusMenu() )
1276 Kopete::StatusRootAction::createAccountStatusActions ( account, menu );
1277
1278 account->fillActionMenu ( menu );
1279 popup->addAction ( menu );
1280
1281 connect ( popup , SIGNAL (aboutToHide()) , menu , SLOT (deleteLater()) );
1282 }
1283
1284 popup->addSeparator();
1285 popup->addAction ( d->actionStatusMenu );
1286 popup->addSeparator();
1287 popup->addAction ( d->actionPrefs );
1288 popup->addAction ( d->actionAddContact );
1289 popup->addSeparator();
1290 popup->addAction ( actionCollection->action ( "minimizeRestore" ) );
1291 popup->addAction ( actionCollection->action ( KStandardAction::name ( KStandardAction::Quit ) ) );
1292}
1293
1294void KopeteWindow::showExportDialog()
1295{
1296 KabcExportWizard* wizard = new KabcExportWizard ( this );
1297 wizard->setObjectName ( QLatin1String ( "export_contact_dialog" ) );
1298 wizard->show();
1299}
1300
1301void KopeteWindow::leaveEvent ( QEvent * )
1302{
1303 startAutoHideTimer();
1304}
1305
1306void KopeteWindow::showEvent ( QShowEvent * )
1307{
1308 startAutoHideTimer();
1309 slotStartAutoResizeTimer();
1310}
1311
1312void KopeteWindow::hideEvent ( QHideEvent * )
1313{
1314 d->autoResizeTimer->stop();
1315}
1316
1317void KopeteWindow::slotAutoHide()
1318{
1319 if ( this->geometry().contains ( QCursor::pos() ) == false )
1320 {
1321 /* The autohide-timer doesn't need to emit
1322 * timeouts when the window is hidden already. */
1323 d->autoHideTimer->stop();
1324 hide();
1325 }
1326}
1327
1328void KopeteWindow::startAutoHideTimer()
1329{
1330 if ( d->autoHideTimeout > 0 && d->autoHide == true && isVisible() && Kopete::BehaviorSettings::self()->showSystemTray() )
1331 d->autoHideTimer->start ( d->autoHideTimeout * 1000 );
1332}
1333
1334void KopeteWindow::slotStartAutoResizeTimer()
1335{
1336 if ( Kopete::AppearanceSettings::contactListAutoResize() == true )
1337 if ( ! d->autoResizeTimer->isActive() )
1338 d->autoResizeTimer->start ( 1000 );
1339}
1340
1341void KopeteWindow::setOnlineStatus( uint category, const Kopete::StatusMessage& statusMessage )
1342{
1343 Kopete::AccountManager::self()->setOnlineStatus( category, statusMessage, 0, true );
1344}
1345
1346void KopeteWindow::setStatusMessage ( const Kopete::StatusMessage& statusMessage )
1347{
1348 Kopete::StatusManager::self()->setGlobalStatusMessage ( statusMessage );
1349}
1350
1351void KopeteWindow::globalStatusChanged()
1352{
1353 QString statusTitle = Kopete::StatusManager::self()->globalStatusMessage().title();
1354 QString statusMessage = Kopete::StatusManager::self()->globalStatusMessage().message();
1355 d->globalStatusMessage->setText( statusTitle );
1356
1357 QString toolTip;
1358 toolTip += i18nc("@label:textbox formatted status title", "<b>Status&nbsp;Title:</b>&nbsp;%1",
1359 Kopete::Emoticons::parseEmoticons( Kopete::Message::escape(statusTitle) ) );
1360
1361 toolTip += i18nc("@label:textbox formatted status message", "<br /><b>Status&nbsp;Message:</b>&nbsp;%1",
1362 Kopete::Emoticons::parseEmoticons( Kopete::Message::escape(statusMessage) ) );
1363
1364 d->globalStatusMessage->setToolTip( toolTip );
1365}
1366
1367void KopeteWindow::slotGlobalStatusMessageIconClicked ( const QPoint &position )
1368{
1369 KMenu *menu = new KMenu ( this );
1370
1371 menu->addTitle ( i18n ( "Status Message" ) );
1372
1373 Kopete::UI::StatusEditAction* statusEditAction = new Kopete::UI::StatusEditAction ( this );
1374 statusEditAction->setStatusMessage ( Kopete::StatusManager::self()->globalStatusMessage() );
1375 connect ( statusEditAction, SIGNAL (statusChanged(Kopete::StatusMessage)),
1376 this, SLOT (setStatusMessage(Kopete::StatusMessage)) );
1377
1378 menu->addAction ( statusEditAction );
1379 menu->exec ( position );
1380
1381 statusEditAction->deleteLater();
1382 delete menu;
1383}
1384
1385void KopeteWindow::slotAddContactDialogInternal ( const QString & accountIdentifier )
1386{
1387 QString protocolId = accountIdentifier.section ( QChar ( 0xE000 ), 0, 0 );
1388 QString accountId = accountIdentifier.section ( QChar ( 0xE000 ), 1, 1 );
1389 Kopete::Account *account = Kopete::AccountManager::self()->findAccount ( protocolId, accountId );
1390 showAddContactDialog ( account );
1391}
1392
1393void KopeteWindow::updateStatusMenuMessage ( Kopete::StatusRootAction *statusRootAction )
1394{
1395 statusRootAction->setCurrentMessage ( Kopete::StatusManager::self()->globalStatusMessage() );
1396}
1397
1398void KopeteWindow::showAddContactDialog ( Kopete::Account * account )
1399{
1400 if ( !account ) {
1401 kDebug ( 14000 ) << "no account given";
1402 return;
1403 }
1404
1405 KDialog *addDialog = new KDialog ( this );
1406 addDialog->setCaption ( i18n ( "Add Contact" ) );
1407 addDialog->setButtons ( KDialog::Ok | KDialog::Cancel );
1408 addDialog->setDefaultButton ( KDialog::Ok );
1409 addDialog->showButtonSeparator ( true );
1410
1411 KVBox * mainWid = new KVBox ( addDialog );
1412
1413 AddContactPage *addContactPage =
1414 account->protocol()->createAddContactWidget ( mainWid, account );
1415
1416 QWidget* groupKABC = new QWidget ( mainWid );
1417 groupKABC->setObjectName ( "groupkabcwidget" );
1418 Ui::GroupKABCSelectorWidget ui_groupKABC;
1419 ui_groupKABC.setupUi ( groupKABC );
1420
1421 // Populate the groups list
1422 Kopete::GroupList groups=Kopete::ContactList::self()->groups();
1423 QHash<QString, Kopete::Group*> groupItems;
1424
1425 // Add top level group
1426 groupItems.insert ( Kopete::Group::topLevel()->displayName(), Kopete::Group::topLevel() );
1427 ui_groupKABC.groupCombo->addItem ( Kopete::Group::topLevel()->displayName() );
1428
1429 foreach ( Kopete::Group *group, groups )
1430 {
1431 if ( group->type() != Kopete::Group::Normal )
1432 continue;
1433 QString groupname = group->displayName();
1434 if ( !groupname.isEmpty() )
1435 {
1436 groupItems.insert ( groupname, group );
1437 ui_groupKABC.groupCombo->addItem ( groupname );
1438 }
1439 }
1440
1441 if ( !addContactPage )
1442 {
1443 kDebug ( 14000 ) <<
1444 "Error while creating addcontactpage" << endl;
1445 }
1446 else
1447 {
1448 addDialog->setMainWidget ( mainWid );
1449 if ( addDialog->exec() == QDialog::Accepted )
1450 {
1451 if ( addContactPage->validateData() )
1452 {
1453 Kopete::MetaContact * metacontact = new Kopete::MetaContact();
1454 metacontact->addToGroup ( groupItems[ ui_groupKABC.groupCombo->currentText() ] );
1455 metacontact->setKabcId ( ui_groupKABC.widAddresseeLink->uid() );
1456 if ( addContactPage->apply ( account, metacontact ) )
1457 {
1458 Kopete::ContactList::self()->addMetaContact ( metacontact );
1459 }
1460 else
1461 {
1462 delete metacontact;
1463 }
1464 }
1465 }
1466 }
1467 addDialog->deleteLater();
1468}
1469
1470void KopeteWindow::slotUpdateSize()
1471{
1472 /* resize with rules:
1473 - never will be taller than maxTall
1474 - never shorter than minTall
1475 - never will resize if contactlist is empty
1476 - never will resize if cursor is in window
1477 */
1478
1479 if ( this->geometry().contains ( QCursor::pos() ) == true )
1480 {
1481 return; // don't do anything if cursor is inside window
1482 }
1483 const int amountWindowBiggerThanContactList = 200;
1484 const QRect workArea = KWindowSystem::workArea();
1485 const int minHeight = 400;
1486 QRect newGeometry = geometry();
1487 const QRect oldGeometry = geometry();
1488 const int topFrameWidth = - ( frameGeometry().top() - oldGeometry.top() );
1489 const int bottomFrameWidth = frameGeometry().bottom() - oldGeometry.bottom();
1490
1491 // desired height is height of full contents of contact list tree, as well as
1492 // some buffer for other elements in the main window
1493 int height = d->contactlist->visibleContentHeight();
1494 newGeometry.setHeight ( height + amountWindowBiggerThanContactList );
1495
1496 if ( height ) {
1497 // if new size is too big or too small, bring inside limits
1498 if ( newGeometry.height() > workArea.height() )
1499 newGeometry.setHeight ( workArea.height() - topFrameWidth - bottomFrameWidth );
1500 else if ( newGeometry.height() < minHeight )
1501 newGeometry.setHeight ( minHeight );
1502
1503 // set position of new geometry rectangle to same position as the old one
1504 if ( Kopete::AppearanceSettings::contactListResizeAnchor() ==
1505 Kopete::AppearanceSettings::EnumContactListResizeAnchor::Top )
1506 newGeometry.moveTop ( oldGeometry.top() );
1507 else
1508 newGeometry.moveBottom ( oldGeometry.bottom() );
1509
1510 // if the window + its frame is out of the work area, bring it just inside
1511 if ( ( newGeometry.top() - topFrameWidth ) < workArea.top() )
1512 newGeometry.moveTop ( workArea.top() + topFrameWidth );
1513
1514 else if ( ( newGeometry.bottom() + bottomFrameWidth ) > workArea.bottom() )
1515 newGeometry.moveBottom ( workArea.bottom() - bottomFrameWidth );
1516
1517 // do it!
1518 setGeometry ( newGeometry );
1519 }
1520}
1521
1522
1523void KopeteWindow::slotNewInfoEvent()
1524{
1525 if ( !d->infoEventWidget->isVisible() )
1526 {
1527 if ( d->identitywidget->isVisible() )
1528 {
1529 d->identitywidget->setIdentity( 0 );
1530 d->identitywidget->setVisible( false );
1531 }
1532 d->infoEventWidget->setVisible( true );
1533 }
1534}
1535
1536#include "kopetewindow.moc"
1537// vim: set noet ts=4 sts=4 sw=4:
1538