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