
#include "stdafx.h"
#include "resource.h"
#include "mainframe.h"
#include "about.h"
#include "blitzide.h"
#include "libs.h"

#include <mmsystem.h>

#include <fstream>
#include <sstream>

IMPLEMENT_DYNAMIC( MainFrame,CFrameWnd )
BEGIN_MESSAGE_MAP( MainFrame,CFrameWnd )
	ON_WM_CREATE()
	ON_WM_CLOSE()
	ON_WM_DESTROY()
	ON_WM_ERASEBKGND()
	ON_WM_SIZE()
	ON_WM_ACTIVATE()

	ON_COMMAND( Inew,fileNew )
	ON_COMMAND( ID_OPEN,fileOpen )
	ON_COMMAND( ID_SAVE,fileSave )
	ON_COMMAND( ID_SAVEAS,fileSaveAs )
	ON_COMMAND( ID_SAVEALL,fileSaveAll )
	ON_COMMAND( ID_PRINT,filePrint )
	ON_COMMAND( ID_CLOSE,fileClose )
	ON_COMMAND( ID_CLOSEALL,fileCloseAll )
	ON_COMMAND( ID_EXIT,fileExit )
	ON_COMMAND_RANGE( 333,343,fileRecent )
	ON_COMMAND( ID_CUT,editCut )
	ON_COMMAND( ID_COPY,editCopy )
	ON_COMMAND( ID_PASTE,editPaste )
	ON_COMMAND( ID_SELECTALL,editSelectAll )
	ON_COMMAND( ID_FIND,editFind )
	ON_COMMAND( ID_FINDNEXT,editFindNext )
	ON_COMMAND( ID_REPLACE,editReplace )
	ON_COMMAND( ID_CTRLTAB,ctrlTab )
	ON_COMMAND( ID_CTRLSHIFTTAB,ctrlShiftTab )
	ON_COMMAND( ID_ESCAPE,escape )
	ON_COMMAND( ID_QUICKHELP,quick_Help )

	ON_COMMAND( ID_EXECUTE,programExecute )
	ON_COMMAND( ID_REEXECUTE,programReExecute )
	ON_COMMAND( ID_COMPILE,programCompile )
	ON_COMMAND( ID_PUBLISH,programPublish )
	ON_COMMAND( ID_COMMANDLINE,programCommandLine )
	ON_COMMAND( ID_DEBUG,programDebug )

	ON_COMMAND( ID_HOME,helpHome )
	ON_COMMAND( ID_BACK,helpBack )
	ON_COMMAND( ID_FORWARD,helpForward )
	ON_COMMAND( ID_ABOUT,helpAbout )

	ON_UPDATE_COMMAND_UI( Inew,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_OPEN,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_SAVE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_SAVEAS,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_SAVEALL,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_PRINT,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_CLOSE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_CLOSEALL,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_CUT,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_COPY,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_PASTE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_SELECTALL,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_FIND,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_FINDNEXT,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_REPLACE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_EXECUTE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_REEXECUTE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_COMPILE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_PUBLISH,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_COMMANDLINE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_DEBUG,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_HOME,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_BACK,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_FORWARD,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_ESCAPE,updateCmdUI )
	ON_UPDATE_COMMAND_UI( ID_QUICKHELP,updateCmdUI )
	ON_UPDATE_COMMAND_UI_RANGE( 333,343,updateCmdUIRange )
END_MESSAGE_MAP()

enum{
	TAB_INVALID,TAB_EDITOR,TAB_HTMLHELP,TAB_DEBUGLOG
};

static string getFile( const string &f ){
	int n;
	string t=f;
	n=t.rfind( '/' );if( n!=string::npos ) t=t.substr(n+1);
	n=t.rfind( '\\' );if( n!=string::npos ) t=t.substr(n+1);
	return t;
}

static string getPath( const string &f ){
	int n;
	string t=f;
	n=t.rfind( '/' );if( n!=string::npos ) t=t.substr(0,n );
	n=t.rfind( '\\' );if( n!=string::npos ) t=t.substr(0,n);
	return t;
}

