1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 1999-2013 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 "searchuserdlg.h"
21 
22 #include "config.h"
23 
24 #include <QCheckBox>
25 #include <QComboBox>
26 #include <QDialogButtonBox>
27 #include <QGridLayout>
28 #include <QGroupBox>
29 #include <QHBoxLayout>
30 #include <QLabel>
31 #include <QLineEdit>
32 #include <QPushButton>
33 #include <QTreeWidget>
34 #include <QVBoxLayout>
35 
36 #include <licq/contactlist/user.h>
37 #include <licq/contactlist/usermanager.h>
38 #include <licq/event.h>
39 #include <licq/icq/icq.h>
40 #include <licq/icq/icqdata.h>
41 #include <licq/plugin/pluginmanager.h>
42 
43 #include "contactlist/contactlist.h"
44 
45 #include "core/gui-defines.h"
46 #include "core/messagebox.h"
47 #include "core/signalmanager.h"
48 
49 #include "dialogs/adduserdlg.h"
50 
51 #include "helpers/support.h"
52 
53 #include "userdlg/userdlg.h"
54 
55 using namespace LicqQtGui;
56 /* TRANSLATOR LicqQtGui::SearchUserDlg */
57 
SearchUserDlg(const Licq::UserId & ownerId)58 SearchUserDlg::SearchUserDlg(const Licq::UserId& ownerId)
59   : myOwnerId(ownerId),
60     searchTag(0)
61 {
62   Support::setWidgetProps(this, "SearchUserDialog");
63   setAttribute(Qt::WA_DeleteOnClose, true);
64   setWindowTitle(tr("Licq - User Search"));
65 
66   Licq::IcqData::Ptr icq = plugin_internal_cast<Licq::IcqData>(
67       Licq::gPluginManager.getProtocolPlugin(ICQ_PPID));
68   if (!icq)
69   {
70     close();
71     return;
72   }
73 
74   connect(gGuiSignalManager, SIGNAL(searchResult(const Licq::Event*)),
75       SLOT(searchResult(const Licq::Event*)));
76 
77   QVBoxLayout* lay = new QVBoxLayout(this);
78 
79   // pre-search widgets
80   grpParms = new QGroupBox(tr("Search Criteria"));
81   lay->addWidget(grpParms);
82 
83   QGridLayout* grp_lay = new QGridLayout(grpParms);
84   grp_lay->setColumnMinimumWidth(3, 10); // column interspacing
85 
86   QList<QComboBox*> combos;
87 
88   QStringList ages = QStringList()
89     << tr("Unspecified")
90     << "18 - 22"
91     << "23 - 29"
92     << "30 - 39"
93     << "40 - 49"
94     << "50 - 59"
95     << "60+";
96 
97   QStringList genders = QStringList()
98     << tr("Unspecified")
99     << tr("Female")
100     << tr("Male");
101 
102   QStringList languages;
103   for (unsigned i = 0; i < Licq::NUM_LANGUAGES; i++)
104     languages << icq->getLanguageByIndex(i)->szName;
105 
106   QStringList countries;
107   for (unsigned i = 0; i < Licq::NUM_COUNTRIES; i++)
108     countries << icq->getCountryByIndex(i)->szName;
109 
110   int row = 0;
111   int column = 0;
112 
113   QLabel* label;
114 
115 #define ADDFULLLINE(name, var) \
116   label = new QLabel(name); \
117   var = new QLineEdit(); \
118   label->setBuddy(var); \
119   grp_lay->addWidget(label, row, 0); \
120   grp_lay->addWidget(var, row++, 2, 1, 5);
121 
122 #define ADDLINE(name, var) \
123   label = new QLabel(name); \
124   var = new QLineEdit(); \
125   label->setBuddy(var); \
126   grp_lay->addWidget(label, row, column); \
127   grp_lay->addWidget(var, row++, column + 2);
128 
129 #define ADDCMB(name, var, list) \
130   label = new QLabel(name); \
131   var = new QComboBox(); \
132   label->setBuddy(var); \
133   grp_lay->addWidget(label, row, column); \
134   grp_lay->addWidget(var, row++, column + 2); \
135   (var)->addItems((list)); combos << (var);
136 
137   ADDFULLLINE(tr("UIN:"), edtUin);
138   edtUin->setValidator(new QIntValidator(10000,2147483647, edtUin));
139 
140   // add stretchable space
141   grp_lay->setRowMinimumHeight(row, 20);
142   grp_lay->setRowStretch(row++, 1);
143 
144   ADDLINE(tr("Alias:"), edtNick);
145   ADDLINE(tr("First name:"), edtFirst);
146   ADDLINE(tr("Last name:"), edtLast);
147   ADDCMB(tr("Age range:"), cmbAge, ages);
148   ADDCMB(tr("Gender:"), cmbGender, genders);
149   ADDCMB(tr("Language:"), cmbLanguage, languages);
150 
151   column = 4; // start new column
152   row = 2; // skip UIN line
153 
154   ADDLINE(tr("City:"), edtCity);
155   ADDLINE(tr("State:"), edtState);
156   ADDCMB(tr("Country:"), cmbCountry, countries);
157   ADDLINE(tr("Company name:"), edtCoName);
158   ADDLINE(tr("Company department:"), edtCoDept);
159   ADDLINE(tr("Company position:"), edtCoPos);
160 
161   ADDFULLLINE(tr("Email address:"), edtEmail);
162   ADDFULLLINE(tr("Keyword:"), edtKeyword);
163 
164 #undef ADDLINE
165 #undef ADDFULLLINE
166 #undef ADDCMB
167 
168   chkOnlineOnly = new QCheckBox(tr("Return online users only"));
169   grp_lay->addWidget(chkOnlineOnly, row++, 0, 1, 7);
170 
171   // Don't let comboboxes grow too much.
172   while (!combos.empty())
173     combos.takeFirst()->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
174 
175   // post-search widgets
176   grpResult = new QGroupBox(tr("Result"));
177   grp_lay = new QGridLayout(grpResult);
178 
179   foundView = new QTreeWidget();
180   QStringList headers = QStringList()
181     << tr("Alias") << tr("UIN") << tr("Name") << tr("Email")
182     << tr("Status") << tr("A/G") << tr("Auth");
183   foundView->setHeaderLabels(headers);
184   foundView->setAllColumnsShowFocus(true);
185   foundView->setSelectionMode(QTreeWidget::ExtendedSelection);
186   foundView->setSortingEnabled(true);
187   foundView->sortByColumn(1, Qt::AscendingOrder);
188   foundView->setIndentation(0);
189   for (int i = 0; i < foundView->columnCount(); i++)
190     foundView->resizeColumnToContents(i);
191   connect(foundView, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged()));
192   grp_lay->addWidget(foundView, 0, 0, 1, 4);
193 
194   btnInfo = new QPushButton(tr("View &Info"));
195   btnInfo->setEnabled(false);
196   connect(btnInfo, SIGNAL(clicked()), SLOT(viewInfo()));
197   grp_lay->addWidget(btnInfo, 1, 1);
198 
199   btnAdd = new QPushButton(tr("&Add User"));
200   btnAdd->setEnabled(false);
201   connect(btnAdd, SIGNAL(clicked()), SLOT(addUser()));
202   grp_lay->addWidget(btnAdd, 1, 2);
203 
204   grp_lay->setRowStretch(0, 1);
205   grp_lay->setColumnStretch(0, 1);
206   lay->addWidget(grpResult, 1);
207 
208   // search-control widgets
209   QDialogButtonBox* buttons = new QDialogButtonBox();
210 
211   btnSearch = new QPushButton(tr("&Search"), this);
212   btnSearch->setDefault(true);
213   buttons->addButton(btnSearch, QDialogButtonBox::ActionRole);
214   connect(btnSearch, SIGNAL(clicked()), SLOT(startSearch()));
215 
216   btnReset = new QPushButton(tr("Reset Search"), this);
217   buttons->addButton(btnReset, QDialogButtonBox::ResetRole);
218   // dirty hack, needed to ensure the text fits into the button...
219   btnReset->setMinimumWidth(btnReset->fontMetrics().width(btnReset->text()) + 20);
220   connect(btnReset, SIGNAL(clicked()), SLOT(resetSearch()));
221 
222   btnDone = new QPushButton(tr("Close"), this);
223   buttons->addButton(btnDone, QDialogButtonBox::RejectRole);
224   connect(btnDone, SIGNAL(clicked()), SLOT(close()));
225 
226   lay->addWidget(buttons);
227 
228   // pseudo Status Bar
229   lblSearch = new QLabel(tr("Enter search parameters and select 'Search'"));
230   lblSearch->setFrameStyle(QLabel::StyledPanel | QLabel::Sunken);
231   lay->addWidget(lblSearch);
232 
233   resetSearch();
234 
235   show();
236 }
237 
startSearch()238 void SearchUserDlg::startSearch()
239 {
240   unsigned short mins[7] = {0, 18, 23, 30, 40, 50, 60};
241   unsigned short maxs[7] = {0, 22, 29, 39, 49, 59, 120};
242 
243   Licq::IcqProtocol::Ptr icq = plugin_internal_cast<Licq::IcqProtocol>(
244       Licq::gPluginManager.getProtocolInstance(myOwnerId));
245   if (!icq)
246     return;
247   Licq::IcqData::Ptr icqdata = plugin_internal_cast<Licq::IcqData>(
248       Licq::gPluginManager.getProtocolPlugin(ICQ_PPID));
249 
250   foundView->clear();
251   for (int i = 0; i < foundView->columnCount(); i++)
252     foundView->resizeColumnToContents(i);
253 
254   grpParms->hide();
255   grpResult->show();
256 
257   btnSearch->setEnabled(false);
258   btnReset->setText(tr("Cancel"));
259   btnDone->setEnabled(false);
260 
261   if (edtUin->text().trimmed().isEmpty())
262   {
263     searchTag = icq->icqSearchWhitePages(myOwnerId,
264         edtFirst->text().toUtf8().constData(),
265         edtLast->text().toUtf8().constData(),
266         edtNick->text().toUtf8().constData(),
267         edtEmail->text().toUtf8().constData(),
268         mins[cmbAge->currentIndex()],
269         maxs[cmbAge->currentIndex()],
270         cmbGender->currentIndex(),
271         icqdata->getCategoryByIndex(Licq::IcqCatTypeLanguage, cmbLanguage->currentIndex())->nCode,
272         edtCity->text().toUtf8().constData(),
273         edtState->text().toUtf8().constData(),
274         icqdata->getCountryByIndex(cmbCountry->currentIndex())->nCode,
275         edtCoName->text().toUtf8().constData(),
276         edtCoDept->text().toUtf8().constData(),
277         edtCoPos->text().toUtf8().constData(),
278         edtKeyword->text().toUtf8().constData(),
279         chkOnlineOnly->isChecked());
280   }
281   else
282   {
283     Licq::UserId userId(myOwnerId, edtUin->text().trimmed().toUtf8().constData());
284     searchTag = icq->icqSearchByUin(userId);
285   }
286 
287   lblSearch->setText(tr("Searching (this can take awhile)..."));
288 }
289 
resetSearch()290 void SearchUserDlg::resetSearch()
291 {
292   if (searchTag)
293   {
294     searchTag = 0;
295     btnReset->setText(tr("New Search"));
296     lblSearch->setText(tr("Search interrupted"));
297   }
298   else
299   {
300     if (grpParms->isVisible())
301     {
302       edtUin->clear();
303 
304       edtNick->clear();
305       edtFirst->clear();
306       edtLast->clear();
307       cmbAge->setCurrentIndex(0);
308       cmbGender->setCurrentIndex(0);
309       cmbLanguage->setCurrentIndex(0);
310       edtCity->clear();
311       edtState->clear();
312       cmbCountry->setCurrentIndex(0);
313       edtCoName->clear();
314       edtCoDept->clear();
315       edtCoPos->clear();
316       edtEmail->clear();
317       edtKeyword->clear();
318       chkOnlineOnly->setChecked(false);
319     }
320     else
321     {
322       foundView->clear();
323       for (int i = 0; i < foundView->columnCount(); i++)
324         foundView->resizeColumnToContents(i);
325 
326       grpResult->hide();
327       grpParms->show();
328 
329       btnReset->setText(tr("Reset Search"));
330       lblSearch->setText(tr("Enter search parameters and select 'Search'"));
331     }
332   }
333 
334   btnDone->setEnabled(true);
335   btnSearch->setEnabled(true);
336 }
337 
searchResult(const Licq::Event * e)338 void SearchUserDlg::searchResult(const Licq::Event* e)
339 {
340   if (!e->Equals(searchTag))
341     return;
342 
343   btnSearch->setEnabled(true);
344   btnDone->setEnabled(true);
345 
346   if (e->SearchAck() != NULL && e->SearchAck()->userId().isValid())
347     searchFound(e->SearchAck());
348 
349   if (e->Result() == Licq::Event::ResultSuccess)
350     searchDone(e->SearchAck());
351   else if (e->Result() != Licq::Event::ResultAcked)
352     searchFailed();
353 }
354 
searchFound(const Licq::SearchData * s)355 void SearchUserDlg::searchFound(const Licq::SearchData* s)
356 {
357   QString text;
358   QTreeWidgetItem* item = new QTreeWidgetItem(foundView);
359 
360   item->setData(0, Qt::UserRole, QVariant::fromValue(s->userId()));
361   item->setText(0, QString::fromUtf8(s->alias().c_str()));
362 
363   item->setTextAlignment(1, Qt::AlignRight);
364   item->setText(1, s->userId().accountId().c_str());
365 
366   item->setText(2, QString::fromUtf8(s->firstName().c_str()) + " " + QString::fromUtf8(s->lastName().c_str()));
367 
368   item->setText(3, s->email().c_str());
369 
370   switch (s->status())
371   {
372     case Licq::SearchData::StatusOffline:
373             text = tr("Offline");
374             break;
375     case Licq::SearchData::StatusOnline:
376             text = tr("Online");
377             break;
378     case Licq::SearchData::StatusDisabled:
379           default:
380             text = tr("Unknown");
381         }
382   item->setText(4, text);
383 
384   text = (s->age() ? QString::number(s->age()) : tr("?")) + "/";
385   switch (s->gender())
386   {
387     case Licq::User::GenderFemale:
388             text += tr("F");
389             break;
390     case Licq::User::GenderMale:
391             text += tr("M");
392             break;
393           default:
394             text += tr("?");
395         }
396   item->setText(5, text);
397 
398   item->setText(6, s->auth() ? tr("No") : tr("Yes"));
399 }
400 
searchDone(const Licq::SearchData * sa)401 void SearchUserDlg::searchDone(const Licq::SearchData* sa)
402 {
403   if (sa == NULL || sa->more() == 0)
404     lblSearch->setText(tr("Search complete."));
405   else if (sa->more() == ~0UL)
406     lblSearch->setText(tr("More users found. Narrow search."));
407   else
408     lblSearch->setText(tr("%1 more users found. Narrow search.").arg(sa->more()));
409 
410   searchTag = 0;
411   for (int i = 0; i < foundView->columnCount(); i++)
412     foundView->resizeColumnToContents(i);
413   btnReset->setText(tr("New Search"));
414 }
415 
searchFailed()416 void SearchUserDlg::searchFailed()
417 {
418   searchTag = 0;
419   resetSearch();
420   lblSearch->setText(tr("Search failed."));
421 }
422 
selectionChanged()423 void SearchUserDlg::selectionChanged()
424 {
425   int count = foundView->selectedItems().size();
426 
427   btnInfo->setEnabled(true);
428   btnAdd->setEnabled(true);
429 
430   switch (count)
431   {
432     case 0:
433       btnInfo->setEnabled(false);
434       btnAdd->setEnabled(false);
435       // fall through
436     case 1:
437       btnAdd->setText(tr("&Add User"));
438       break;
439     default:
440       btnAdd->setText(tr("&Add %1 Users").arg(count));
441   }
442 }
443 
viewInfo()444 void SearchUserDlg::viewInfo()
445 {
446   foreach (QTreeWidgetItem* current, foundView->selectedItems())
447   {
448     Licq::UserId userId = current->data(0, Qt::UserRole).value<Licq::UserId>();
449 
450     Licq::gUserManager.addUser(userId, false);
451     UserDlg::showDialog(userId, UserDlg::GeneralPage, true);
452   }
453 }
454 
addUser()455 void SearchUserDlg::addUser()
456 {
457   foreach (QTreeWidgetItem* current, foundView->selectedItems())
458   {
459     Licq::UserId userId = current->data(0, Qt::UserRole).value<Licq::UserId>();
460 
461     new AddUserDlg(userId, this);
462   }
463 
464   foundView->clearSelection();
465 }
466 
reject()467 void SearchUserDlg::reject()
468 {
469   if (searchTag != 0)
470     resetSearch();
471   else
472     QDialog::reject();
473 }
474