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

kopete/kopete

chattexteditpart.cpp

Go to the documentation of this file.
00001 /*
00002     chattexteditpart.cpp - Chat Text Edit Part
00003 
00004     Copyright (c) 2004      by Richard Smith         <kde@metafoo.co.uk>
00005 
00006     Kopete    (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
00007 
00008     *************************************************************************
00009     *                                                                       *
00010     * This program is free software; you can redistribute it and/or modify  *
00011     * it under the terms of the GNU General Public License as published by  *
00012     * the Free Software Foundation; either version 2 of the License, or     *
00013     * (at your option) any later version.                                   *
00014     *                                                                       *
00015     *************************************************************************
00016 */
00017 
00018 #include "chattexteditpart.h"
00019 
00020 #include "kopetecontact.h"
00021 #include "kopetechatsession.h"
00022 #include "kopeteonlinestatus.h"
00023 #include "kopeteprotocol.h"
00024 #include "kopeteglobal.h"
00025 #include <kopeteappearancesettings.h>
00026 
00027 #include <kcompletion.h>
00028 #include <kdebug.h>
00029 #include <ktextedit.h>
00030 
00031 #include <QtCore/QTimer>
00032 #include <QtCore/QRegExp>
00033 
00034 ChatTextEditPart::ChatTextEditPart( Kopete::ChatSession *session, QWidget *parent )
00035     : KRichTextEditPart(parent, 0, QStringList()), m_session(session)
00036 {
00037     // Set rich support in the part
00038     setProtocolRichTextSupport();
00039 
00040     historyPos = -1;
00041     
00042     mComplete = new KCompletion();
00043     mComplete->setIgnoreCase( true );
00044     mComplete->setOrder( KCompletion::Weighted );
00045     
00046     // set params on the edit widget
00047     textEdit()->setMinimumSize( QSize( 75, 20 ) );
00048 //  textEdit()->setWordWrap( Q3TextEdit::WidgetWidth );
00049 //  textEdit()->setWrapPolicy( Q3TextEdit::AtWhiteSpace );
00050 //  textEdit()->setAutoFormatting( Q3TextEdit::AutoNone );
00051 
00052     // some signals and slots connections
00053     connect( textEdit(), SIGNAL( textChanged()), this, SLOT( slotTextChanged() ) );
00054 
00055     // timers for typing notifications
00056     m_typingRepeatTimer = new QTimer(this);
00057     m_typingRepeatTimer->setObjectName("m_typingRepeatTimer");
00058     m_typingStopTimer   = new QTimer(this);
00059     m_typingStopTimer->setObjectName("m_typingStopTimer");
00060 
00061     connect( m_typingRepeatTimer, SIGNAL( timeout() ), this, SLOT( slotRepeatTypingTimer() ) );
00062     connect( m_typingStopTimer,   SIGNAL( timeout() ), this, SLOT( slotStoppedTypingTimer() ) );
00063 
00064     connect( session, SIGNAL( contactAdded(const Kopete::Contact*, bool) ),
00065              this, SLOT( slotContactAdded(const Kopete::Contact*) ) );
00066     connect( session, SIGNAL( contactRemoved(const Kopete::Contact*, const QString&, Qt::TextFormat, bool) ),
00067              this, SLOT( slotContactRemoved(const Kopete::Contact*) ) );
00068     connect( session, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & , const Kopete::OnlineStatus &) ),
00069              this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
00070 
00071     connect( Kopete::AppearanceSettings::self(), SIGNAL( appearanceChanged() ),
00072              this, SLOT( slotAppearanceChanged() ) );
00073     
00074     connect( KGlobalSettings::self(), SIGNAL( kdisplayFontChanged() ),
00075              this, SLOT( slotAppearanceChanged() ) );
00076 
00077     slotAppearanceChanged();
00078 
00079     slotContactAdded( session->myself() );
00080 
00081     foreach( Kopete::Contact *contact, session->members() )
00082         slotContactAdded( contact );
00083 }
00084 
00085 ChatTextEditPart::~ChatTextEditPart()
00086 {
00087     delete mComplete;
00088 }
00089 
00090 // NAUGHTY, BAD AND WRONG! (but needed to fix nick complete bugs)
00091 /*
00092 #include <private/qrichtext_p.h>
00093 class EvilTextEdit : public KTextEdit
00094 {
00095 public:
00096     // grab the paragraph as plain text - very very evil.
00097     QString plainText( int para )
00098     {
00099         QString str = document()->paragAt( para )->string()->toString();
00100         // str includes an extra space on the end (from the newline character?) - remove it
00101         return str.left( str.length() - 1 );
00102     }
00103 };
00104 */
00105 void ChatTextEditPart::complete()
00106 {
00107 #ifdef __GNUC__
00108 #warning disabled nick completion to make it compile
00109 #endif
00110 #if 0
00111     int para = 1, parIdx = 1;
00112     textEdit()->getCursorPosition( &para, &parIdx);
00113 
00114     // FIXME: strips out all formatting
00115 //QString txt = static_cast<EvilTextEdit*>(textEdit())->plainText( para );
00116     QString txt = textEdit()->text(para);
00117 
00118     if ( parIdx > 0 )
00119     {
00120         int firstSpace = txt.lastIndexOf( QRegExp( QLatin1String("\\s\\S+") ), parIdx - 1 ) + 1;
00121         int lastSpace = txt.find( QRegExp( QLatin1String("[\\s\\:]") ), firstSpace );
00122         if( lastSpace == -1 )
00123             lastSpace = txt.length();
00124 
00125         QString word = txt.mid( firstSpace, lastSpace - firstSpace );
00126         QString match;
00127 
00128         kDebug(14000) << word << " from '" << txt << "'";
00129 
00130         if ( word != m_lastMatch )
00131         {
00132             match = mComplete->makeCompletion( word );
00133             m_lastMatch.clear();
00134             parIdx -= word.length();
00135         }
00136         else
00137         {
00138             match = mComplete->nextMatch();
00139             parIdx -= m_lastMatch.length();
00140         }
00141 
00142         if ( !match.isNull() && !match.isEmpty() )
00143         {
00144             QString rightText = txt.right( txt.length() - lastSpace );
00145 
00146             if ( para == 0 && firstSpace == 0 && rightText[0] != QChar(':') )
00147             {
00148                 rightText = match + QLatin1String(": ") + rightText;
00149                 parIdx += 2;
00150             }
00151             else
00152                 rightText = match + rightText;
00153 
00154             // insert *before* remove. this is becase Qt adds an extra blank line
00155             // if the rich text control becomes empty (if you remove the only para).
00156             // disable updates while we change the contents to eliminate flicker.
00157             textEdit()->setUpdatesEnabled( false );
00158             textEdit()->insertParagraph( txt.left(firstSpace) + rightText, para );
00159             textEdit()->removeParagraph( para + 1 );
00160             textEdit()->setCursorPosition( para, parIdx + match.length() );
00161             textEdit()->setUpdatesEnabled( true );
00162             // must call this rather than update because QTextEdit is broken :(
00163             textEdit()->updateContents();
00164             m_lastMatch = match;
00165         }
00166         else
00167         {
00168             kDebug(14000) << "No completions! Tried " << mComplete->items();
00169         }
00170     }
00171 #endif
00172 }
00173 
00174 void ChatTextEditPart::slotPropertyChanged( Kopete::PropertyContainer*, const QString &key,
00175         const QVariant& oldValue, const QVariant &newValue  )
00176 {
00177     if ( key == Kopete::Global::Properties::self()->nickName().key() )
00178     {
00179         mComplete->removeItem( oldValue.toString() );
00180         mComplete->addItem( newValue.toString() );
00181     }
00182 }
00183 
00184 void ChatTextEditPart::slotContactAdded( const Kopete::Contact *contact )
00185 {
00186     connect( contact, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
00187              this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
00188     
00189     QString contactName = contact->property(Kopete::Global::Properties::self()->nickName()).value().toString();
00190     mComplete->addItem( contactName );
00191 }
00192 
00193 void ChatTextEditPart::slotContactRemoved( const Kopete::Contact *contact )
00194 {
00195     disconnect( contact, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
00196                 this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
00197     
00198     QString contactName = contact->property(Kopete::Global::Properties::self()->nickName()).value().toString();
00199     mComplete->removeItem( contactName );
00200 }
00201 
00202 bool ChatTextEditPart::canSend()
00203 {
00204     int i;
00205     
00206     if ( !m_session ) return false;
00207 
00208     // can't send if there's nothing *to* send...
00209     if ( text(Qt::PlainText).isEmpty() )
00210         return false;
00211 
00212     Kopete::ContactPtrList members = m_session->members();
00213     
00214     // if we can't send offline, make sure we have a reachable contact...
00215     if ( !( m_session->protocol()->capabilities() & Kopete::Protocol::CanSendOffline ) )
00216     {
00217         bool reachableContactFound = false;
00218 
00219         //TODO: does this perform badly in large / busy IRC channels? - no, doesn't seem to
00220         for( i = 0; i != members.size(); i++ )
00221         {
00222             if ( members[i]->isReachable() )
00223             {
00224                 reachableContactFound = true;
00225                 break;
00226             }
00227         }
00228 
00229         // no online contact found and can't send offline? can't send.
00230         if ( !reachableContactFound )
00231             return false;
00232     }
00233 
00234     return true;
00235 }
00236 
00237 void ChatTextEditPart::slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus )
00238 {
00239     //FIXME: should use signal contact->isReachableChanged, but it doesn't exist ;(
00240     if ( ( oldStatus.status() == Kopete::OnlineStatus::Offline )
00241       != ( newStatus.status() == Kopete::OnlineStatus::Offline ) )
00242     {
00243         emit canSendChanged( canSend() );
00244     }
00245 }
00246 
00247 void ChatTextEditPart::sendMessage()
00248 {
00249     QString txt = this->text( Qt::PlainText );
00250     // avoid sending emtpy messages or enter keys (see bug 100334)
00251     if ( txt.isEmpty() || txt == "\n" )
00252         return;
00253 
00254     if ( m_lastMatch.isNull() && ( txt.indexOf( QRegExp( QLatin1String("^\\w+:\\s") ) ) > -1 ) )
00255     { //no last match and it finds something of the form of "word:" at the start of a line
00256         QString search = txt.left( txt.indexOf(':') );
00257         if( !search.isEmpty() )
00258         {
00259             QString match = mComplete->makeCompletion( search );
00260             if( !match.isNull() )
00261                 textEdit()->setText( txt.replace(0,search.length(),match) );
00262         }
00263     }
00264 
00265     if ( !m_lastMatch.isNull() )
00266     {
00267         //FIXME: what is the next line for?
00268         mComplete->addItem( m_lastMatch );
00269         m_lastMatch.clear();
00270     }
00271 
00272     slotStoppedTypingTimer();
00273     Kopete::Message sentMessage = contents();
00274     emit messageSent( sentMessage );
00275     historyList.prepend( this->text( Qt::PlainText) );
00276     historyPos = -1;
00277     clear();
00278     emit canSendChanged( false );
00279 }
00280 
00281 bool ChatTextEditPart::isTyping()
00282 {
00283     QString txt = text( Qt::PlainText );
00284 
00285     //Make sure the message is empty. QString::isEmpty()
00286     //returns false if a message contains just whitespace
00287     //which is the reason why we strip the whitespace   
00288     return !txt.trimmed().isEmpty();
00289 }
00290 
00291 void ChatTextEditPart::slotTextChanged()
00292 {
00293     if ( isTyping() )
00294     {
00295         // And they were previously typing
00296         if( !m_typingRepeatTimer->isActive() )
00297         {
00298             m_typingRepeatTimer->setSingleShot( false );
00299             m_typingRepeatTimer->start( 4000 );
00300             slotRepeatTypingTimer();
00301         }
00302 
00303         // Reset the stop timer again, regardless of status
00304         m_typingStopTimer->setSingleShot( true );
00305         m_typingStopTimer->start( 4500 );
00306     }
00307 
00308     emit canSendChanged( canSend() );
00309 }
00310 
00311 void ChatTextEditPart::historyUp()
00312 {
00313     if ( historyList.empty() || historyPos == historyList.count() - 1 )
00314         return;
00315     
00316     QString text = this->text(Qt::PlainText);
00317     bool empty = text.trimmed().isEmpty();
00318     
00319     // got text? save it
00320     if ( !empty )
00321     {
00322         if ( historyPos == -1 )
00323         {
00324             historyList.prepend( text );
00325             historyPos = 0;
00326         }
00327         else
00328         {
00329             historyList[historyPos] = text;
00330         }
00331     }
00332     
00333     historyPos++;
00334     
00335     QString newText = historyList[historyPos];
00336 //  TextFormat format=textEdit()->textFormat();
00337 //  textEdit()->setTextFormat(AutoText); //workaround bug 115690
00338     textEdit()->setText( newText );
00339 //  textEdit()->setTextFormat(format);
00340     textEdit()->moveCursor( QTextCursor::End );
00341 }
00342 
00343 void ChatTextEditPart::historyDown()
00344 {
00345     if ( historyList.empty() || historyPos == -1 )
00346         return;
00347     
00348     QString text = this->text(Qt::PlainText);
00349     bool empty = text.trimmed().isEmpty();
00350     
00351     // got text? save it
00352     if ( !empty )
00353     {
00354         historyList[historyPos] = text;
00355     }
00356     
00357     historyPos--;
00358     
00359     QString newText = ( historyPos >= 0 ? historyList[historyPos] : QString() );
00360     
00361     
00362 //  TextFormat format=textEdit()->textFormat();
00363 //  textEdit()->setTextFormat(AutoText); //workaround bug 115690
00364     textEdit()->setText( newText );
00365 //  textEdit()->setTextFormat(format);
00366     textEdit()->moveCursor( QTextCursor::End );
00367 }
00368 
00369 void ChatTextEditPart::addText( const QString &text )
00370 {
00371     if( Qt::mightBeRichText(text) )
00372     {
00373         textEdit()->insertHtml( text );
00374     }
00375     else
00376     {
00377         textEdit()->insertPlainText( text );
00378     }
00379 }
00380 
00381 void ChatTextEditPart::setContents( const Kopete::Message &message )
00382 {
00383     if ( useRichText() )
00384         textEdit()->setHtml ( message.escapedBody() );
00385     else
00386         textEdit()->setPlainText ( message.plainBody() );
00387     textEdit()->moveCursor ( QTextCursor::End );
00388     
00389     setFont( message.font() );
00390     setTextColor( message.foregroundColor() );
00391 //  setBackgroundColorColor( message.backgroundColor() );
00392 }
00393 
00394 Kopete::Message ChatTextEditPart::contents()
00395 {
00396     Kopete::Message currentMsg( m_session->myself(), m_session->members() );
00397     currentMsg.setDirection( Kopete::Message::Outbound );
00398     useRichText() ? currentMsg.setHtmlBody( text() ) : currentMsg.setPlainBody( text() );
00399     
00400 //  currentMsg.setBackgroundColor( bgColor() );
00401     currentMsg.setForegroundColor( textColor() );
00402     currentMsg.setFont( font() );
00403     
00404     return currentMsg;
00405 }
00406 
00407 void ChatTextEditPart::slotRepeatTypingTimer()
00408 {
00409     emit typing( true );
00410 }
00411 
00412 void ChatTextEditPart::slotStoppedTypingTimer()
00413 {
00414     m_typingRepeatTimer->stop();
00415     m_typingStopTimer->stop();
00416     emit typing( false );
00417 }
00418 
00419 void ChatTextEditPart::slotAppearanceChanged()
00420 {
00421     Kopete::AppearanceSettings *settings = Kopete::AppearanceSettings::self();
00422 
00423     QFont chatFont = KGlobalSettings::generalFont();
00424     if ( settings->chatFontSelection() == 1 )
00425         chatFont = settings->chatFont();
00426 
00427     setFont( chatFont );
00428 }
00429 
00430 void ChatTextEditPart::setProtocolRichTextSupport()
00431 {
00432     KRichTextEditPart::RichTextSupport richText;
00433     Kopete::Protocol::Capabilities protocolCaps = m_session->protocol()->capabilities();
00434 
00435     // Check for bold
00436     if( (protocolCaps & Kopete::Protocol::BaseBFormatting) || (protocolCaps & Kopete::Protocol::RichBFormatting) )
00437     {
00438         richText |= KRichTextEditPart::SupportBold;
00439     }
00440     // Check for italic
00441     if( (protocolCaps & Kopete::Protocol::BaseIFormatting) || (protocolCaps & Kopete::Protocol::RichIFormatting) )
00442     {
00443         richText |= KRichTextEditPart::SupportItalic;
00444     }
00445     // Check for underline
00446     if( (protocolCaps & Kopete::Protocol::BaseUFormatting) || (protocolCaps & Kopete::Protocol::RichUFormatting) )
00447     {
00448         richText |= KRichTextEditPart::SupportUnderline;
00449     }
00450     // Check for font support
00451     if( (protocolCaps & Kopete::Protocol::BaseFont) || (protocolCaps & Kopete::Protocol::RichFont) )
00452     {
00453         richText |= KRichTextEditPart::SupportFont;
00454     }
00455     // Check for text color support
00456     if( (protocolCaps & Kopete::Protocol::BaseFgColor) || (protocolCaps & Kopete::Protocol::RichFgColor) )
00457     {
00458         richText |= KRichTextEditPart::SupportTextColor;
00459     }
00460     // Check for alignment
00461     if( protocolCaps & Kopete::Protocol::Alignment )
00462     {
00463         richText |= KRichTextEditPart::SupportAlignment;
00464     }
00465 
00466     // Set rich text support in KRichTextEditPart
00467     setRichTextSupport( richText );
00468 }
00469 
00470 #include "chattexteditpart.moc"
00471 
00472 // vim: set noet ts=4 sts=4 sw=4:

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