/**
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sIRC.h"
#include "commthread.h"
#include "settingswindow.h"
#include "settings.h"
#include "messages.h"
#include "chathelp.h"
//----------------------------------------------------------------------------------
// Here is where we construct the window e.g views, buttons, textviews, etc...
MainView::MainView( const os::Rect &r ):os::View( r, "MainView", os::CF_FOLLOW_ALL, os::WID_FULL_UPDATE_ON_RESIZE )
{
//create the vertical root structure
os::LayoutView* pcView = new os::LayoutView( GetBounds(), "root_view" );
os::LayoutNode* pcRootNode = new os::VLayoutNode( "root_node" );
//create main menu
mainMenuBar = new os::Menu( os::Rect(), "mainMenuBar", os::ITEMS_IN_ROW );
mainMenuBar->SetFrame( os::Rect( 0, 0, GetBounds().Width() + 1, mainMenuBar->GetPreferredSize( false ).y + 1 ) );
// Application menu
tempMenu = new os::Menu( os::Rect(), "Application", os::ITEMS_IN_COLUMN );
tempMenu->AddItem( "Settings", new os::Message( M_MENU_SETTINGS ) );
tempMenu->AddItem( new os::MenuSeparator() );
tempMenu->AddItem( "Help", new os::Message( os::M_MENU_EVENT ) );
tempMenu->AddItem( "About", new os::Message( os::M_ABOUT_REQUESTED ) );
tempMenu->AddItem( "Quit", new os::Message( os::M_QUIT_REQUESTED ) );
mainMenuBar->AddItem( tempMenu );
// Server menu
tempMenu = new os::Menu( os::Rect(), "Server", os::ITEMS_IN_COLUMN );
tempMenu->AddItem( "Connect", new os::Message( M_MENU_START ) );
tempMenu->AddItem( "Login", new os::Message( M_MENU_LOGIN ) );
tempMenu->AddItem( "Disconnect", new os::Message( M_MENU_STOP ) );
mainMenuBar->AddItem( tempMenu );
// Channel menu
tempMenu = new os::Menu( os::Rect(), "Channel", os::ITEMS_IN_COLUMN );
tempMenu->AddItem( "Join Channel", new os::Message( M_MENU_JOINCHANNEL ) );
tempMenu->AddItem( "Leave Channel", new os::Message( M_MENU_LEAVECHANNEL ) );
mainMenuBar->AddItem( tempMenu );
pcRootNode->AddChild( mainMenuBar );
//create main node
os::LayoutNode* pcMainHNode = new os::HLayoutNode( "main_node", 1 );
pcMainHNode->SetBorders( os::Rect( 2, 0, 2, 0 ) );
//create output box
fOutputView = new os::TextView( os::Rect(), "output_view", "", os::CF_FOLLOW_ALL );
fOutputView->SetMultiLine( true );
fOutputView->SetReadOnly( true );
pcMainHNode->AddChild( fOutputView, 1 );
pcMainHNode->AddChild( new os::HLayoutSpacer( "output_user_spacer", 2, 2 ) );
// create user box
fUserView = new os::TextView( os::Rect(), "user_list", "" );
fUserView->SetMultiLine( true );
fUserView->SetReadOnly( true );
pcMainHNode->AddChild( fUserView, 0.2 );
pcRootNode->AddChild( pcMainHNode );
//create input node
os::LayoutNode *pcInputHNode = new os::HLayoutNode( "input_node", 0 );
pcInputHNode->SetBorders( os::Rect( 2, 2, 2, 2 ) );
//create input box
fInputView = new os::TextView( os::Rect(), "input_view", "", os::CF_FOLLOW_ALL );
pcInputHNode->AddChild( fInputView, 1 );
pcRootNode->AddChild( pcInputHNode );
//add it all to the layout structure
pcView->SetRoot( pcRootNode );
AddChild( pcView );
}
void MainView::AllAttached()
{
mainMenuBar->SetTargetForItems( this );
fInputView->SetEventMask( os::TextView::EI_ENTER_PRESSED | os::TextView::EI_ESC_PRESSED );
fInputView->SetMessage( new os::Message( MSG_TOLOOPER_NEW_MESSAGE ) );
fInputView->SetTarget( this );
bInChannel = false;
os::View::AllAttached();
}
void MainView::AttachedToWindow()
{
//ensure messages are sent to the view and not the window
os::View::AttachedToWindow();
/* Load settings from the default file */
m_pcSettings = new AppSettings();
m_pcSettings->Load();
/* create a protected commthread instance */
CommThread *m_pCommThread;
m_pCommThread = new CommThread( os::Messenger(this) );
if( m_pCommThread == NULL )
{
fprintf( stderr, "Commthread: %s (%d)\n", strerror( errno ), errno );
os::Application::GetInstance()->PostMessage( os::M_QUIT );
}
m_CommThread = m_pCommThread;
m_CommThread->Run();
}
void MainView::FrameSized( const os::Point& cDelta )
{
Invalidate( mainMenuBar->GetFrame(), true );
os::View::FrameSized( cDelta );
}
void MainView::HandleMessage( os::Message *pcMessage )
{
switch ( pcMessage->GetCode() )
{
case M_MENU_START:
{
if( !m_CommThread->IsConnected() )
{
os::String zHost, zPort, zUsername, zPassword, zRealname, zNickname, zChannel;
m_pcSettings->LoadHistory( &zHost, &zPort, &zUsername, &zPassword, &zRealname, &zNickname, &zChannel );
fOutputView->Clear();
os::Message cMsg( MSG_TOLOOPER_START );
m_CommThread->SetHost( zHost );
m_CommThread->SetPort( zPort );
m_CommThread->SetUsername( zUsername );
m_CommThread->SetPassword( zPassword );
m_CommThread->SetRealname( zRealname );
m_CommThread->SetNickname( zNickname );
m_CommThread->SetChannel( zChannel );
m_CommThread->PostMessage( &cMsg, m_CommThread );
}
break;
}
case M_MENU_STOP:
{
if( m_CommThread->IsConnected() )
{
m_CommThread->Send( os::String().Format( "QUIT %s\r\n", ":-)" ) );
sleep( 3 ); // wait for server exit message
m_CommThread->PostMessage( MSG_TOLOOPER_STOP, m_CommThread );
}
break;
}
case MSG_TOLOOPER_NEW_MESSAGE:
{
uint32 nEvents = 0;
pcMessage->FindInt( "events", &nEvents );
if ( nEvents & os::TextView::EI_ESC_PRESSED )
{
fInputView->Clear();
fInputView->MakeFocus();
}
else if ( nEvents & os::TextView::EI_ENTER_PRESSED )
{
if( bInChannel )
m_CommThread->Send( os::String().Format( "PRIVMSG %s :%s\r\n", m_CommThread->GetChannel().c_str(), fInputView->GetBuffer()[0].c_str() ) );
else
m_CommThread->Send( os::String().Format( "%s\r\n", fInputView->GetBuffer()[0].c_str() ) );
fInputView->Clear();
fInputView->MakeFocus();
}
break;
}
case MSG_FROMLOOPER_NEW_MESSAGE:
{
const char *pzName;
int nCount = 0;
pcMessage->GetNameInfo( "name", NULL, &nCount );
for( int i = 0; i < nCount ; ++i )
{
if( pcMessage->FindString( "name", &pzName, i ) == 0 )
{
Update( os::String( pzName ) );
PopulateUserList( pzName );
break;
}
}
break;
}
case M_MENU_LOGIN:
{
m_CommThread->Send( os::String().Format( "PRIVMSG NickServ :IDENTIFY %s\r\n", m_CommThread->GetPassword().c_str() ) );
break;
}
case M_MENU_JOINCHANNEL:
{
if( m_CommThread->IsConnected() )
{
if( !bInChannel )
{
m_CommThread->Send( os::String().Format( "JOIN %s\r\n", m_CommThread->GetChannel().c_str() ) );
bInChannel = true;
}
}
break;
}
case M_MENU_LEAVECHANNEL:
{
if( m_CommThread->IsConnected() )
{
if( bInChannel )
{
bInChannel = false;
fUserView->Clear();
m_CommThread->Send( os::String().Format( "PART %s\r\n", m_CommThread->GetChannel().c_str() ) );
}
}
break;
}
case M_MENU_SETTINGS:
{
// Get screen width and height to centre the window
os::Desktop *pcDesktop = new os::Desktop();
int iWidth = pcDesktop->GetScreenMode().m_nWidth;
int iHeight = pcDesktop->GetScreenMode().m_nHeight;
int iLeft = (iWidth-450)/2;
int iTop = (iHeight-290)/2;
// Show settings window
SettingsWindow *pcWindow = new SettingsWindow( os::Rect( iLeft, iTop, iLeft+450, iTop+170 ), m_pcSettings );
pcWindow->Show();
pcWindow->MakeFocus();
break;
}
case os::M_MENU_EVENT:
case os::M_ABOUT_REQUESTED:
case os::M_QUIT_REQUESTED:
{
os::Application::GetInstance()->PostMessage( pcMessage, os::Application::GetInstance() );
break;
}
default:
{
os::View::HandleMessage( pcMessage );
break;
}
}
}
//this parser will only work on a single line of server stream
void MainView::PopulateUserList( const char *pzName )
{
std::string data( pzName );
// erase \r and \n
data.erase( data.find_last_not_of( "\r\n" ) + 1 );
if( data[0] == ':' )
{
// Find prefix
std::string::size_type pos1 = data.find_first_of( " ", 1 );
// Find command
std::string::size_type pos2 = data.find_first_of( " ", pos1 + 1 );
// Check whether there is any params
std::string::size_type pos3 = data.find_first_of( ":", pos2 + 1 );
if( atoi( data.substr( pos1 + 1, ( pos2 - 1 ) - pos1 ).c_str() ) == 353 )
{
// get whats after the ':', if there were any ':'
if( pos3 != std::string::npos )
{
std::stringstream ss( data.substr( pos3 + 1 ) );
std::string buf;
// overload ss data into buf
while( ss >> buf )
{
std::vector < std::string > vec;
if( buf[0] == '@' || buf[0] == '+' )
{
vec.push_back( buf.substr( 0, 1 ) );
vec.push_back( buf.substr( 1 ) );
}
else
{
vec.push_back( " " );
vec.push_back( buf );
}
fUserView->Insert( os::String().Format( "%s\n", buf.c_str() ).c_str() );
}
}
}
}
}
void MainView::Update( const os::String cBufString )
{
os::String cTempString( cBufString );
if( bInChannel )
{
const os::String find1 = "!";
const std::string::size_type pos1 = cBufString.find( find1, 0 );
const os::String find2 = "#";
const std::string::size_type pos2 = cBufString.find( find2, 0 );
// cut between pos1 '!' and pos2 '#'
if( pos1 != std::string::npos && pos2 != std::string::npos )
cTempString = cTempString.erase( pos1, pos2 - pos1 - 1 );
}
AddStringToTextView( cTempString );
}
void MainView::AddStringToTextView( const os::String &cName ) const
{
// add new text at the end of the textview buffer
const os::TextView::buffer_type cBuffer = fOutputView->GetBuffer();
const int posX = cBuffer[cBuffer.size()-1].size();
const int posY = cBuffer.size()-1;
const os::IPoint cPoint = os::IPoint( posX, posY );
fOutputView->Insert( cPoint, cName.c_str() );
}
//----------------------------------------------------------------------------------
MainWindow::MainWindow( const os::Rect &r ):os::Window( r, "MainWindow", "sIRC:- Syllable Internet Relay Chat Client", 0, os::CURRENT_DESKTOP )
{
/* Set up "view" as the main view for the window, filling up the entire */
/* window (thus the call to GetBounds()). */
view = new MainView( GetBounds() );
AddChild( view );
}
void MainWindow::HandleMessage( os::Message *pcMessage )
{
switch ( pcMessage->GetCode() )
{
case os::M_MENU_EVENT:
{
ShowHelp();
break;
}
case os::M_ABOUT_REQUESTED:
{
ShowAbout();
break;
}
case os::M_QUIT_REQUESTED:
{
OkToQuit();
break;
}
default:
{
os::Window::HandleMessage( pcMessage );
break;
}
}
}
void MainWindow::ShowHelp( void )
{
( new os::Alert( "Help", ChatHelp(), os::Alert::ALERT_TIP, 0x00, "Right", NULL ) )->Go();
}
void MainWindow::ShowAbout( void )
{
retry:
os::String cAbout = "sIRC 0.33\n";
cAbout += "Syllable Internet Relay Chat Client\n";
cAbout += os::String("Build: " ) + os::DateTime::Now().GetDate() + os::String( "\n\n" );
cAbout += "sIRC 0.03 Copyright © James Coxon 2006\n";
cAbout += "sIRC 0.33 Copyright © David Kent 2023\n\n";
cAbout += "sIRC is released under the GNU General Public License.\n";
cAbout += "Please see the file COPYING, distributed with sIRC,\n";
cAbout += "or http://www.gnu.org for more information.";
os::Alert *sAbout = new os::Alert( "About sIRC...", cAbout, os::Alert::ALERT_INFO, 0x00, "Credits", "License", "Close", NULL );
sAbout->CenterInWindow( this );
int32 reply = sAbout->Go();
switch ( reply )
{
// the first button 'Credits'
case 0x00:
{
os::Alert *sCredits = new os::Alert( "Credits",
"James Coxon - For his original concept and gui layout\n"
" [http://www.srcf.ucam.org/~jac208/sIRC.tar] (discontinued)\n"
" [http://allatheos.free.fr/softs/sources/sIRC.tar] (discontinued)\n"
" [https://web.archive.org/web/20081213015820/http://allatheos.free.fr/softs/sources/sIRC.tar]\n"
" or else docs/index.html\n\n"
"Beej's Guide to Network Programming. Version 1.5.5 (13-Jan-1999)\n"
" [http://www.ecst.csuchico.edu/~beej/guide/net/] (discontinued)\n"
" [https://web.archive.org/web/20051210035802/http://beej.us/guide/bgnet/output/html/index.html]\n\n"
"David Kent - For bug fixs and code upgrades",
0x00, "Close", NULL );
sCredits->CenterInWindow( this );
sCredits->Go();
goto retry;
}
// the second button 'License'
case 0x01:
{
os::Alert *sLicense = new os::Alert( "License",
"This program is free software; you can redistribute it and/or modify it\n"
"under the terms of the GNU General Public Licence as published by the\n"
"Free Software Foundation; either version 2 of the License, or (at your\n"
"option) any later version.\n\n"
"This program is distributed in the hope that it will be useful, but\n"
"WITHOUT ANY WARRANTY: without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public Licence for more details.\n\n"
"You should have received a copy of the GNU General Public Licence\n"
"along with this program; if not, write to the Free Software Foundation,\n"
"Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA",
0x00, "Close", NULL );
sLicense->CenterInWindow( this );
sLicense->Go();
goto retry;
}
// the third button 'Close'
case 0x02: break;
}
}
bool MainWindow::OkToQuit( void )
{
os::Application::GetInstance()->PostMessage( os::M_QUIT );
return ( true );
}
//----------------------------------------------------------------------------------
MyApplication::MyApplication( void ):os::Application( "application/x-vnd.Syllable-sIRC" )
{
// Remember it goes "x" then "y"
myMainWindow = new MainWindow( os::Rect( 20, 20, 600, 400 ) );
myMainWindow->Show();
myMainWindow->MakeFocus();
}
void MyApplication::HandleMessage( os::Message * pcMessage )
{
// bounce message to the window
myMainWindow->PostMessage( pcMessage, myMainWindow );
return ( os::Application::HandleMessage( pcMessage ) );
}
//----------------------------------------------------------------------------------
int main( void )
{
MyApplication *thisApp;
thisApp = new MyApplication();
thisApp->Run();
return ( 0 );
}