1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 1999-2014 Licq developers <licq-dev@googlegroups.com>
4  *
5  * Licq is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Licq is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "mainwin.h"
21 
22 #include "config.h"
23 
24 #include <boost/foreach.hpp>
25 #include <cctype>
26 
27 #ifdef USE_KDE
28 #include <KDE/KApplication>
29 #include <KDE/KGlobal>
30 #include <KDE/KGlobalSettings>
31 #include <KDE/KStandardDirs>
32 #include <KDE/KWindowSystem>
33 #include <KDE/KIconLoader>
34 #include <KDE/KUrl>
35 #else
36 #include <QApplication>
37 #endif
38 
39 #include <QAction>
40 #include <QActionGroup>
41 #include <QCloseEvent>
42 #include <QDateTime>
43 #include <QDesktopWidget>
44 #include <QHBoxLayout>
45 #include <QImage>
46 #include <QKeyEvent>
47 #include <QMenu>
48 #include <QMoveEvent>
49 #include <QMouseEvent>
50 #include <QPainter>
51 #include <QPixmap>
52 #include <QPushButton>
53 #include <QResizeEvent>
54 #include <QShortcut>
55 #include <QStyle>
56 #include <QStyleFactory>
57 #include <QTextEdit>
58 #include <QVBoxLayout>
59 
60 #if defined(Q_WS_X11)
61 #include <QX11Info>
62 #endif /* defined(Q_WS_X11) */
63 
64 #include <licq/logging/log.h>
65 #include <licq/contactlist/group.h>
66 #include <licq/contactlist/owner.h>
67 #include <licq/contactlist/usermanager.h>
68 #include <licq/daemon.h>
69 #include <licq/event.h>
70 #include <licq/icq/icq.h>
71 #include <licq/plugin/protocolplugin.h>
72 #include <licq/pluginsignal.h>
73 
74 #include "config/contactlist.h"
75 #include "config/general.h"
76 #include "config/iconmanager.h"
77 #include "config/shortcuts.h"
78 #include "config/skin.h"
79 
80 #include "contactlist/contactlist.h"
81 
82 #include "dockicons/dockicon.h"
83 
84 #include "dialogs/aboutdlg.h"
85 #include "dialogs/adduserdlg.h"
86 #include "dialogs/awaymsgdlg.h"
87 #include "dialogs/hintsdlg.h"
88 #include "dialogs/historydlg.h"
89 #include "dialogs/logwindow.h"
90 #include "dialogs/ownermanagerdlg.h"
91 #include "dialogs/showawaymsgdlg.h"
92 #include "dialogs/statsdlg.h"
93 
94 #include "helpers/support.h"
95 
96 #include "widgets/skinnablebutton.h"
97 #include "widgets/skinnablecombobox.h"
98 #include "widgets/skinnablelabel.h"
99 
100 #include "views/userview.h"
101 
102 #include "messagebox.h"
103 #include "gui-defines.h"
104 #include "licqgui.h"
105 #include "signalmanager.h"
106 #include "systemmenu.h"
107 #include "usermenu.h"
108 
109 using namespace LicqQtGui;
110 /* TRANSLATOR LicqQtGui::MainWindow */
111 
112 MainWindow* LicqQtGui::gMainWindow = NULL;
113 
MainWindow(bool bStartHidden,QWidget * parent)114 MainWindow::MainWindow(bool bStartHidden, QWidget* parent)
115   : QWidget(parent),
116     myInMiniMode(false)
117 {
118   Support::setWidgetProps(this, "MainWindow");
119   setAttribute(Qt::WA_AlwaysShowToolTips, true);
120 
121   assert(gMainWindow == NULL);
122   gMainWindow = this;
123 
124   Config::General* conf = Config::General::instance();
125 
126   connect(conf, SIGNAL(mainwinChanged()), SLOT(updateConfig()));
127   connect(Config::ContactList::instance(),
128       SIGNAL(currentListChanged()), SLOT(updateCurrentGroup()));
129 
130   myCaption = "Licq";
131   setWindowTitle(myCaption);
132   setWindowIconText(myCaption);
133 
134   // Group Combo Box
135   myUserGroupsBox = new SkinnableComboBox(this);
136   connect(myUserGroupsBox, SIGNAL(activated(int)), SLOT(setCurrentGroup(int)));
137 
138   // Widgets controlled completely by the skin
139   mySystemButton = NULL;
140   myMessageField = NULL;
141   myStatusField = NULL;
142   myMenuBar = NULL;
143   connect(IconManager::instance(),
144       SIGNAL(statusIconsChanged()), SLOT(updateStatus()));
145 
146   mySystemMenu = new SystemMenu(this);
147 
148   QActionGroup* userFuncGroup = new QActionGroup(this);
149   userFuncGroup->setExclusive(false);
150   connect(userFuncGroup,
151       SIGNAL(triggered(QAction*)), SLOT(callUserFunction(QAction*)));
152 #define ADD_USERFUNCACTION(var, data) \
153   var = new QAction(userFuncGroup); \
154   var->setData(data);
155 
156   ADD_USERFUNCACTION(myViewEventAction, -1)
157   ADD_USERFUNCACTION(mySendMessageAction, MessageEvent)
158   ADD_USERFUNCACTION(mySendUrlAction, UrlEvent)
159   ADD_USERFUNCACTION(mySendFileAction, ChatEvent)
160   ADD_USERFUNCACTION(mySendChatRequestAction, FileEvent)
161 #undef ADD_USERFUNCACTION
162   addActions(userFuncGroup->actions());
163 
164   myCheckUserArAction = new QAction(this);
165   addAction(myCheckUserArAction);
166   connect(myCheckUserArAction, SIGNAL(triggered()), SLOT(checkUserAutoResponse()));
167   myViewHistoryAction = new QAction(this);
168   addAction(myViewHistoryAction);
169   connect(myViewHistoryAction, SIGNAL(triggered()), SLOT(showUserHistory()));
170   QShortcut* shortcut;
171   shortcut = new QShortcut(Qt::CTRL + Qt::Key_Delete, this);
172   connect(shortcut, SIGNAL(activated()), SLOT(removeUserFromList()));
173   shortcut = new QShortcut(Qt::Key_Delete, this);
174   connect(shortcut, SIGNAL(activated()), SLOT(removeUserFromGroup()));
175 
176   updateShortcuts();
177   connect(Config::Shortcuts::instance(), SIGNAL(shortcutsChanged()), SLOT(updateShortcuts()));
178 
179   CreateUserView();
180 
181   usprintfHelp = tr(
182       "<ul>"
183       "<li><tt>%a - </tt>user alias</li>"
184       "<li><tt>%c - </tt>cellular number</li>"
185       "<li><tt>%e - </tt>email</li>"
186       "<li><tt>%f - </tt>first name</li>"
187       "<li><tt>%h - </tt>phone number</li>"
188       "<li><tt>%i - </tt>user ip</li>"
189       "<li><tt>%l - </tt>last name</li>"
190       "<li><tt>%L - </tt>local time</li>"
191       "<li><tt>%m - </tt># pending messages</li>"
192       "<li><tt>%M - </tt># pending messages (if any)</li>"
193       "<li><tt>%n - </tt>full name</li>"
194       "<li><tt>%o - </tt>last seen online</li>"
195       "<li><tt>%O - </tt>online since</li>"
196       "<li><tt>%p - </tt>user port</li>"
197       "<li><tt>%P - </tt>Protocol</li>"
198       "<li><tt>%s - </tt>full status</li>"
199       "<li><tt>%S - </tt>abbreviated status</li>"
200       "<li><tt>%u - </tt>uin</li>"
201       "<li><tt>%w - </tt>webpage</li></ul>");
202 
203   connect(gGuiSignalManager, SIGNAL(updatedList(unsigned long, int, const Licq::UserId&)),
204       SLOT(slot_updatedList(unsigned long)));
205   connect(gGuiSignalManager, SIGNAL(updatedUser(const Licq::UserId&, unsigned long, int, unsigned long)),
206       SLOT(slot_updatedUser(const Licq::UserId&, unsigned long, int)));
207   connect(gGuiSignalManager, SIGNAL(updatedStatus(const Licq::UserId&)),
208       SLOT(updateStatus()));
209   connect(gGuiSignalManager, SIGNAL(ownerAdded(const Licq::UserId&)),
210       SLOT(updateStatus()));
211   connect(gGuiSignalManager, SIGNAL(ownerRemoved(const Licq::UserId&)),
212       SLOT(updateStatus()));
213   connect(gGuiSignalManager, SIGNAL(logon()),
214       SLOT(slot_logon()));
215   connect(gGuiSignalManager, SIGNAL(ui_showuserlist()), SLOT(unhide()));
216   connect(gGuiSignalManager, SIGNAL(ui_hideuserlist()), SLOT(hide()));
217 
218   if (conf->mainwinRect().isValid())
219     setGeometry(conf->mainwinRect());
220   else
221   {
222     QSize newSize = myUserView->sizeHint();
223     if (newSize.width() > newSize.height())
224       newSize.transpose();
225     myUserView->resize(newSize);
226     adjustSize();
227   }
228   updateSkin();
229   connect(Config::Skin::active(), SIGNAL(changed()), SLOT(updateSkin()));
230   connect(Config::General::instance(), SIGNAL(styleChanged()), SLOT(updateSkin()));
231 
232   updateGroups(true);
233 
234   setMiniMode(conf->miniMode());
235   if(!conf->mainwinStartHidden() && !bStartHidden)
236   {
237     setVisible(true);
238     raise();
239   }
240 
241   // verify we exist
242   if (Licq::gUserManager.NumOwners() == 0)
243     OwnerManagerDlg::showOwnerManagerDlg();
244 
245   updateStatus();
246 
247   // Check if MainWin should be sticky
248   if (Config::General::instance()->mainwinSticky())
249     setMainwinSticky(true);
250 }
251 
~MainWindow()252 MainWindow::~MainWindow()
253 {
254   delete myUserView;
255 
256   gMainWindow = NULL;
257 }
258 
updateConfig()259 void MainWindow::updateConfig()
260 {
261   Config::General* generalConfig = Config::General::instance();
262 
263   setMainwinSticky(generalConfig->mainwinSticky());
264   setMiniMode(generalConfig->miniMode());
265 
266   // Redraw group/event label with new settings
267   updateEvents();
268 }
269 
updateShortcuts()270 void MainWindow::updateShortcuts()
271 {
272   Config::Shortcuts* shortcuts = Config::Shortcuts::instance();
273 
274   myViewEventAction->setShortcut(shortcuts->getShortcut(Config::Shortcuts::MainwinUserViewMessage));
275   mySendMessageAction->setShortcut(shortcuts->getShortcut(Config::Shortcuts::MainwinUserSendMessage));
276   mySendUrlAction->setShortcut(shortcuts->getShortcut(Config::Shortcuts::MainwinUserSendUrl));
277   mySendFileAction->setShortcut(shortcuts->getShortcut(Config::Shortcuts::MainwinUserSendFile));
278   mySendChatRequestAction->setShortcut(shortcuts->getShortcut(Config::Shortcuts::MainwinUserSendChatRequest));
279   myCheckUserArAction->setShortcut(shortcuts->getShortcut(Config::Shortcuts::MainwinUserCheckAutoresponse));
280   myViewHistoryAction->setShortcut(shortcuts->getShortcut(Config::Shortcuts::MainwinUserViewHistory));
281 }
282 
trayIconClicked()283 void MainWindow::trayIconClicked()
284 {
285   if (isVisible() && !isMinimized() && isActiveWindow())
286     hide();
287   else
288     unhide();
289 }
290 
unhide()291 void MainWindow::unhide()
292 {
293   show();
294 #ifdef USE_KDE
295   KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
296 #endif
297   if (isMaximized())
298     showMaximized();
299   else
300     showNormal();
301 
302   // Sticky state is lost when window is hidden so restore it now
303   if (Config::General::instance()->mainwinSticky())
304     setMainwinSticky(true);
305 
306   activateWindow();
307   raise();
308 }
309 
updateSkin()310 void MainWindow::updateSkin()
311 {
312   Config::Skin* skin = Config::Skin::active();
313 
314 #define CLEAR(elem) \
315   if ((elem) != NULL) \
316   { \
317     delete (elem); \
318     (elem) = NULL; \
319   }
320 
321   // Set the background pixmap and mask
322   if (skin->frame.pixmap.isNull())
323     setPalette(QPalette());
324 
325   if (skin->frame.mask.isNull())
326     clearMask();
327 
328   // System Button
329   CLEAR(mySystemButton);
330   CLEAR(myMenuBar);
331 
332   if (skin->frame.hasMenuBar ||
333       skin->btnSys.rect.isNull())
334   {
335 #ifdef USE_KDE
336     myMenuBar = new KMenuBar(this);
337 #else
338     myMenuBar = new QMenuBar(this);
339 #endif
340     mySystemMenu->setTitle(skin->btnSys.caption.isNull() ?
341         tr("&System") : skin->btnSys.caption);
342     myMenuBar->addMenu(mySystemMenu);
343     myMenuBar->setMinimumWidth(mySystemMenu->width());
344     myMenuBar->show();
345 #ifndef Q_WS_MAC
346     skin->AdjustForMenuBar(myMenuBar->height());
347 #endif
348   }
349   else
350   {
351     mySystemButton = new SkinnableButton(skin->btnSys, tr("System"), this);
352     mySystemButton->setMenu(mySystemMenu);
353     mySystemButton->show();
354   }
355 
356   unsigned minHeight = skin->frame.border.top + skin->frame.border.bottom;
357   setMinimumHeight(minHeight);
358   setMaximumHeight(Config::General::instance()->miniMode() ?
359       minHeight : QWIDGETSIZE_MAX);
360 
361   // Group Combo Box
362   myUserGroupsBox->applySkin(skin->cmbGroups);
363   myUserGroupsBox->setVisible(!skin->cmbGroups.rect.isNull());
364 
365   // Message Label
366   CLEAR(myMessageField);
367   if (!skin->lblMsg.rect.isNull())
368   {
369     myMessageField = new SkinnableLabel(skin->lblMsg,
370         mySystemMenu->getGroupMenu(), this);
371     connect(myMessageField, SIGNAL(doubleClicked()),
372         gLicqGui, SLOT(showNextEvent()));
373     connect(myMessageField, SIGNAL(wheelDown()), SLOT(nextGroup()));
374     connect(myMessageField, SIGNAL(wheelUp()), SLOT(prevGroup()));
375     myMessageField->setToolTip(tr("Right click - User groups\n"
376           "Double click - Show next message"));
377     myMessageField->show();
378   }
379 
380   // Status Label
381   CLEAR(myStatusField);
382   if (!skin->lblStatus.rect.isNull())
383   {
384     myStatusField = new SkinnableLabel(skin->lblStatus,
385         mySystemMenu->getStatusMenu(), this);
386     connect(myStatusField, SIGNAL(doubleClicked()), SLOT(showAwayMsgDlg()));
387     myStatusField->setToolTip(tr("Right click - Status menu\n"
388           "Double click - Set auto response"));
389     myStatusField->show();
390   }
391 
392 #undef CLEAR
393 
394   // update geometry and contents of the elements
395   resizeEvent(NULL);
396   updateEvents();
397   updateStatus();
398 }
399 
CreateUserView()400 void MainWindow::CreateUserView()
401 {
402   myUserView = new UserView(gGuiContactList, this);
403   connect (myUserView, SIGNAL(userDoubleClicked(const Licq::UserId&)),
404       gLicqGui, SLOT(showDefaultEventDialog(const Licq::UserId&)));
405 }
406 
resizeEvent(QResizeEvent *)407 void MainWindow::resizeEvent(QResizeEvent* /* e */)
408 {
409   Config::Skin* skin = Config::Skin::active();
410 
411   myUserView->setGeometry(skin->frame.border.left, skin->frame.border.top,
412                         width() - skin->frameWidth(), height() - skin->frameHeight());
413 
414   // Resize the background pixmap and mask
415   QPixmap pixmap = skin->mainwinPixmap(width(), height());
416   if (!pixmap.isNull())
417   {
418     QPalette pal(palette());
419     pal.setBrush(backgroundRole(), pixmap);
420     setPalette(pal);
421   }
422   QPixmap mask = skin->mainwinMask(width(), height());
423   if (!mask.isNull())
424     setMask(mask);
425 
426 #define UPDATE(wid, group) \
427   if ((wid) != NULL) \
428     (wid)->setGeometry(skin->group.borderToRect(this));
429 
430   // Set geometry of our widgets
431   UPDATE(myUserGroupsBox, cmbGroups);
432   UPDATE(myMessageField, lblMsg);
433   UPDATE(myStatusField, lblStatus);
434   UPDATE(mySystemButton, btnSys)
435   else
436     myMenuBar->resize(contentsRect().width(), myMenuBar->height());
437 
438 #undef UPDATE
439 
440   if (isVisible())
441     saveGeometry();
442 }
443 
saveGeometry()444 void MainWindow::saveGeometry()
445 {
446   Config::General* conf = Config::General::instance();
447   QRect geom = geometry();
448 
449   // When first showing window, we may get a rect without frame which will be
450   // become wrong if used for a window with a frame
451   if (geom == frameGeometry())
452     return;
453 
454   // For setGeometry to get it right we need position including frame but
455   // size without frame
456   geom.moveTo(frameGeometry().topLeft());
457 
458   if (myInMiniMode)
459     geom.setHeight(conf->mainwinRect().height());
460 
461   conf->setMainwinRect(geom);
462 }
463 
moveEvent(QMoveEvent *)464 void MainWindow::moveEvent(QMoveEvent* /* e */)
465 {
466   saveGeometry();
467 }
468 
closeEvent(QCloseEvent * e)469 void MainWindow::closeEvent(QCloseEvent* e)
470 {
471   e->ignore();
472 
473   if (gLicqGui->dockIcon() != NULL)
474     hide();
475   else
476     slot_shutdown();
477 }
478 
removeUserFromList()479 void MainWindow::removeUserFromList()
480 {
481   Licq::UserId userId = myUserView->currentUserId();
482 
483   gLicqGui->removeUserFromList(userId, this);
484 }
485 
removeUserFromGroup()486 void MainWindow::removeUserFromGroup()
487 {
488   int groupId = Config::ContactList::instance()->groupId();
489 
490   // Removing "All users" is the same as removing user from the list
491   if (groupId == ContactListModel::MostUsersGroupId ||
492       groupId == ContactListModel::AllGroupsGroupId)
493   {
494     removeUserFromList();
495     return;
496   }
497 
498   // Get currently selected user
499   Licq::UserId userId = myUserView->currentUserId();
500 
501   gLicqGui->setUserInGroup(userId, groupId, false);
502 }
503 
callUserFunction(QAction * action)504 void MainWindow::callUserFunction(QAction* action)
505 {
506   int index = action->data().toInt();
507   Licq::UserId userId = myUserView->currentUserId();
508 
509   if (index == -1)
510     gLicqGui->showViewEventDialog(userId);
511   else
512     gLicqGui->showEventDialog(index, userId);
513 }
514 
checkUserAutoResponse()515 void MainWindow::checkUserAutoResponse()
516 {
517   Licq::UserId userId = myUserView->currentUserId();
518   if (userId.isValid())
519     new ShowAwayMsgDlg(userId, true);
520 }
521 
showUserHistory()522 void MainWindow::showUserHistory()
523 {
524   Licq::UserId userId = myUserView->currentUserId();
525   if (userId.isValid())
526     new HistoryDlg(userId);
527 }
528 
hide()529 void MainWindow::hide()
530 {
531   if (gLicqGui->dockIcon() != NULL)
532     QWidget::hide();
533 }
534 
mousePressEvent(QMouseEvent * m)535 void MainWindow::mousePressEvent(QMouseEvent* m)
536 {
537    myMouseX = m->x();
538    myMouseY = m->y();
539 }
540 
541 /*! \brief Drags the mainwindow around
542  *
543  * If the appropriate option is set (EnableMainwinMouseMovement = 1)
544  * this drags the mainwindow around when moving the mouse and left
545  * button is pressed.
546  */
mouseMoveEvent(QMouseEvent * m)547 void MainWindow::mouseMoveEvent(QMouseEvent* m)
548 {
549   if (Config::General::instance()->mainwinDraggable() && (m->buttons() == Qt::LeftButton))
550   {
551     int deltaX = m->x() - myMouseX;
552     int deltaY = m->y() - myMouseY;
553     move(x() + deltaX, y() + deltaY);
554   }
555 }
556 
slot_updatedUser(const Licq::UserId & userId,unsigned long subSignal,int argument)557 void MainWindow::slot_updatedUser(const Licq::UserId& userId, unsigned long subSignal, int argument)
558 {
559   switch(subSignal)
560   {
561     case Licq::PluginSignal::UserEvents:
562     {
563       // Skip all this if it was just an away message check
564       if (argument == 0)
565         break;
566 
567       // Otherwise an event was added or removed
568       updateEvents();
569       // autoRaise if needed
570       if (Config::General::instance()->autoRaiseMainwin() &&
571           argument > 0)
572         raise();
573 
574       // Fall through
575     }
576     case Licq::PluginSignal::UserStatus:
577     case Licq::PluginSignal::UserBasic: // for alias
578     case Licq::PluginSignal::UserSettings: // for online notify
579     case Licq::PluginSignal::UserSecurity:
580     case Licq::PluginSignal::UserTyping:
581     {
582       Licq::UserReadGuard u(userId);
583       if (!u.isLocked())
584       {
585         Licq::gLog.warning("MainWindow::slot_updatedUser(): Invalid user received: %s",
586             userId.toString().c_str());
587         break;
588       }
589 
590       if (subSignal == Licq::PluginSignal::UserStatus &&
591           argument == 1 &&
592           Config::General::instance()->trayMsgOnlineNotify())
593       {
594         // User on notify list went online -> show popup at systray icon
595         if (gLicqGui->dockIcon() != NULL && u->OnlineNotify())
596         {
597           QString alias = QString::fromUtf8(u->getAlias().c_str());
598           QPixmap px = IconManager::instance()->iconForUser(*u);
599           gLicqGui->dockIcon()->popupMessage(alias, tr("is online"), px, 4000);
600         }
601       }
602 
603       break;
604     }
605   }
606 }
607 
slot_updatedList(unsigned long subSignal)608 void MainWindow::slot_updatedList(unsigned long subSignal)
609 {
610   switch(subSignal)
611   {
612     case Licq::PluginSignal::ListUserRemoved:
613       updateEvents();
614       break;
615   }
616 }
617 
updateEvents()618 void MainWindow::updateEvents()
619 {
620   QString szCaption;
621   unsigned short nNumOwnerEvents = 0;
622 
623   {
624     Licq::OwnerListGuard ownerList;
625     BOOST_FOREACH(const Licq::Owner* owner, **ownerList)
626     {
627       Licq::OwnerReadGuard o(owner);
628       nNumOwnerEvents += o->NewMessages();
629     }
630   }
631 
632   unsigned short nNumUserEvents = Licq::User::getNumUserEvents() - nNumOwnerEvents;
633 
634   if (myMessageField != NULL)
635     myMessageField->setBold(false);
636   QString s, l;
637 
638   if (nNumOwnerEvents > 0)
639   {
640     s = tr("SysMsg");
641     l = tr("System Message");
642     if (Config::General::instance()->boldOnMsg() &&
643         myMessageField != NULL)
644       myMessageField->setBold(true);
645     szCaption = "* " + myCaption;
646   }
647   else if (nNumUserEvents > 0)
648   {
649     s = tr("%1 msg%2").arg(nNumUserEvents).arg(nNumUserEvents == 1 ? tr(" ") : tr("s"));
650     l = tr("%1 message%2").arg(nNumUserEvents).arg(nNumUserEvents == 1 ? tr(" ") : tr("s"));
651     if (Config::General::instance()->boldOnMsg() &&
652         myMessageField != NULL)
653       myMessageField->setBold(true);
654     szCaption = "* " + myCaption;
655   }
656   else
657   {
658     // Update the msg label if necessary
659     if (Config::General::instance()->showGroupIfNoMsg() && Licq::User::getNumUserEvents() == 0)
660     {
661       s = myUserGroupsBox->currentText();
662       l = myUserGroupsBox->currentText();
663     }
664     else
665     {
666       s = tr("No msgs");
667       l = tr("No messages");
668     }
669     szCaption = myCaption;
670   }
671 
672   setWindowTitle(szCaption);
673   setWindowIconText(szCaption);
674 
675   if (myMessageField != NULL)
676   {
677     if (myMessageField->fontMetrics().width(l) +
678         myMessageField->margin() > myMessageField->width())
679       myMessageField->setText(s);
680     else
681       myMessageField->setText(l);
682     myMessageField->update();
683   }
684 
685   if (gLicqGui->dockIcon() != NULL)
686     gLicqGui->dockIcon()->updateIconMessages(nNumUserEvents, nNumOwnerEvents);
687 }
688 
setCurrentGroup(int index)689 void MainWindow::setCurrentGroup(int index)
690 {
691   int groupId = myUserGroupsBox->itemData(index).toInt();
692 
693   Config::ContactList::instance()->setGroup(groupId);
694 }
695 
nextGroup()696 void MainWindow::nextGroup()
697 {
698   int curGroupId = Config::ContactList::instance()->groupId();
699   int groupId = 0;
700 
701   // Threaded view is selected, set all users
702   if (curGroupId == ContactListModel::AllGroupsGroupId)
703   {
704     Config::ContactList::instance()->setGroup(ContactListModel::MostUsersGroupId);
705     return;
706   }
707 
708   {
709     Licq::GroupListGuard groupList;
710     BOOST_FOREACH(Licq::Group *g, **groupList)
711     {
712       Licq::GroupReadGuard group(g);
713 
714       // If current selection is all users, select first group in list
715       if (groupId == 0 && curGroupId == ContactListModel::MostUsersGroupId)
716       {
717         Config::ContactList::instance()->setGroup(group->id());
718         return;
719       }
720 
721       // If previous group is selected, select current group
722       if (groupId != 0 && curGroupId == groupId)
723       {
724         Config::ContactList::instance()->setGroup(group->id());
725         return;
726       }
727 
728       groupId = group->id();
729     }
730   }
731 
732   // Last user group is currently selected, set first system group
733   if (groupId != 0 && curGroupId == groupId)
734   {
735     Config::ContactList::instance()->setGroup(ContactListModel::SystemGroupOffset + 0);
736     return;
737   }
738 
739   // No users groups exist and current selection is all users, set first system group
740   if (groupId == 0 && curGroupId == ContactListModel::MostUsersGroupId)
741   {
742     Config::ContactList::instance()->setGroup(ContactListModel::SystemGroupOffset + 0);
743     return;
744   }
745 
746   groupId = 0;
747 
748   for (int i = ContactListModel::SystemGroupOffset; i <= ContactListModel::LastSystemGroup; ++i)
749   {
750     // If previous system group is selected, set current group
751     if (groupId != 0 && groupId == curGroupId)
752     {
753       Config::ContactList::instance()->setGroup(i);
754       return;
755     }
756     groupId = i;
757   }
758 
759   // Last system group is currently selected or selection not found, set thread view
760   Config::ContactList::instance()->setGroup(ContactListModel::AllGroupsGroupId);
761 }
762 
763 
prevGroup()764 void MainWindow::prevGroup()
765 {
766   int curGroupId = Config::ContactList::instance()->groupId();
767   int groupId = 0;
768 
769   // All users is selected, set thread view
770   if (curGroupId == ContactListModel::MostUsersGroupId)
771   {
772     Config::ContactList::instance()->setGroup(ContactListModel::AllGroupsGroupId);
773     return;
774   }
775 
776   {
777     Licq::GroupListGuard groupList;
778     BOOST_FOREACH(Licq::Group* g, **groupList)
779     {
780       Licq::GroupReadGuard group(g);
781 
782       // If current group is selected, set previous group
783       if (curGroupId == group->id())
784       {
785         if (groupId == 0)
786           Config::ContactList::instance()->setGroup(ContactListModel::MostUsersGroupId);
787         else
788           Config::ContactList::instance()->setGroup(groupId);
789         return;
790       }
791 
792       groupId = group->id();
793     }
794   }
795 
796   // If first system group is selected, set last user group
797   if (groupId != 0 && curGroupId == ContactListModel::SystemGroupOffset + 0)
798   {
799     if (groupId == 0)
800       Config::ContactList::instance()->setGroup(ContactListModel::MostUsersGroupId);
801     else
802       Config::ContactList::instance()->setGroup(groupId);
803     return;
804   }
805   groupId = 0;
806 
807   for (int i = ContactListModel::SystemGroupOffset; i <= ContactListModel::LastSystemGroup; ++i)
808   {
809     // If current system group is selected, set previous group
810     if (curGroupId == i)
811     {
812       Config::ContactList::instance()->setGroup(groupId);
813       return;
814     }
815     groupId = i;
816   }
817 
818   // If current selection is thread view or selection not found, set last system group
819   Config::ContactList::instance()->setGroup(ContactListModel::LastSystemGroup);
820 }
updateCurrentGroup()821 void MainWindow::updateCurrentGroup()
822 {
823   int groupId = Config::ContactList::instance()->groupId();
824 
825   // Update the combo box
826   int index = myUserGroupsBox->findData(groupId);
827   if (index == -1)
828     return;
829   myUserGroupsBox->setCurrentIndex(index);
830 
831   // Update the msg label if necessary
832   if (myMessageField != NULL &&
833       Config::General::instance()->showGroupIfNoMsg() &&
834       Licq::User::getNumUserEvents() == 0)
835     myMessageField->setText(myUserGroupsBox->currentText());
836 }
837 
updateGroups(bool initial)838 void MainWindow::updateGroups(bool initial)
839 {
840   if (!initial)
841   {
842     // update group list in user and system menus
843     mySystemMenu->updateGroups();
844   }
845 
846   // update the combo box
847   myUserGroupsBox->clear();
848 
849 #define ADD_SYSTEMGROUP(groupId) \
850   myUserGroupsBox->addItem(ContactListModel::systemGroupName(groupId), groupId);
851 
852   ADD_SYSTEMGROUP(ContactListModel::AllGroupsGroupId);
853   ADD_SYSTEMGROUP(ContactListModel::MostUsersGroupId);
854 
855   {
856     Licq::GroupListGuard groupList(true);
857     BOOST_FOREACH(const Licq::Group* group, **groupList)
858     {
859       Licq::GroupReadGuard pGroup(group);
860       myUserGroupsBox->addItem(QString::fromLocal8Bit(pGroup->name().c_str()), pGroup->id());
861     }
862   }
863 
864   for (int i = ContactListModel::SystemGroupOffset; i <= ContactListModel::LastSystemGroup; i++)
865   {
866     ADD_SYSTEMGROUP(i);
867   }
868 
869 #undef ADD_SYSTEMGROUP
870 
871   updateCurrentGroup();
872 }
873 
updateStatus()874 void MainWindow::updateStatus()
875 {
876   if (gLicqGui->dockIcon() != NULL)
877     gLicqGui->dockIcon()->updateIconStatus();
878 
879   if (myStatusField == NULL)
880     return;
881 
882   IconManager* iconman = IconManager::instance();
883   Config::Skin* skin = Config::Skin::active();
884   QColor theColor = skin->offlineColor;
885 
886   myStatusField->clearPixmaps();
887   myStatusField->clearPrependPixmap();
888   myStatusField->setText(QString::null);
889 
890   {
891     Licq::OwnerListGuard ownerList;
892     Licq::OwnerList::const_iterator it = ownerList->begin();
893     switch (ownerList->size())
894     {
895       case 0:
896         break;
897       case 1:
898       {
899         Licq::OwnerReadGuard o(*it);
900         myStatusField->setText(Licq::User::statusToString(o->status()).c_str());
901         myStatusField->setPrependPixmap(iconman->iconForUser(*o));
902         if (o->status() == Licq::User::OfflineStatus)
903           theColor = skin->offlineColor;
904         else if (o->status() & Licq::User::AwayStatuses)
905           theColor = skin->awayColor;
906         else
907           theColor = skin->onlineColor;
908         break;
909       }
910       default:
911         BOOST_FOREACH(Licq::Owner* owner, **ownerList)
912         {
913           Licq::OwnerReadGuard o(owner);
914           myStatusField->addPixmap(iconman->iconForUser(*o));
915         }
916     }
917   }
918 
919   myStatusField->update();
920 
921   // set the color if it isn't set by the skin
922   if (!skin->lblStatus.foreground.isValid() && theColor.isValid())
923   {
924     QPalette pal(myStatusField->palette());
925     pal.setColor(QPalette::WindowText, theColor);
926     myStatusField->setPalette(pal);
927   }
928 }
929 
showAwayMsgDlg()930 void MainWindow::showAwayMsgDlg()
931 {
932   // Find an owner that can hold a status message
933   // If multiple exist, get the worst status (e.g. most away)
934   unsigned status = Licq::User::OfflineStatus;
935   {
936     Licq::OwnerListGuard ownerList;
937     BOOST_FOREACH(const Licq::Owner* owner, **ownerList)
938     {
939       Licq::OwnerReadGuard o(owner);
940       if ((o->protocolCapabilities() & Licq::ProtocolPlugin::CanHoldStatusMsg) == 0)
941         continue;
942 
943       if (o->status() > status)
944         status = o->status();
945     }
946   }
947 
948   // If no owner supports status message or all are offline, don't bother
949   if (status == Licq::User::OfflineStatus)
950     return;
951 
952   AwayMsgDlg::showAwayMsgDlg(status);
953 }
954 
slot_shutdown()955 void MainWindow::slot_shutdown()
956 {
957   Licq::gDaemon.Shutdown();
958 }
959 
slot_logon()960 void MainWindow::slot_logon()
961 {
962   updateStatus();
963 }
964 
showAboutBox()965 void MainWindow::showAboutBox()
966 {
967   new AboutDlg(this);
968 }
969 
showStats()970 void MainWindow::showStats()
971 {
972   new StatsDlg(this);
973 }
974 
setMiniMode(bool miniMode)975 void MainWindow::setMiniMode(bool miniMode)
976 {
977   if (miniMode == myInMiniMode)
978     return;
979 
980   myInMiniMode = miniMode;
981 
982   if (miniMode)
983     setMaximumHeight(minimumHeight());
984   else
985   {
986     setMaximumHeight(QWIDGETSIZE_MAX);
987     resize(width(), Config::General::instance()->mainwinRect().height());
988   }
989 
990   if (myUserView != NULL)
991     myUserView->setVisible(!miniMode);
992 }
993 
showHints()994 void MainWindow::showHints()
995 {
996   QString hints = tr(
997     "<h2>Hints for Using<br>the Licq %1 Plugin</h2><hr>"
998     "<ul>"
999     "<li>Change your status by right clicking on the status label.</li>"
1000     "<li>Change your auto response by double-clicking on the status label.</li>"
1001     "<li>View system messages by double clicking on the message label.</li>"
1002     "<li>Change groups by right clicking on the message label.</li>"
1003     "<li>Hold control while clicking on close in the function window to remove"
1004     "   the user from your contact list.</li>"
1005     "<li>Hit Ctrl-Enter from most text entry fields to select \"Ok\" or \"Accept\"."
1006     "   For example in the send tab of the user function window.</li>"
1007     "<li>Here is the complete list of user % options, which can be used in <b>OnEvent</b>"
1008     "   parameters and <b>auto responses</b>:")
1009     .arg(DISPLAY_PLUGIN_NAME) +
1010     gMainWindow->usprintfHelp +
1011     "</li></ul><hr>" +
1012 #ifndef USE_KDE
1013     "<h2>" + tr("Qt URL handling") + "</h2>"
1014     "<center><table border=1 cellpadding=5>"
1015     "<tr><th colspan=2>Web</th><th colspan=2>E-mail (mailto:)</th></tr>"
1016     "<tr><th>KDE</th><th>GNOME</th><th>GNOME</th><th>KDE</th></tr>"
1017     "<tr><td align=center colspan=4>xdg-open</td></tr>"
1018     "<tr><td align=center colspan=2>$DEFAULT_BROWSER</td>"
1019         "<td colspan=2 rowspan=2>&nbsp;</td></tr>"
1020     "<tr><td align=center colspan=2>$BROWSER</td></tr>"
1021     "<tr><td align=center>kfmclient openURL</td>"
1022         "<td align=center colspan=2>gnome-open</td>"
1023         "<td align=center>kfmclient exec</td></tr>"
1024     "<tr><td align=center colspan=4>firefox</td></tr>"
1025     "<tr><td align=center colspan=4>mozilla</td></tr>"
1026     "<tr><td align=center colspan=4>netscape</td></tr>"
1027     "<tr><td align=center colspan=4>opera</td></tr>"
1028     "</table></center><hr>" +
1029 #endif
1030     tr("<p>For more information, see the Licq webpage (<tt>http://www.licq.org</tt>).</p>");
1031 
1032   new HintsDlg(hints);
1033 }
1034 
showAutoResponseHints(QWidget * parent)1035 void MainWindow::showAutoResponseHints(QWidget* parent)
1036 {
1037   AwayMsgDlg::showAutoResponseHints(parent);
1038 }
1039 
addUser(const Licq::UserId & userId)1040 void MainWindow::addUser(const Licq::UserId& userId)
1041 {
1042   if (!userId.isValid())
1043     return;
1044 
1045   new AddUserDlg(userId);
1046 }
1047 
setMainwinSticky(bool sticky)1048 void MainWindow::setMainwinSticky(bool sticky)
1049 {
1050   Support::changeWinSticky(winId(), sticky);
1051 }
1052