/* * groupchatdlg.cpp - dialogs for handling groupchat * Copyright (C) 2001, 2002 Justin Karneges * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include"groupchatdlg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include"psicon.h" #include"psiaccount.h" #include"userlist.h" #include"statusdlg.h" #include"busywidget.h" #include"common.h" #include"msgmle.h" #include"iconwidget.h" #include"iconselect.h" #include"psitoolbar.h" #include"iconaction.h" #ifdef Q_WS_WIN #include #endif //---------------------------------------------------------------------------- // GCJoinDlg //---------------------------------------------------------------------------- class GCJoinDlg::Private { public: Private() {} PsiCon *psi; PsiAccount *pa; AccountsComboBox *cb_ident; BusyWidget *busy; QStringList rl; Jid jid; }; GCJoinDlg::GCJoinDlg(PsiCon *psi, PsiAccount *pa) :GCJoinUI(0, 0, false, WDestructiveClose) { d = new Private; d->psi = psi; d->pa = 0; d->psi->dialogRegister(this); d->busy = busy; updateIdentity(pa); d->cb_ident = d->psi->accountsComboBox(this,true); connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *))); d->cb_ident->setAccount(pa); replaceWidget(lb_ident, d->cb_ident); d->rl = d->psi->recentGCList(); for(QStringList::ConstIterator it = d->rl.begin(); it != d->rl.end(); ++it) { Jid j(*it); QString s = tr("%1 on %2").arg(j.resource()).arg(j.userHost()); cb_recent->insertItem(s); } setCaption(CAP(caption())); pb_join->setDefault(true); connect(pb_close, SIGNAL(clicked()), SLOT(close())); connect(pb_join, SIGNAL(clicked()), SLOT(doJoin())); connect(cb_recent, SIGNAL(activated(int)), SLOT(recent_activated(int))); if(d->rl.isEmpty()) { cb_recent->setEnabled(false); le_host->setFocus(); } else recent_activated(0); } GCJoinDlg::~GCJoinDlg() { if(d->psi) d->psi->dialogUnregister(this); if(d->pa) d->pa->dialogUnregister(this); delete d; } /*void GCJoinDlg::closeEvent(QCloseEvent *e) { e->ignore(); reject(); }*/ void GCJoinDlg::done(int r) { if(d->busy->isActive()) { int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel joining groupchat?"), tr("&Yes"), tr("&No")); if(n != 0) return; d->pa->groupChatLeave(d->jid.host(), d->jid.user()); } QDialog::done(r); } void GCJoinDlg::updateIdentity(PsiAccount *pa) { if(d->pa) disconnect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected())); d->pa = pa; pb_join->setEnabled(d->pa); if(!d->pa) { d->busy->stop(); return; } connect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected())); } void GCJoinDlg::pa_disconnected() { if(d->busy->isActive()) { d->busy->stop(); } } void GCJoinDlg::recent_activated(int x) { int n = 0; bool found = false; QString str; for(QStringList::ConstIterator it = d->rl.begin(); it != d->rl.end(); ++it) { if(n == x) { found = true; str = *it; break; } ++n; } if(!found) return; Jid j(str); le_host->setText(j.host()); le_room->setText(j.user()); le_nick->setText(j.resource()); } void GCJoinDlg::doJoin() { if(!d->pa->checkConnected(this)) return; QString host = le_host->text(); QString room = le_room->text(); QString nick = le_nick->text(); if(host.isEmpty() || room.isEmpty() || nick.isEmpty()) { QMessageBox::information(this, tr("Error"), tr("You must fill out the fields in order to join.")); return; } Jid j = room + '@' + host + '/' + nick; if(!j.isValid()) { QMessageBox::information(this, tr("Error"), tr("You entered an invalid room name.")); return; } if(!d->pa->groupChatJoin(host, room, nick)) { QMessageBox::information(this, tr("Error"), tr("You are in or joining this room already!")); return; } d->psi->dialogUnregister(this); d->jid = room + '@' + host + '/' + nick; d->pa->dialogRegister(this, d->jid); disableWidgets(); d->busy->start(); } void GCJoinDlg::disableWidgets() { d->cb_ident->setEnabled(false); cb_recent->setEnabled(false); gb_info->setEnabled(false); pb_join->setEnabled(false); } void GCJoinDlg::enableWidgets() { d->cb_ident->setEnabled(true); if(!d->rl.isEmpty()) cb_recent->setEnabled(true); gb_info->setEnabled(true); pb_join->setEnabled(true); } void GCJoinDlg::joined() { d->psi->recentGCAdd(d->jid.full()); d->busy->stop(); closeDialogs(this); deleteLater(); } void GCJoinDlg::error(int, const QString &str) { d->busy->stop(); enableWidgets(); pb_join->setFocus(); d->pa->dialogUnregister(this); d->psi->dialogRegister(this); QMessageBox::information(this, tr("Error"), tr("Unable to join groupchat.\nReason: %1").arg(str)); } //---------------------------------------------------------------------------- // GCUserView //---------------------------------------------------------------------------- GCUserViewItem::GCUserViewItem(QListView *par) :QListViewItem(par) { } GCUserView::GCUserView(QWidget *parent, const char *name) :QListView(parent, name), QToolTip(viewport()) { setResizeMode(QListView::AllColumns); setSorting(0); header()->hide(); addColumn(""); connect(this, SIGNAL(doubleClicked(QListViewItem *)), SLOT(qlv_doubleClicked(QListViewItem *))); connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int))); } GCUserView::~GCUserView() { } void GCUserView::updateAll() { for(GCUserViewItem *i = (GCUserViewItem *)firstChild(); i; i = (GCUserViewItem *)i->nextSibling()) i->setPixmap(0, is->status(i->s)); } QStringList GCUserView::nickList() const { QStringList list; for(QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling()) list << lvi->text(0); qstringlistisort(list); // caseless sorting return list; } QListViewItem *GCUserView::findEntry(const QString &nick) { for(QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling()) { if(lvi->text(0) == nick) return lvi; } return 0; } void GCUserView::updateEntry(const QString &nick, const Status &s) { GCUserViewItem *lvi = (GCUserViewItem *)findEntry(nick); if(!lvi) { lvi = new GCUserViewItem(this); lvi->setText(0, nick); } lvi->s = s; lvi->setPixmap(0, is->status(lvi->s)); } void GCUserView::removeEntry(const QString &nick) { QListViewItem *lvi = findEntry(nick); if(lvi) delete lvi; } void GCUserView::maybeTip(const QPoint &pos) { GCUserViewItem *lvi = (GCUserViewItem *)itemAt(pos); if(!lvi) return; QRect r(itemRect(lvi)); const QString &nick = lvi->text(0); const Status &s = lvi->s; UserListItem u; // SICK SICK SICK SICK u.setJid(((GCMainDlg *)topLevelWidget())->jid().withResource(nick)); u.setName(nick); // make a resource so the contact appears online UserResource ur; ur.setName(nick); ur.setStatus(s); u.userResourceList().append(ur); tip(r, u.makeTip()); } void GCUserView::qlv_doubleClicked(QListViewItem *i) { GCUserViewItem *lvi = (GCUserViewItem *)i; if(!lvi) return; if(option.defaultAction == 0) action(lvi->text(0), lvi->s, 0); else action(lvi->text(0), lvi->s, 1); } void GCUserView::qlv_contextMenuRequested(QListViewItem *i, const QPoint &pos, int) { GCUserViewItem *lvi = (GCUserViewItem *)i; if(!lvi) return; QPopupMenu *pm = new QPopupMenu; pm->insertItem(IconsetFactory::icon("psi/sendMessage"), tr("Send &message"), 0); pm->insertItem(IconsetFactory::icon("psi/start-chat"), tr("Open &chat window"), 1); pm->insertSeparator(); //pm->insertItem(tr("Send &file"), 4); //pm->insertSeparator(); pm->insertItem(tr("Check &Status"), 2); pm->insertItem(IconsetFactory::icon("psi/vCard"), tr("User &Info"), 3); int x = pm->exec(pos); delete pm; if(x == -1) return; action(lvi->text(0), lvi->s, x); } //---------------------------------------------------------------------------- // GCMainDlg //---------------------------------------------------------------------------- class GCMainDlg::Private : public QObject { Q_OBJECT public: enum { Connecting, Connected, Idle }; Private(GCMainDlg *d) { dlg = d; nickSeparator = ":"; typingStatus = Typing_Normal; } GCMainDlg *dlg; int state; PsiAccount *pa; Jid jid; QString self; ChatView *te_log; ChatEdit *mle; QLineEdit *le_topic; GCUserView *lv_users; QPushButton *pb_topic; PsiToolBar *toolbar; IconAction *act_find, *act_clear, *act_icon; QPopupMenu *pm_settings; bool smallChat; int pending; bool trackBar; int trackBarParagraph; QTimer *flashTimer; int flashCount; QStringList hist; int histAt; QGuardedPtr findDlg; QString lastSearch; public slots: void addEmoticon(const Icon *icon) { if ( !dlg->isActiveWindow() ) return; QString text; QDict itext = icon->text(); QDictIterator it ( itext ); for ( ; it.current(); ++it) { if ( it.current() && !it.current()->isEmpty() ) { text = *(it.current()) + " "; break; } } if ( !text.isEmpty() ) mle->insert( text ); } void addEmoticon(QString text) { if ( !dlg->isActiveWindow() ) return; mle->insert( text + " " ); } void deferredScroll() { //QTimer::singleShot(250, this, SLOT(slotScroll())); te_log->scrollToBottom(); } protected slots: void slotScroll() { te_log->scrollToBottom(); } public: // Nick auto-completion code follows... enum TypingStatus { Typing_Normal = 0, Typing_TabPressed, Typing_TabbingNicks, Typing_MultipleSuggestions }; TypingStatus typingStatus; QString lastReferrer; // contains nick of last person, who have said "yourNick: ..." QString nickSeparator; // in case of "nick: ...", it equals ":" QStringList suggestedNicks; int suggestedIndex; bool suggestedFromStart; QString beforeNickText(QString text) { int i; for (i = text.length() - 1; i >= 0; --i) if ( text[i].isSpace() ) break; QString beforeNick = text.left(i+1); return beforeNick; } QStringList suggestNicks(QString text, bool fromStart) { QString nickText = text; QString beforeNick; if ( !fromStart ) { beforeNick = beforeNickText(text); nickText = text.mid(beforeNick.length()); } QStringList nicks = lv_users->nickList(); QStringList::Iterator it = nicks.begin(); QStringList suggestedNicks; for ( ; it != nicks.end(); ++it) { if ( (*it).left(nickText.length()).lower() == nickText.lower() ) { if ( fromStart ) suggestedNicks << *it; else suggestedNicks << beforeNick + *it; } } return suggestedNicks; } QString longestSuggestedString(QStringList suggestedNicks) { QString testString = suggestedNicks.first(); while ( testString.length() > 0 ) { bool found = true; QStringList::Iterator it = suggestedNicks.begin(); for ( ; it != suggestedNicks.end(); ++it) { if ( (*it).left(testString.length()).lower() != testString.lower() ) { found = false; break; } } if ( found ) break; testString = testString.left( testString.length() - 1 ); } return testString; } QString insertNick(bool fromStart, QString beforeNick = "") { typingStatus = Typing_MultipleSuggestions; suggestedFromStart = fromStart; suggestedNicks = lv_users->nickList(); QStringList::Iterator it = suggestedNicks.begin(); for ( ; it != suggestedNicks.end(); ++it) *it = beforeNick + *it; QString newText; if ( !lastReferrer.isEmpty() ) { newText = beforeNick + lastReferrer; suggestedIndex = -1; } else { newText = suggestedNicks.first(); suggestedIndex = 0; } if ( fromStart ) { newText += nickSeparator; newText += " "; } return newText; } QString suggestNick(bool fromStart, QString origText, bool *replaced) { suggestedFromStart = fromStart; suggestedNicks = suggestNicks(origText, fromStart); suggestedIndex = -1; QString newText; if ( suggestedNicks.count() ) { if ( suggestedNicks.count() == 1 ) { newText = suggestedNicks.first(); if ( fromStart ) { newText += nickSeparator; newText += " "; } } else { newText = longestSuggestedString(suggestedNicks); if ( !newText.length() ) return origText; typingStatus = Typing_MultipleSuggestions; // TODO: display a tooltip that will contain all suggestedNicks } *replaced = true; } return newText; } void doAutoNickInsertion() { int para, index; mle->getCursorPosition(¶, &index); QString paraText = mle->text(para); QString origText = paraText.left(index); QString newText; bool replaced = false; if ( typingStatus == Typing_MultipleSuggestions ) { suggestedIndex++; if ( suggestedIndex >= (int)suggestedNicks.count() ) suggestedIndex = 0; newText = suggestedNicks[suggestedIndex]; if ( suggestedFromStart ) { newText += nickSeparator; newText += " "; } replaced = true; } if ( !para && !replaced ) { if ( !index && typingStatus == Typing_TabbingNicks ) { newText = insertNick(true, ""); replaced = true; } else { newText = suggestNick(true, origText, &replaced); } } if ( !replaced ) { if ( (!index || origText[index-1].isSpace()) && typingStatus == Typing_TabbingNicks ) { newText = insertNick(false, beforeNickText(origText)); replaced = true; } else { newText = suggestNick(false, origText, &replaced); } } if ( replaced ) { mle->setUpdatesEnabled( false ); QString newParaText = newText + paraText.mid(index, paraText.length() - index - 1); mle->insertParagraph(newParaText, para); mle->removeParagraph(para+1); mle->setCursorPosition(para, newText.length()); mle->setUpdatesEnabled( true ); mle->viewport()->update(); } } bool eventFilter( QObject *obj, QEvent *ev ) { if ( obj == mle && ev->type() == QEvent::KeyPress ) { QKeyEvent *e = (QKeyEvent *)ev; if ( e->key() == Key_Tab ) { switch ( typingStatus ) { case Typing_Normal: typingStatus = Typing_TabPressed; break; case Typing_TabPressed: typingStatus = Typing_TabbingNicks; break; default: break; } doAutoNickInsertion(); return TRUE; } typingStatus = Typing_Normal; return FALSE; } return QObject::eventFilter( obj, ev ); } }; GCMainDlg::GCMainDlg(PsiAccount *pa, const Jid &j) :AdvancedWidget(0, 0, WDestructiveClose) { nicknumber=0; d = new Private(this); d->pa = pa; d->jid = j.userHost(); d->self = j.resource(); d->pa->dialogRegister(this, d->jid); connect(d->pa, SIGNAL(updatedActivity()), SLOT(pa_updatedActivity())); d->pending = 0; d->flashTimer = 0; d->histAt = 0; d->findDlg = 0; d->trackBar = false; d->trackBarParagraph = 0; d->state = Private::Connected; #ifndef Q_WS_MAC setIcon(IconsetFactory::icon("psi/groupChat")); #endif QVBoxLayout *dlg_layout = new QVBoxLayout(this, 4); QWidget *vsplit; if ( !option.chatLineEdit ) { vsplit = new QSplitter(this); ((QSplitter *)vsplit)->setOrientation( QSplitter::Vertical ); dlg_layout->addWidget(vsplit); } else vsplit = this; // --- top part --- QWidget *sp_top = new QWidget(vsplit); sp_top->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); if ( option.chatLineEdit ) dlg_layout->addWidget( sp_top ); QVBoxLayout *vb_top = new QVBoxLayout(sp_top, 0, 4); // top row QWidget *sp_top_top = new QWidget( sp_top ); vb_top->addWidget( sp_top_top ); QHBoxLayout *hb_top = new QHBoxLayout( sp_top_top, 0, 4 ); d->pb_topic = new QPushButton(tr("Topic:"), sp_top_top); connect(d->pb_topic, SIGNAL(clicked()), SLOT(doTopic())); hb_top->addWidget(d->pb_topic); d->le_topic = new QLineEdit(sp_top_top); d->le_topic->setReadOnly(true); hb_top->addWidget(d->le_topic); d->act_find = new IconAction(tr("Find"), "psi/search", tr("&Find"), CTRL+Key_F, this); connect(d->act_find, SIGNAL(activated()), SLOT(openFind())); d->act_find->addTo( sp_top_top ); QLabel *lb_ident = d->pa->accountLabel(sp_top_top, true); lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum )); hb_top->addWidget(lb_ident); // bottom row QSplitter *hsp = new QSplitter(sp_top); hsp->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); vb_top->addWidget(hsp); hsp->setOrientation(QSplitter::Horizontal); d->te_log = new ChatView(hsp); d->te_log->setTextFormat( RichText ); #ifdef Q_WS_MAC connect(d->te_log,SIGNAL(selectionChanged()),SLOT(logSelectionChanged())); d->te_log->setFocusPolicy(QWidget::NoFocus); #endif d->lv_users = new GCUserView(hsp); d->lv_users->setMinimumWidth(20); connect(d->lv_users, SIGNAL(action(const QString &, const Status &, int)), SLOT(lv_action(const QString &, const Status &, int))); // --- bottom part --- QWidget *sp_bottom = new QWidget(vsplit); sp_bottom->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum ); if ( option.chatLineEdit ) dlg_layout->addWidget( sp_bottom ); QVBoxLayout *vb_bottom = new QVBoxLayout(sp_bottom); // toolbar d->act_clear = new IconAction (tr("Clear chat window"), "psi/clearChat", tr("Clear chat window"), 0, this); connect( d->act_clear, SIGNAL( activated() ), SLOT( doClearButton() ) ); connect(pa->psi()->iconSelectPopup(), SIGNAL(textSelected(QString)), d, SLOT(addEmoticon(QString))); d->act_icon = new IconAction( tr( "Select icon" ), "psi/smile", tr( "Select icon" ), 0, this ); d->act_icon->setPopup( pa->psi()->iconSelectPopup() ); d->toolbar = new PsiToolBar( tr("Groupchat toolbar"), 0, sp_bottom ); d->toolbar->setCustomizeable( false ); // it isn't ready now, and we don't want segfaults d->toolbar->setFrameShape( QFrame::NoFrame ); vb_bottom->addWidget( d->toolbar ); d->act_clear->addTo( d->toolbar ); d->toolbar->setStretchableWidget(new StretchWidget(d->toolbar)); d->act_icon->addTo( d->toolbar ); // chat edit if ( !option.chatLineEdit ) { d->mle = new ChatEdit(sp_bottom); vb_bottom->addWidget(d->mle); } else { QHBoxLayout *hb5 = new QHBoxLayout( dlg_layout ); d->mle = new LineEdit( vsplit ); #ifdef Q_WS_MAC hb5->addSpacing( 16 ); #endif hb5->addWidget( d->mle ); #ifdef Q_WS_MAC hb5->addSpacing( 16 ); #endif } d->mle->installEventFilter( d ); d->pm_settings = new QPopupMenu(this); connect(d->pm_settings, SIGNAL(aboutToShow()), SLOT(buildMenu())); // resize the horizontal splitter QValueList list; list << 500; list << 80; hsp->setSizes(list); list.clear(); list << 324; list << 10; if ( !option.chatLineEdit ) (( QSplitter *)vsplit)->setSizes(list); resize(580,420); d->smallChat = option.smallChats; X11WM_CLASS("groupchat"); d->mle->setFocus(); setLooks(); updateCaption(); } GCMainDlg::~GCMainDlg() { if(d->state != Private::Idle) d->pa->groupChatLeave(d->jid.host(), d->jid.user()); //QMimeSourceFactory *m = d->te_log->mimeSourceFactory(); //d->te_log->setMimeSourceFactory(0); //delete m; d->pa->dialogUnregister(this); delete d; } void GCMainDlg::keyPressEvent(QKeyEvent *e) { if(e->key() == Key_Return || e->key() == Key_Enter || (e->key() == Key_S && (e->state() & AltButton))) mle_returnPressed(); else if(e->key() == Key_Escape && e->state() == 0) showMinimized(); else if(e->key() == Key_PageUp && (e->state() & ShiftButton)) d->te_log->setContentsPos(d->te_log->contentsX(), d->te_log->contentsY() - d->te_log->visibleHeight()/2); else if(e->key() == Key_PageDown && (e->state() & ShiftButton)) d->te_log->setContentsPos(d->te_log->contentsX(), d->te_log->contentsY() + d->te_log->visibleHeight()/2); else e->ignore(); } void GCMainDlg::closeEvent(QCloseEvent *e) { e->accept(); } void GCMainDlg::windowActivationChange(bool oldstate) { QWidget::windowActivationChange(oldstate); // if we're bringing it to the front, get rid of the '*' if necessary if(isActiveWindow()) { if(d->pending > 0) { d->pending = 0; updateCaption(); } doFlash(false); d->mle->setFocus(); d->trackBar = false; } else { d->trackBar = true; } } void GCMainDlg::logSelectionChanged() { #ifdef Q_WS_MAC // A hack to only give the message log focus when text is selected if (d->te_log->hasSelectedText()) d->te_log->setFocus(); else d->mle->setFocus(); #endif } void GCMainDlg::mle_returnPressed() { if(d->mle->text().isEmpty()) return; QString str = d->mle->text(); if(str == "/clear") { doClear(); d->histAt = 0; d->hist.prepend(str); d->mle->setText(""); return; } if(str.lower().startsWith("/nick ")) { QString nick = str.mid(6).stripWhiteSpace(); if ( !nick.isEmpty() ) { d->self = nick; d->pa->groupChatChangeNick(d->jid.host(), d->jid.user(), d->self, d->pa->status()); } d->mle->setText(""); return; } if(d->state != Private::Connected) return; Message m(d->jid); m.setType("groupchat"); m.setBody(str); m.setTimeStamp(QDateTime::currentDateTime()); aSend(m); d->histAt = 0; d->hist.prepend(str); d->mle->setText(""); } /*void GCMainDlg::le_upPressed() { if(d->histAt < (int)d->hist.count()) { ++d->histAt; d->le_input->setText(d->hist[d->histAt-1]); } } void GCMainDlg::le_downPressed() { if(d->histAt > 0) { --d->histAt; if(d->histAt == 0) d->le_input->setText(""); else d->le_input->setText(d->hist[d->histAt-1]); } }*/ void GCMainDlg::doTopic() { bool ok = false; QString str = QInputDialog::getText( tr("Set Groupchat Topic"), tr("Enter a topic:"), QLineEdit::Normal, d->le_topic->text(), &ok, this); if(ok) { Message m(d->jid); m.setType("groupchat"); m.setSubject(str); m.setBody(QString("/me ") + tr("has set the topic to: %1").arg(str)); m.setTimeStamp(QDateTime::currentDateTime()); aSend(m); } } void GCMainDlg::doClear() { d->te_log->setText(""); } void GCMainDlg::doClearButton() { int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to clear the chat window?\n(note: does not affect saved history)"), tr("&Yes"), tr("&No")); if(n == 0) doClear(); } void GCMainDlg::openFind() { if(d->findDlg) bringToFront(d->findDlg); else { d->findDlg = new GCFindDlg(0, 0, d->lastSearch, this); connect(d->findDlg, SIGNAL(find(int, int, const QString &)), SLOT(doFind(int, int, const QString &))); d->findDlg->show(); } } void GCMainDlg::doFind(int para, int index, const QString &str) { d->lastSearch = str; if(!d->te_log->find(str, false, false, true, ¶, &index)) d->findDlg->error(str); else { // pass one character over d->findDlg->found(para, index+1); } } void GCMainDlg::goDisc() { if(d->state != Private::Idle) { d->state = Private::Idle; d->pb_topic->setEnabled(false); appendSysMsg(tr("Disconnected."), true); } } void GCMainDlg::goConn() { if(d->state == Private::Idle) { d->state = Private::Connecting; appendSysMsg(tr("Reconnecting..."), true); QString host = d->jid.host(); QString room = d->jid.user(); QString nick = d->self; if(!d->pa->groupChatJoin(host, room, nick)) { appendSysMsg(tr("Error: You are in or joining this room already!"), true); d->state = Private::Idle; } } } void GCMainDlg::pa_updatedActivity() { if(!d->pa->loggedIn()) goDisc(); else { if(d->state == Private::Idle) goConn(); else if(d->state == Private::Connected) d->pa->groupChatSetStatus(d->jid.host(), d->jid.user(), d->pa->status()); } } Jid GCMainDlg::jid() const { return d->jid; } void GCMainDlg::error(int, const QString &str) { d->pb_topic->setEnabled(false); if(d->state == Private::Connecting) appendSysMsg(tr("Unable to join groupchat. Reason: %1").arg(str), true); else appendSysMsg(tr("Unexpected groupchat error: %1").arg(str), true); d->state = Private::Idle; } void GCMainDlg::presence(const QString &nick, const Status &s) { if(s.isAvailable()) { /*if ((option.showJoins)&&(d->lv_users->findEntry(nick)==0)) { //contact joining QString m=nick+tr(" has joined the channel"); appendSysMsg(m, false, QDateTime::currentDateTime()); }*/ d->lv_users->updateEntry(nick, s); } else { /*if (option.showJoins) { //contact leaving QString m=nick+tr(" has left the channel"); appendSysMsg(m, false, QDateTime::currentDateTime()); }*/ d->lv_users->removeEntry(nick); } } void GCMainDlg::message(const Message &m) { QString from = m.from().resource(); bool alert = false; if(!m.subject().isEmpty()) { d->le_topic->setText(m.subject()); d->le_topic->setCursorPosition(0); QToolTip::add(d->le_topic, QString("

%1

").arg(m.subject())); } // code to determine if the speaker was addressing this client in chat if(m.body().contains(d->self) > 0) alert = true; if (m.body().left(d->self.length()) == d->self) d->lastReferrer = m.from().resource(); if(option.gcHighlighting) { for(QStringList::Iterator it=option.gcHighlights.begin();it!=option.gcHighlights.end();it++) { if(m.body().contains((*it),false) > 0) { alert = true; } } } // play sound? if(from == d->self) { if(!m.spooled()) d->pa->playSound(option.onevent[eSend]); } else { if(alert || (!option.noGCSound && !m.spooled() && !from.isEmpty()) ) d->pa->playSound(option.onevent[eChat2]); } if(from.isEmpty()) appendSysMsg(m.body(), alert, m.timeStamp()); else appendMessage(m, alert); } void GCMainDlg::joined() { if(d->state == Private::Connecting) { d->lv_users->QListView::clear(); d->state = Private::Connected; d->pb_topic->setEnabled(true); appendSysMsg(tr("Connected."), true); } } void GCMainDlg::appendSysMsg(const QString &str, bool alert, const QDateTime &ts) { bool atBottom = d->te_log->contentsY() >= d->te_log->contentsHeight() - d->te_log->visibleHeight(); QString hr =""; if (d->trackBar) { hr = QString("
"); d->trackBar = false; if ( d->trackBarParagraph ) { d->te_log->setUpdatesEnabled( false ); d->te_log->setSelection(d->trackBarParagraph, 0, d->trackBarParagraph, 1, 1); d->te_log->removeSelectedText(1); d->te_log->setUpdatesEnabled( true ); } d->trackBarParagraph = d->te_log->paragraphs(); } QString timestr; if (!option.gcHighlighting) alert=false; QDateTime time; if(!ts.isNull()) time = ts; else time = QDateTime::currentDateTime(); //timestr.sprintf("%02d:%02d:%02d", time.time().hour(), time.time().minute(), time.time().second()); timestr = time.time().toString(LocalDate); /*int y = d->te_log->contentsHeight() - d->te_log->visibleHeight(); if(y < 0) y = 0; bool atBottom = (d->te_log->contentsY() < y - 32) ? false: true;*/ d->te_log->append(hr + QString("[%1]").arg(timestr) + QString(" *** %1").arg(expandEntities(str))); if(atBottom) d->deferredScroll(); if(alert) doAlert(); } QString GCMainDlg::getNickColor(QString nick) { int sender; if(nick == d->self||nick.isEmpty()) sender = -1; else { if (!nicks.contains(nick)) { //not found in map nicks.insert(nick,nicknumber); nicknumber++; } sender=nicks[nick]; } if(!option.gcNickColoring || option.gcNickColors.empty()) { return "#000000"; } else if(sender == -1 || option.gcNickColors.size() == 1) { return option.gcNickColors[option.gcNickColors.size()-1]; } else { int n = sender % (option.gcNickColors.size()-1); return option.gcNickColors[n]; } } void GCMainDlg::appendMessage(const Message &m, bool alert) { //QString who, color; if (!option.gcHighlighting) alert=false; QString who, textcolor, nickcolor,alerttagso,alerttagsc; bool atBottom = d->te_log->contentsY() >= d->te_log->contentsHeight() - d->te_log->visibleHeight(); who = m.from().resource(); QString hr = ""; if (d->trackBar&&m.from().resource() != d->self&&!m.spooled()) { hr = QString("
"); d->trackBar = false; if ( d->trackBarParagraph ) { d->te_log->setUpdatesEnabled( false ); d->te_log->setSelection(d->trackBarParagraph, 0, d->trackBarParagraph, 1, 1); d->te_log->removeSelectedText(1); d->te_log->setUpdatesEnabled( true ); } d->trackBarParagraph = d->te_log->paragraphs(); } /*if(local) { color = "#FF0000"; } else { color = "#0000FF"; }*/ nickcolor = getNickColor(who); textcolor = d->te_log->palette().active().text().name(); if(alert) { textcolor = "#FF0000"; alerttagso = ""; alerttagsc = ""; } if(m.spooled()) nickcolor = "#008000"; //color = "#008000"; QString timestr; QDateTime time = m.timeStamp(); //timestr.sprintf("%02d:%02d:%02d", time.time().hour(), time.time().minute(), time.time().second()); timestr = time.time().toString(LocalDate); /*int y = d->te_log->contentsHeight() - d->te_log->visibleHeight(); if(y < 0) y = 0; bool atBottom = (d->te_log->contentsY() < y - 32) ? false: true;*/ bool emote = false; if(m.body().left(4) == "/me ") emote = true; QString txt; if(emote) txt = plain2rich(m.body().mid(4)); else txt = plain2rich(m.body()); txt = linkify(txt); if(option.useEmoticons) txt = emoticonify(txt); if(emote) { //d->te_log->append(QString("").arg(color) + QString("[%1]").arg(timestr) + QString(" *%1 ").arg(expandEntities(who)) + txt + ""); d->te_log->append(hr + QString("").arg(nickcolor) + QString("[%1]").arg(timestr) + QString(" *%1 ").arg(expandEntities(who)) + alerttagso + txt + alerttagsc + ""); } else { if(option.chatSays) { //d->te_log->append(QString("").arg(color) + QString("[%1] ").arg(timestr) + QString("%1 says:").arg(expandEntities(who)) + "
" + txt); d->te_log->append(hr + QString("").arg(nickcolor) + QString("[%1] ").arg(timestr) + QString("%1 says:").arg(expandEntities(who)) + "
" + QString("").arg(textcolor) + alerttagso + txt + alerttagsc + ""); } else { //d->te_log->append(QString("").arg(color) + QString("[%1] <").arg(timestr) + expandEntities(who) + QString("> ") + txt); d->te_log->append(hr + QString("").arg(nickcolor) + QString("[%1] <").arg(timestr) + expandEntities(who) + QString("> ") + QString("").arg(textcolor) + alerttagso + txt + alerttagsc +""); } } //if(local || atBottom) if(m.from().resource() == d->self || atBottom) d->deferredScroll(); // if we're not active, notify the user by changing the title if(!isActiveWindow()) { ++d->pending; updateCaption(); } //if someone directed their comments to us, notify the user if(alert) doAlert(); //if the message spoke to us, alert the user before closing this window //except that keepopen doesn't seem to be implemented for this class yet. /*if(alert) { d->keepOpen = true; QTimer::singleShot(1000, this, SLOT(setKeepOpenFalse())); }*/ } void GCMainDlg::doAlert() { if(!isActiveWindow()) doFlash(true); } void GCMainDlg::updateCaption() { QString cap = ""; if(d->pending > 0) { cap += "* "; if(d->pending > 1) cap += QString("[%1] ").arg(d->pending); } cap += d->jid.full(); // if taskbar flash, then we need to erase first and redo #ifdef Q_WS_WIN bool on = false; if(d->flashTimer) on = d->flashCount & 1; if(on) FlashWindow(winId(), true); #endif setCaption(cap); #ifdef Q_WS_WIN if(on) FlashWindow(winId(), true); #endif } #ifdef Q_WS_WIN void GCMainDlg::doFlash(bool yes) { if(yes) { if(d->flashTimer) return; d->flashTimer = new QTimer(this); connect(d->flashTimer, SIGNAL(timeout()), SLOT(flashAnimate())); d->flashCount = 0; flashAnimate(); // kick the first one immediately d->flashTimer->start(500); } else { if(d->flashTimer) { delete d->flashTimer; d->flashTimer = 0; FlashWindow(winId(), false); } } } #else void GCMainDlg::doFlash(bool) { } #endif void GCMainDlg::flashAnimate() { #ifdef Q_WS_WIN FlashWindow(winId(), true); ++d->flashCount; if(d->flashCount == 5) d->flashTimer->stop(); #endif } void GCMainDlg::setLooks() { // update the fonts QFont f; f.fromString(option.font[fChat]); d->te_log->setFont(f); d->mle->setFont(f); f.fromString(option.font[fRoster]); d->lv_users->QListView::setFont(f); if ( d->smallChat ) { d->toolbar->hide(); } else { d->toolbar->show(); } // update the widget icon #ifndef Q_WS_MAC setIcon(IconsetFactory::icon("psi/groupChat")); #endif } void GCMainDlg::optionsUpdate() { /*QMimeSourceFactory *m = d->te_log->mimeSourceFactory(); d->te_log->setMimeSourceFactory(is->emoticons.generateFactory()); delete m;*/ setLooks(); // update status icons d->lv_users->updateAll(); } void GCMainDlg::lv_action(const QString &nick, const Status &s, int x) { if(x == 0) { d->pa->invokeGCMessage(d->jid.withResource(nick)); } else if(x == 1) { d->pa->invokeGCChat(d->jid.withResource(nick)); } else if(x == 2) { UserListItem u; u.setJid(d->jid.withResource(nick)); u.setName(nick); // make a resource so the contact appears online UserResource ur; ur.setName(nick); ur.setStatus(s); u.userResourceList().append(ur); StatusShowDlg *w = new StatusShowDlg(u); w->show(); } else if(x == 3) { d->pa->invokeGCInfo(d->jid.withResource(nick)); } else if(x == 4) { d->pa->invokeGCFile(d->jid.withResource(nick)); } } void GCMainDlg::contextMenuEvent(QContextMenuEvent *) { d->pm_settings->exec(QCursor::pos()); } void GCMainDlg::buildMenu() { // Dialog menu d->pm_settings->clear(); d->pm_settings->insertItem(tr("Toggle Compact/Full Size"), this, SLOT(toggleSmallChat())); d->act_clear->addTo( d->pm_settings ); d->pm_settings->insertSeparator(); d->act_icon->addTo( d->pm_settings ); } void GCMainDlg::toggleSmallChat() { d->smallChat = !d->smallChat; setLooks(); } //---------------------------------------------------------------------------- // GCFindDlg //---------------------------------------------------------------------------- GCFindDlg::GCFindDlg(int startPara, int startIndex, const QString &str, QWidget *parent, const char *name) :QDialog(parent, name, false, WDestructiveClose) { para = startPara; index = startIndex; setCaption(tr("Find")); QVBoxLayout *vb = new QVBoxLayout(this, 4); QHBoxLayout *hb = new QHBoxLayout(vb); QLabel *l = new QLabel(tr("Find:"), this); hb->addWidget(l); le_input = new QLineEdit(this); hb->addWidget(le_input); vb->addStretch(1); QFrame *Line1 = new QFrame(this); Line1->setFrameShape( QFrame::HLine ); Line1->setFrameShadow( QFrame::Sunken ); Line1->setFrameShape( QFrame::HLine ); vb->addWidget(Line1); hb = new QHBoxLayout(vb); hb->addStretch(1); QPushButton *pb_close = new QPushButton(tr("&Close"), this); connect(pb_close, SIGNAL(clicked()), SLOT(close())); hb->addWidget(pb_close); QPushButton *pb_find = new QPushButton(tr("&Find"), this); pb_find->setDefault(true); connect(pb_find, SIGNAL(clicked()), SLOT(doFind())); hb->addWidget(pb_find); pb_find->setAutoDefault(true); resize(200, minimumSize().height()); le_input->setText(str); le_input->setFocus(); } GCFindDlg::~GCFindDlg() { } void GCFindDlg::found(int _para, int _index) { para = _para; index = _index; } void GCFindDlg::error(const QString &str) { QMessageBox::warning(this, tr("Find"), tr("Search string '%1' not found.").arg(str)); le_input->setFocus(); } void GCFindDlg::doFind() { emit find(para, index, le_input->text()); } #include "groupchatdlg.moc"