• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdenetwork
  • Sitemap
  • Contact Us
 

kopete/kopete

chatview.cpp

Go to the documentation of this file.
00001 /*
00002     chatview.cpp - Chat View
00003 
00004     Copyright (c) 2002-2004 by Olivier Goffart       <ogoffart@kde.org>
00005     Copyright (c) 2002-2003 by Martijn Klingens      <klingens@kde.org>
00006 
00007     Kopete    (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
00008 
00009     *************************************************************************
00010     *                                                                       *
00011     * This program is free software; you can redistribute it and/or modify  *
00012     * it under the terms of the GNU General Public License as published by  *
00013     * the Free Software Foundation; either version 2 of the License, or     *
00014     * (at your option) any later version.                                   *
00015     *                                                                       *
00016     *************************************************************************
00017 */
00018 
00019 #include "chatview.h"
00020 
00021 #include "chatmessagepart.h"
00022 #include "chattexteditpart.h"
00023 #include "kopetechatwindow.h"
00024 #include "kopetechatsession.h"
00025 #include "kopetemetacontact.h"
00026 #include "kopetepluginmanager.h"
00027 #include "kopeteprotocol.h"
00028 #include "kopeteaccount.h"
00029 #include "kopeteglobal.h"
00030 #include "kopetecontactlist.h"
00031 #include "kopeteviewmanager.h"
00032 #include "kopetebehaviorsettings.h"
00033 
00034 #include <kconfig.h>
00035 #include <ktabwidget.h>
00036 #include <kdebug.h>
00037 #include <klocale.h>
00038 #include <kmessagebox.h>
00039 #include <kmenu.h>
00040 #include <kstringhandler.h>
00041 #include <kwindowsystem.h>
00042 #include <kglobalsettings.h>
00043 #include <kgenericfactory.h>
00044 #include <khtmlview.h>
00045 //#include <ksyntaxhighlighter.h>
00046 
00047 
00048 #include <QTimer>
00049 #include <QSplitter>
00050 
00051 //Added by qt3to4:
00052 #include <QPixmap>
00053 #include <QDropEvent>
00054 #include <QDragEnterEvent>
00055 #include <Q3UriDrag>
00056 #include <QObject>
00057 
00058 typedef KGenericFactory<ChatWindowPlugin> ChatWindowPluginFactory;
00059 K_EXPORT_COMPONENT_FACTORY( kopete_chatwindow, ChatWindowPluginFactory( "kopete_chatwindow" )  )
00060 
00061 ChatWindowPlugin::ChatWindowPlugin(QObject *parent, const QStringList &) :
00062     Kopete::ViewPlugin( ChatWindowPluginFactory::componentData(), parent )
00063 {}
00064 
00065 KopeteView* ChatWindowPlugin::createView( Kopete::ChatSession *manager )
00066 {
00067     return (KopeteView*)new ChatView(manager,this);
00068 }
00069 
00070 class KopeteChatViewPrivate
00071 {
00072 public:
00073     QString captionText;
00074     QString statusText;
00075     bool isActive;
00076     bool sendInProgress;
00077     bool visibleMembers;
00078     QSplitter * splitter;
00079 };
00080 
00081 ChatView::ChatView( Kopete::ChatSession *mgr, ChatWindowPlugin *parent )
00082      : KVBox( 0l ), KopeteView( mgr, parent )
00083          , d(new KopeteChatViewPrivate)
00084 {
00085     int i;
00086 
00087     d->isActive = false;
00088     d->visibleMembers = false;
00089     d->sendInProgress = false;
00090 
00091     KVBox *vbox=this;
00092 
00093     m_mainWindow = 0L;
00094     m_tabState = Normal;
00095 
00096 
00097     //FIXME: don't widgets start off hidden anyway?
00098     hide();
00099 
00100     d->splitter = new QSplitter( Qt::Vertical, vbox );
00101 
00102     //Create the view dock widget (KHTML Part), and set it to no docking (lock it in place)
00103     m_messagePart = new ChatMessagePart( mgr , this );
00104 
00105     //Create the bottom dock widget, with the edit area, statusbar and send button
00106     m_editPart = new ChatTextEditPart( mgr, vbox );
00107 
00108     d->splitter->addWidget(m_messagePart->view());
00109     d->splitter->addWidget(m_editPart->widget());
00110     d->splitter->setChildrenCollapsible( false );
00111     QList<int> sizes;
00112     sizes << 240 << 40;
00113     d->splitter->setSizes( sizes ); 
00114 
00115     // FIXME: is this used these days? it seems totally unnecessary
00116     connect( editPart(), SIGNAL( toolbarToggled(bool)), this, SLOT(slotToggleRtfToolbar(bool)) );
00117 
00118     connect( editPart(), SIGNAL( messageSent( Kopete::Message & ) ),
00119              this, SIGNAL( messageSent( Kopete::Message & ) ) );
00120     connect( editPart(), SIGNAL( canSendChanged( bool ) ),
00121              this, SIGNAL( canSendChanged(bool) ) );
00122     connect( editPart(), SIGNAL( typing(bool) ),
00123          mgr, SLOT( typing(bool) ) );
00124 
00125     //Set the view as the main widget
00126 //  setView(viewDock);
00127 
00128     //It is possible to drag and drop on this widget.
00129     // I had to disable the acceptDrop in the khtml widget to be able to intercept theses events.
00130     setAcceptDrops(true);
00131 //  viewDock->setAcceptDrops(false);
00132 
00133     //Manager signals
00134     connect( mgr, SIGNAL( displayNameChanged() ),
00135              this, SLOT( slotChatDisplayNameChanged() ) );
00136     connect( mgr, SIGNAL( contactAdded(const Kopete::Contact*, bool) ),
00137              this, SLOT( slotContactAdded(const Kopete::Contact*, bool) ) );
00138     connect( mgr, SIGNAL( contactRemoved(const Kopete::Contact*, const QString&, Qt::TextFormat, bool) ),
00139              this, SLOT( slotContactRemoved(const Kopete::Contact*, const QString&, Qt::TextFormat, bool) ) );
00140     connect( mgr, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & , const Kopete::OnlineStatus &) ),
00141              this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
00142     connect( mgr, SIGNAL( remoteTyping( const Kopete::Contact *, bool) ),
00143          this, SLOT( remoteTyping(const Kopete::Contact *, bool) ) );
00144     connect( mgr, SIGNAL( eventNotification( const QString& ) ),
00145          this, SLOT( setStatusText( const QString& ) ) );
00146 
00147     //Connections to the manager and the ViewManager that every view should have
00148     connect( this, SIGNAL( closing( KopeteView * ) ),
00149          KopeteViewManager::viewManager(), SLOT( slotViewDestroyed( KopeteView * ) ) );
00150     connect( this, SIGNAL( activated( KopeteView * ) ),
00151          KopeteViewManager::viewManager(), SLOT( slotViewActivated( KopeteView * ) ) );
00152     connect( this, SIGNAL( messageSent(Kopete::Message &) ),
00153          mgr, SLOT( sendMessage(Kopete::Message &) ) );
00154     connect( mgr, SIGNAL( messageSuccess() ),
00155          this, SLOT( messageSentSuccessfully() ));
00156 
00157     // add contacts
00158     slotContactAdded( mgr->myself(), true );
00159     for ( i = 0; i != mgr->members().size(); i++ )
00160         slotContactAdded( mgr->members()[i], true );
00161 
00162     setFocusProxy( editPart()->widget() );
00163     editPart()->widget()->setFocus();
00164 
00165     setCaption( m_manager->displayName(), false );
00166 
00167     // restore docking positions
00168     readOptions();
00169 }
00170 
00171 ChatView::~ChatView()
00172 {
00173     emit( closing( static_cast<KopeteView*>(this) ) );
00174 
00175     saveOptions();
00176 
00177     delete d;
00178 }
00179 
00180 KTextEdit *ChatView::editWidget()
00181 {
00182     return editPart()->textEdit();
00183 }
00184 
00185 QWidget *ChatView::mainWidget()
00186 {
00187     return this;
00188 }
00189 
00190 bool ChatView::canSend()
00191 {
00192     return editPart()->canSend();
00193 }
00194 
00195 Kopete::Message ChatView::currentMessage()
00196 {
00197     return editPart()->contents();
00198 }
00199 
00200 void ChatView::setCurrentMessage( const Kopete::Message &message )
00201 {
00202     editPart()->setContents( message );
00203 }
00204 
00205 void ChatView::cut()
00206 {
00207     editPart()->textEdit()->cut();
00208 }
00209 
00210 void ChatView::copy()
00211 {
00212     if ( messagePart()->hasSelection() )
00213         messagePart()->copy();
00214     else
00215         editPart()->textEdit()->copy();
00216 }
00217 
00218 void ChatView::paste()
00219 {
00220     editPart()->textEdit()->paste();
00221 }
00222 
00223 void ChatView::nickComplete()
00224 {
00225     return editPart()->complete();
00226 }
00227 
00228 void ChatView::addText( const QString &text )
00229 {
00230     editPart()->addText( text );
00231 }
00232 
00233 void ChatView::clear()
00234 {
00235     messagePart()->clear();
00236 }
00237 
00238 void ChatView::setBgColor( const QColor &newColor )
00239 {
00240     Q_UNUSED(newColor);
00241 //  editPart()->setBackgroundColorColor( newColor );
00242 }
00243 
00244 void ChatView::setFont()
00245 {
00246     editPart()->setFont();
00247 }
00248 
00249 QFont ChatView::font()
00250 {
00251     return editPart()->font();
00252 }
00253 
00254 void ChatView::setFont( const QFont &font )
00255 {
00256     editPart()->setFont( font );
00257 }
00258 
00259 void ChatView::setFgColor( const QColor &newColor )
00260 {
00261     if ( newColor.isValid() )
00262     {
00263         editPart()->setTextColor( newColor );
00264     }
00265     else
00266     {
00267         editPart()->setTextColor();
00268     }
00269 }
00270 
00271 void ChatView::raise( bool activate )
00272 {
00273     // this shouldn't change the focus. When the window is raised when a new message arrives
00274     // if i am coding, or talking to someone else, i want to end my sentence before switching to
00275     // the other chat. i just want to KNOW and SEE the other chat to switch to it later
00276     // (except if activate==true)
00277 
00278     if ( !m_mainWindow || !m_mainWindow->isActiveWindow() || activate )
00279         makeVisible();
00280 #ifdef Q_WS_X11
00281     if ( !KWindowSystem::windowInfo( m_mainWindow->winId(), NET::WMDesktop ).onAllDesktops() )
00282         if( Kopete::BehaviorSettings::self()->trayflashNotifySetCurrentDesktopToChatView() && activate )
00283             KWindowSystem::setCurrentDesktop( KWindowSystem::windowInfo( m_mainWindow->winId(), NET::WMDesktop ).desktop() );
00284         else
00285             KWindowSystem::setOnDesktop( m_mainWindow->winId(), KWindowSystem::currentDesktop() );
00286 #endif
00287     if(m_mainWindow->isMinimized())
00288     {
00289         KWindowSystem::unminimizeWindow( m_mainWindow->winId());
00290     }
00291 
00292 
00293     m_mainWindow->raise();
00294 
00295     /* Removed Nov 2003
00296     According to Zack, the user double-clicking a contact is not valid reason for a non-pager
00297     to grab window focus. While I don't agree with this, and it runs contradictory to every other
00298     IM out there, commenting this code out to agree with KWin policy.
00299 
00300     Redirect any bugs relating to the widnow now not grabbing focus on clicking a contact to KWin.
00301         - Jason K
00302     */
00303     //Will not activate window if user was typing
00304     if ( activate )
00305         KWindowSystem::activateWindow( m_mainWindow->winId() );
00306 
00307 }
00308 
00309 void ChatView::makeVisible()
00310 {
00311     if ( !m_mainWindow )
00312     {
00313         m_mainWindow = KopeteChatWindow::window( m_manager );
00314         m_mainWindow->setObjectName( QLatin1String("KopeteChatWindow") );
00315 //      if ( root )
00316 //          root->repaint( true );
00317         emit windowCreated();
00318     }
00319 
00320     if ( !m_mainWindow->isVisible() )
00321     {
00322         m_mainWindow->show();
00323         // scroll down post show and layout, otherwise the geometry is wrong to scroll to the bottom.
00324         m_messagePart->keepScrolledDown();
00325     }
00326 
00327 
00328 
00329     m_mainWindow->setActiveView( this );
00330 }
00331 
00332 bool ChatView::isVisible()
00333 {
00334     return ( m_mainWindow && m_mainWindow->isVisible() );
00335 }
00336 
00337 bool ChatView::sendInProgress()
00338 {
00339     return d->sendInProgress;
00340 }
00341 
00342 bool ChatView::closeView( bool force )
00343 {
00344     int response = KMessageBox::Continue;
00345 
00346     if ( !force )
00347     {
00348         if ( m_manager->members().count() > 1 )
00349         {
00350             QString shortCaption = d->captionText;
00351             shortCaption = KStringHandler::rsqueeze( shortCaption );
00352 
00353             response = KMessageBox::warningContinueCancel( this, i18n("<qt>You are about to leave the groupchat session <b>%1</b>.<br />"
00354                 "You will not receive future messages from this conversation.</qt>", shortCaption ), i18n( "Closing Group Chat" ),
00355                 KGuiItem( i18nc( "@action:button", "Close Chat" ) ), KStandardGuiItem::cancel(), QLatin1String( "AskCloseGroupChat" ) );
00356         }
00357 
00358         if ( !unreadMessageFrom.isNull() && ( response == KMessageBox::Continue ) )
00359         {
00360             response = KMessageBox::warningContinueCancel( this, i18n("<qt>You have received a message from <b>%1</b> in the last "
00361                 "second. Are you sure you want to close this chat?</qt>", unreadMessageFrom ), i18n( "Unread Message" ),
00362                 KGuiItem( i18nc( "@action:button", "Close Chat" ) ), KStandardGuiItem::cancel(), QLatin1String("AskCloseChatRecentMessage" ) );
00363         }
00364 
00365         if ( d->sendInProgress && ( response == KMessageBox::Continue ) )
00366         {
00367             response = KMessageBox::warningContinueCancel( this, i18n( "You have a message send in progress, which will be "
00368                 "aborted if this chat is closed. Are you sure you want to close this chat?" ), i18n( "Message in Transit" ),
00369                 KGuiItem( i18nc( "@action:button", "Close Chat" ) ), KStandardGuiItem::cancel(), QLatin1String( "AskCloseChatMessageInProgress" ) );
00370         }
00371     }
00372 
00373     if( response == KMessageBox::Continue )
00374     {
00375         // Remove the widget from the window it's attached to
00376         // and schedule it for deletion
00377         if( m_mainWindow )
00378             m_mainWindow->detachChatView( this );
00379         deleteLater();
00380 
00381         return true;
00382     }
00383 
00384     return false;
00385 }
00386 
00387 void ChatView::updateChatState( KopeteTabState newState )
00388 {
00389     if ( newState == Undefined )
00390         newState = m_tabState;
00391     else if ( newState != Typing && ( newState != Changed || ( m_tabState != Message && m_tabState != Highlighted ) )
00392         && ( newState != Message ||  m_tabState != Highlighted ) )
00393     { //if the new state is not a typing state and we don't already have a message or a highlighted message
00394       //change the tab state
00395         m_tabState = newState;
00396     }
00397 
00398     newState = m_remoteTypingMap.isEmpty() ? m_tabState : Typing ;
00399 
00400     emit updateChatState( this, newState );
00401 
00402     if( newState != Typing )
00403     {
00404         setStatusText( i18np( "One other person in the chat",
00405                    "%1 other people in the chat", m_manager->members().count() ) );
00406     }
00407 }
00408 
00409 void ChatView::setMainWindow( KopeteChatWindow* parent )
00410 {
00411     m_mainWindow = parent;
00412 }
00413 
00414 void ChatView::remoteTyping( const Kopete::Contact *contact, bool isTyping )
00415 {
00416     TypingMap::iterator it = m_remoteTypingMap.find(contact);
00417     if (it != m_remoteTypingMap.end())
00418     {
00419         if (it.value()->isActive())
00420             it.value()->stop();
00421         delete it.value();
00422         m_remoteTypingMap.erase(it);
00423     }
00424     if( isTyping )
00425     {
00426         m_remoteTypingMap.insert( contact, new QTimer(this) );
00427         connect( m_remoteTypingMap[ contact ], SIGNAL( timeout() ), SLOT( slotRemoteTypingTimeout() ) );
00428 
00429         m_remoteTypingMap[ contact ]->setSingleShot( true );
00430         m_remoteTypingMap[ contact ]->start( 6000 );
00431     }
00432 
00433     // Loop through the map, constructing a string of people typing
00434     QStringList typingList;
00435 
00436     for( it = m_remoteTypingMap.begin(); it != m_remoteTypingMap.end(); ++it )
00437     {
00438         const Kopete::Contact *c = it.key();
00439         QString nick;
00440         if( c->metaContact() && c->metaContact() != Kopete::ContactList::self()->myself() )
00441         {
00442             nick = c->metaContact()->displayName();
00443         }
00444         else
00445         {
00446             nick = c->nickName();
00447         }
00448         typingList.append( nick );
00449     }
00450 
00451     // Update the status area
00452     if( !typingList.isEmpty() )
00453     {
00454         if ( typingList.count() == 1 )
00455             setStatusText( i18n( "%1 is typing a message", typingList.first() ) );
00456         else
00457         {
00458             QString statusTyping = typingList.join( QLatin1String( ", " ) );
00459             setStatusText( i18nc( "%1 is a list of names", "%1 are typing a message", statusTyping ) );
00460         }
00461         updateChatState( Typing );
00462     }
00463     else
00464     {
00465         updateChatState();
00466     }
00467 }
00468 
00469 void ChatView::setStatusText( const QString &status )
00470 {
00471     d->statusText = status;
00472     if ( d->isActive )
00473         m_mainWindow->setStatus( status );
00474 }
00475 
00476 const QString& ChatView::statusText()
00477 {
00478     return d->statusText;
00479 }
00480 
00481 void ChatView::slotChatDisplayNameChanged()
00482 {
00483     // This fires whenever a contact or MC changes displayName, so only
00484     // update the caption if it changed to avoid unneeded updates that
00485     // could cause flickering
00486     QString chatName = m_manager->displayName();
00487     if ( chatName != d->captionText )
00488         setCaption( chatName, true );
00489 }
00490 
00491 void ChatView::slotPropertyChanged( Kopete::PropertyContainer*, const QString &key,
00492         const QVariant& oldValue, const QVariant &newValue  )
00493 {
00494     if ( key == Kopete::Global::Properties::self()->nickName().key() )
00495     {
00496         QString newName=newValue.toString();
00497         QString oldName=oldValue.toString();
00498 
00499         if(Kopete::BehaviorSettings::self()->showEvents())
00500             if ( oldName != newName && !oldName.isEmpty())
00501                 sendInternalMessage( i18n( "%1 is now known as %2", oldName, newName ) );
00502     }
00503 }
00504 
00505 void ChatView::slotDisplayNameChanged( const QString &oldValue, const QString &newValue )
00506 {
00507     if( Kopete::BehaviorSettings::self()->showEvents() )
00508     {
00509         if( oldValue != newValue )
00510             sendInternalMessage( i18n( "%1 is now known as %2", oldValue, newValue ) );
00511     }
00512 }
00513 
00514 void ChatView::slotContactAdded(const Kopete::Contact *contact, bool suppress)
00515 {
00516     QString contactName;
00517     // Myself metacontact is not a reliable source.
00518     if( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
00519     {
00520         contactName = contact->metaContact()->displayName();
00521     }
00522     else
00523     {
00524         contactName = contact->nickName();
00525     }
00526 
00527     if( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
00528     {
00529         connect( contact->metaContact(), SIGNAL( displayNameChanged(const QString&, const QString&) ),
00530             this, SLOT( slotDisplayNameChanged(const QString &, const QString &) ) );
00531     }
00532     else
00533     {
00534         connect( contact, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
00535         this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
00536     }
00537 
00538     if( !suppress && m_manager->members().count() > 1 )
00539         sendInternalMessage(  i18n("%1 has joined the chat.", contactName) );
00540 
00541     updateChatState();
00542     emit updateStatusIcon( this );
00543 }
00544 
00545 void ChatView::slotContactRemoved( const Kopete::Contact *contact, const QString &reason, Qt::TextFormat format, bool suppressNotification )
00546 {
00547 //  kDebug(14000) ;
00548     if ( contact != m_manager->myself() )
00549     {
00550         TypingMap::iterator it = m_remoteTypingMap.find( contact );
00551         if ( it != m_remoteTypingMap.end() )
00552         {
00553             if ((*it)->isActive())
00554                 (*it)->stop();
00555             delete (*it);
00556             m_remoteTypingMap.remove( contact );
00557         }
00558 
00559         QString contactName;
00560         if( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
00561         {
00562             contactName = contact->metaContact()->displayName();
00563         }
00564         else
00565         {
00566             contactName = contact->nickName();
00567         }
00568 
00569         // When the last person leaves, don't disconnect the signals, since we're in a one-to-one chat
00570         if ( m_manager->members().count() > 0 )
00571         {
00572             if( contact->metaContact() )
00573             {
00574                 disconnect( contact->metaContact(), SIGNAL( displayNameChanged(const QString&, const QString&) ),
00575                 this, SLOT( slotDisplayNameChanged(const QString&, const QString&) ) );
00576             }
00577             else
00578             {
00579                 disconnect(contact,SIGNAL(propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & )),
00580                 this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
00581             }
00582         }
00583 
00584         if ( !suppressNotification )
00585         {
00586             if ( reason.isEmpty() )
00587                 sendInternalMessage( i18n( "%1 has left the chat.", contactName ), format ) ;
00588             else
00589                 sendInternalMessage( i18n( "%1 has left the chat (%2).", contactName, reason ), format);
00590         }
00591     }
00592 
00593     updateChatState();
00594     emit updateStatusIcon( this );
00595 }
00596 
00597 QString& ChatView::caption() const
00598 {
00599      return d->captionText;
00600 }
00601 
00602 void ChatView::setCaption( const QString &text, bool modified )
00603 {
00604 //  kDebug(14000) ;
00605     QString newCaption = text;
00606 
00607     //Save this caption
00608     d->captionText = text;
00609 
00610     //Turncate if needed
00611     newCaption = KStringHandler::rsqueeze( d->captionText, 20 );
00612 
00613     //Call the original set caption
00614     QWidget::setWindowTitle( newCaption );
00615 
00616     emit updateChatTooltip( this, QString::fromLatin1("<qt>%1</qt>").arg( d->captionText ) );
00617     emit updateChatLabel( this, newCaption );
00618     //Blink icon if modified and not active
00619     if( !d->isActive && modified )
00620         updateChatState( Changed );
00621     else
00622         updateChatState();
00623 
00624     //Tell the parent we changed our caption
00625     emit( captionChanged( d->isActive ) );
00626 }
00627 
00628 void ChatView::appendMessage(Kopete::Message &message)
00629 {
00630     remoteTyping( message.from(), false );
00631 
00632     messagePart()->appendMessage(message);
00633 
00634     if( !d->isActive )
00635     {
00636         switch ( message.importance() )
00637         {
00638             case Kopete::Message::Highlight:
00639                 updateChatState( Highlighted );
00640                 break;
00641             case Kopete::Message::Normal:
00642                 if ( message.direction() == Kopete::Message::Inbound )
00643                 {
00644                     updateChatState( Message );
00645                     break;
00646                 } // if it's an enternal message or a outgoing, fall thought
00647             default:
00648                 updateChatState( Changed );
00649         }
00650     }
00651 
00652     if( message.direction() == Kopete::Message::Inbound )
00653     {
00654         if( message.from()->metaContact() && message.from()->metaContact() != Kopete::ContactList::self()->myself() )
00655         {
00656             unreadMessageFrom = message.from()->metaContact()->displayName();
00657         }
00658         else
00659         {
00660             unreadMessageFrom = message.from()->nickName();
00661         }
00662         QTimer::singleShot( 1000, this, SLOT( slotMarkMessageRead() ) );
00663     }
00664     else
00665         unreadMessageFrom.clear();
00666 }
00667 
00668 void ChatView::slotMarkMessageRead()
00669 {
00670     unreadMessageFrom.clear();
00671 }
00672 
00673 void ChatView::slotToggleRtfToolbar( bool enabled )
00674 {
00675     emit rtfEnabled( this, enabled );
00676 }
00677 
00678 void ChatView::slotContactStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus )
00679 {
00680     kDebug(14000) << contact;
00681     bool inhibitNotification = ( newStatus.status() == Kopete::OnlineStatus::Unknown ||
00682                                  oldStatus.status() == Kopete::OnlineStatus::Unknown );
00683     if ( contact && Kopete::BehaviorSettings::self()->showEvents() && !inhibitNotification )
00684     {
00685         if ( contact->account() && contact == contact->account()->myself() )
00686         {
00687             // Separate notification for the 'self' contact
00688             if ( newStatus.status() != Kopete::OnlineStatus::Connecting )
00689                 sendInternalMessage( i18n( "You are now marked as %1.", newStatus.description() ) );
00690         }
00691         else if ( !contact->account() || !contact->account()->suppressStatusNotification() )
00692         {
00693             // Don't send notifications when we just connected ourselves, i.e. when suppressions are still active
00694             if ( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
00695             {
00696                 sendInternalMessage( i18n( "%2 is now %1.",
00697                     newStatus.description(), contact->metaContact()->displayName() ) );
00698             }
00699             else
00700             {
00701                 QString nick=contact->nickName();
00702                 sendInternalMessage( i18n( "%2 is now %1.",
00703                     newStatus.description(), nick ) );
00704             }
00705         }
00706     }
00707 
00708     // update the windows caption
00709     slotChatDisplayNameChanged();
00710     emit updateStatusIcon( this );
00711 }
00712 
00713 void ChatView::sendInternalMessage(const QString &msg, Qt::TextFormat format )
00714 {
00715     // When closing kopete, some internal message may be sent because some contact are deleted
00716     // these contacts can already be deleted
00717     Kopete::Message message = Kopete::Message();
00718     message.setDirection( Kopete::Message::Internal );
00719     switch(format)
00720     {
00721         default:
00722         case Qt::PlainText:
00723             message.setPlainBody( msg );
00724             break;
00725         case Qt::RichText:
00726             message.setHtmlBody( msg );
00727             break;
00728     }
00729 
00730     // (in many case, this is useless to set myself as contact)
00731     // TODO: set the contact which initiate the internal message,
00732     // so we can later show a icon of it (for example, when he join a chat)
00733     messagePart()->appendMessage( message );
00734 }
00735 
00736 void ChatView::sendMessage()
00737 {
00738     d->sendInProgress = true;
00739     editPart()->sendMessage();
00740 }
00741 
00742 void ChatView::messageSentSuccessfully()
00743 {
00744     d->sendInProgress = false;
00745     emit messageSuccess( this );
00746 }
00747 
00748 void ChatView::saveOptions()
00749 {
00750     KSharedConfig::Ptr config = KGlobal::config();
00751     KConfigGroup kopeteChatWindowMainWinSettings( config, ( msgManager()->form() == Kopete::ChatSession::Chatroom ? QLatin1String( "KopeteChatWindowGroupMode" ) : QLatin1String( "KopeteChatWindowIndividualMode" ) ) );
00752     kopeteChatWindowMainWinSettings.writeEntry( QLatin1String("ChatViewSplitter"), d->splitter->saveState().toBase64() );
00753     saveChatSettings();
00754     config->sync();
00755 }
00756 
00757 void ChatView::saveChatSettings()
00758 {
00759     Kopete::ContactPtrList contacts = msgManager()->members();
00760 
00761     if ( contacts.count() == 0 )
00762         return;
00763 
00764     Kopete::MetaContact* mc = contacts.first()->metaContact();
00765 
00766     if ( contacts.count() > 1 )
00767         return; //can't save with more than one person in chatview
00768 
00769     if ( !mc )
00770         return;
00771 
00772     QString contactListGroup = QLatin1String("chatwindow_") +
00773                                mc->metaContactId();
00774     KConfigGroup config = KGlobal::config()->group(contactListGroup);
00775     config.writeEntry( "EnableRichText", editPart()->isRichTextEnabled() );
00776     config.writeEntry( "EnableAutoSpellCheck", editPart()->autoSpellCheckEnabled() );
00777     config.sync();
00778 }
00779 
00780 void ChatView::loadChatSettings()
00781 {
00782     Kopete::ContactPtrList contacts = msgManager()->members();
00783     if ( contacts.count() > 1 )
00784         return; //can't load with more than one other person in the chat
00785 
00786     //read settings for metacontact
00787     QString contactListGroup = QLatin1String("chatwindow_") +
00788                                contacts.first()->metaContact()->metaContactId();
00789     KConfigGroup config(KGlobal::config(), contactListGroup );
00790     bool enableRichText = config.readEntry( "EnableRichText", true );
00791     editPart()->setRichTextEnabled( enableRichText );
00792     emit rtfEnabled( this, editPart()->isRichTextEnabled() );
00793     bool enableAutoSpell = config.readEntry( "EnableAutoSpellCheck", false );
00794     emit autoSpellCheckEnabled( this, enableAutoSpell );
00795 }
00796 
00797 void ChatView::readOptions()
00798 {
00799     KConfigGroup kopeteChatWindowMainWinSettings( KGlobal::config(), ( msgManager()->form() == Kopete::ChatSession::Chatroom ? QLatin1String( "KopeteChatWindowGroupMode" ) : QLatin1String( "KopeteChatWindowIndividualMode" ) ) );
00800     //kDebug(14000) << "reading splitterpos from key: " << dockKey;
00801     QByteArray state;
00802     state = kopeteChatWindowMainWinSettings.readEntry( QLatin1String("ChatViewSplitter"), state );
00803     d->splitter->restoreState( QByteArray::fromBase64( state ) );
00804 }
00805 
00806 void ChatView::setActive( bool value )
00807 {
00808     d->isActive = value;
00809     if ( d->isActive )
00810     {
00811         updateChatState( Normal );
00812         emit( activated( static_cast<KopeteView*>(this) ) );
00813     }
00814 }
00815 
00816 void ChatView::slotRemoteTypingTimeout()
00817 {
00818     // Remove the topmost timer from the list. Why does QPtrDict use void* keys and not typed keys? *sigh*
00819     // FIXME: should remove the right item, not the topmost
00820     if ( !m_remoteTypingMap.isEmpty() )
00821         remoteTyping( m_remoteTypingMap.begin().key(), false );
00822 }
00823 
00824 void ChatView::dragEnterEvent ( QDragEnterEvent * event )
00825 {
00826     if( event->provides( "kopete/x-contact" ) )
00827     {
00828         QStringList lst = QString::fromUtf8(event->encodedData ( "kopete/x-contact" )).split( QChar( 0xE000 ) , QString::SkipEmptyParts );
00829         if(m_manager->mayInvite() && m_manager->protocol()->pluginId() == lst[0] && m_manager->account()->accountId() == lst[1])
00830         {
00831             QString contact=lst[2];
00832 
00833             bool found =false;
00834             foreach(Kopete::Contact * cts, m_manager->members())
00835             {
00836                 if(cts->contactId() == contact)
00837                 {
00838                     found=true;
00839                     break;
00840                 }
00841             }
00842 
00843             if(!found && contact != m_manager->myself()->contactId())
00844                 event->accept();
00845         }
00846     }
00847     else if( event->provides( "kopete/x-metacontact" ) )
00848     {
00849         QString metacontactID=QString::fromUtf8(event->encodedData ( "kopete/x-metacontact" ));
00850         Kopete::MetaContact *parent = Kopete::ContactList::self()->metaContact(metacontactID);
00851         if ( parent && m_manager->mayInvite() )
00852         {
00853             foreach ( Kopete::Contact * candidate, parent->contacts() )
00854             {
00855                 if( candidate && candidate->account() == m_manager->account() && candidate->isOnline())
00856                 {
00857                     if( candidate != m_manager->myself() &&  !m_manager->members().contains( candidate ) )
00858                         event->accept();
00859                 }
00860             }
00861         }
00862     }
00863     // make sure it doesn't come from the current chat view - then it's an emoticon
00864     else if ( event->provides( "text/uri-list" ) && m_manager->members().count() == 1 &&
00865                  ( event->source() != (QWidget*)m_messagePart->view()->viewport() ) )
00866     {
00867         Kopete::ContactPtrList members = m_manager->members();
00868         Kopete::Contact *contact = members.first();
00869         if ( contact && contact->canAcceptFiles() )
00870             event->accept();
00871     }
00872     else
00873         QWidget::dragEnterEvent(event);
00874 }
00875 
00876 void ChatView::dropEvent ( QDropEvent * event )
00877 {
00878     Kopete::ContactPtrList contacts;
00879 
00880     if( event->provides( "kopete/x-contact" ) )
00881     {
00882         QStringList lst = QString::fromUtf8(event->encodedData ( "kopete/x-contact" )).split( QChar( 0xE000 ) , QString::SkipEmptyParts );
00883         if(m_manager->mayInvite() && m_manager->protocol()->pluginId() == lst[0] && m_manager->account()->accountId() == lst[1])
00884         {
00885             QString contact=lst[2];
00886 
00887             bool found =false;
00888             foreach ( Kopete::Contact * candidate, m_manager->members() )
00889             {
00890                 if(candidate->contactId() == contact)
00891                 {
00892                     found=true;
00893                     break;
00894                 }
00895             }
00896             if(!found && contact != m_manager->myself()->contactId())
00897                 m_manager->inviteContact(contact);
00898         }
00899     }
00900     else if( event->provides( "kopete/x-metacontact" ) )
00901     {
00902         QString metacontactID=QString::fromUtf8(event->encodedData ( "kopete/x-metacontact" ));
00903         Kopete::MetaContact *parent = Kopete::ContactList::self()->metaContact(metacontactID);
00904         if ( parent && m_manager->mayInvite() )
00905         {
00906             foreach ( Kopete::Contact * candidate, parent->contacts() )
00907             {
00908                 if( candidate && candidate->account() == m_manager->account() && candidate->isOnline())
00909                 {
00910                     if( candidate != m_manager->myself() &&  !m_manager->members().contains( candidate ) )
00911                         m_manager->inviteContact(candidate->contactId());
00912                 }
00913             }
00914         }
00915     }
00916     else if ( event->provides( "text/uri-list" ) && m_manager->members().count() == 1 )
00917     {
00918         Kopete::ContactPtrList members = m_manager->members();
00919         Kopete::Contact *contact = members.first();
00920 
00921         if ( !contact || !contact->canAcceptFiles() || !Q3UriDrag::canDecode( event ) )
00922         {
00923             event->ignore();
00924             return;
00925         }
00926 
00927         KUrl::List urlList = KUrl::List::fromMimeData( event->mimeData() );
00928 
00929         for ( KUrl::List::Iterator it = urlList.begin(); it != urlList.end(); ++it )
00930         {
00931             if ( (*it).isLocalFile() )
00932             { //send a file
00933                 contact->sendFile( *it );
00934             }
00935             else
00936             { //this is a URL, send the URL in a message
00937                 addText( (*it).url() );
00938             }
00939         }
00940         event->accept();
00941         return;
00942     }
00943     else
00944         QWidget::dropEvent(event);
00945 
00946 }
00947 
00948 void ChatView::registerContextMenuHandler( QObject *target, const char* slot )
00949 {
00950     connect( m_messagePart,
00951         SIGNAL( contextMenuEvent( Kopete::Message &, const QString &, KMenu * ) ),
00952         target,
00953         slot
00954     );
00955 }
00956 
00957 void ChatView::registerTooltipHandler( QObject *target, const char* slot )
00958 {
00959     connect( m_messagePart,
00960         SIGNAL( tooltipEvent( Kopete::Message &, const QString &, QString & ) ),
00961         target,
00962         slot
00963     );
00964 }
00965 
00966 #include "chatview.moc"
00967 
00968 // vim: set noet ts=4 sts=4 sw=4:
00969 

kopete/kopete

Skip menu "kopete/kopete"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdenetwork

Skip menu "kdenetwork"
  • kget
  • kopete
  •   kopete
  •   libkopete
  •       libpapillon
  • krfb
Generated for kdenetwork by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal