1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2000-2012 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 "usereventtabdlg.h"
21 
22 #include "config.h"
23 
24 #include <licq/contactlist/user.h>
25 #include <licq/userevents.h>
26 
27 #include <QAction>
28 #include <QActionGroup>
29 #include <QTimer>
30 #include <QVBoxLayout>
31 
32 #ifdef USE_KDE
33 #include <KDE/KWindowSystem>
34 #endif
35 
36 #include "config/chat.h"
37 #include "config/iconmanager.h"
38 #include "config/shortcuts.h"
39 
40 #include "helpers/support.h"
41 
42 #include "widgets/tabwidget.h"
43 
44 #include "usereventcommon.h"
45 #include "usersendevent.h"
46 
47 using namespace LicqQtGui;
48 /* TRANSLATOR LicqQtGui::UserEventTabDlg */
49 
50 using std::list;
51 using std::string;
52 
UserEventTabDlg(QWidget * parent,const char * name)53 UserEventTabDlg::UserEventTabDlg(QWidget* parent, const char* name)
54   : QWidget(parent)
55 {
56   Support::setWidgetProps(this, name);
57   setAttribute(Qt::WA_DeleteOnClose, true);
58 
59   QVBoxLayout* lay = new QVBoxLayout(this);
60   lay->setContentsMargins(0, 0, 0, 0);
61 
62   if (Config::Chat::instance()->tabDialogRect().isValid())
63     setGeometry(Config::Chat::instance()->tabDialogRect());
64 
65   myTabs = new TabWidget();
66   lay->addWidget(myTabs);
67 
68   connect(myTabs, SIGNAL(currentChanged(int)), SLOT(currentChanged(int)));
69   connect(myTabs, SIGNAL(mouseMiddleClick(QWidget*)), SLOT(removeTab(QWidget*)));
70 
71   QActionGroup* tabActionGroup = new QActionGroup(this);
72   connect(tabActionGroup, SIGNAL(triggered(QAction*)), SLOT(switchTab(QAction*)));
73 
74 #define ADD_TABSHORTCUT(var, shortcut, index) \
75   var = new QAction(tabActionGroup); \
76   var->setData(index);
77 
78   ADD_TABSHORTCUT(myTabSwitch01Action, Config::Shortcuts::ChatTab01, 0);
79   ADD_TABSHORTCUT(myTabSwitch02Action, Config::Shortcuts::ChatTab02, 1);
80   ADD_TABSHORTCUT(myTabSwitch03Action, Config::Shortcuts::ChatTab03, 2);
81   ADD_TABSHORTCUT(myTabSwitch04Action, Config::Shortcuts::ChatTab04, 3);
82   ADD_TABSHORTCUT(myTabSwitch05Action, Config::Shortcuts::ChatTab05, 4);
83   ADD_TABSHORTCUT(myTabSwitch06Action, Config::Shortcuts::ChatTab06, 5);
84   ADD_TABSHORTCUT(myTabSwitch07Action, Config::Shortcuts::ChatTab07, 6);
85   ADD_TABSHORTCUT(myTabSwitch08Action, Config::Shortcuts::ChatTab08, 7);
86   ADD_TABSHORTCUT(myTabSwitch09Action, Config::Shortcuts::ChatTab09, 8);
87   ADD_TABSHORTCUT(myTabSwitch10Action, Config::Shortcuts::ChatTab10, 9);
88 
89 #undef ADD_TABSHORTCUT
90 
91   addActions(tabActionGroup->actions());
92   updateShortcuts();
93   connect(Config::Shortcuts::instance(), SIGNAL(shortcutsChanged()), SLOT(updateShortcuts()));
94 }
95 
updateShortcuts()96 void UserEventTabDlg::updateShortcuts()
97 {
98   const Config::Shortcuts* shortcuts = Config::Shortcuts::instance();
99   myTabSwitch01Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab01));
100   myTabSwitch02Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab02));
101   myTabSwitch03Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab03));
102   myTabSwitch04Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab04));
103   myTabSwitch05Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab05));
104   myTabSwitch06Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab06));
105   myTabSwitch07Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab07));
106   myTabSwitch08Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab08));
107   myTabSwitch09Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab09));
108   myTabSwitch10Action->setShortcut(shortcuts->getShortcut(Config::Shortcuts::ChatTab10));
109 }
110 
~UserEventTabDlg()111 UserEventTabDlg::~UserEventTabDlg()
112 {
113   saveGeometry();
114   emit signal_done();
115 }
116 
addTab(UserEventCommon * tab,int index)117 void UserEventTabDlg::addTab(UserEventCommon* tab, int index)
118 {
119   // Insert tab before we lock the user as our event() function will be called
120   // when tab is inserted and will also need to fetch user.
121   // Tab label will be set later by updateTabLabel()
122   myTabs->insertTab(index, tab, QString());
123 
124   Licq::UserReadGuard u(tab->userId());
125   if (!u.isLocked())
126     return;
127 
128   updateTabLabel(tab, *u);
129 }
130 
switchTab(QAction * action)131 void UserEventTabDlg::switchTab(QAction* action)
132 {
133   int index = action->data().toInt();
134 
135   myTabs->setCurrentIndex(index);
136 }
137 
selectTab(QWidget * tab)138 void UserEventTabDlg::selectTab(QWidget* tab)
139 {
140   myTabs->setCurrentIndex(myTabs->indexOf(tab));
141   updateTitle(tab);
142 }
143 
replaceTab(QWidget * oldTab,UserEventCommon * newTab)144 void UserEventTabDlg::replaceTab(QWidget* oldTab, UserEventCommon* newTab)
145 {
146   addTab(newTab, myTabs->indexOf(oldTab) + 1);
147   removeTab(oldTab);
148 }
149 
tabIsSelected(QWidget * tab)150 bool UserEventTabDlg::tabIsSelected(QWidget* tab)
151 {
152   return (myTabs->currentIndex() == myTabs->indexOf(tab));
153 }
154 
tabExists(QWidget * tab)155 bool UserEventTabDlg::tabExists(QWidget* tab)
156 {
157   return (myTabs->indexOf(tab) != -1);
158 }
159 
updateConvoLabel(UserEventCommon * tab)160 void UserEventTabDlg::updateConvoLabel(UserEventCommon* tab)
161 {
162   // Show the list of users in the conversation
163   list<Licq::UserId> users = tab->convoUsers();
164   list<Licq::UserId>::iterator it;
165   QString newLabel = QString::null;
166 
167   for (it = users.begin(); it != users.end(); ++it)
168   {
169     if (!newLabel.isEmpty())
170       newLabel += ", ";
171 
172     Licq::UserReadGuard u(*it);
173     if (!u.isLocked())
174       newLabel += tr("[UNKNOWN_USER]");
175     else
176       newLabel += QString::fromUtf8(u->getAlias().c_str());
177   }
178 
179   myTabs->setTabText(myTabs->indexOf(tab), newLabel);
180 }
181 
updateTabLabel(const Licq::User * u)182 void UserEventTabDlg::updateTabLabel(const Licq::User* u)
183 {
184   if (u == NULL)
185     return;
186 
187   for (int index = 0; index < myTabs->count(); index++)
188   {
189     UserEventCommon* tab = dynamic_cast<UserEventCommon*>(myTabs->widget(index));
190 
191     if (tab->isUserInConvo(u->id()))
192       updateTabLabel(tab, u);
193   }
194 }
195 
updateTabLabel(UserEventCommon * tab,const Licq::User * u)196 void UserEventTabDlg::updateTabLabel(UserEventCommon* tab, const Licq::User* u)
197 {
198   if (tab == NULL)
199     return;
200 
201   int index = myTabs->indexOf(tab);
202   myTabs->setTabText(index, QString::fromUtf8(u->getAlias().c_str()));
203 
204   QIcon icon;
205 
206   if (u->NewMessages() > 0) // use an event icon
207   {
208     unsigned eventType = Licq::UserEvent::TypeMessage;
209     for (unsigned short i = 0; i < u->NewMessages(); i++)
210       switch (u->EventPeek(i)->eventType())
211       {
212         case Licq::UserEvent::TypeFile:
213           eventType = Licq::UserEvent::TypeFile;
214           break;
215         case Licq::UserEvent::TypeChat:
216           if (eventType != Licq::UserEvent::TypeFile)
217             eventType = Licq::UserEvent::TypeChat;
218           break;
219         case Licq::UserEvent::TypeUrl:
220           if (eventType != Licq::UserEvent::TypeFile &&
221               eventType != Licq::UserEvent::TypeChat)
222             eventType = Licq::UserEvent::TypeUrl;
223           break;
224         case Licq::UserEvent::TypeContactList:
225           if (eventType != Licq::UserEvent::TypeFile &&
226               eventType != Licq::UserEvent::TypeChat &&
227               eventType != Licq::UserEvent::TypeUrl)
228             eventType = Licq::UserEvent::TypeContactList;
229           break;
230       }
231 
232     icon = IconManager::instance()->iconForEvent(eventType);
233     myTabs->setTabColor(tab, QColor("blue"));
234 
235     // to clear it..
236     tab->setTyping(u->isTyping());
237   }
238   else // use status icon
239   {
240     icon = IconManager::instance()->iconForUser(u);
241 
242     if (u->isTyping())
243       myTabs->setTabColor(tab, Config::Chat::instance()->tabTypingColor());
244     else
245       myTabs->setTabColor(tab, QColor());
246   }
247 
248   myTabs->setTabIcon(index, icon);
249   if (myTabs->currentIndex() == index)
250     setWindowIcon(icon);
251 }
252 
setTyping(const Licq::User * u,int convoId)253 void UserEventTabDlg::setTyping(const Licq::User* u, int convoId)
254 {
255   for (int index = 0; index < myTabs->count(); index++)
256   {
257     UserEventCommon* tab = dynamic_cast<UserEventCommon*>(myTabs->widget(index));
258 
259     if (tab->convoId() == static_cast<unsigned long>(convoId) &&
260         tab->isUserInConvo(u->id()))
261       tab->setTyping(u->isTyping());
262   }
263 }
264 
265 #ifdef USE_KDE
266 /* KDE 3.2 handles app-icon updates differently, since KDE 3.2 a simple setIcon() call
267    does no longer update the icon in kicker anymore :(
268    So we do it the "kde-way" here */
setIcon(const QPixmap & icon)269 void UserEventTabDlg::setIcon(const QPixmap& icon)
270 {
271   KWindowSystem::setIcons(winId(), icon, icon);
272 }
273 #endif
274 
275 /*! This slot should get called when the current tab has
276  *  changed.
277  */
currentChanged(int index)278 void UserEventTabDlg::currentChanged(int index)
279 {
280   QWidget* tab = myTabs->widget(index);
281   tab->setFocus();  // prevents users from accidentally typing in the wrong widget
282   updateTitle(tab);
283   clearEvents(tab);
284 }
285 
moveLeft()286 void UserEventTabDlg::moveLeft()
287 {
288   myTabs->setPreviousPage();
289 }
290 
moveRight()291 void UserEventTabDlg::moveRight()
292 {
293   myTabs->setNextPage();
294 }
295 
removeTab(QWidget * tab)296 void UserEventTabDlg::removeTab(QWidget* tab)
297 {
298   if (myTabs->count() > 1)
299   {
300     int index = myTabs->indexOf(tab);
301     myTabs->removeTab(index);
302     tab->close();
303     tab->setEnabled(false);
304     tab->deleteLater();
305   }
306   else
307     close();
308 }
309 
setMsgWinSticky(bool sticky)310 void UserEventTabDlg::setMsgWinSticky(bool sticky)
311 {
312   Support::changeWinSticky(winId(), sticky);
313 }
314 
updateTitle(QWidget * tab)315 void UserEventTabDlg::updateTitle(QWidget* tab)
316 {
317   QString title = tab->windowTitle();
318   if (!title.isEmpty())
319     setWindowTitle(title);
320 
321   setWindowIconText(tab->windowIconText());
322 
323   QIcon icon = myTabs->tabIcon(myTabs->indexOf(tab));
324   if (!icon.isNull())
325     setWindowIcon(icon);
326 }
327 
clearEvents(QWidget * tab)328 void UserEventTabDlg::clearEvents(QWidget* tab)
329 {
330   if (!isActiveWindow())
331     return;
332 
333   UserSendEvent* e = dynamic_cast<UserSendEvent*>(tab);
334   QTimer::singleShot(e->clearDelay, e, SLOT(clearNewEvents()));
335 }
336 
saveGeometry()337 void UserEventTabDlg::saveGeometry()
338 {
339   QRect geom = geometry();
340 
341   // When first showing window, we may get a rect without frame which will be
342   // become wrong if used for a window with a frame
343   if (geom == frameGeometry())
344     return;
345 
346   // For setGeometry to get it right we need position including frame but
347   // size without frame
348   geom.moveTo(frameGeometry().topLeft());
349 
350   Config::Chat::instance()->setTabDialogRect(geom);
351 }
352 
moveEvent(QMoveEvent * event)353 void UserEventTabDlg::moveEvent(QMoveEvent* event)
354 {
355   saveGeometry();
356   QWidget::moveEvent(event);
357 }
358 
resizeEvent(QResizeEvent * event)359 void UserEventTabDlg::resizeEvent(QResizeEvent* event)
360 {
361   saveGeometry();
362   QWidget::resizeEvent(event);
363 }
364