MainFrame::MainFrame():exit_flag(false){
}

int MainFrame::OnCreate( LPCREATESTRUCT lpCreateStruct ){
	CFrameWnd::OnCreate( lpCreateStruct );

	static HBITMAP toolbmp;
	static SIZE imgsz,butsz;
	static UINT toolbuts[]={ 
		Inew,ID_OPEN,ID_SAVE,ID_CLOSE,ID_SEPARATOR,
		ID_CUT,ID_COPY,ID_PASTE,ID_SEPARATOR,
		ID_FIND,ID_SEPARATOR,
		ID_EXECUTE,ID_SEPARATOR,
		ID_HOME,ID_BACK,ID_FORWARD };
	static int toolcnt=sizeof(toolbuts)/sizeof(UINT);

	if( !toolbmp ){
		BITMAP bm;
		string t=prefs.homeDir+"/cfg/ide_toolbar.bmp";
		toolbmp=(HBITMAP)LoadImage( 0,t.c_str(),IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_LOADMAP3DCOLORS );
		if( !toolbmp ){
			AfxMessageBox( "toolbar bitmap failed to load!" );
			ExitProcess(0);
		}
		GetObject( toolbmp,sizeof(bm),&bm );
		int n=0;
		for( int k=0;k<toolcnt;++k ) if( toolbuts[k]!=ID_SEPARATOR ) ++n;
		imgsz.cx=bm.bmWidth/n;imgsz.cy=bm.bmHeight;
		butsz.cx=imgsz.cx+7;butsz.cy=imgsz.cy+6;
	}
	toolBar.CreateEx( this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_TOOLTIPS );
	toolBar.SetBitmap( toolbmp );
	toolBar.SetSizes( butsz,imgsz );
	toolBar.SetButtons( toolbuts,toolcnt );

	int style;
	style=WS_CHILD|WS_VISIBLE|CBRS_ALIGN_BOTTOM;
	statusBar.CreateEx( this,0,style );
	UINT IDS[]={ ID_STATUSTEXT,ID_COLROWTEXT };
	statusBar.SetIndicators( IDS,2 );
	statusBar.SetPaneInfo( 0,ID_STATUSTEXT,SBPS_NOBORDERS|SBPS_STRETCH,0 );
	statusBar.SetPaneInfo( 1,ID_COLROWTEXT,0,128 );
	statusBar.SetPaneText( 0,"" );statusBar.SetPaneText( 1,"" );

	tabber.Create( WS_VISIBLE|WS_CHILD|TCS_HOTTRACK,CRect( 0,0,0,0 ),this,1 );
	tabber.SetFont( &prefs.tabsFont );
	tabber.setListener( this );

	prefs.win_notoolbar=!prefs.win_notoolbar;
	escape();

	//create recent file list
	CMenu menu;
	menu.CreatePopupMenu();
	for( int k=0;k<prefs.recentFiles.size();++k ){
		menu.InsertMenu( k,MF_BYPOSITION|MF_ENABLED,333+k,prefs.recentFiles[k].c_str() );
	}
	CMenu *file=GetMenu()->GetSubMenu( 0 );
	file->InsertMenu( 12,MF_BYPOSITION|MF_ENABLED|MF_POPUP,(UINT)menu.m_hMenu,"&Recent Files" );
	menu.Detach();

	helpHome();

	trackmem( true );

	if( blitzIDE.m_lpCmdLine[0] ){
		string t=string( blitzIDE.m_lpCmdLine );
		if( t[0]=='\"' ) t=t.substr( 1,t.size()-2 );
		open( t );
	}else{
		SetCurrentDirectory( (prefs.homeDir+"/samples").c_str() );
	}

	return 0;
}

