You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/kopete/chatwindow/kopetechatwindow.cpp

1281 lines
37 KiB

/*
kopetechatwindow.cpp - Chat Window
Copyright (c) 2002-2006 by Olivier Goffart <ogoffart@ kde.org>
Copyright (c) 2003-2004 by Richard Smith <kde@metafoo.co.uk>
Copyright (C) 2002 by James Grant
Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
Copyright (c) 2002-2004 by Martijn Klingens <klingens@kde.org>
Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* 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. *
* *
*************************************************************************
*/
#include <tqtimer.h>
#include <tqhbox.h>
#include <tqvbox.h>
#include <tqlayout.h>
#include <tqtooltip.h>
#include <tqfileinfo.h>
#include <tdeapplication.h>
#include <kcursor.h>
#include <klocale.h>
#include <kmenubar.h>
#include <tdeconfig.h>
#include <tdepopupmenu.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <twin.h>
#include <ktempfile.h>
#include <kkeydialog.h>
#include <kedittoolbar.h>
#include <kstatusbar.h>
#include <kpushbutton.h>
#include <ktabwidget.h>
#include <kstandarddirs.h>
#include <kdialog.h>
#include <kstringhandler.h>
#include <ksqueezedtextlabel.h>
#include <tdestdaccel.h>
#include <kglobalsettings.h>
#include "chatmessagepart.h"
#include "chattexteditpart.h"
#include "chatview.h"
#include "kopeteapplication.h"
#include "kopetechatwindow.h"
#include "kopeteemoticonaction.h"
#include "kopetegroup.h"
#include "kopetechatsession.h"
#include "kopetemetacontact.h"
#include "kopetepluginmanager.h"
#include "kopeteprefs.h"
#include "kopeteprotocol.h"
#include "kopetestdaction.h"
#include "kopeteviewmanager.h"
#include <tqtoolbutton.h>
#include <tdeactionclasses.h>
typedef TQMap<Kopete::Account*,KopeteChatWindow*> AccountMap;
typedef TQMap<Kopete::Group*,KopeteChatWindow*> GroupMap;
typedef TQMap<Kopete::MetaContact*,KopeteChatWindow*> MetaContactMap;
typedef TQPtrList<KopeteChatWindow> WindowList;
namespace
{
AccountMap accountMap;
GroupMap groupMap;
MetaContactMap mcMap;
WindowList windows;
}
KopeteChatWindow *KopeteChatWindow::window( Kopete::ChatSession *manager )
{
bool windowCreated = false;
KopeteChatWindow *myWindow;
//Take the first and the first? What else?
Kopete::Group *group = 0L;
Kopete::ContactPtrList members = manager->members();
Kopete::MetaContact *metaContact = members.first()->metaContact();
if ( metaContact )
{
Kopete::GroupList gList = metaContact->groups();
group = gList.first();
}
switch( KopetePrefs::prefs()->chatWindowPolicy() )
{
case GROUP_BY_ACCOUNT: //Open chats from the same protocol in the same window
if( accountMap.contains( manager->account() ) )
myWindow = accountMap[ manager->account() ];
else
windowCreated = true;
break;
case GROUP_BY_GROUP: //Open chats from the same group in the same window
if( group && groupMap.contains( group ) )
myWindow = groupMap[ group ];
else
windowCreated = true;
break;
case GROUP_BY_METACONTACT: //Open chats from the same metacontact in the same window
if( mcMap.contains( metaContact ) )
myWindow = mcMap[ metaContact ];
else
windowCreated = true;
break;
case GROUP_ALL: //Open all chats in the same window
if( windows.isEmpty() )
windowCreated = true;
else
{
//Here we are finding the window with the most tabs and
//putting it there. Need this for the cases where config changes
//midstream
int viewCount = -1;
for ( KopeteChatWindow *thisWindow = windows.first(); thisWindow; thisWindow = windows.next() )
{
if( thisWindow->chatViewCount() > viewCount )
{
myWindow = thisWindow;
viewCount = thisWindow->chatViewCount();
}
}
}
break;
case NEW_WINDOW: //Open every chat in a new window
default:
windowCreated = true;
break;
}
if ( windowCreated )
{
myWindow = new KopeteChatWindow();
if ( !accountMap.contains( manager->account() ) )
accountMap.insert( manager->account(), myWindow );
if ( !mcMap.contains( metaContact ) )
mcMap.insert( metaContact, myWindow );
if ( group && !groupMap.contains( group ) )
groupMap.insert( group, myWindow );
}
// kdDebug( 14010 ) << k_funcinfo << "Open Windows: " << windows.count() << endl;
return myWindow;
}
KopeteChatWindow::KopeteChatWindow( TQWidget *parent, const char* name )
: KParts::MainWindow( parent, name )
{
m_activeView = 0L;
m_popupView = 0L;
backgroundFile = 0L;
updateBg = true;
m_tabBar = 0L;
initActions();
TQVBox *vBox = new TQVBox( this );
vBox->setLineWidth( 0 );
vBox->setSpacing( 0 );
vBox->setFrameStyle( TQFrame::NoFrame );
// set default window size. This could be removed by fixing the size hints of the contents
resize( 500, 500 );
setCentralWidget( vBox );
mainArea = new TQFrame( vBox );
mainArea->setLineWidth( 0 );
mainArea->setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ) );
mainLayout = new TQVBoxLayout( mainArea );
if ( KopetePrefs::prefs()->chatWShowSend() )
{
//Send Button
m_button_send = new KPushButton( i18n("Send"), statusBar() );
m_button_send->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ) );
m_button_send->setEnabled( false );
m_button_send->setFont( statusBar()->font() );
m_button_send->setFixedHeight( statusBar()->sizeHint().height() );
connect( m_button_send, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotSendMessage() ) );
statusBar()->addWidget( m_button_send, 0, true );
}
else
m_button_send = 0L;
m_status_text = new KSqueezedTextLabel( i18n("Ready."), statusBar(), "m_status_text" );
m_status_text->setAlignment( AlignLeft | AlignVCenter );
m_status_text->setFont( statusBar()->font() );
m_status_text->setFixedHeight( statusBar()->sizeHint().height() );
statusBar()->addWidget( m_status_text, 1 );
readOptions();
setWFlags( TQt::WDestructiveClose );
windows.append( this );
windowListChanged();
TDEGlobal::config()->setGroup( TQString::fromLatin1("ChatWindowSettings") );
m_alwaysShowTabs = TDEGlobal::config()->readBoolEntry( TQString::fromLatin1("AlwaysShowTabs"), false );
m_showFormatToolbar = TDEGlobal::config()->readBoolEntry( TQString::fromLatin1("Show Format Toolbar"), true );
adjustingFormatToolbar = false;
// kdDebug( 14010 ) << k_funcinfo << "Open Windows: " << windows.count() << endl;
kapp->ref();
}
KopeteChatWindow::~KopeteChatWindow()
{
kdDebug( 14010 ) << k_funcinfo << endl;
emit( closing( this ) );
for( AccountMap::Iterator it = accountMap.begin(); it != accountMap.end(); )
{
AccountMap::Iterator mayDeleteIt = it;
++it;
if( mayDeleteIt.data() == this )
accountMap.remove( mayDeleteIt.key() );
}
for( GroupMap::Iterator it = groupMap.begin(); it != groupMap.end(); )
{
GroupMap::Iterator mayDeleteIt = it;
++it;
if( mayDeleteIt.data() == this )
groupMap.remove( mayDeleteIt.key() );
}
for( MetaContactMap::Iterator it = mcMap.begin(); it != mcMap.end(); )
{
MetaContactMap::Iterator mayDeleteIt = it;
++it;
if( mayDeleteIt.data() == this )
mcMap.remove( mayDeleteIt.key() );
}
windows.remove( this );
windowListChanged();
// kdDebug( 14010 ) << "Open Windows: " << windows.count() << endl;
saveOptions();
if( backgroundFile )
{
backgroundFile->close();
backgroundFile->unlink();
delete backgroundFile;
}
delete anim;
kapp->deref();
}
void KopeteChatWindow::windowListChanged()
{
// update all windows' Move Tab to Window action
for ( TQPtrListIterator<KopeteChatWindow> it( windows ); *it; ++it )
(*it)->checkDetachEnable();
}
void KopeteChatWindow::slotNickComplete()
{
if( m_activeView )
m_activeView->nickComplete();
}
void KopeteChatWindow::slotTabContextMenu( TQWidget *tab, const TQPoint &pos )
{
m_popupView = static_cast<ChatView*>( tab );
TDEPopupMenu *popup = new TDEPopupMenu;
popup->insertTitle( KStringHandler::rsqueeze( m_popupView->caption() ) );
actionContactMenu->plug( popup );
popup->insertSeparator();
actionTabPlacementMenu->plug( popup );
tabDetach->plug( popup );
actionDetachMenu->plug( popup );
tabClose->plug( popup );
popup->exec( pos );
delete popup;
m_popupView = 0;
}
ChatView *KopeteChatWindow::activeView()
{
return m_activeView;
}
void KopeteChatWindow::initActions(void)
{
TDEActionCollection *coll = actionCollection();
createStandardStatusBarAction();
chatSend = new TDEAction( i18n( "&Send Message" ), TQString::fromLatin1( "mail_send" ), TQKeySequence(Key_Return) ,
TQT_TQOBJECT(this), TQT_SLOT( slotSendMessage() ), coll, "chat_send" );
chatSend->setEnabled( false );
KStdAction::save ( TQT_TQOBJECT(this), TQT_SLOT(slotChatSave()), coll );
KStdAction::print ( TQT_TQOBJECT(this), TQT_SLOT(slotChatPrint()), coll );
TDEAction* quitAction = KStdAction::quit ( TQT_TQOBJECT(this), TQT_SLOT(close()), coll );
quitAction->setText( i18n("Close All Chats") );
tabClose = KStdAction::close ( TQT_TQOBJECT(this), TQT_SLOT(slotChatClosed()), coll, "tabs_close" );
tabRight=new TDEAction( i18n( "&Activate Next Tab" ), 0, TDEStdAccel::tabNext(),
TQT_TQOBJECT(this), TQT_SLOT( slotNextTab() ), coll, "tabs_right" );
tabLeft=new TDEAction( i18n( "&Activate Previous Tab" ), 0, TDEStdAccel::tabPrev(),
TQT_TQOBJECT(this), TQT_SLOT( slotPreviousTab() ), coll, "tabs_left" );
tabLeft->setEnabled( false );
tabRight->setEnabled( false );
nickComplete = new TDEAction( i18n( "Nic&k Completion" ), TQString(), 0, TQT_TQOBJECT(this), TQT_SLOT( slotNickComplete() ), coll , "nick_compete");
nickComplete->setShortcut( TQKeySequence( Key_Tab ) );
tabDetach = new TDEAction( i18n( "&Detach Chat" ), TQString::fromLatin1( "tab_breakoff" ), 0,
TQT_TQOBJECT(this), TQT_SLOT( slotDetachChat() ), coll, "tabs_detach" );
tabDetach->setEnabled( false );
actionDetachMenu = new TDEActionMenu( i18n( "&Move Tab to Window" ), TQString::fromLatin1( "tab_breakoff" ), coll, "tabs_detachmove" );
actionDetachMenu->setDelayed( false );
connect ( actionDetachMenu->popupMenu(), TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotPrepareDetachMenu()) );
connect ( actionDetachMenu->popupMenu(), TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotDetachChat(int)) );
actionTabPlacementMenu = new TDEActionMenu( i18n( "&Tab Placement" ), coll, "tabs_placement" );
connect ( actionTabPlacementMenu->popupMenu(), TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotPreparePlacementMenu()) );
connect ( actionTabPlacementMenu->popupMenu(), TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotPlaceTabs(int)) );
tabDetach->setShortcut( TQKeySequence(CTRL + SHIFT + Key_B) );
KStdAction::cut( TQT_TQOBJECT(this), TQT_SLOT(slotCut()), coll);
KStdAction::copy( TQT_TQOBJECT(this), TQT_SLOT(slotCopy()), coll);
KStdAction::paste( TQT_TQOBJECT(this), TQT_SLOT(slotPaste()), coll);
new TDEAction( i18n( "Set Default &Font..." ), TQString::fromLatin1( "charset" ), 0, TQT_TQOBJECT(this), TQT_SLOT( slotSetFont() ), coll, "format_font" );
new TDEAction( i18n( "Set Default Text &Color..." ), TQString::fromLatin1( "pencil" ), 0, TQT_TQOBJECT(this), TQT_SLOT( slotSetFgColor() ), coll, "format_fgcolor" );
new TDEAction( i18n( "Set &Background Color..." ), TQString::fromLatin1( "fill" ), 0, TQT_TQOBJECT(this), TQT_SLOT( slotSetBgColor() ), coll, "format_bgcolor" );
historyUp = new TDEAction( i18n( "Previous History" ), TQString(), 0,
TQT_TQOBJECT(this), TQT_SLOT( slotHistoryUp() ), coll, "history_up" );
historyUp->setShortcut( TQKeySequence(CTRL + Key_Up) );
historyDown = new TDEAction( i18n( "Next History" ), TQString(), 0,
TQT_TQOBJECT(this), TQT_SLOT( slotHistoryDown() ), coll, "history_down" );
historyDown->setShortcut( TQKeySequence(CTRL + Key_Down) );
KStdAction::prior( TQT_TQOBJECT(this), TQT_SLOT( slotPageUp() ), coll, "scroll_up" );
KStdAction::next( TQT_TQOBJECT(this), TQT_SLOT( slotPageDown() ), coll, "scroll_down" );
KStdAction::showMenubar( TQT_TQOBJECT(this), TQT_SLOT(slotViewMenuBar()), coll );
membersLeft = new TDEToggleAction( i18n( "Place to Left of Chat Area" ), TQString(), 0,
TQT_TQOBJECT(this), TQT_SLOT( slotViewMembersLeft() ), coll, "options_membersleft" );
membersRight = new TDEToggleAction( i18n( "Place to Right of Chat Area" ), TQString(), 0,
TQT_TQOBJECT(this), TQT_SLOT( slotViewMembersRight() ), coll, "options_membersright" );
toggleMembers = new TDEToggleAction( i18n( "Show" ), TQString(), 0,
TQT_TQOBJECT(this), TQT_SLOT( slotToggleViewMembers() ), coll, "options_togglemembers" );
toggleMembers->setCheckedState(i18n("Hide"));
toggleAutoSpellCheck = new TDEToggleAction( i18n( "Automatic Spell Checking" ), TQString(), 0,
TQT_TQOBJECT(this), TQT_SLOT( toggleAutoSpellChecking() ), coll, "enable_auto_spell_check" );
toggleAutoSpellCheck->setChecked( true );
actionSmileyMenu = new KopeteEmoticonAction( coll, "format_smiley" );
actionSmileyMenu->setDelayed( false );
connect(actionSmileyMenu, TQT_SIGNAL(activated(const TQString &)), this, TQT_SLOT(slotSmileyActivated(const TQString &)));
actionContactMenu = new TDEActionMenu(i18n("Co&ntacts"), coll, "contacts_menu" );
actionContactMenu->setDelayed( false );
connect ( actionContactMenu->popupMenu(), TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotPrepareContactMenu()) );
// add configure key bindings menu item
KStdAction::keyBindings( guiFactory(), TQT_SLOT( configureShortcuts() ), coll );
KStdAction::configureToolbars(TQT_TQOBJECT(this), TQT_SLOT(slotConfToolbar()), coll);
KopeteStdAction::preferences( coll , "settings_prefs" );
//The Sending movie
normalIcon = TQPixmap( BarIcon( TQString::fromLatin1( "kopete" ) ) );
animIcon = TDEGlobal::iconLoader()->loadMovie( TQString::fromLatin1( "newmessage" ), TDEIcon::Toolbar);
// Pause the animation because otherwise it's running even when we're not
// showing it. This eats resources, and also triggers a pixmap leak in
// TQMovie in at least TQt 3.1, TQt 3.2 and the current TQt 3.3 beta
if( !animIcon.isNull() ) //and another QT bug: it crash if we pause a null movie
animIcon.pause();
// we can't set the tool bar as parent, if we do, it will be deleted when we configure toolbars
anim = new TQLabel( TQString(), 0L ,"kde toolbar widget" );
anim->setMargin(5);
anim->setPixmap( normalIcon );
new KWidgetAction( anim , i18n("Toolbar Animation") , 0, 0 , 0 , coll , "toolbar_animation");
//toolBar()->insertWidget( 99, anim->width(), anim );
//toolBar()->alignItemRight( 99 );
setStandardToolBarMenuEnabled( true );
setXMLFile( TQString::fromLatin1( "kopetechatwindow.rc" ) );
createGUI( 0L );
// Special handling for remembering whether the format toolbar is visible or not
connect ( toolBar("formatToolBar"), TQT_SIGNAL(visibilityChanged(bool)), this, TQT_SLOT(slotToggleFormatToolbar(bool)) );
}
const TQString KopeteChatWindow::fileContents( const TQString &path ) const
{
TQString contents;
TQFile file( path );
if ( file.open( IO_ReadOnly ) )
{
TQTextStream stream( &file );
contents = stream.read();
file.close();
}
return contents;
}
void KopeteChatWindow::slotStopAnimation( ChatView* view )
{
if( view == m_activeView )
anim->setPixmap( normalIcon );
}
void KopeteChatWindow::slotUpdateSendEnabled()
{
if ( !m_activeView ) return;
bool enabled = m_activeView->canSend();
chatSend->setEnabled( enabled );
if(m_button_send)
m_button_send->setEnabled( enabled );
}
void KopeteChatWindow::updateMembersActions()
{
if( m_activeView )
{
const KDockWidget::DockPosition pos = m_activeView->membersListPosition();
bool visibleMembers = m_activeView->visibleMembersList();
membersLeft->setChecked( pos == KDockWidget::DockLeft );
membersLeft->setEnabled( visibleMembers );
membersRight->setChecked( pos == KDockWidget::DockRight );
membersRight->setEnabled( visibleMembers );
toggleMembers->setChecked( visibleMembers );
}
}
void KopeteChatWindow::slotViewMembersLeft()
{
m_activeView->placeMembersList( KDockWidget::DockLeft );
updateMembersActions();
}
void KopeteChatWindow::slotViewMembersRight()
{
m_activeView->placeMembersList( KDockWidget::DockRight );
updateMembersActions();
}
void KopeteChatWindow::slotToggleViewMembers()
{
m_activeView->toggleMembersVisibility();
updateMembersActions();
}
void KopeteChatWindow::toggleAutoSpellChecking()
{
if ( !m_activeView )
return;
bool currentSetting = m_activeView->editPart()->autoSpellCheckEnabled();
m_activeView->editPart()->toggleAutoSpellCheck( !currentSetting );
updateSpellCheckAction();
}
void KopeteChatWindow::updateSpellCheckAction()
{
if ( !m_activeView )
return;
if ( m_activeView->editPart()->richTextEnabled() )
{
toggleAutoSpellCheck->setEnabled( false );
toggleAutoSpellCheck->setChecked( false );
m_activeView->editPart()->toggleAutoSpellCheck( false );
}
else
{
toggleAutoSpellCheck->setEnabled( true );
if ( KopetePrefs::prefs()->spellCheck() )
{
kdDebug(14000) << k_funcinfo << "spell check enabled" << endl;
toggleAutoSpellCheck->setChecked( true );
m_activeView->editPart()->toggleAutoSpellCheck(true);
}
else
{
kdDebug(14000) << k_funcinfo << "spell check disabled" << endl;
toggleAutoSpellCheck->setChecked( false );
m_activeView->editPart()->toggleAutoSpellCheck(false);
}
}
}
void KopeteChatWindow::slotHistoryUp()
{
if( m_activeView )
m_activeView->editPart()->historyUp();
}
void KopeteChatWindow::slotHistoryDown()
{
if( m_activeView )
m_activeView->editPart()->historyDown();
}
void KopeteChatWindow::slotPageUp()
{
if( m_activeView )
m_activeView->messagePart()->pageUp();
}
void KopeteChatWindow::slotPageDown()
{
if( m_activeView )
m_activeView->messagePart()->pageDown();
}
void KopeteChatWindow::slotCut()
{
m_activeView->cut();
}
void KopeteChatWindow::slotCopy()
{
m_activeView->copy();
}
void KopeteChatWindow::slotPaste()
{
m_activeView->paste();
}
void KopeteChatWindow::slotSetFont()
{
m_activeView->setFont();
}
void KopeteChatWindow::slotSetFgColor()
{
m_activeView->setFgColor();
}
void KopeteChatWindow::slotSetBgColor()
{
m_activeView->setBgColor();
}
void KopeteChatWindow::setStatus(const TQString &text)
{
m_status_text->setText(text);
}
void KopeteChatWindow::createTabBar()
{
if( !m_tabBar )
{
TDEGlobal::config()->setGroup( TQString::fromLatin1("ChatWindowSettings") );
m_tabBar = new KTabWidget( mainArea );
m_tabBar->setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ) );
m_tabBar->setHoverCloseButton(TDEGlobal::config()->readBoolEntry( TQString::fromLatin1("HoverClose"), false ));
m_tabBar->setTabReorderingEnabled(true);
#if KDE_IS_VERSION(3,4,0)
m_tabBar->setAutomaticResizeTabs(true);
#endif
connect( m_tabBar, TQT_SIGNAL( closeRequest( TQWidget* )), this, TQT_SLOT( slotCloseChat( TQWidget* ) ) );
TQToolButton* m_rightWidget = new TQToolButton( m_tabBar );
connect( m_rightWidget, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotChatClosed() ) );
m_rightWidget->setIconSet( SmallIcon( "tab_remove" ) );
m_rightWidget->adjustSize();
TQToolTip::add( m_rightWidget, i18n("Close the current tab"));
m_tabBar->setCornerWidget( m_rightWidget, TQWidget::TopRight );
mainLayout->addWidget( m_tabBar );
m_tabBar->show();
connect ( m_tabBar, TQT_SIGNAL(currentChanged(TQWidget *)), this, TQT_SLOT(setActiveView(TQWidget *)) );
connect ( m_tabBar, TQT_SIGNAL(contextMenu(TQWidget *, const TQPoint & )), this, TQT_SLOT(slotTabContextMenu( TQWidget *, const TQPoint & )) );
for( ChatView *view = chatViewList.first(); view; view = chatViewList.next() )
addTab( view );
if( m_activeView )
m_tabBar->showPage( m_activeView );
else
setActiveView( chatViewList.first() );
int tabPosition = TDEGlobal::config()->readNumEntry( TQString::fromLatin1("Tab Placement") , 0 );
slotPlaceTabs( tabPosition );
}
}
void KopeteChatWindow::slotCloseChat( TQWidget *chatView )
{
static_cast<ChatView*>( chatView )->closeView();
}
void KopeteChatWindow::addTab( ChatView *view )
{
TQPtrList<Kopete::Contact> chatMembers=view->msgManager()->members();
Kopete::Contact *c=0L;
for ( Kopete::Contact *contact = chatMembers.first(); contact; contact = chatMembers.next() )
{
if(!c || c->onlineStatus() < contact->onlineStatus())
c=contact;
}
TQPixmap pluginIcon = c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c) : SmallIcon( view->msgManager()->protocol()->pluginIcon() );
view->reparent( m_tabBar, 0, TQPoint(), true );
m_tabBar->addTab( view, pluginIcon, view->caption() );
if( view == m_activeView )
view->show();
else
view->hide();
connect( view, TQT_SIGNAL( captionChanged( bool ) ), this, TQT_SLOT( updateChatLabel() ) );
connect( view, TQT_SIGNAL( updateStatusIcon( ChatView* ) ), this, TQT_SLOT( slotUpdateCaptionIcons( ChatView* ) ) );
view->setCaption( view->caption(), false );
}
void KopeteChatWindow::setPrimaryChatView( ChatView *view )
{
//TODO figure out what else we have to save here besides the font
//reparent clears a lot of stuff out
TQFont savedFont = view->font();
view->reparent( mainArea, 0, TQPoint(), true );
view->setFont( savedFont );
view->show();
mainLayout->addWidget( view );
setActiveView( view );
}
void KopeteChatWindow::deleteTabBar()
{
if( m_tabBar )
{
disconnect ( m_tabBar, TQT_SIGNAL(currentChanged(TQWidget *)), this, TQT_SLOT(setActiveView(TQWidget *)) );
disconnect ( m_tabBar, TQT_SIGNAL(contextMenu(TQWidget *, const TQPoint & )), this, TQT_SLOT(slotTabContextMenu( TQWidget *, const TQPoint & )) );
if( !chatViewList.isEmpty() )
setPrimaryChatView( chatViewList.first() );
m_tabBar->deleteLater();
m_tabBar = 0L;
}
}
void KopeteChatWindow::attachChatView( ChatView* newView )
{
chatViewList.append( newView );
if ( !m_alwaysShowTabs && chatViewList.count() == 1 )
setPrimaryChatView( newView );
else
{
if ( !m_tabBar )
createTabBar();
else
addTab( newView );
newView->setActive( false );
}
newView->setMainWindow( this );
newView->editWidget()->installEventFilter( this );
KCursor::setAutoHideCursor( newView->editWidget(), true, true );
connect( newView, TQT_SIGNAL(captionChanged( bool)), this, TQT_SLOT(slotSetCaption(bool)) );
connect( newView, TQT_SIGNAL(messageSuccess( ChatView* )), this, TQT_SLOT(slotStopAnimation( ChatView* )) );
connect( newView, TQT_SIGNAL(rtfEnabled( ChatView*, bool ) ), this, TQT_SLOT( slotRTFEnabled( ChatView*, bool ) ) );
connect( newView, TQT_SIGNAL(updateStatusIcon( ChatView* ) ), this, TQT_SLOT(slotUpdateCaptionIcons( ChatView* ) ) );
connect( newView, TQT_SIGNAL(updateChatState( ChatView*, int ) ), this, TQT_SLOT( updateChatState( ChatView*, int ) ) );
updateSpellCheckAction();
checkDetachEnable();
newView->loadChatSettings();
connect( newView, TQT_SIGNAL(autoSpellCheckEnabled( ChatView*, bool ) ),
this, TQT_SLOT( slotAutoSpellCheckEnabled( ChatView*, bool ) ) );
}
void KopeteChatWindow::checkDetachEnable()
{
bool haveTabs = (chatViewList.count() > 1);
tabDetach->setEnabled( haveTabs );
tabLeft->setEnabled( haveTabs );
tabRight->setEnabled( haveTabs );
actionTabPlacementMenu->setEnabled( m_tabBar != 0 );
bool otherWindows = (windows.count() > 1);
actionDetachMenu->setEnabled( otherWindows );
}
void KopeteChatWindow::detachChatView( ChatView *view )
{
if( !chatViewList.removeRef( view ) )
return;
disconnect( view, TQT_SIGNAL(captionChanged( bool)), this, TQT_SLOT(slotSetCaption(bool)) );
disconnect( view, TQT_SIGNAL( updateStatusIcon( ChatView* ) ), this, TQT_SLOT( slotUpdateCaptionIcons( ChatView* ) ) );
disconnect( view, TQT_SIGNAL( updateChatState( ChatView*, int ) ), this, TQT_SLOT( updateChatState( ChatView*, int ) ) );
view->editWidget()->removeEventFilter( this );
if( m_tabBar )
{
int curPage = m_tabBar->currentPageIndex();
TQWidget *page = m_tabBar->page( curPage );
// if the current view is to be detached, switch to a different one
if( page == view )
{
if( curPage > 0 )
m_tabBar->setCurrentPage( curPage - 1 );
else
m_tabBar->setCurrentPage( curPage + 1 );
}
m_tabBar->removePage( view );
if( m_tabBar->currentPage() )
setActiveView( static_cast<ChatView*>(m_tabBar->currentPage()) );
}
if( chatViewList.isEmpty() )
close();
else if( !m_alwaysShowTabs && chatViewList.count() == 1)
deleteTabBar();
checkDetachEnable();
}
void KopeteChatWindow::slotDetachChat( int newWindowIndex )
{
KopeteChatWindow *newWindow = 0L;
ChatView *detachedView;
if( m_popupView )
detachedView = m_popupView;
else
detachedView = m_activeView;
if( !detachedView )
return;
//if we don't do this, we might crash
createGUI(0L);
guiFactory()->removeClient(detachedView->msgManager());
if( newWindowIndex == -1 )
newWindow = new KopeteChatWindow();
else
newWindow = windows.at( newWindowIndex );
newWindow->show();
newWindow->raise();
detachChatView( detachedView );
newWindow->attachChatView( detachedView );
}
void KopeteChatWindow::slotPreviousTab()
{
int curPage = m_tabBar->currentPageIndex();
if( curPage > 0 )
m_tabBar->setCurrentPage( curPage - 1 );
else
m_tabBar->setCurrentPage( m_tabBar->count() - 1 );
}
void KopeteChatWindow::slotNextTab()
{
int curPage = m_tabBar->currentPageIndex();
if( curPage == ( m_tabBar->count() - 1 ) )
m_tabBar->setCurrentPage( 0 );
else
m_tabBar->setCurrentPage( curPage + 1 );
}
void KopeteChatWindow::slotSetCaption( bool active )
{
if( active && m_activeView )
{
setCaption( m_activeView->caption(), false );
}
}
void KopeteChatWindow::updateBackground( const TQPixmap &pm )
{
if( updateBg )
{
updateBg = false;
if( backgroundFile != 0L )
{
backgroundFile->close();
backgroundFile->unlink();
}
backgroundFile = new KTempFile( TQString(), TQString::fromLatin1( ".bmp" ) );
pm.save( backgroundFile->name(), "BMP" );
TQTimer::singleShot( 100, this, TQT_SLOT( slotEnableUpdateBg() ) );
}
}
void KopeteChatWindow::setActiveView( TQWidget *widget )
{
ChatView *view = static_cast<ChatView*>(widget);
if( m_activeView == view )
return;
if(m_activeView)
{
disconnect( m_activeView, TQT_SIGNAL( canSendChanged(bool) ), this, TQT_SLOT( slotUpdateSendEnabled() ) );
guiFactory()->removeClient(m_activeView->msgManager());
m_activeView->saveChatSettings();
}
guiFactory()->addClient(view->msgManager());
createGUI( view->editPart() );
if( m_activeView )
m_activeView->setActive( false );
m_activeView = view;
if( !chatViewList.contains( view ) )
attachChatView( view );
connect( m_activeView, TQT_SIGNAL( canSendChanged(bool) ), this, TQT_SLOT( slotUpdateSendEnabled() ) );
//Tell it it is active
m_activeView->setActive( true );
//Update icons to match
slotUpdateCaptionIcons( m_activeView );
//Update chat members actions
updateMembersActions();
if ( m_activeView->sendInProgress() && !animIcon.isNull() )
{
anim->setMovie( animIcon );
animIcon.unpause();
}
else
{
anim->setPixmap( normalIcon );
if( !animIcon.isNull() )
animIcon.pause();
}
if ( m_alwaysShowTabs || chatViewList.count() > 1 )
{
if( !m_tabBar )
createTabBar();
m_tabBar->showPage( m_activeView );
}
setCaption( m_activeView->caption() );
setStatus( m_activeView->statusText() );
m_activeView->setFocus();
updateSpellCheckAction();
slotUpdateSendEnabled();
m_activeView->editPart()->reloadConfig();
m_activeView->loadChatSettings();
}
void KopeteChatWindow::slotUpdateCaptionIcons( ChatView *view )
{
if ( !view )
return; //(pas de charité)
TQPtrList<Kopete::Contact> chatMembers=view->msgManager()->members();
Kopete::Contact *c=0L;
for ( Kopete::Contact *contact = chatMembers.first(); contact; contact = chatMembers.next() )
{
if(!c || c->onlineStatus() < contact->onlineStatus())
c=contact;
}
if ( view == m_activeView )
{
TQPixmap icon16 = c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c , 16) :
SmallIcon( view->msgManager()->protocol()->pluginIcon() );
TQPixmap icon32 = c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c , 32) :
SmallIcon( view->msgManager()->protocol()->pluginIcon() );
KWin::setIcons( winId(), icon32, icon16 );
}
if ( m_tabBar )
m_tabBar->setTabIconSet( view, c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c ) :
SmallIcon( view->msgManager()->protocol()->pluginIcon() ) );
}
void KopeteChatWindow::slotChatClosed()
{
if( m_popupView )
m_popupView->closeView();
else
m_activeView->closeView();
}
void KopeteChatWindow::slotPrepareDetachMenu(void)
{
TQPopupMenu *detachMenu = actionDetachMenu->popupMenu();
detachMenu->clear();
for ( unsigned id=0; id < windows.count(); id++ )
{
KopeteChatWindow *win = windows.at( id );
if( win != this )
detachMenu->insertItem( win->caption(), id );
}
}
void KopeteChatWindow::slotSendMessage()
{
if ( m_activeView && m_activeView->canSend() )
{
if( !animIcon.isNull() )
{
anim->setMovie( animIcon );
animIcon.unpause();
}
m_activeView->sendMessage();
}
}
void KopeteChatWindow::slotPrepareContactMenu(void)
{
TQPopupMenu *contactsMenu = actionContactMenu->popupMenu();
contactsMenu->clear();
Kopete::Contact *contact;
Kopete::ContactPtrList m_them;
if( m_popupView )
m_them = m_popupView->msgManager()->members();
else
m_them = m_activeView->msgManager()->members();
//TODO: don't display a menu with one contact in it, display that
// contact's menu instead. Will require changing text and icon of
// 'Contacts' action, or something cleverer.
uint contactCount = 0;
for ( contact = m_them.first(); contact; contact = m_them.next() )
{
TDEPopupMenu *p = contact->popupMenu();
connect ( actionContactMenu->popupMenu(), TQT_SIGNAL(aboutToHide()),
p, TQT_SLOT(deleteLater() ) );
if( contact->metaContact() )
contactsMenu->insertItem( contact->onlineStatus().iconFor( contact ) , contact->metaContact()->displayName(), p );
else
contactsMenu->insertItem( contact->onlineStatus().iconFor( contact ) , contact->contactId(), p );
//FIXME: This number should be a config option
if( ++contactCount == 15 && contact != m_them.getLast() )
{
TDEActionMenu *moreMenu = new TDEActionMenu( i18n("More..."),
TQString::fromLatin1("folder_open"), TQT_TQOBJECT(contactsMenu) );
connect ( actionContactMenu->popupMenu(), TQT_SIGNAL(aboutToHide()),
moreMenu, TQT_SLOT(deleteLater() ) );
moreMenu->plug( contactsMenu );
contactsMenu = moreMenu->popupMenu();
contactCount = 0;
}
}
}
void KopeteChatWindow::slotPreparePlacementMenu()
{
TQPopupMenu *placementMenu = actionTabPlacementMenu->popupMenu();
placementMenu->clear();
placementMenu->insertItem( i18n("Top"), 0 );
placementMenu->insertItem( i18n("Bottom"), 1 );
}
void KopeteChatWindow::slotPlaceTabs( int placement )
{
if( m_tabBar )
{
if( placement == 0 )
m_tabBar->setTabPosition( TQTabWidget::Top );
else
m_tabBar->setTabPosition( TQTabWidget::Bottom );
saveOptions();
}
}
void KopeteChatWindow::readOptions()
{
// load and apply config file settings affecting the appearance of the UI
// kdDebug(14010) << k_funcinfo << endl;
TDEConfig *config = TDEGlobal::config();
applyMainWindowSettings( config, TQString::fromLatin1( "KopeteChatWindow" ) );
config->setGroup( TQString::fromLatin1("ChatWindowSettings") );
m_showFormatToolbar = config->readBoolEntry( TQString::fromLatin1("Show Format Toolbar"), true );
}
void KopeteChatWindow::saveOptions()
{
// kdDebug(14010) << k_funcinfo << endl;
TDEConfig *config = TDEGlobal::config();
// saves menubar,toolbar and statusbar setting
saveMainWindowSettings( config, TQString::fromLatin1( "KopeteChatWindow" ) );
config->setGroup( TQString::fromLatin1("ChatWindowSettings") );
if( m_tabBar )
config->writeEntry ( TQString::fromLatin1("Tab Placement"), m_tabBar->tabPosition() );
config->writeEntry( TQString::fromLatin1("Show Format Toolbar"), m_showFormatToolbar );
config->sync();
}
void KopeteChatWindow::slotChatSave()
{
// kdDebug(14010) << "KopeteChatWindow::slotChatSave()" << endl;
if( isActiveWindow() && m_activeView )
m_activeView->messagePart()->save();
}
void KopeteChatWindow::windowActivationChange( bool )
{
if( isActiveWindow() && m_activeView )
m_activeView->setActive( true );
}
void KopeteChatWindow::slotChatPrint()
{
m_activeView->messagePart()->print();
}
void KopeteChatWindow::slotToggleStatusBar()
{
if (statusBar()->isVisible())
statusBar()->hide();
else
statusBar()->show();
}
void KopeteChatWindow::slotToggleFormatToolbar(bool visible)
{
if ( adjustingFormatToolbar )
return;
m_showFormatToolbar = visible;
}
void KopeteChatWindow::slotViewMenuBar()
{
if( !menuBar()->isHidden() )
menuBar()->hide();
else
menuBar()->show();
}
void KopeteChatWindow::slotSmileyActivated(const TQString &sm)
{
if ( !sm.isNull() )
m_activeView->addText( " " + sm + " " );
//we are adding space around the emoticon becasue our parser only display emoticons not in a word.
}
void KopeteChatWindow::slotRTFEnabled( ChatView* cv, bool enabled)
{
if ( cv != m_activeView )
return;
adjustingFormatToolbar = true;
if ( enabled && m_showFormatToolbar )
toolBar( "formatToolBar" )->show();
else
toolBar( "formatToolBar" )->hide();
adjustingFormatToolbar = false;
updateSpellCheckAction();
}
void KopeteChatWindow::slotAutoSpellCheckEnabled( ChatView* view, bool isEnabled )
{
if ( view != m_activeView )
return;
toggleAutoSpellCheck->setEnabled( isEnabled );
toggleAutoSpellCheck->setChecked( isEnabled );
m_activeView->editPart()->toggleAutoSpellCheck( isEnabled );
}
bool KopeteChatWindow::queryClose()
{
bool canClose = true;
// kdDebug( 14010 ) << " Windows left open:" << endl;
// for( TQPtrListIterator<ChatView> it( chatViewList ); it; ++it)
// kdDebug( 14010 ) << " " << *it << " (" << (*it)->caption() << ")" << endl;
for( TQPtrListIterator<ChatView> it( chatViewList ); it; )
{
ChatView *view = *it;
// move out of the way before view is removed
++it;
// FIXME: This should only check if it *can* close
// and not start closing if the close can be aborted halfway, it would
// leave us with half the chats open and half of them closed. - Martijn
// if the view is closed, it is removed from chatViewList for us
if ( !view->closeView() )
{
kdDebug() << k_funcinfo << "Closing view failed!" << endl;
canClose = false;
}
}
return canClose;
}
bool KopeteChatWindow::queryExit()
{
KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
if ( app->sessionSaving()
|| app->isShuttingDown() /* only set if KopeteApplication::quitKopete() or
KopeteApplication::commitData() called */
|| !KopetePrefs::prefs()->showTray() /* also close if our tray icon is hidden! */
|| !isShown() )
{
Kopete::PluginManager::self()->shutdown();
return true;
}
else
return false;
}
void KopeteChatWindow::closeEvent( TQCloseEvent * e )
{
// if there's a system tray applet and we are not shutting down then just do what needs to be done if a
// window is closed.
KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
if ( KopetePrefs::prefs()->showTray() && !app->isShuttingDown() && !app->sessionSaving() ) {
// hide();
// BEGIN of code borrowed from TDEMainWindow::closeEvent
// Save settings if auto-save is enabled, and settings have changed
if ( settingsDirty() && autoSaveSettings() )
saveAutoSaveSettings();
if ( queryClose() ) {
e->accept();
}
// END of code borrowed from TDEMainWindow::closeEvent
}
else
TDEMainWindow::closeEvent( e );
}
void KopeteChatWindow::slotConfKeys()
{
KKeyDialog dlg( false, this );
dlg.insert( actionCollection() );
if( m_activeView )
{
dlg.insert(m_activeView->msgManager()->actionCollection() , i18n("Plugin Actions") );
TQPtrListIterator<KXMLGUIClient> it( *m_activeView->msgManager()->childClients() );
KXMLGUIClient *c = 0;
while( (c = it.current()) != 0 )
{
dlg.insert( c->actionCollection() /*, i18n("Plugin Actions")*/ );
++it;
}
if( m_activeView->editPart() )
dlg.insert( m_activeView->editPart()->actionCollection(), m_activeView->editPart()->name() );
}
dlg.configure();
}
void KopeteChatWindow::slotConfToolbar()
{
saveMainWindowSettings(TDEGlobal::config(), TQString::fromLatin1( "KopeteChatWindow" ));
KEditToolbar *dlg = new KEditToolbar(factory(), this );
if (dlg->exec())
{
if( m_activeView )
createGUI( m_activeView->editPart() );
else
createGUI( 0L );
applyMainWindowSettings(TDEGlobal::config(), TQString::fromLatin1( "KopeteChatWindow" ));
}
delete dlg;
}
void KopeteChatWindow::updateChatState( ChatView* cv, int newState )
{
if ( m_tabBar )
{
switch( newState )
{
case ChatView::Highlighted:
m_tabBar->setTabColor( cv, TQt::blue );
break;
case ChatView::Message:
m_tabBar->setTabColor( cv, TQt::red );
break;
case ChatView::Changed:
m_tabBar->setTabColor( cv, TQt::darkRed );
break;
case ChatView::Typing:
m_tabBar->setTabColor( cv, TQt::darkGreen );
break;
case ChatView::Normal:
default:
m_tabBar->setTabColor( cv, TDEGlobalSettings::textColor() );
break;
}
}
}
void KopeteChatWindow::updateChatTooltip( ChatView* cv )
{
if ( m_tabBar )
m_tabBar->setTabToolTip( cv, TQString::fromLatin1("<qt>%1</qt>").arg( cv->caption() ) );
}
void KopeteChatWindow::updateChatLabel()
{
const ChatView* cv = dynamic_cast<const ChatView*>( sender() );
if ( !cv || !m_tabBar )
return;
ChatView* chat = const_cast<ChatView*>( cv );
if ( m_tabBar )
{
m_tabBar->setTabLabel( chat, chat->caption() );
if ( m_tabBar->count() < 2 || m_tabBar->currentPage() == static_cast<const TQWidget *>(cv) )
setCaption( chat->caption() );
}
}
#include "kopetechatwindow.moc"
// vim: set noet ts=4 sts=4 sw=4: