/* 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 Copyright (C) 2006-2008 Eike Hein */ #include "chatwindow.h" #include "channel.h" #include "ircview.h" #include "server.h" #include "konversationapplication.h" #include "logfilereader.h" #include #include #include #include #include #include #include #include #include #include #include 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"