void MainFrame::OnDestroy(){
	trackmem( false );
	WINDOWPLACEMENT wp={sizeof(wp)};
	if( GetWindowPlacement( &wp ) ){
		prefs.win_rect=wp.rcNormalPosition;
		if( wp.showCmd==SW_SHOWMAXIMIZED ){
			prefs.win_maximized=true;
		}else{
			prefs.win_maximized=false;
		}
	}
}

void MainFrame::setTitle( const string &s ){
	SetWindowText( ("Blitz3D - "+s ).c_str() );
	return;
}

void MainFrame::OnClose(){
	bool exit_flag=true;
	for( int k=tabber.size()-1;k>=0;--k ){
		exit_flag=close( k );
		if( !exit_flag ) break;
	}
	if( exit_flag ) DestroyWindow();
}

BOOL MainFrame::OnEraseBkgnd( CDC *dc ){
	return true;
}

void MainFrame::OnSize( UINT type,int sw,int sh ){
	CFrameWnd::OnSize( type,sw,sh );

	CRect r,t;GetClientRect( &r );
	int x=r.left,y=r.top,w=r.Width(),h=r.Height();

	if( !prefs.win_notoolbar ){
		statusBar.GetWindowRect( &t );h-=t.Height();
		toolBar.GetWindowRect( &t );y+=t.Height();h-=t.Height();
	}

	tabber.MoveWindow( x,y,w,h );
}

static char *bbFilter=

"Blitz Basic files (.bb)|*.bb|"

"Image files (.bmp,.jpg,.png,.tga,.iff,.pcx)|*.bmp;*.jpg;*.png;*.tga;*.iff;*.pcx|"
"Audio files (.wav,.mid,.mod,.mp3,.s3m,.xm,.it,.rmi,.sgt)|*.wav;*.mid;*.mod;*.mp3;*.s3m;*.xm;*.it;*.rmi;*.sgt|"
"3D Mesh files (.x,.3ds,.md2)|*.x;*.3ds;*.md2|"

"All files|*.*||";

Editor *MainFrame::getEditor(){
	return getEditor( tabber.getCurrent() );
}

Editor *MainFrame::getEditor( int n ){
	map<CWnd*,Editor*>::iterator it=editors.find( tabber.getTabWnd( n ) );
	return it==editors.end() ? 0 : it->second;
}

HtmlHelp *MainFrame::getHelp( int n ){
	map<CWnd*,HtmlHelp*>::iterator it=helps.find( tabber.getTabWnd( n ) );
	return it==helps.end() ? 0 : it->second;
}

HtmlHelp *MainFrame::getHelp(){
	return getHelp( tabber.getCurrent() );
}

HtmlHelp *MainFrame::findHelp(){
	int n;
	HtmlHelp *h;
	for( n=0;n<tabber.size();++n ){
		if( h=getHelp( n ) ) break;
	}
	if( n==tabber.size() ){
		h=new HtmlHelp( this );
		h->Create( 0,"Help",WS_CHILD|WS_BORDER,CRect( 0,0,0,0 ),&tabber,1 );
		helps[h]=h;
		tabber.insert( n,h,"Help" );
	}
	tabber.setCurrent( n );
	return h;
}

void MainFrame::cursorMoved( Editor *editor ){
	if( editor!=getEditor() ) return;
	int row,col;
	editor->getCursor( &row,&col );
	char mod=editor->getModified() ? '*' : ' ';
	char str[64];sprintf( str,"Row:%i Col:%i %c",row,col,mod );
	statusBar.SetPaneText( 1,str );
}

void MainFrame::currentSet( Tabber *tabber,int index ){
	if( Editor *e=getEditor() ){
		string t=e->getName();
		if( !t.size() ) t="<untitled>";
		setTitle( t );
		cursorMoved( e );
	}else if( HtmlHelp *h=getHelp() ){
		setTitle( h->getTitle() );
		statusBar.SetPaneText( 1,"" );
	}else{
		setTitle( "no file" );
		statusBar.SetPaneText( 1,"" );
	}
}

void MainFrame::helpOpen( HtmlHelp *help,const string &file ){
	open( file );
}

void MainFrame::helpTitleChange( HtmlHelp *help,const string &title ){
	if( HtmlHelp *h=getHelp() ) setTitle( h->getTitle() );
}

void MainFrame::insertRecent( const string &file ){
	//check recent files
	if( CMenu *list=GetMenu()->GetSubMenu(  0 )->GetSubMenu( 12 ) ){
		vector<string>::iterator it;
		vector<string> &f=prefs.recentFiles;
		for( it=f.begin();it!=f.end();++it ){
			if( tolower( *it )==tolower( file ) ) break;
		}
		if( it!=f.end() ){
			//move to top
			string t=*it;
			f.erase( it );
			f.insert( f.begin(),t );
		}else{
			//insert
			if( f.size()==10 ){
				f.pop_back();
				list->RemoveMenu( 9,MF_BYPOSITION );
			}
			f.insert( f.begin(),file );
			list->InsertMenu( 0,MF_BYPOSITION|MF_ENABLED,333,file.c_str() );
		}
		//renumber menu items
		for( int k=0;k<f.size();++k ){
			list->ModifyMenu( k,MF_BYPOSITION|MF_ENABLED,333+k,f[k].c_str() );
		}
	}
}

void MainFrame::newed( const string &t ){

	CRect r( 0,0,0,0 );

	Editor *e=new Editor( this );
	e->Create( 0,"Editor",WS_CHILD,r,&tabber,1 );
	e->setName( t );
	editors[e]=e;
	int n=tabber.size();
	string s=t.size() ? getFile( t ) : "<untitled>";
	tabber.insert( n,e,s.c_str() );
	tabber.setCurrent( n );
}

bool MainFrame::open( const string &f ){

	string file=f,filter="*.bb";

	int n=f.find( "*." );
	if( n!=string::npos ){
		file=f.substr( 0,n );
		filter=f.substr( n );
	}

	int attr=GetFileAttributes( file.c_str() );if( attr==-1 ) attr=0;
	if( !file.size() || (attr & FILE_ATTRIBUTE_DIRECTORY) ){

		int n=OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST;
		CFileDialog fd( true,"bb",filter.c_str(),n,bbFilter );
		char *i_dir=strdup( file.c_str() );
		fd.m_ofn.lpstrInitialDir=i_dir;
		fd.m_ofn.lpstrTitle="Open Blitz Basic File...";
		int nn=fd.DoModal();free( i_dir );
		if( nn==IDCANCEL ) return false;

		file=fd.GetPathName();

	}else{

		char buff[MAX_PATH],*p;
		if( GetFullPathName( file.c_str(),MAX_PATH,buff,&p ) ) file=buff;
		else file=f;
	}

	if( isMediaFile( tolower( file ) ) ){
		string t=prefs.homeDir+"/bin/mediaview.exe";
		if( (int)ShellExecute( ::GetDesktopWindow(),0,t.c_str(),file.c_str(),0,SW_SHOW )>32 ){
		}
		return false;
	}

	//is file already open?
	int k;
	for( k=0;k<tabber.size();++k ){
		if( Editor *e=getEditor( k ) ){
			if( tolower( e->getName() )==tolower( file ) ){
				tabber.setCurrent( k );
				return true;
			}
		}
	}
	//open new file
	ifstream in( file.c_str() );
	if( !in.good() ){
		string e="Error reading file \""+f+"\"";
		AfxMessageBox( e.c_str(),MB_ICONWARNING );
		return false;
	}
	newed( file );
	tabber.UpdateWindow();
	Editor *e=getEditor();
	e->setText( in );
	e->setName( file );
	e->setModified( false );
	insertRecent( file );
	cursorMoved( e );
	return true;
}

bool MainFrame::close( int n ){
	if( Editor *e=getEditor( n ) ){
		if( e->getModified() ){
			tabber.setCurrent( n );
			string t="File "+e->getName()+" has been modified!\nSave changes before closing?";
			int rt=AfxMessageBox( t.c_str(),MB_YESNOCANCEL|MB_ICONWARNING );
			if( rt==IDYES ) return save( n );
			if( rt==IDCANCEL ) return false;
		}
		tabber.remove( n );
		e->DestroyWindow();
		editors.erase( e );
		delete e;
	}else if( HtmlHelp *h=getHelp( n ) ){
	}
	return true;
}

bool MainFrame::save( int n ){

	Editor *e=getEditor( n );
	if( !e ) return true;
	string t=e->getName();
	if( !t.size() ){
		tabber.setCurrent( n );
		int df=OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_EXPLORER|OFN_OVERWRITEPROMPT;
		CFileDialog fd( false,"bb","*.bb",df,bbFilter );
		fd.m_ofn.lpstrTitle="Save Blitz Basic program as...";
		if( fd.DoModal()==IDCANCEL ) return false;
		t=fd.GetPathName();
		tabber.setTabText( n,getFile( t ) );
		e->setName( t );
		insertRecent( t );
	}
	//Do backups!
	if( prefs.edit_backup ){
		for( int k=prefs.edit_backup;k>1;--k ){
			CopyFile( (t+"_bak"+itoa(k-1)).c_str(),(t+"_bak"+itoa(k)).c_str(),false );
		}
		CopyFile( t.c_str(),(t+"_bak1").c_str(),false );
	}
	int om=ios_base::binary|ios_base::out|ios_base::trunc;
	ofstream out( t.c_str(),om );
	if( !out.good() ){
		string e="Error writing file \""+t+"\"";
		AfxMessageBox( e.c_str(),MB_ICONWARNING );
		return false;
	}
	e->getText( out );
	out.close();
	e->setModified( false );
	cursorMoved( e );
	return true;
}

void MainFrame::fileNew(){
	newed( "" );
}

void MainFrame::fileOpen(){
	open( "" );
}

void MainFrame::fileSave(){
	save( tabber.getCurrent() );
}

void MainFrame::fileSaveAs(){
	if( Editor *e=getEditor() ){
		string t=e->getName();
		e->setName( "" );
		if( !save( tabber.getCurrent() ) ) e->setName( t );
		currentSet( &tabber,tabber.getCurrent() );
	}
}

void MainFrame::fileSaveAll(){
	for( int k=tabber.size()-1;k>=0;--k ){
		if( !save( k ) ) return;
	}
}

void MainFrame::filePrint(){
	if( Editor *e=getEditor() ) e->print();
}

void MainFrame::fileClose(){
	close( tabber.getCurrent() );
}

void MainFrame::fileCloseAll(){
	for( int k=tabber.size()-1;k>=0;--k ){
		if( !close( k ) ) return;
	}
}

void MainFrame::fileExit(){
	PostMessage( WM_CLOSE );
}

void MainFrame::fileRecent( UINT id ){
	id-=333;
	if( id<0 || id>=prefs.recentFiles.size() ) return;
	open( prefs.recentFiles[id] );
}

void MainFrame::editCut(){
	if( Editor *e=getEditor() ) e->cut();
}

void MainFrame::editCopy(){
	if( Editor *e=getEditor() ) e->copy();
}

void MainFrame::editPaste(){
	if( Editor *e=getEditor() ) e->paste();
}

void MainFrame::editSelectAll(){
	if( Editor *e=getEditor() ) e->selectAll();
}

void MainFrame::editFind(){
	if( Editor *e=getEditor() ) e->find();
}

void MainFrame::editFindNext(){
	if( Editor *e=getEditor() ) e->findNext( true );
}

void MainFrame::editReplace(){
	if( Editor *e=getEditor() ) e->replace();
}

static HANDLE startProc( const string &proc ){
	HANDLE rd,wr;

	SECURITY_ATTRIBUTES sa={sizeof(sa),0,true};

	if( CreatePipe( &rd,&wr,&sa,0 ) ){
		STARTUPINFO si={sizeof(si)};
		si.dwFlags=STARTF_USESTDHANDLES;
		si.hStdOutput=si.hStdError=wr;
		PROCESS_INFORMATION pi={0};
		if( CreateProcess( 0,(char*)proc.c_str(),0,0,true,DETACHED_PROCESS,0,0,&si,&pi ) ){
			CloseHandle( pi.hProcess );
			CloseHandle( pi.hThread );
			CloseHandle( wr );
			return rd;
		}
		CloseHandle( rd );
		CloseHandle( wr );
	}
	return 0;
}

class PDialog : public CDialog{
public:
	void OnOk(){}
	void OnCancel(){}
};

void MainFrame::compile( const string &cmd ){

	CDialog compiling;
	compiling.Create( IDD_COMPILING );

	CProgressCtrl *cp=(CProgressCtrl*)compiling.GetDlgItem( IDC_COMPILEPROGRESS );
	cp->SetStep(20);

	putenv( "blitzide=1" );

	HANDLE rd=startProc( cmd );

	if( !rd ){
		putenv( "blitzide" );
		compiling.DestroyWindow();
		AfxMessageBox( "Error launching compiler",MB_ICONWARNING|MB_OK );
		return;
	}

	string line,err;

	//OK....here we go!
	for(;;){
		char buff;
		unsigned long sz;
		int n=ReadFile( rd,&buff,1,&sz,0 );

		if( n && !sz ) break;	//EOF!
		if( !n && GetLastError()==ERROR_BROKEN_PIPE ) break;	//PROC END!
		if( !n ){ err="Internal Error";break; }

		if( buff=='\r' ) continue;
		if( buff!='\n' ){ line+=buff;continue; }
		if( !line.size() ) continue;

		if( line[0]=='\"' ){
			err=line;
			int n=line.find( "\"",1 );
			if( n==string::npos ) break;
			if( ++n==line.size() || line[n]!=':' ) break;
			string file=line.substr( 1,n-2 );line=line.substr(n+1);
			n=line.find( ':' );if( !n || n==string::npos ) break;
			int row1=atoi(line.substr(0,n));line=line.substr(n+1);
			n=line.find( ':' );if( !n || n==string::npos ) break;
			int col1=atoi(line.substr(0,n));line=line.substr(n+1);
			n=line.find( ':' );if( !n || n==string::npos ) break;
			int row2=atoi(line.substr(0,n));line=line.substr(n+1);
			n=line.find( ':' );if( !n || n==string::npos ) break;
			int col2=atoi(line.substr(0,n));line=line.substr(n+1);

			int pos=((row1-1)<<16)|(col1-1);

			if( !open( file ) ) return;
			if( Editor *e=getEditor() ) e->setCursor( pos );

			err=line;
			break;
		}else if( line.find( "..." )!=line.size()-3 ){
			err=line;break;
		}

		if( line.find( "Executing" )==0 ){
			//OK, we're running!
			break;
		}

		cp->StepIt();
		line="";
	}

	compiling.DestroyWindow();

	CloseHandle( rd );
	putenv( "blitzide" );

	if( !err.size() ) return;

	AfxMessageBox( err.c_str(),MB_ICONWARNING|MB_OK );
}

void MainFrame::build( bool exec,bool publish ){
	Editor *e=getEditor();
	if( !e ) return;

	string src_file=e->getName();

	for( int k=tabber.size()-1;k>=0;--k ){
		if( Editor *e=getEditor( k ) ){
			if( e->getModified() && e->getName().size() ){
				if( !save(k) ) return;
			}
		}
	}

	string opts=" ";

	if( prefs.prg_debug ) opts+="-d ";

	if( publish ){
		string exe=src_file;
		if( exe.size() ){
			int n=exe.find( '.' );
			if( n!=string::npos ) exe=exe.substr( 0,n );
		}else{
			exe="untitled";
		}

		static char *exeFilter="Executable files (*.exe)|*.exe||";
		int t=OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT;
		CFileDialog fd( false,"exe",exe.c_str(),t,exeFilter );
		fd.m_ofn.lpstrTitle="Select executable filename";
		fd.m_ofn.lpstrInitialDir="./";
		if( fd.DoModal()==IDCANCEL ) return;

		opts+="-o \""+string( fd.GetPathName() )+"\" ";
	}else if( !exec ){
		opts+="-c ";
	}

	string src=src_file;

	if( !src.size() ){
		src=prefs.homeDir+"\\tmp\\tmp.bb";
		int om=ios_base::binary|ios_base::out|ios_base::trunc;
		ofstream out( src.c_str(),om );
		if( !out.good() ){
			string e="Error writing file \""+src+"\"";
			AfxMessageBox( e.c_str(),MB_ICONWARNING );
			return;
		}
		e->getText( out );
		out.close();
		e->setName( src );
	}else{
		prefs.prg_lastbuild=e->getName();
	}

	compile( prefs.homeDir+"/bin/blitzcc -q "+opts+" \""+src+"\" "+prefs.cmd_line );

	if( !src_file.size() ) e->setName( "" );
}

void MainFrame::programExecute(){
	build( true,false );
}

void MainFrame::programReExecute(){
	if( prefs.prg_lastbuild.size() && open( prefs.prg_lastbuild ) ){
		build( true,false );
	}
}

void MainFrame::programCompile(){
	build( false,false );
}

void MainFrame::programPublish(){
#ifdef DEMO
	MessageBox( "Create Executable unavailable in demo version","Sorry!",MB_TOPMOST|MB_SETFOREGROUND|MB_ICONINFORMATION );
	return;
#endif
	Editor *e=getEditor();if( !e ) return;
	if( prefs.prg_debug ){
		string t=
		"You currently have the debugging feature enabled!\n\n"
		"This will result in slower executables - continue anyway?";
		if( MessageBox( t.c_str(),0,MB_OKCANCEL )==IDCANCEL ) return;
	}
	build( false,true );
}

struct CmdLineDialog : public CDialog{
	CmdLineDialog():CDialog( IDD_COMMANDLINE ){
	}

	BOOL OnInitDialog(){
		CDialog::OnInitDialog();
		SetDlgItemText( IDC_CMDLINE,prefs.cmd_line.c_str() );
		return TRUE;
	}

	void OnOK(){
		CString c_str;
		GetDlgItemText( IDC_CMDLINE,c_str );
		prefs.cmd_line=c_str;
		CDialog::OnOK();
	}
};

void MainFrame::programCommandLine(){
	CmdLineDialog d;
	d.DoModal();
}

void MainFrame::programDebug(){
	prefs.prg_debug=!prefs.prg_debug;
}

void MainFrame::helpHome(){
	HtmlHelp *h=findHelp();
	string t;
	t="index.html";
	h->Navigate( (prefs.homeDir+"/help/"+t).c_str() );
}

void MainFrame::helpAutodoc(){
	HtmlHelp *h=findHelp();
	h->Navigate( (prefs.homeDir+"/help/autodoc.html").c_str() );
}

void MainFrame::helpBack(){
	if( HtmlHelp *h=findHelp() ) h->GoBack();
}

void MainFrame::helpForward(){
	if( HtmlHelp *h=findHelp() ) h->GoForward();
}

void MainFrame::helpAbout(){
	aboutBlitz( false );
}

void MainFrame::ctrlTab(){
	int n=tabber.getCurrent()+1;
	if( n>=tabber.size() ) n-=tabber.size();
	tabber.setCurrent( n );
}

void MainFrame::ctrlShiftTab(){
	int n=tabber.getCurrent()-1;
	if( n<0 ) n+=tabber.size();
	tabber.setCurrent( n );
}

void MainFrame::escape(){
	if( !prefs.win_notoolbar ){
		toolBar.ShowWindow( SW_HIDE );
		statusBar.ShowWindow( SW_HIDE );
		prefs.win_notoolbar=true;
	}else{
		toolBar.ShowWindow( SW_SHOW );
		statusBar.ShowWindow( SW_SHOW );
		prefs.win_notoolbar=false;
	}
	RECT r;GetClientRect( &r );
	int w=r.right-r.left,h=r.bottom-r.top;
	int n=prefs.win_maximized ? SIZE_MAXIMIZED : SIZE_RESTORED;
	PostMessage( WM_SIZE,n,(h<<16)|w );
}

