1 #include "psichatdlg.h"
2
3 #include <QLabel>
4 #include <QCursor>
5 #include <QLineEdit>
6 #include <QToolButton>
7 #include <QLayout>
8 #include <QSplitter>
9 #include <QToolBar>
10 #include <QPixmap>
11 #include <QColor>
12 #include <QCloseEvent>
13 #include <QHBoxLayout>
14 #include <QVBoxLayout>
15 #include <QContextMenuEvent>
16 #include <QResizeEvent>
17 #include <QMenu>
18 #include <QDragEnterEvent>
19 #include <QMessageBox>
20 #include <QDebug>
21 #include <QClipboard>
22
23 #include "psicon.h"
24 #include "psiaccount.h"
25 #include "iconaction.h"
26 #include "stretchwidget.h"
27 #include "psiiconset.h"
28 #include "iconwidget.h"
29 #include "fancylabel.h"
30 #include "textutil.h"
31 #include "msgmle.h"
32 #include "messageview.h"
33 #include "iconselect.h"
34 #include "avatars.h"
35 #include "activecontactsmenu.h"
36 #include "psitooltip.h"
37 #include "psioptions.h"
38 #include "coloropt.h"
39 #include "shortcutmanager.h"
40 #include "accountlabel.h"
41 #include "iconlabel.h"
42 #include "psicontactlist.h"
43 #include "userlist.h"
44 #include "psicontact.h"
45 #include "jidutil.h"
46 #include "xmpp_tasks.h"
47 #include "xmpp_caps.h"
48 #include "lastactivitytask.h"
49 #include "avcall/avcall.h"
50 #include "actionlist.h"
51 #include "psiactionlist.h"
52 #ifdef PSI_PLUGINS
53 #include "pluginmanager.h"
54 #endif
55
56 #define MCMDCHAT "http://psi-im.org/ids/mcmd#chatmain"
57
58 PsiIcon* PsiChatDlg::throbber_icon = 0;
59
60 class PsiChatDlg::ChatDlgMCmdProvider : public QObject, public MCmdProviderIface {
61 Q_OBJECT
62 public:
ChatDlgMCmdProvider(PsiChatDlg * dlg)63 ChatDlgMCmdProvider(PsiChatDlg *dlg) : dlg_(dlg) {};
64
mCmdTryStateTransit(MCmdStateIface * oldstate,QStringList command,MCmdStateIface * & newstate,QStringList & preset)65 virtual bool mCmdTryStateTransit(MCmdStateIface *oldstate, QStringList command, MCmdStateIface *&newstate, QStringList &preset) {
66 Q_UNUSED(preset);
67 if (oldstate->getName() == MCMDCHAT) {
68 QString cmd;
69 if (command.count() > 0) cmd = command[0].toLower();
70 if (cmd == "version") {
71 JT_ClientVersion *version = new JT_ClientVersion(dlg_->account()->client()->rootTask());
72 connect(version, SIGNAL(finished()), SLOT(version_finished()));
73
74 //qDebug() << "querying: " << dlg_->jid().full();
75 version->get(dlg_->jid());
76 version->go();
77 newstate = 0;
78 } else if (cmd == "idle") {
79 LastActivityTask *idle = new LastActivityTask(dlg_->jid(), dlg_->account()->client()->rootTask());
80 connect(idle, SIGNAL(finished()), SLOT(lastactivity_finished()));
81 idle->go();
82 newstate = 0;
83 } else if (cmd == "clear") {
84 dlg_->doClear();
85 newstate = 0;
86 } else if (cmd == "vcard") {
87 dlg_->doInfo();
88 newstate = 0;
89 } else if (cmd == "auth") {
90 if (command.count() == 2) {
91 if (command[1].toLower() == "request") {
92 dlg_->account()->actionAuthRequest(dlg_->jid());
93 }
94 }
95 newstate = 0;
96 } else if (cmd == "compact") {
97 if (command.count() == 2) {
98 QString sub = command[1].toLower();
99 if ("on" == sub) {
100 dlg_->smallChat_ = true;
101 } else if ("off" == sub) {
102 dlg_->smallChat_ = false;
103 } else {
104 dlg_->appendSysMsg("usage: compact {on,off}");
105 }
106 } else {
107 dlg_->smallChat_ = !dlg_->smallChat_;
108 }
109 dlg_->setLooks();
110 newstate = 0;
111 } else if (!cmd.isEmpty()) {
112 return false;
113 }
114 return true;
115 } else {
116 return false;
117 }
118 };
119
mCmdTryCompleteCommand(MCmdStateIface * state,QString query,QStringList partcommand,int item)120 virtual QStringList mCmdTryCompleteCommand(MCmdStateIface *state, QString query, QStringList partcommand, int item) {
121 Q_UNUSED(partcommand);
122 QStringList all;
123 if (state->getName() == MCMDCHAT) {
124 if (item == 0) {
125 all << "version" << "idle" << "clear" << "vcard" << "auth" << "compact";
126 }
127 }
128 QStringList res;
129 foreach(QString cmd, all) {
130 if (cmd.startsWith(query)) {
131 res << cmd;
132 }
133 }
134 return res;
135 };
136
mCmdSiteDestroyed()137 virtual void mCmdSiteDestroyed() {};
~ChatDlgMCmdProvider()138 virtual ~ChatDlgMCmdProvider() {};
139
140
141 public slots:
version_finished()142 void version_finished() {
143 JT_ClientVersion *version = qobject_cast<JT_ClientVersion*>(sender());
144 if (!version) {
145 dlg_->appendSysMsg("No version information available.");
146 return;
147 }
148 dlg_->appendSysMsg(TextUtil::escape(QString("Version response: N: %2 V: %3 OS: %4")
149 .arg(version->name(), version->version(), version->os())));
150 };
151
lastactivity_finished()152 void lastactivity_finished()
153 {
154 LastActivityTask *idle = (LastActivityTask *)sender();
155
156 if (!idle->success()) {
157 dlg_->appendSysMsg("Could not determine time of last activity.");
158 return;
159 }
160
161 if (idle->status().isEmpty()) {
162 dlg_->appendSysMsg(QString("Last activity at %1")
163 .arg(idle->time().toString()));
164 } else {
165 dlg_->appendSysMsg(QString("Last activity at %1 (%2)")
166 .arg(idle->time().toString(), TextUtil::escape(idle->status())));
167 }
168 }
169
170 private:
171 PsiChatDlg *dlg_;
172 };
173
174
175
PsiChatDlg(const Jid & jid,PsiAccount * pa,TabManager * tabManager)176 PsiChatDlg::PsiChatDlg(const Jid& jid, PsiAccount* pa, TabManager* tabManager)
177 : ChatDlg(jid, pa, tabManager), actions_(new ActionList("", 0, false)), mCmdManager_(&mCmdSite_), tabCompletion(&mCmdManager_)
178 , autoPGP_(true)
179 {
180 connect(account()->psi(), SIGNAL(accountCountChanged()), this, SLOT(updateIdentityVisibility()));
181 connect(account(), SIGNAL(addedContact(PsiContact*)), SLOT(updateContactAdding(PsiContact*)));
182 connect(account(), SIGNAL(removedContact(PsiContact*)), SLOT(updateContactAdding(PsiContact*)));
183 connect(account(), SIGNAL(updateContact(const Jid &)), SLOT(updateContactAdding(const Jid &)));
184 mCmdManager_.registerProvider(new ChatDlgMCmdProvider(this));
185 }
186
~PsiChatDlg()187 PsiChatDlg::~PsiChatDlg()
188 {
189 delete actions_;
190 }
191
initUi()192 void PsiChatDlg::initUi()
193 {
194 ui_.setupUi(this);
195
196 le_autojid = new ActionLineEdit(ui_.le_jid);
197 ui_.le_jid->setLineEdit(le_autojid);
198 ui_.le_jid->lineEdit()->setReadOnly(true);
199 if (autoSelectContact_) {
200 QStringList excl = PsiOptions::instance()->getOption("options.ui.chat.default-jid-mode-ignorelist").toString().toLower().split(",", QString::SkipEmptyParts);
201 if (excl.indexOf(jid().bare()) == -1) {
202 ui_.le_jid->insertItem(0, "auto", jid().full());
203 ui_.le_jid->setCurrentIndex(0);
204 } else {
205 autoSelectContact_ = false;
206 }
207 }
208 connect(ui_.le_jid, SIGNAL(activated(int)), this, SLOT(contactChanged()));
209 UserListItem *ul = account()->findFirstRelevant(jid());
210 if (!ul || !ul->isPrivate()) {
211 act_autojid = new IconAction(this);
212 updateAutojidIcon();
213 connect(act_autojid, SIGNAL(triggered()), SLOT(doSwitchJidMode()));
214 le_autojid->addAction(act_autojid);
215
216 QAction *act_copy_user_jid = new QAction(tr("Copy user JID"), this);
217 le_autojid->addAction(act_copy_user_jid);
218 connect(act_copy_user_jid, SIGNAL(triggered()), SLOT(copyUserJid()));
219 }
220
221 ui_.lb_ident->setAccount(account());
222 ui_.lb_ident->setShowJid(false);
223
224 PsiToolTip::install(ui_.lb_status);
225 ui_.lb_status->setPsiIcon(IconsetFactory::iconPtr("status/noauth"));
226 setTabIcon(IconsetFactory::iconPtr("status/noauth")->icon());
227
228 ui_.tb_emoticons->setIcon(IconsetFactory::icon("psi/smile").icon());
229
230 connect(ui_.mle, SIGNAL(textEditCreated(QTextEdit*)), SLOT(chatEditCreated()));
231 chatEditCreated();
232
233 #ifdef Q_OS_MAC
234 //connect(chatView(), SIGNAL(selectionChanged()), SLOT(logSelectionChanged()));
235 #endif
236
237 initToolButtons();
238 initToolBar();
239 updateAvatar();
240
241 PsiToolTip::install(ui_.avatar);
242
243 UserListItem* u = account()->findFirstRelevant(jid());
244 if (u && u->isSecure(jid().resource())) {
245 setPGPEnabled(true);
246 }
247
248 connect(account()->avatarFactory(), SIGNAL(avatarChanged(const Jid&)), this, SLOT(updateAvatar(const Jid&)));
249
250 pm_settings_ = new QMenu(this);
251 connect(pm_settings_, SIGNAL(aboutToShow()), SLOT(buildMenu()));
252 ui_.tb_actions->setMenu(pm_settings_);
253 ui_.tb_actions->setIcon(IconsetFactory::icon("psi/select").icon());
254 ui_.tb_actions->setStyleSheet(" QToolButton::menu-indicator { image:none } ");
255
256 connect(account()->client()->capsManager(), SIGNAL(capsChanged(const Jid&)), SLOT(capsChanged(const Jid&)));
257
258 QList<int> list;
259 list << 324;
260 list << 96;
261 ui_.splitter->setSizes(list);
262
263 smallChat_ = PsiOptions::instance()->getOption("options.ui.chat.use-small-chats").toBool();
264 ui_.pb_send->setIcon(IconsetFactory::icon("psi/action_button_send").icon());
265 connect(ui_.pb_send, SIGNAL(clicked()), this, SLOT(doSend()));
266
267 act_mini_cmd_ = new QAction(this);
268 act_mini_cmd_->setText(tr("Input command..."));
269 connect(act_mini_cmd_, SIGNAL(triggered()), SLOT(doMiniCmd()));
270 addAction(act_mini_cmd_);
271
272 ui_.log->realTextWidget()->installEventFilter(this);
273 ui_.mini_prompt->hide();
274
275 if (throbber_icon == 0) {
276 throbber_icon = (PsiIcon *)IconsetFactory::iconPtr("psi/throbber");
277 }
278 #ifdef PSI_PLUGINS
279 PluginManager::instance()->setupChatTab(this, account(), jid().full());
280 #endif
281 }
282
updateCountVisibility()283 void PsiChatDlg::updateCountVisibility()
284 {
285 if (PsiOptions::instance()->getOption("options.ui.message.show-character-count").toBool() && !smallChat_) {
286 ui_.lb_count->show();
287 }
288 else {
289 ui_.lb_count->hide();
290 }
291 }
292
setLooks()293 void PsiChatDlg::setLooks()
294 {
295 ChatDlg::setLooks();
296
297 const QString css = PsiOptions::instance()->getOption("options.ui.chat.css").toString();
298 if (!css.isEmpty()) {
299 setStyleSheet(css);
300 chatEdit()->setCssString(css);
301 }
302 ui_.splitter->optionsChanged();
303 ui_.mle->optionsChanged();
304
305 int s = PsiIconset::instance()->system().iconSize();
306 ui_.lb_status->setFixedSize(s,s);
307 ui_.lb_client->setFixedSize(s,s);
308
309 ui_.tb_pgp->hide();
310 if (smallChat_) {
311 ui_.lb_status->hide();
312 ui_.le_jid->hide();
313 ui_.tb_actions->hide();
314 ui_.tb_emoticons->hide();
315 ui_.toolbar->hide();
316 ui_.tb_voice->hide();
317 ui_.lb_client->hide();
318 }
319 else {
320 ui_.lb_client->show();
321 ui_.lb_status->show();
322 ui_.le_jid->show();
323 if (PsiOptions::instance()->getOption("options.ui.contactlist.toolbars.m0.visible").toBool()) {
324 ui_.toolbar->show();
325 ui_.tb_actions->hide();
326 ui_.tb_emoticons->hide();
327 ui_.tb_voice->hide();
328 }
329 else {
330 ui_.toolbar->hide();
331 ui_.tb_emoticons->setVisible(PsiOptions::instance()->getOption("options.ui.emoticons.use-emoticons").toBool());
332 ui_.tb_actions->show();
333 ui_.tb_voice->setVisible(AvCallManager::isSupported());
334 }
335 }
336
337 updateIdentityVisibility();
338 updateCountVisibility();
339 updateContactAdding();
340
341 // toolbuttons
342 QIcon i;
343 i.addPixmap(IconsetFactory::icon("psi/cryptoNo").impix(), QIcon::Normal, QIcon::Off);
344 i.addPixmap(IconsetFactory::icon("psi/cryptoYes").impix(), QIcon::Normal, QIcon::On);
345 actions_->action("chat_pgp")->setPsiIcon(0);
346 actions_->action("chat_pgp")->setIcon(i);
347 }
348
setShortcuts()349 void PsiChatDlg::setShortcuts()
350 {
351 ChatDlg::setShortcuts();
352
353 actions_->action("chat_clear")->setShortcuts(ShortcutManager::instance()->shortcuts("chat.clear"));
354 // typeahead find bar
355 actions_->action("chat_find")->setShortcuts(ShortcutManager::instance()->shortcuts("chat.find"));
356 // -- typeahead
357 actions_->action("chat_info")->setShortcuts(ShortcutManager::instance()->shortcuts("common.user-info"));
358 actions_->action("chat_history")->setShortcuts(ShortcutManager::instance()->shortcuts("common.history"));
359
360 act_mini_cmd_->setShortcuts(ShortcutManager::instance()->shortcuts("chat.quick-command"));
361
362 }
363
updateIdentityVisibility()364 void PsiChatDlg::updateIdentityVisibility()
365 {
366 if (!smallChat_) {
367 bool visible = account()->psi()->contactList()->enabledAccounts().count() > 1;
368 ui_.lb_ident->setVisible(visible);
369 }
370 else {
371 ui_.lb_ident->setVisible(false);
372 }
373
374 if (PsiOptions::instance()->getOption("options.ui.disable-send-button").toBool()) {
375 ui_.pb_send->hide();
376 }
377 else {
378 ui_.pb_send->show();
379 }
380 }
381
updateContactAdding(PsiContact * c)382 void PsiChatDlg::updateContactAdding(PsiContact* c)
383 {
384 if (!c || realJid().compare(c->jid(), false)) {
385 Jid rj = realJid();
386 UserListItem *uli;
387 if (rj.isNull() || ((uli = account()->findFirstRelevant(rj)) && (uli->inList() || uli->isSelf()))) {
388 actions_->action("chat_add_contact")->setVisible(false);
389 } else {
390 actions_->action("chat_add_contact")->setVisible(true);
391 }
392 }
393 }
394
updateToolbuttons()395 void PsiChatDlg::updateToolbuttons()
396 {
397 ui_.toolbar->clear();
398 PsiOptions *options = PsiOptions::instance();
399 QStringList actionsNames = options->getOption("options.ui.contactlist.toolbars.m0.actions").toStringList();
400 foreach (const QString &actionName, actionsNames) {
401 if (actionName == "chat_voice" && !AvCallManager::isSupported()) {
402 continue;
403 }
404 if (actionName == "chat_pgp" && !options->getOption("options.pgp.enable").toBool()) {
405 continue;
406 }
407
408 #ifdef PSI_PLUGINS
409 if (actionName.endsWith("-plugin")) {
410 QString name = PluginManager::instance()->nameByShortName(actionName.mid(0, actionName.length() - 7));
411 if (!name.isEmpty())
412 PluginManager::instance()->addToolBarButton(this, ui_.toolbar, account(), jid().full(), name);
413 continue;
414 }
415 #endif
416
417 // Hack. separator action can be added only once.
418 if (actionName == "separator") {
419 ui_.toolbar->addSeparator();
420 continue;
421 }
422
423 IconAction *action = actions_->action(actionName);
424 if (action) {
425 action->addTo(ui_.toolbar);
426 if (actionName == "chat_icon") {
427 ((QToolButton *)ui_.toolbar->widgetForAction(action))->setPopupMode(QToolButton::InstantPopup);
428 }
429 }
430 }
431
432 }
433
copyUserJid()434 void PsiChatDlg::copyUserJid()
435 {
436 QApplication::clipboard()->setText(jid().bare());
437 }
438
updateContactAdding(const Jid & j)439 void PsiChatDlg::updateContactAdding(const Jid &j)
440 {
441 if (realJid().compare(j, false)) {
442 updateContactAdding();
443 }
444 }
445
initToolButtons()446 void PsiChatDlg::initToolButtons()
447 {
448 // typeahead find
449 QHBoxLayout *hb3a = new QHBoxLayout();
450 typeahead_ = new TypeAheadFindBar(ui_.log->textWidget(), tr("Find toolbar"), 0);
451 hb3a->addWidget( typeahead_ );
452 ui_.vboxLayout1->addLayout(hb3a);
453 // -- typeahead
454
455 ActionList* list = account()->psi()->actionList()->actionLists(PsiActionList::Actions_Chat).at(0);
456 foreach (const QString &name, list->actions()) {
457 IconAction *action = list->action(name)->copy();
458 action->setParent(this);
459 actions_->addAction(name, action);
460 if (name == "chat_clear") {
461 connect(action, SIGNAL(triggered()), SLOT(doClearButton()));
462 }
463 else if (name == "chat_find") {
464 // typeahead find
465 connect(action, SIGNAL(triggered()), typeahead_, SLOT(toggleVisibility()));
466 // -- typeahead
467 }
468 else if (name == "chat_html_text") {
469 connect(action, SIGNAL(triggered()), chatEdit(), SLOT(doHTMLTextMenu()));
470 }
471 else if (name == "chat_add_contact") {
472 connect(action, SIGNAL(triggered()), SLOT(addContact()));
473 }
474 else if (name == "chat_icon") {
475 connect(account()->psi()->iconSelectPopup(), SIGNAL(textSelected(QString)), this, SLOT(addEmoticon(QString)));
476 action->setMenu(account()->psi()->iconSelectPopup());
477 ui_.tb_emoticons->setMenu(account()->psi()->iconSelectPopup());
478 }
479 else if (name == "chat_voice") {
480 connect(action, SIGNAL(triggered()), SLOT(doVoice()));
481 //act_voice_->setEnabled(false);
482 ui_.tb_voice->setDefaultAction(actions_->action("chat_voice"));
483 }
484 else if (name == "chat_file") {
485 connect(action, SIGNAL(triggered()), SLOT(doFile()));
486 }
487 else if (name == "chat_pgp") {
488 ui_.tb_pgp->setDefaultAction(actions_->action("chat_pgp"));
489 connect(action, SIGNAL(triggered(bool)), SLOT(actPgpToggled(bool)));
490 }
491 else if (name == "chat_info") {
492 connect(action, SIGNAL(triggered()), SLOT(doInfo()));
493 }
494 else if (name == "chat_history") {
495 connect(action, SIGNAL(triggered()), SLOT(doHistory()));
496 }
497 else if (name == "chat_compact") {
498 connect(action, SIGNAL(triggered()), SLOT(toggleSmallChat()));
499 }
500 else if (name == "chat_active_contacts") {
501 connect(action, SIGNAL(triggered()), SLOT(actActiveContacts()));
502 }
503 }
504
505 list = account()->psi()->actionList()->actionLists(PsiActionList::Actions_Common).at(0);
506 foreach (const QString &name, list->actions()) {
507 IconAction *action = list->action(name)->copy();
508 action->setParent(this);
509 actions_->addAction(name, action);
510 }
511 }
512
initToolBar()513 void PsiChatDlg::initToolBar()
514 {
515 ui_.toolbar->setWindowTitle(tr("Chat Toolbar"));
516 int s = PsiIconset::instance()->system().iconSize();
517 ui_.toolbar->setIconSize(QSize(s, s));
518
519 updateToolbuttons();
520 }
521
contextMenuEvent(QContextMenuEvent *)522 void PsiChatDlg::contextMenuEvent(QContextMenuEvent *)
523 {
524 pm_settings_->exec(QCursor::pos());
525 }
526
capsChanged()527 void PsiChatDlg::capsChanged()
528 {
529 ChatDlg::capsChanged();
530
531 QString resource = jid().resource();
532 UserListItem *ul = account()->findFirstRelevant(jid());
533 if (resource.isEmpty() && ul && !ul->userResourceList().isEmpty()) {
534 resource = (*(ul->userResourceList().priority())).name();
535 }
536 //act_voice_->setEnabled(!account()->capsManager()->isEnabled() || (ul && ul->isAvailable() && account()->capsManager()->features(jid().withResource(resource)).canVoice()));
537 }
538
activated()539 void PsiChatDlg::activated()
540 {
541 ChatDlg::activated();
542
543 updateCountVisibility();
544 }
545
setContactToolTip(QString text)546 void PsiChatDlg::setContactToolTip(QString text)
547 {
548 ui_.lb_status->setToolTip(text);
549 ui_.avatar->setToolTip(text);
550 }
551
updateJidWidget(const QList<UserListItem * > & ul,int status,bool fromPresence)552 void PsiChatDlg::updateJidWidget(const QList<UserListItem*> &ul, int status, bool fromPresence)
553 {
554 static bool internal_change = false;
555 if (!internal_change) {
556 // Filling jid's combobox
557 const UserListItem *u = ul.first();
558 if (!u)
559 return;
560 UserResourceList resList = u->userResourceList();
561 const QString name = u->name();
562 QComboBox *jidCombo = ui_.le_jid;
563 if (!u->isPrivate()) {
564 // If no conference private chat
565 const int combo_idx = jidCombo->currentIndex();
566 Jid old_jid = (combo_idx != -1) ? Jid(jidCombo->itemData(combo_idx).toString()) : Jid();
567 //if (fromPresence || jid() != old_jid) {
568 bool auto_mode = autoSelectContact_;
569 Jid new_auto_jid = jid();
570 if (auto_mode) {
571 if (fromPresence && !resList.isEmpty()) {
572 UserResourceList::ConstIterator it = resList.priority();
573 new_auto_jid = jid().withResource((*it).name());
574 }
575 }
576 // Filling address combobox
577 QString iconStr;
578 const int resCnt = resList.size();
579 if (resCnt == 1) {
580 UserResourceList::ConstIterator it = resList.begin();
581 if (it != resList.end() && (*it).name().isEmpty()) {
582 // Empty resource, but online. Transport?
583 QString client(u->findClient(*it));
584 if (!client.isEmpty()) {
585 iconStr = "clients/" + client;
586 }
587 }
588 }
589 setJidComboItem(0, makeContactName(name, u->jid().bare()), u->jid().bare(), iconStr);
590 int new_index = -1;
591 int curr_index = 1;
592 for (UserResourceList::ConstIterator it = resList.begin(); it != resList.end(); it++) {
593 UserResource r = *it;
594 if (!r.name().isEmpty()) {
595 Jid tmp_jid(u->jid().withResource(r.name()));
596 QString client(u->findClient(r));
597 QString iconStr2;
598 if (!client.isEmpty()) {
599 iconStr2 = "clients/" + client;
600 }
601 setJidComboItem(curr_index, makeContactName(name, tmp_jid), tmp_jid, iconStr2);
602 if (new_index == -1 && tmp_jid == new_auto_jid) {
603 new_index = curr_index;
604 }
605 curr_index++;
606 }
607 }
608 if (new_index == -1) {
609 new_index = 0;
610 if (autoSelectContact_) {
611 new_auto_jid = jid().bare();
612 } else {
613 if (!jid().resource().isEmpty()) {
614 new_index = jidCombo->count();
615 setJidComboItem(curr_index, makeContactName(name, jid()), jid(), iconStr);
616 new_index = curr_index++;
617 }
618 }
619 }
620 // Сlean combobox's tail
621 while (curr_index < jidCombo->count())
622 jidCombo->removeItem(curr_index);
623
624 ui_.le_jid->setCurrentIndex(new_index);
625 if (new_auto_jid != jid()) {
626 internal_change = true;
627 setJid(new_auto_jid);
628 if (old_jid != new_auto_jid) {
629 if (autoSelectContact_ && (status != XMPP::Status::Offline || !new_auto_jid.resource().isEmpty())) {
630 appendSysMsg(tr("Contact has been switched: %1").arg(TextUtil::escape(JIDUtil::toString(new_auto_jid, true))));
631 }
632 }
633 }
634 //}
635 } else {
636 // Conference private chat
637 QString iconStr;
638 Jid tmp_jid = jid();
639 UserResourceList::ConstIterator it = resList.begin();
640 if (it != resList.end()) {
641 QString client(u->findClient(*it));
642 if (!client.isEmpty()) {
643 iconStr = "clients/" + client;
644 }
645 tmp_jid = u->jid().withResource((*it).name());
646 } else if (jidCombo->count() > 0) {
647 tmp_jid = Jid(jidCombo->itemData(0).toString());
648 }
649 if (tmp_jid.isValid()) {
650 setJidComboItem(0, makeContactName(name, tmp_jid), tmp_jid, iconStr);
651 }
652 // Clean combobox's tail
653 while (jidCombo->count() > 1)
654 jidCombo->removeItem(1);
655 //-
656 jidCombo->setCurrentIndex(0);
657 }
658 jidCombo->setToolTip(jidCombo->currentText());
659 }
660 internal_change = false;
661 }
662
actActiveContacts()663 void PsiChatDlg::actActiveContacts()
664 {
665 ActiveContactsMenu* acm = new ActiveContactsMenu(account()->psi(), this);
666 if(!acm->actions().isEmpty())
667 acm->exec(QCursor::pos());
668 delete acm;
669 }
670
contactUpdated(UserListItem * u,int status,const QString & statusString)671 void PsiChatDlg::contactUpdated(UserListItem* u, int status, const QString& statusString)
672 {
673 Q_UNUSED(statusString);
674
675 if (status == -1 || !u) {
676 ui_.lb_status->setPsiIcon(IconsetFactory::iconPtr("status/noauth"));
677 setTabIcon(IconsetFactory::iconPtr("status/noauth")->icon());
678 }
679 else {
680 ui_.lb_status->setPsiIcon(PsiIconset::instance()->statusPtr(jid(), status));
681 setTabIcon(PsiIconset::instance()->statusPtr(jid(), status)->icon());
682 }
683
684 if (u) {
685 setContactToolTip(u->makeTip(true, false));
686 }
687 else {
688 setContactToolTip(QString());
689 }
690
691 if (u) {
692 UserResourceList srl = u->userResourceList();
693 if(!srl.isEmpty()) {
694 UserResource r;
695 if(!jid().resource().isEmpty()) {
696 QString res = jid().resource();
697 UserResourceList::ConstIterator it = srl.find(res);
698 if(it != srl.end())
699 r = *it;
700 }
701 if(r.clientName().isEmpty()) {
702 srl.sort();
703 r = srl.first();
704 }
705 QString client(u->findClient(r));
706 if (!client.isEmpty()) {
707 const QPixmap &pix = IconsetFactory::iconPixmap("clients/" + client );
708 ui_.lb_client->setPixmap(pix);
709 }
710 ui_.lb_client->setToolTip(r.versionString());
711 }
712 }
713 }
714
updateAvatar()715 void PsiChatDlg::updateAvatar()
716 {
717 QString res;
718 QString client;
719
720 if (!PsiOptions::instance()->getOption("options.ui.chat.avatars.show").toBool()) {
721 ui_.avatar->hide();
722 return;
723 }
724
725 UserListItem *ul = account()->findFirstRelevant(jid());
726 bool private_ = false;
727 if (ul && !ul->userResourceList().isEmpty()) {
728 UserResourceList::Iterator it = ul->userResourceList().find(jid().resource());
729 if (it == ul->userResourceList().end())
730 it = ul->userResourceList().priority();
731
732 res = (*it).name();
733 client = (*it).clientName();
734 private_ = ul->isPrivate();
735 }
736 //QPixmap p = account()->avatarFactory()->getAvatar(jid().withResource(res),client);
737 QPixmap p = private_ ?
738 account()->avatarFactory()->getMucAvatar(jid().withResource(res)) :
739 account()->avatarFactory()->getAvatar(jid().withResource(res));
740 if (p.isNull()) {
741 if (!PsiOptions::instance()->getOption("options.ui.contactlist.avatars.use-default-avatar").toBool()) {
742 ui_.avatar->hide();
743 return;
744 }
745 p = IconsetFactory::iconPixmap("psi/default_avatar");
746 }
747 int optSize = PsiOptions::instance()->getOption("options.ui.chat.avatars.size").toInt();
748 ui_.avatar->setFixedSize(optSize, optSize);
749 int avatarSize = p.width(); //qMax(p.width(), p.height());
750 if (avatarSize > optSize)
751 avatarSize = optSize;
752 ui_.avatar->setPixmap(p.scaled(QSize(avatarSize, avatarSize), Qt::KeepAspectRatio, Qt::SmoothTransformation));
753 ui_.avatar->show();
754 }
755
optionsUpdate()756 void PsiChatDlg::optionsUpdate()
757 {
758 smallChat_ = PsiOptions::instance()->getOption("options.ui.chat.use-small-chats").toBool();
759
760 ChatDlg::optionsUpdate();
761 // typeahead find bar
762 typeahead_->optionsUpdate();
763 }
764
updatePGP()765 void PsiChatDlg::updatePGP()
766 {
767 if (account()->hasPGP()) {
768 actions_->action("chat_pgp")->setEnabled(true);
769 }
770 else {
771 setPGPEnabled(false);
772 actions_->action("chat_pgp")->setEnabled(false);
773 }
774
775 checkPGPAutostart();
776
777 ui_.tb_pgp->setVisible(account()->hasPGP() &&
778 !smallChat_ &&
779 !PsiOptions::instance()->getOption("options.ui.contactlist.toolbars.m0.visible").toBool());
780 ui_.log->setEncryptionEnabled(isEncryptionEnabled());
781 }
782
checkPGPAutostart()783 void PsiChatDlg::checkPGPAutostart()
784 {
785 if(account()->hasPGP() && autoPGP_ && PsiOptions::instance()->getOption("options.pgp.auto-start").toBool()) {
786 UserListItem *item = account()->findFirstRelevant(jid());
787 if(item && !item->publicKeyID().isEmpty()) {
788 if(!jid().resource().isEmpty()) {
789 UserResourceList::Iterator rit = item->userResourceList().find(jid().resource());
790 if(rit !=item->userResourceList().end()) {
791 UserResource r = *rit;
792 if(r.pgpVerifyStatus() != 0) {
793 setPGPEnabled(false);
794 return;
795 }
796 }
797 }
798 setPGPEnabled(true);
799 }
800 }
801 }
802
actPgpToggled(bool b)803 void PsiChatDlg::actPgpToggled(bool b)
804 {
805 autoPGP_ = false;
806 ui_.log->setEncryptionEnabled(b);
807 }
808
doClearButton()809 void PsiChatDlg::doClearButton()
810 {
811 if (PsiOptions::instance()->getOption("options.ui.chat.warn-before-clear").toBool()) {
812 switch (
813 QMessageBox::warning(
814 this,
815 tr("Warning"),
816 tr("Are you sure you want to clear the chat window?\n(note: does not affect saved history)"),
817 QMessageBox::Yes, QMessageBox::YesAll, QMessageBox::No
818 )
819 ) {
820 case QMessageBox::No:
821 break;
822 case QMessageBox::YesAll:
823 PsiOptions::instance()->setOption("options.ui.chat.warn-before-clear", false);
824 // fall-through
825 case QMessageBox::Yes:
826 doClear();
827 }
828 } else {
829 doClear();
830 }
831 }
832
setPGPEnabled(bool enabled)833 void PsiChatDlg::setPGPEnabled(bool enabled)
834 {
835 actions_->action("chat_pgp")->setChecked(enabled);
836 ui_.log->setEncryptionEnabled(enabled);
837 }
838
toggleSmallChat()839 void PsiChatDlg::toggleSmallChat()
840 {
841 smallChat_ = !smallChat_;
842 setLooks();
843 }
844
buildMenu()845 void PsiChatDlg::buildMenu()
846 {
847 // Dialog menu
848 pm_settings_->clear();
849 pm_settings_->addAction(actions_->action("chat_compact"));
850 pm_settings_->addAction(actions_->action("chat_clear"));
851 pm_settings_->addSeparator();
852
853 pm_settings_->addAction(actions_->action("chat_icon"));
854 pm_settings_->addAction(actions_->action("chat_file"));
855 if (AvCallManager::isSupported()) {
856 pm_settings_->addAction(actions_->action("chat_voice"));
857 }
858 pm_settings_->addAction(actions_->action("chat_pgp"));
859 pm_settings_->addSeparator();
860
861 pm_settings_->addAction(actions_->action("chat_info"));
862 pm_settings_->addAction(actions_->action("chat_history"));
863 #ifdef PSI_PLUGINS
864 if(!PsiOptions::instance()->getOption("options.ui.contactlist.toolbars.m0.visible").toBool()) {
865 pm_settings_->addSeparator();
866 PluginManager::instance()->addToolBarButton(this, pm_settings_, account(), jid().full());
867 }
868 #endif
869 }
870
updateCounter()871 void PsiChatDlg::updateCounter()
872 {
873 ui_.lb_count->setNum(chatEdit()->toPlainText().length());
874 }
875
isEncryptionEnabled() const876 bool PsiChatDlg::isEncryptionEnabled() const
877 {
878 return actions_->action("chat_pgp")->isChecked();
879 }
880
appendSysMsg(const QString & str)881 void PsiChatDlg::appendSysMsg(const QString &str)
882 {
883 chatView()->dispatchMessage(MessageView::fromHtml(str, MessageView::System));
884 }
885
chatView() const886 ChatView* PsiChatDlg::chatView() const
887 {
888 return ui_.log;
889 }
890
chatEdit() const891 ChatEdit* PsiChatDlg::chatEdit() const
892 {
893 return ui_.mle->chatEdit();
894 }
895
chatEditCreated()896 void PsiChatDlg::chatEditCreated()
897 {
898 ChatDlg::chatEditCreated();
899
900 connect(chatEdit(), SIGNAL(textChanged()), this, SLOT(updateCounter()));
901 chatEdit()->installEventFilter(this);
902
903 mCmdSite_.setInput(chatEdit());
904 mCmdSite_.setPrompt(ui_.mini_prompt);
905 tabCompletion.setTextEdit(chatEdit());
906 }
907
908
doSend()909 void PsiChatDlg::doSend() {
910 tabCompletion.reset();
911 if (mCmdSite_.isActive()) {
912 QString str = chatEdit()->toPlainText();
913 if (!mCmdManager_.processCommand(str)) {
914 appendSysMsg(tr("Error: Can not parse command: ") + TextUtil::escape(str));
915 }
916 } else {
917 ChatDlg::doSend();
918 }
919 }
920
doMiniCmd()921 void PsiChatDlg::doMiniCmd()
922 {
923 mCmdManager_.open(new MCmdSimpleState(MCMDCHAT, tr("Command>")), QStringList() );
924 }
925
addContact()926 void PsiChatDlg::addContact()
927 {
928 Jid j(realJid());
929 UserListItem *uli = account()->findFirstRelevant(jid());
930 QString name = uli && !uli->name().isEmpty()? uli->name() : j.node();
931 account()->openAddUserDlg(j.withResource(""), name.isEmpty()?j.node():name, "");
932 }
933
eventFilter(QObject * obj,QEvent * ev)934 bool PsiChatDlg::eventFilter( QObject *obj, QEvent *ev ) {
935 if ( obj == chatEdit() ) {
936 if ( ev->type() == QEvent::KeyPress ) {
937 QKeyEvent *e = (QKeyEvent *)ev;
938
939 if ( e->key() == Qt::Key_Tab ) {
940 tabCompletion.tryComplete();
941 return true;
942 }
943
944 tabCompletion.reset();
945 }
946 }
947
948 else if ( obj == ui_.log->realTextWidget() ) {
949 if ( ev->type() == QEvent::MouseButtonPress )
950 chatEdit()->setFocus();
951 }
952
953 return ChatDlg::eventFilter( obj, ev );
954 }
955
956
makeContactName(const QString & name,const Jid & jid) const957 QString PsiChatDlg::makeContactName(const QString &name, const Jid &jid) const
958 {
959 QString name_;
960 QString j = JIDUtil::toString(jid, true);
961 if (!name.isEmpty())
962 name_ = name + QString(" <%1>").arg(j);
963 else
964 name_ = j;
965 return name_;
966 }
967
contactChanged()968 void PsiChatDlg::contactChanged() /* current jid was chanegd in Jid combobox.TODO rename this func*/
969 {
970 int curr_index = ui_.le_jid->currentIndex();
971 Jid jid_(ui_.le_jid->itemData(curr_index).toString());
972 if (jid_ != jid()) {
973 autoSelectContact_ = false;
974 setJid(jid_);
975 updateAutojidIcon();
976 }
977 }
978
updateAutojidIcon()979 void PsiChatDlg::updateAutojidIcon()
980 {
981 QIcon icon(IconsetFactory::iconPixmap("psi/autojid"));
982 QPixmap pix;
983 QString text;
984 if (autoSelectContact_) {
985 pix = icon.pixmap(QSize(16, 16), QIcon::Normal, QIcon::Off);
986 text = tr("turn off autojid");
987 } else {
988 pix = icon.pixmap(QSize(16, 16), QIcon::Disabled, QIcon::Off);
989 text = tr("turn on autojid");
990 }
991 act_autojid->setIcon(QIcon(pix));
992 act_autojid->setText(text);
993 act_autojid->setToolTip(text);
994 act_autojid->setStatusTip(text);
995 }
996
setJidComboItem(int pos,const QString & text,const Jid & jid,const QString & icon_str)997 void PsiChatDlg::setJidComboItem(int pos, const QString &text, const Jid &jid, const QString &icon_str)
998 {
999 // Warning! If pos >= items count, the element will be added in a list tail
1000 //-
1001 QIcon icon;
1002 QComboBox *jid_combo = ui_.le_jid;
1003 if (!icon_str.isEmpty()) {
1004 const PsiIcon picon = IconsetFactory::icon(icon_str);
1005 icon = picon.icon();
1006 }
1007 if (jid_combo->count() > pos) {
1008 jid_combo->setItemText(pos, text);
1009 jid_combo->setItemData(pos, JIDUtil::toString(jid, true));
1010 jid_combo->setItemIcon(pos, icon);
1011 } else {
1012 jid_combo->addItem(icon, text, JIDUtil::toString(jid, true));
1013 }
1014 }
1015
doSwitchJidMode()1016 void PsiChatDlg::doSwitchJidMode()
1017 {
1018 autoSelectContact_ = ! autoSelectContact_;
1019 updateAutojidIcon();
1020 if (autoSelectContact_) {
1021 const QList<UserListItem*> ul = account()->findRelevant(jid().bare());
1022 UserStatus userStatus = userStatusFor(jid(), ul, false);
1023 updateJidWidget(ul, userStatus.statusType, true);
1024 userStatus = userStatusFor(jid(), ul, false);
1025 contactUpdated(userStatus.userListItem, userStatus.statusType, userStatus.status);
1026 }
1027 }
1028
1029 #include "psichatdlg.moc"
1030