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.
527 lines
15 KiB
527 lines
15 KiB
/*
|
|
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.
|
|
*/
|
|
|
|
/*
|
|
Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
|
|
Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
|
|
*/
|
|
|
|
#include "chatwindow.h"
|
|
#include "channel.h"
|
|
#include "ircview.h"
|
|
#include "server.h"
|
|
#include "konversationapplication.h"
|
|
#include "logfilereader.h"
|
|
|
|
#include <tqdatetime.h>
|
|
#include <tqdir.h>
|
|
#include <tqregexp.h>
|
|
#include <tqtextcodec.h>
|
|
#include <tqtooltip.h>
|
|
#include <tqlayout.h>
|
|
|
|
#include <tdelocale.h>
|
|
#include <kdialog.h>
|
|
#include <kdebug.h>
|
|
#include <tdeactioncollection.h>
|
|
#include <tdeaction.h>
|
|
|
|
|
|
ChatWindow::ChatWindow(TQWidget* parent) : TQVBox(parent)
|
|
{
|
|
setName("ChatWindowObject");
|
|
setTextView(0);
|
|
parentWidget=parent;
|
|
firstLog=true;
|
|
m_server=0;
|
|
m_notificationsEnabled = true;
|
|
m_channelEncodingSupported = false;
|
|
m_currentTabNotify = Konversation::tnfNone;
|
|
|
|
setMargin(margin());
|
|
setSpacing(spacing());
|
|
|
|
// The font size of the KTabWidget container may be inappropriately
|
|
// small due to the "Tab bar" font size setting.
|
|
setFont(TDEGlobalSettings::generalFont());
|
|
}
|
|
|
|
ChatWindow::~ChatWindow()
|
|
{
|
|
emit closing(this);
|
|
m_server=0;
|
|
}
|
|
|
|
void ChatWindow::updateAppearance()
|
|
{
|
|
// The font size of the KTabWidget container may be inappropriately
|
|
// small due to the "Tab bar" font size setting.
|
|
setFont(TDEGlobalSettings::generalFont());
|
|
|
|
if (textView)
|
|
{
|
|
if (Preferences::showIRCViewScrollBar())
|
|
textView->setVScrollBarMode(TQScrollView::AlwaysOn);
|
|
else
|
|
textView->setVScrollBarMode(TQScrollView::AlwaysOff);
|
|
}
|
|
}
|
|
|
|
void ChatWindow::setName(const TQString& newName)
|
|
{
|
|
name=newName;
|
|
emit nameChanged(this,newName);
|
|
}
|
|
|
|
TQString ChatWindow::getName()
|
|
{
|
|
return name;
|
|
}
|
|
|
|
void ChatWindow::setType(WindowType newType)
|
|
{
|
|
type=newType;
|
|
}
|
|
|
|
ChatWindow::WindowType ChatWindow::getType()
|
|
{
|
|
return type;
|
|
}
|
|
|
|
void ChatWindow::setServer(Server* newServer)
|
|
{
|
|
if (!newServer)
|
|
{
|
|
kdDebug("ChatWindow::setServer(0)!") << endl;
|
|
}
|
|
else
|
|
{
|
|
m_server=newServer;
|
|
connect(m_server,TQT_SIGNAL (serverOnline(bool)),this,TQT_SLOT (serverOnline(bool)) );
|
|
|
|
// check if we need to set up the signals
|
|
if(getType() != ChannelList)
|
|
{
|
|
if(textView) textView->setServer(newServer);
|
|
else kdDebug() << "ChatWindow::setServer(): textView==0!" << endl;
|
|
}
|
|
|
|
emit serverOnline(m_server->isConnected());
|
|
}
|
|
}
|
|
|
|
Server* ChatWindow::getServer()
|
|
{
|
|
return m_server;
|
|
}
|
|
|
|
void ChatWindow::serverOnline(bool /* state */)
|
|
{
|
|
//emit online(this,state);
|
|
}
|
|
|
|
void ChatWindow::setTextView(IRCView* newView)
|
|
{
|
|
textView = newView;
|
|
|
|
if(!textView)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(Preferences::showIRCViewScrollBar())
|
|
{
|
|
textView->setVScrollBarMode(TQScrollView::Auto);
|
|
}
|
|
else
|
|
{
|
|
textView->setVScrollBarMode(TQScrollView::AlwaysOff);
|
|
}
|
|
|
|
textView->setChatWin(this);
|
|
connect(textView,TQT_SIGNAL(textToLog(const TQString&)), this,TQT_SLOT(logText(const TQString&)));
|
|
connect(textView,TQT_SIGNAL(setStatusBarTempText(const TQString&)), this, TQT_SIGNAL(setStatusBarTempText(const TQString&)));
|
|
connect(textView,TQT_SIGNAL(clearStatusBarTempText()), this, TQT_SIGNAL(clearStatusBarTempText()));
|
|
}
|
|
|
|
void ChatWindow::appendRaw(const TQString& message, bool suppressTimestamps)
|
|
{
|
|
if(!textView) return;
|
|
textView->appendRaw(message, suppressTimestamps);
|
|
}
|
|
|
|
void ChatWindow::append(const TQString& nickname,const TQString& message)
|
|
{
|
|
if(!textView) return ;
|
|
textView->append(nickname,message);
|
|
}
|
|
|
|
void ChatWindow::appendQuery(const TQString& nickname,const TQString& message, bool inChannel)
|
|
{
|
|
if(!textView) return ;
|
|
textView->appendQuery(nickname,message, inChannel);
|
|
}
|
|
|
|
void ChatWindow::appendAction(const TQString& nickname, const TQString& message)
|
|
{
|
|
if(!textView) return;
|
|
|
|
if (getType() == Query || getType() == DccChat)
|
|
textView->appendQueryAction(nickname, message);
|
|
else
|
|
textView->appendChannelAction(nickname, message);
|
|
}
|
|
|
|
void ChatWindow::appendServerMessage(const TQString& type,const TQString& message, bool parseURL)
|
|
{
|
|
if(!textView) return ;
|
|
textView->appendServerMessage(type,message, parseURL);
|
|
}
|
|
|
|
void ChatWindow::appendCommandMessage(const TQString& command,const TQString& message, bool important, bool parseURL, bool self)
|
|
{
|
|
if(!textView) return ;
|
|
textView->appendCommandMessage(command,message,important, parseURL, self);
|
|
}
|
|
|
|
void ChatWindow::appendBacklogMessage(const TQString& firstColumn,const TQString& message)
|
|
{
|
|
if(!textView) return ;
|
|
textView->appendBacklogMessage(firstColumn,message);
|
|
}
|
|
|
|
void ChatWindow::cdIntoLogPath()
|
|
{
|
|
TQDir logPath=TQDir::home();
|
|
// Try to "cd" into the logfile path
|
|
if(!logPath.cd(Preferences::logfilePath(),true))
|
|
{
|
|
// Only create log path if logging is enabled
|
|
if(log)
|
|
{
|
|
// Try to create the logfile path and "cd" into it again
|
|
logPath.mkdir(Preferences::logfilePath(),true);
|
|
logPath.cd(Preferences::logfilePath(),true);
|
|
}
|
|
}
|
|
|
|
// add the logfile name to the path
|
|
logfile.setName(logPath.path()+'/'+logName);
|
|
}
|
|
|
|
void ChatWindow::setLogfileName(const TQString& name)
|
|
{
|
|
// Only change name of logfile if the window was new.
|
|
if(firstLog)
|
|
{
|
|
// status panels get special treatment here, since they have no server at the beginning
|
|
if (getType() == Status || getType() == DccChat)
|
|
{
|
|
logName = name + ".log";
|
|
}
|
|
else if (m_server)
|
|
{
|
|
// make sure that no path delimiters are in the name
|
|
logName = TQString(TQString(m_server->getDisplayName().lower()).append('_').append(name).append(".log")).replace('/','_');
|
|
}
|
|
|
|
// load backlog to show
|
|
if(Preferences::showBacklog())
|
|
{
|
|
// "cd" into log path or create path, if it's not there
|
|
cdIntoLogPath();
|
|
// Show last log lines. This idea was stole ... um ... inspired by PMP :)
|
|
// Don't do this for the server status windows, though
|
|
if((getType() != Status) && logfile.open(IO_ReadOnly))
|
|
{
|
|
unsigned long filePosition;
|
|
|
|
TQString backlogLine;
|
|
TQTextStream backlog(&logfile);
|
|
backlog.setEncoding(TQTextStream::UnicodeUTF8);
|
|
|
|
TQStringList firstColumns;
|
|
TQStringList messages;
|
|
int offset = 0;
|
|
unsigned int lastPacketHeadPosition = backlog.device()->size();
|
|
const unsigned int packetSize = 4096;
|
|
while(messages.count() < (unsigned int)Preferences::backlogLines() && backlog.device()->size() > packetSize * offset)
|
|
{
|
|
TQStringList firstColumnsInPacket;
|
|
TQStringList messagesInPacket;
|
|
|
|
// packetSize * offset < size <= packetSize * ( offset + 1 )
|
|
|
|
// Check if the log is bigger than packetSize * ( offset + 1 )
|
|
if(backlog.device()->size() > packetSize * ( offset + 1 ))
|
|
{
|
|
// Set file pointer to the packet size above the offset
|
|
backlog.device()->at(backlog.device()->size() - packetSize * ( offset + 1 ));
|
|
// Skip first line, since it may be incomplete
|
|
backlog.readLine();
|
|
}
|
|
else
|
|
{
|
|
// Set file pointer to the head
|
|
backlog.device()->reset();
|
|
}
|
|
|
|
unsigned int currentPacketHeadPosition = backlog.device()->at();
|
|
|
|
// Loop until end of file reached
|
|
while(!backlog.atEnd() && backlog.device()->at() < lastPacketHeadPosition)
|
|
{
|
|
// remember actual file position to check for deadlocks
|
|
filePosition = backlog.device()->at();
|
|
backlogLine = backlog.readLine();
|
|
|
|
// check for deadlocks
|
|
if(backlog.device()->at() == filePosition) backlog.device()->at(filePosition + 1);
|
|
|
|
// if a tab character is present in the line
|
|
if(backlogLine.find('\t') != -1)
|
|
{
|
|
// extract first column from log
|
|
TQString backlogFirst = backlogLine.left(backlogLine.find('\t'));
|
|
// cut first column from line
|
|
backlogLine = backlogLine.mid(backlogLine.find('\t') + 1);
|
|
// Logfile is in utf8 so we don't need to do encoding stuff here
|
|
// append backlog with time and first column to text view
|
|
firstColumnsInPacket << backlogFirst;
|
|
messagesInPacket << backlogLine;
|
|
}
|
|
} // while
|
|
|
|
// remember the position not to read the same lines again
|
|
lastPacketHeadPosition = currentPacketHeadPosition;
|
|
++offset;
|
|
|
|
firstColumns = firstColumnsInPacket + firstColumns;
|
|
messages = messagesInPacket + messages;
|
|
}
|
|
backlog.unsetDevice();
|
|
logfile.close();
|
|
|
|
// trim
|
|
int surplus = messages.count() - Preferences::backlogLines();
|
|
// "surplus" can be a minus value. (when the backlog is too short)
|
|
if(surplus > 0)
|
|
{
|
|
for(int i = 0 ; i < surplus ; ++i)
|
|
{
|
|
firstColumns.pop_front();
|
|
messages.pop_front();
|
|
}
|
|
}
|
|
|
|
TQStringList::Iterator itFirstColumn = firstColumns.begin();
|
|
TQStringList::Iterator itMessage = messages.begin();
|
|
for( ; itFirstColumn != firstColumns.end() ; ++itFirstColumn, ++itMessage )
|
|
appendBacklogMessage(*itFirstColumn, *itMessage);
|
|
}
|
|
} // if(Preferences::showBacklog())
|
|
}
|
|
}
|
|
|
|
void ChatWindow::logText(const TQString& text)
|
|
{
|
|
if(log)
|
|
{
|
|
// "cd" into log path or create path, if it's not there
|
|
cdIntoLogPath();
|
|
|
|
if(logfile.open(IO_WriteOnly | IO_Append))
|
|
{
|
|
// wrap the file into a stream
|
|
TQTextStream logStream(&logfile);
|
|
// write log in utf8 to help i18n
|
|
logStream.setEncoding(TQTextStream::UnicodeUTF8);
|
|
|
|
if(firstLog)
|
|
{
|
|
TQString intro(i18n("\n*** Logfile started\n*** on %1\n\n").arg(TQDateTime::currentDateTime().toString()));
|
|
logStream << intro;
|
|
firstLog=false;
|
|
}
|
|
|
|
TQTime time=TQTime::currentTime();
|
|
TQString logLine(TQString("[%1] [%2] %3\n").arg(TQDate::currentDate(Qt::LocalTime).toString()).
|
|
arg(time.toString("hh:mm:ss")).arg(text));
|
|
|
|
logStream << logLine;
|
|
|
|
// detach stream from file
|
|
logStream.unsetDevice();
|
|
|
|
// close file
|
|
logfile.close();
|
|
}
|
|
else kdWarning() << "ChatWindow::logText(): open(IO_Append) for " << logfile.name() << " failed!" << endl;
|
|
}
|
|
}
|
|
|
|
void ChatWindow::setChannelEncodingSupported(bool enabled)
|
|
{
|
|
m_channelEncodingSupported = enabled;
|
|
}
|
|
|
|
bool ChatWindow::isChannelEncodingSupported() const
|
|
{
|
|
return m_channelEncodingSupported;
|
|
}
|
|
|
|
int ChatWindow::spacing()
|
|
{
|
|
if(Preferences::useSpacing())
|
|
return Preferences::spacing();
|
|
else
|
|
return KDialog::spacingHint();
|
|
}
|
|
|
|
int ChatWindow::margin()
|
|
{
|
|
if(Preferences::useSpacing())
|
|
return Preferences::margin();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Accessors
|
|
IRCView* ChatWindow::getTextView() const
|
|
{
|
|
return textView;
|
|
}
|
|
|
|
void ChatWindow::setLog(bool activate)
|
|
{
|
|
log=activate;
|
|
}
|
|
|
|
// reimplement this in all panels that have user input
|
|
TQString ChatWindow::getTextInLine()
|
|
{
|
|
return TQString();
|
|
}
|
|
|
|
bool ChatWindow::canBeFrontView()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool ChatWindow::searchView()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// reimplement this in all panels that have user input
|
|
void ChatWindow::indicateAway(bool)
|
|
{
|
|
}
|
|
|
|
// reimplement this in all panels that have user input
|
|
void ChatWindow::appendInputText(const TQString&, bool)
|
|
{
|
|
}
|
|
|
|
// reimplement this if your window needs special close treatment
|
|
bool ChatWindow::closeYourself(bool /* askForConfirmation */)
|
|
{
|
|
deleteLater();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ChatWindow::eventFilter(TQObject* watched, TQEvent* e)
|
|
{
|
|
if(e->type() == TQEvent::KeyPress)
|
|
{
|
|
TQKeyEvent* ke = TQT_TQKEYEVENT(e);
|
|
|
|
bool scrollMod = (Preferences::useMultiRowInputBox() ? false : (ke->state() == TQt::ShiftButton));
|
|
|
|
if(ke->key() == TQt::Key_Up && scrollMod)
|
|
{
|
|
if(textView)
|
|
{
|
|
TQScrollBar* sbar = textView->verticalScrollBar();
|
|
sbar->setValue(sbar->value() - sbar->lineStep());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if(ke->key() == TQt::Key_Down && scrollMod)
|
|
{
|
|
if(textView)
|
|
{
|
|
TQScrollBar* sbar = textView->verticalScrollBar();
|
|
sbar->setValue(sbar->value() + sbar->lineStep());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if(ke->key() == TQt::Key_Prior)
|
|
{
|
|
if(textView)
|
|
{
|
|
TQScrollBar* sbar = textView->verticalScrollBar();
|
|
sbar->setValue(sbar->value() - sbar->pageStep());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if(ke->key() == TQt::Key_Next)
|
|
{
|
|
if(textView)
|
|
{
|
|
TQScrollBar* sbar = textView->verticalScrollBar();
|
|
sbar->setValue(sbar->value() + sbar->pageStep());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return TQVBox::eventFilter(watched, e);
|
|
}
|
|
|
|
void ChatWindow::adjustFocus()
|
|
{
|
|
childAdjustFocus();
|
|
}
|
|
|
|
void ChatWindow::emitUpdateInfo()
|
|
{
|
|
TQString info = getName();
|
|
emit updateInfo(info);
|
|
}
|
|
|
|
TQColor ChatWindow::highlightColor()
|
|
{
|
|
return getTextView()->highlightColor();
|
|
}
|
|
|
|
void ChatWindow::activateTabNotification(Konversation::TabNotifyType type)
|
|
{
|
|
if (!notificationsEnabled())
|
|
return;
|
|
|
|
if(type > m_currentTabNotify)
|
|
return;
|
|
|
|
m_currentTabNotify = type;
|
|
|
|
emit updateTabNotification(this,type);
|
|
}
|
|
|
|
void ChatWindow::resetTabNotification()
|
|
{
|
|
m_currentTabNotify = Konversation::tnfNone;
|
|
}
|
|
|
|
#include "chatwindow.moc"
|