void MainFrame::updateCmdUIRange( CCmdUI *ui ){
	int n=ui->m_nID-333;
	if( n>=0 && n<prefs.recentFiles.size() ){
		ui->Enable( true );
	}else{
		ui->Enable( false );
	}
}

void MainFrame::updateCmdUI( CCmdUI *ui ){
	int k;

	Editor *e=getEditor();

	switch( ui->m_nID ){
	case Inew:case ID_OPEN:case ID_HOME:
		ui->Enable( true );
		break;
	case ID_DEBUG:
		ui->SetCheck( prefs.prg_debug );ui->Enable( true );
		break;
	case ID_CLOSE:case ID_PRINT:
		ui->Enable( !!e );
		break;
	case ID_CLOSEALL:
		for( k=0;k<tabber.size() && !getEditor(k);++k ){}
		ui->Enable( k<tabber.size() );
		break;
	case ID_CUT:case ID_COPY:
		if( !!e ){
			ui->Enable( e->canCutCopy() );
		}else ui->Enable( false );
		break;
	case ID_PASTE:
		if( !!e ){
			ui->Enable( e->canPaste() );
		}else ui->Enable( false );
		break;
	case ID_SELECTALL:case ID_QUICKHELP:
	case ID_FIND:case ID_FINDNEXT:case ID_REPLACE:
	case ID_EXECUTE:case ID_COMPILE:case ID_PUBLISH:
		ui->Enable( !!e );
		break;
	case ID_REEXECUTE:
		ui->Enable( prefs.prg_lastbuild.size() );
		break;
	case ID_COMMANDLINE:
		ui->Enable( true );
		break;
	case ID_SAVE:case ID_SAVEAS:
		ui->Enable( !!e );
		break;
	case ID_SAVEALL:
		for( k=0;k<tabber.size() && !getEditor(k);++k ){}
		ui->Enable( k<tabber.size() );
		break;
	case ID_BACK:case ID_FORWARD:
		ui->Enable( getHelp()!=0 );
		break;
	case ID_ESCAPE:
		ui->SetCheck( !prefs.win_notoolbar );
		break;
	default:
		ui->Enable( false );
	}
}

static string commandURL( const string &t ){

	static char *dirs[]={
		"help\\commands\\2d_commands\\",
		"help\\commands\\3d_commands\\",
		0
	};

	char **dir_p=dirs;

	while( char *dir=*dir_p++ ){
		WIN32_FIND_DATA fd;
		string path=prefs.homeDir+"/"+dir+t+".htm";
		HANDLE h=FindFirstFile( path.c_str(),&fd );
		if( h==INVALID_HANDLE_VALUE ) continue;
		FindClose( h );
		return path;
	}
	return "";
}

void MainFrame::quick_Help(){
	if( Editor *e=getEditor() ){
		//look for keyword at cursor...
		string t=e->getKeyword();if( !t.size() ) return;
		statusBar.SetPaneText( 0,quickHelp( t ).c_str() );
		if( t!=last_quick_help ){
			last_quick_help=t;
			return;
		}
		string url=commandURL( t );
		if( !url.size() ){
			string ex="Unable to open help file for \""+t+"\"";
			AfxMessageBox( ex.c_str(),MB_ICONWARNING );
			return;
		}
		if( HtmlHelp *h=findHelp() ){
			h->Navigate( url.c_str(),0,0 );
		}
	}
}

void MainFrame::OnActivate( UINT state,CWnd *other,BOOL min ){
	CFrameWnd::OnActivate( state,other,min );

	if( Editor *e=getEditor() ){
		if( state!=WA_ACTIVE && state!=WA_CLICKACTIVE ) return;
		e->SetFocus();
	}
}
