From 71686155e3f721fad2d054843b179d1ecfc8fed4 Mon Sep 17 00:00:00 2001 From: blitz-research Date: Thu, 23 Jan 2014 11:34:04 +1300 Subject: [PATCH] Added src. --- src/bmk/bmk.bmx | 317 ++ src/bmk/bmk_bank.bmx | 122 + src/bmk/bmk_bb2bmx.bmx | 758 ++++ src/bmk/bmk_config.bmx | 159 + src/bmk/bmk_make.bmx | 395 ++ src/bmk/bmk_modinfo.bmx | 60 + src/bmk/bmk_modutil.bmx | 187 + src/bmk/bmk_util.bmx | 359 ++ src/bmk/bmk_zap.bmx | 138 + src/bmk/macos.icns | Bin 0 -> 51417 bytes src/docmods/bmxtoker.bmx | 400 ++ src/docmods/dochistory.bmx | 180 + src/docmods/docmods.bmx | 157 + src/docmods/docparser.bmx | 683 +++ src/fasm2as/fasm2as.bmx | 245 ++ src/makedocs/bbdoc.bmx | 202 + src/makedocs/docnode.bmx | 69 + src/makedocs/docstyle.bmx | 191 + src/makedocs/fredborgstyle.bmx | 128 + src/makedocs/makedocs.bmx | 286 ++ src/makedocs/parse.bmx | 61 + src/makedocs/readme.txt | 144 + src/maxide/Info.plist | 27 + src/maxide/bmxlogo.png | Bin 0 -> 9126 bytes src/maxide/default.language.ini | 307 ++ src/maxide/makeicons/Back.png | Bin 0 -> 533 bytes src/maxide/makeicons/Build-Run.png | Bin 0 -> 795 bytes src/maxide/makeicons/Build.png | Bin 0 -> 630 bytes src/maxide/makeicons/Close.png | Bin 0 -> 827 bytes src/maxide/makeicons/Copy.png | Bin 0 -> 1284 bytes src/maxide/makeicons/Cut.png | Bin 0 -> 382 bytes src/maxide/makeicons/Find.png | Bin 0 -> 461 bytes src/maxide/makeicons/Forward.png | Bin 0 -> 546 bytes src/maxide/makeicons/Go.png | Bin 0 -> 373 bytes src/maxide/makeicons/Home.png | Bin 0 -> 583 bytes src/maxide/makeicons/New.png | Bin 0 -> 898 bytes src/maxide/makeicons/Open.png | Bin 0 -> 1415 bytes src/maxide/makeicons/Paste.png | Bin 0 -> 1299 bytes src/maxide/makeicons/Save.png | Bin 0 -> 1181 bytes src/maxide/makeicons/Step-In.png | Bin 0 -> 208 bytes src/maxide/makeicons/Step-Out.png | Bin 0 -> 215 bytes src/maxide/makeicons/Step.png | Bin 0 -> 194 bytes src/maxide/makeicons/Stop.png | Bin 0 -> 374 bytes src/maxide/makeicons/makeicons.bmx | 62 + src/maxide/maxicons.o | Bin 0 -> 5612 bytes src/maxide/maxide.bmx | 6560 ++++++++++++++++++++++++++++ src/maxide/readme.txt | 6 + src/maxide/splash.png | Bin 0 -> 68171 bytes src/maxide/toolbar.png | Bin 0 -> 7623 bytes src/maxide/window_icon.png | Bin 0 -> 3031 bytes 50 files changed, 12203 insertions(+) create mode 100644 src/bmk/bmk.bmx create mode 100644 src/bmk/bmk_bank.bmx create mode 100644 src/bmk/bmk_bb2bmx.bmx create mode 100644 src/bmk/bmk_config.bmx create mode 100644 src/bmk/bmk_make.bmx create mode 100644 src/bmk/bmk_modinfo.bmx create mode 100644 src/bmk/bmk_modutil.bmx create mode 100644 src/bmk/bmk_util.bmx create mode 100644 src/bmk/bmk_zap.bmx create mode 100644 src/bmk/macos.icns create mode 100644 src/docmods/bmxtoker.bmx create mode 100644 src/docmods/dochistory.bmx create mode 100644 src/docmods/docmods.bmx create mode 100644 src/docmods/docparser.bmx create mode 100644 src/fasm2as/fasm2as.bmx create mode 100644 src/makedocs/bbdoc.bmx create mode 100644 src/makedocs/docnode.bmx create mode 100644 src/makedocs/docstyle.bmx create mode 100644 src/makedocs/fredborgstyle.bmx create mode 100644 src/makedocs/makedocs.bmx create mode 100644 src/makedocs/parse.bmx create mode 100644 src/makedocs/readme.txt create mode 100644 src/maxide/Info.plist create mode 100644 src/maxide/bmxlogo.png create mode 100644 src/maxide/default.language.ini create mode 100644 src/maxide/makeicons/Back.png create mode 100644 src/maxide/makeicons/Build-Run.png create mode 100644 src/maxide/makeicons/Build.png create mode 100644 src/maxide/makeicons/Close.png create mode 100644 src/maxide/makeicons/Copy.png create mode 100644 src/maxide/makeicons/Cut.png create mode 100644 src/maxide/makeicons/Find.png create mode 100644 src/maxide/makeicons/Forward.png create mode 100644 src/maxide/makeicons/Go.png create mode 100644 src/maxide/makeicons/Home.png create mode 100644 src/maxide/makeicons/New.png create mode 100644 src/maxide/makeicons/Open.png create mode 100644 src/maxide/makeicons/Paste.png create mode 100644 src/maxide/makeicons/Save.png create mode 100644 src/maxide/makeicons/Step-In.png create mode 100644 src/maxide/makeicons/Step-Out.png create mode 100644 src/maxide/makeicons/Step.png create mode 100644 src/maxide/makeicons/Stop.png create mode 100644 src/maxide/makeicons/makeicons.bmx create mode 100644 src/maxide/maxicons.o create mode 100644 src/maxide/maxide.bmx create mode 100644 src/maxide/readme.txt create mode 100644 src/maxide/splash.png create mode 100644 src/maxide/toolbar.png create mode 100644 src/maxide/window_icon.png diff --git a/src/bmk/bmk.bmx b/src/bmk/bmk.bmx new file mode 100644 index 0000000..e0ab2e8 --- /dev/null +++ b/src/bmk/bmk.bmx @@ -0,0 +1,317 @@ +' +' Change History : +' BaH 28/09/2007 - Added custom appstub compiles using -b parameter. +' Synched with current bmk source. +' +Strict + +Framework brl.basic + +Import "bmk_make.bmx" +Import "bmk_zap.bmx" +Import "bmk_bb2bmx.bmx" + +?MacOS +Incbin "macos.icns" +? + +If AppArgs.length<2 CmdError + +Local cmd$=AppArgs[1],args$[] + +args=ParseConfigArgs( AppArgs[2..] ) + +CreateDir BlitzMaxPath()+"/tmp" + +Select cmd.ToLower() +Case "makeapp" + SetConfigMung + MakeApplication args,False +Case "makelib" + SetConfigMung + MakeApplication args,True +Case "makemods" +' Local ms=MilliSecs() + If opt_debug Or opt_release + SetConfigMung + MakeModules args + Else + opt_debug=True + opt_release=False + SetConfigMung + MakeModules args + opt_debug=False + opt_release=True + SetConfigMung + MakeModules args + EndIf +' ms=MilliSecs()-ms +' Print "ms="+ms +Case "cleanmods" + CleanModules args +Case "zapmod" + ZapModule args +Case "unzapmod" + UnzapModule args +Case "listmods" + ListModules args +Case "modstatus" + ModuleStatus args +Case "syncmods" + SyncModules args +Case "convertbb" + ConvertBB args +Case "ranlibdir" + RanlibDir args +Default + CmdError +End Select + +Function SetConfigMung() + If opt_release + opt_debug=False + opt_configmung="release" + If opt_threaded opt_configmung:+".mt" + opt_configmung="."+opt_configmung+"."+cfg_platform+"."+opt_arch + Else + opt_debug=True + opt_release=False + opt_configmung="debug" + If opt_threaded opt_configmung:+".mt" + opt_configmung="."+opt_configmung+"."+cfg_platform+"."+opt_arch + EndIf +End Function + +Function SetModfilter( t$ ) + + opt_modfilter=t.ToLower() + + If opt_modfilter="*" + opt_modfilter="" + Else If opt_modfilter[opt_modfilter.length-1]<>"." + opt_modfilter:+"." + EndIf + +End Function + +Function MakeModules( args$[] ) + + If args.length>1 CmdError + + If args.length SetModfilter args[0] Else opt_modfilter="" + + Local mods:TList=EnumModules() + + BeginMake + + MakeMod "brl.blitz" + + For Local name$=EachIn mods + MakeMod name + Next + +End Function + +Function CleanModules( args$[] ) + + If args.length>1 CmdError + + If args.length SetModfilter args[0] Else opt_modfilter="" + + Local mods:TList=EnumModules() + + Local name$ + For name=EachIn mods + + If (name+".").Find(opt_modfilter)<>0 Continue + + Print "Cleaning:"+name + + Local path$=ModulePath(name) + + DeleteDir path+"/.bmx",True + + If Not opt_kill Continue + + For Local f$=EachIn LoadDir( path ) + + Local p$=path+"/"+f + Select FileType(p) + Case FILETYPE_DIR + If f<>"doc" + DeleteDir p,True + EndIf + Case FILETYPE_FILE + Select ExtractExt(f).tolower() + Case "i","a","txt","htm","html" + 'nop + Default + DeleteFile p + End Select + End Select + + Next + Next + +End Function + +Function MakeApplication( args$[],makelib ) + If opt_execute + If Len(args)=0 CmdError + Else + If Len(args)<>1 CmdError + EndIf + + Local Main$=RealPath( args[0] ) + + Select ExtractExt(Main).ToLower() + Case "" + Main:+".bmx" + Case "c","cpp","cxx","mm","bmx" + Default + Throw "Unrecognized app source file type:"+ExtractExt(Main) + End Select + + If FileType(Main)<>FILETYPE_FILE Throw "Unable to open source file '"+Main+"'" + + If Not opt_outfile opt_outfile=StripExt( Main ) + +?Win32 + If makelib + If ExtractExt(opt_outfile).ToLower()<>"dll" opt_outfile:+".dll" + Else + If ExtractExt(opt_outfile).ToLower()<>"exe" opt_outfile:+".exe" + EndIf +? + +?MacOS + If opt_apptype="gui" + + Local appId$=StripDir( opt_outfile ) + + Local exeDir$=opt_outfile+".app",d$,t:TStream + + d=exeDir+"/Contents/MacOS" + Select FileType( d ) + Case FILETYPE_NONE + CreateDir d,True + If FileType( d )<>FILETYPE_DIR + Throw "Unable to create application directory" + EndIf + Case FILETYPE_FILE + Throw "Unable to create application directory" + Case FILETYPE_DIR + End Select + + d=exeDir+"/Contents/Resources" + Select FileType( d ) + Case FILETYPE_NONE + CreateDir d + If FileType( d )<>FILETYPE_DIR + Throw "Unable to create resources directory" + EndIf + Case FILETYPE_FILE + Throw "Unable to create resources directory" + Case FILETYPE_DIR + End Select + + t=WriteStream( exeDir+"/Contents/Info.plist" ) + If Not t Throw "Unable to create Info.plist" + t.WriteLine "" + t.WriteLine "" + t.WriteLine "" + t.WriteLine "" + t.WriteLine "~tCFBundleExecutable" + t.WriteLine "~t"+appId+"" + t.WriteLine "~tCFBundleIconFile" + t.WriteLine "~t"+appId+"" + t.WriteLine "~tCFBundlePackageType" + t.WriteLine "~tAPPL" + t.WriteLine "" + t.WriteLine "" + t.Close + + t=WriteStream( exeDir+"/Contents/Resources/"+appId+".icns" ) + If Not t Throw "Unable to create icons" + Local in:TStream=ReadStream( "incbin::macos.icns" ) + CopyStream in,t + in.Close + t.Close + + opt_outfile=exeDir+"/Contents/MacOS/"+appId + + EndIf +? + BeginMake + + MakeApp Main,makelib + + If opt_execute + + Print "Executing:"+StripDir( opt_outfile ) + + Local cmd$=CQuote( opt_outfile ) + For Local i=1 Until args.length + cmd:+" "+CQuote( args[i] ) + Next + + Sys cmd + + EndIf + +End Function + +Function ZapModule( args$[] ) + If Len(args)<>2 CmdError + + Local modname$=args[0].ToLower() + Local outfile$=RealPath( args[1] ) + + Local stream:TStream=WriteStream( outfile ) + If Not stream Throw "Unable to open output file" + + ZapMod modname,stream + + stream.Close +End Function + +Function UnzapModule( args$[] ) + If Len(args)<>1 CmdError + + Local infile$=args[0] + + Local stream:TStream=ReadStream( infile ) + If Not stream Throw "Unable to open input file" + + UnzapMod stream + + stream.Close +End Function + +Function ListModules( args$[],modid$="" ) + If Len(args)<>0 CmdError + + Throw "Todo!" + +End Function + +Function ModuleStatus( args$[] ) + If Len(args)<>1 CmdError + + ListModules Null,args[0] + +End Function + +Function SyncModules( args$[] ) + If args.length CmdError + + If Sys( BlitzMaxPath()+"/bin/syncmods" ) Throw "SyncMods error" + +End Function + +Function RanlibDir( args$[] ) + If args.length<>1 CmdError + + Ranlib args[0] + +End Function diff --git a/src/bmk/bmk_bank.bmx b/src/bmk/bmk_bank.bmx new file mode 100644 index 0000000..917b66e --- /dev/null +++ b/src/bmk/bmk_bank.bmx @@ -0,0 +1,122 @@ + +Strict + +Import "bmk_config.bmx" + +Import Pub.ZLib + +Function CompressBank:TBank( bank:TBank ) + + Local size=bank.Size() + Local out_size=size+size/10+32 + Local out:TBank=TBank.Create( out_size ) + compress out.Buf()+4,out_size,bank.Buf(),size + out.PokeByte 0,size + out.PokeByte 1,size Shr 8 + out.PokeByte 2,size Shr 16 + out.PokeByte 3,size Shr 24 + out.Resize out_size+4 + Return out + +End Function + +Function UncompressBank:TBank( bank:TBank ) + + Local out_size + out_size:|bank.PeekByte(0) + out_size:|bank.PeekByte(1) Shl 8 + out_size:|bank.PeekByte(2) Shl 16 + out_size:|bank.PeekByte(3) Shl 24 + Local out:TBank=TBank.Create( out_size ) + uncompress out.Buf(),out_size,bank.Buf()+4,bank.Size()-4 + Return out + +End Function + +Function SplitUrl( url$,server$ Var,file$ Var ) + Local i=url.Find( "/",0 ) + If i<>-1 + server=url[..i] + file=url[i..] + Else + server=url + file="/" + EndIf +End Function + +Function HTTPGetBank:TBank( url$ ) + + Local server$,file$ + SplitUrl url,server,file + + Local t_server$=server + If opt_proxy t_server=opt_proxy + + Local t_port=80 + If opt_proxyport t_port=opt_proxyport + + Local stream:TStream=TSocketStream.CreateClient( t_server,t_port ) + If Not stream Return + + stream.WriteLine "GET http://"+url+" HTTP/1.0" + stream.WriteLine "Host: "+server + stream.WriteLine "" + + While Not stream.Eof() + Local t$=stream.ReadLine() + If Not t Exit + Wend + + Local bank:TBank=TBank.Create(0) + Local bank_stream:TStream=TBankStream.Create( bank ) + + CopyStream stream,bank_stream + + bank_stream.Close + stream.Close + + Return bank + +End Function + +Function HTTPPostBank$( bank:TBank,url$ ) + + Local server$,file$ + SplitUrl url,server,file + + Local t_server$=server + If opt_proxy t_server=opt_proxy + + Local t_port=80 + If opt_proxyport t_port=opt_proxyport + + Local stream:TStream=TSocketStream.CreateClient( t_server,t_port ) + If Not stream Return + + stream.WriteLine "POST http://"+url+" HTTP/1.0" + stream.WriteLine "Host: "+server + stream.WriteLine "Content-Type: application/octet-stream" + stream.WriteLine "Content-Length: "+bank.Size() + stream.WriteLine "" + + Local bank_stream:TStream=TBankStream.Create( bank ) + CopyStream bank_stream,stream + bank_stream.Close + + While Not stream.Eof() + Local t$=stream.ReadLine() + If Not t Exit + Wend + + Local r$ + While Not stream.Eof() + Local t$=stream.ReadLine() + r:+t+"~n" + Wend + + stream.Close + + Return r + +End Function + diff --git a/src/bmk/bmk_bb2bmx.bmx b/src/bmk/bmk_bb2bmx.bmx new file mode 100644 index 0000000..daf57ff --- /dev/null +++ b/src/bmk/bmk_bb2bmx.bmx @@ -0,0 +1,758 @@ + +Strict + +' bmk_bb2bmx.bmx v0.4 +' by simonarmstrong@blitzbasic.com + +' converts BlitzBasic source files to BlitzMax + +' history + +' AND OR operators -> bitwise if not followed by conditional operators =<> +' Min and Max -> _Min and _Max +' HandleFromObject and HandleToObject added to bbtype.bmx +' link and list fields in bbtype -> _link and _list +' IncrementArrays$(code$) adds 1 to all array dimensions of array declarations +' ReadString(TStream) function added and FreeSound(sound)->Release(sound) +' hidepointer -> hidemouse showpointer -> showmouse +' delete obj -> obj.remove +' readpixel->dev.blitz3d.readpixel writepixel->dev.blitz3d.writepixel +' seekfile -> seekstream filepos -> streampos +' channelvolume->SetChannelVolume soundvolume-> +' readbytes -> ReadBank writebytes-> WriteBankq +' xor->~ getkey->waitkey mousewait->waitmouse +' handle -> HandleFromObject object -> HandleToObject +' stop -> DebugStop +' .label -> #label +' param[n]->param[] in function declarations +' added TypePrefix$ to help avoid type name / variable name clashes +' processes include files +' output bbtype.bmx and bbvkey.bmx files (thanks to Terabit) +' moved to bmk_bb2bmx +' inline comments replace ; with ' +' command separators replace : with ; +' type specifiers replace . with : +' field separators replace \ with . +' boolean operators replace and or with & | +' array declarations replace Dim x(100) with Global x[100+1] +' graphics commands Line,Rect,Oval,Text -> DrawLine,DrawRect,DrawOval,DrawText +' implement old style Type handling for Each First Last Before After Insert +' Str becomes String +' Delete Each list -> list.Clear() +' Delete -> Release +' Data->DefData Read->ReadData +' KeyDown->VKeyDown KeyHit->VKeyHit +' native Color and ClsColor commands added to header + +' not supported / stubbed out + +' no float->boolean so error is "bitwise operators can only be used with integers" +' MouseZSpeed(), FreeBank(bank), Locate( x,y ) +' LoopSound(sound), ChannelPitch(channel,hz),PlayCDTrack( track,mode=0 ) + +Import brl.retro + +Const TAB$=Chr(9) + +Global TypePrefix$="bb" + +Function main() + Local AppArgs$[]=["bb2bmx","bob.bb"] + Local srcfile$,destfile$ + Local n=Len AppArgs + If n<2 + Print "bb2bmx srcfile.bb [destfile.bmx]" + End + EndIf + srcfile$=AppArgs[1] + destfile$=Replace$(srcfile,".bb",".bmx") + If n>2 destfile$=AppArgs[2] + bb2bmx(srcfile,destfile) +End Function + +Function ConvertBB(args$[]) + Local srcfile$,destfile$ + If Len args<1 Throw "convertbb option requires a filename" + srcfile$=args[0] + If Len args>1 destfile=args[1] + bb2bmx srcfile,destfile +End Function + +Global Includes:TList +Global Complete:TList +Global TypeLists:TList + +Function bb2bmx(srcfile$,destfile$) + Local s$,currdir$,inc$,done$ + Local destinclude$ + Local src:TList,isrc:TList + Local dest:TList,idest:TList + Local incs:TList + + Includes=New TList + TypeLists=New TList + Complete=New TList + + currdir=CurrentDir() + + src=ReadTextFile(srcfile) + If Not src Throw "bb2bmx failed to open "+srcfile + + If Not destfile destfile$=Replace$(srcfile,".bb",".bmx") + ChangeDir ExtractDir(srcfile) + + WriteVKeyFile + WriteBBTypeFile + + dest=New TList + + Print "converting "+srcfile + ConvertSourceList(src,dest) + +' process includes + + While Not Includes.IsEmpty() + incs=Includes + Includes=New TList + For srcfile=EachIn incs + +' check if include already converted + + For done=EachIn complete + If done=srcfile Exit + Next + If done=srcfile Continue + + complete.AddLast srcfile + isrc=ReadTextFile(srcfile) + If Not isrc Throw "bb2bmx failed to open included file "+srcfile + ChangeDir ExtractDir(srcfile) + + idest=New TList + Print "converting "+srcfile + + ConvertSourceList(isrc,idest) + destinclude$=Replace$(srcfile,".bb",".bmx") + WriteTextFile(destinclude,idest) + Next + Wend + + ChangeDir(currdir) + + For s$=EachIn TypeLists + dest.addfirst s$ + Next + + dest.addfirst "" + dest.addfirst "Import "+Chr(34)+"bbvkey.bmx"+Chr(34) + dest.addfirst "Import "+Chr(34)+"bbtype.bmx"+Chr(34) + + WriteTextFile(destfile,dest) + +End Function + +Function ProcessInclude$(s$) + Local path$,inc$ + Local q,r + + q=Instr(s,Chr(34)) + If q=0 Return s$ + r=Instr(s,Chr(34),q+1) + If r=0 Return s$ + path$=s[q..r-1] + + path=RealPath(path$) + For inc=EachIn Includes + If inc=path Exit + Next + If inc<>path + Includes.AddLast(path) + EndIf + + Return Replace$(s$,".bb",".bmx") +End Function + +Function FindToken(s$,t$,p) + Local l$,c + t=Lower(t) + l=Lower(s) + Repeat + p=Instr(l,t,p+1) + If Not p Exit + If p>1 + c=l[p-2] + If c>47 And c<58 Continue '0-9 + If c>95 And c<122 Continue 'a-z + If c=Asc("_") Continue + EndIf + If p+Len(t)-147 And c<58 Continue '0-9 + If c>95 And c<122 Continue 'a-z + If c=Asc("_") Continue + EndIf + Return p + Forever +End Function + +Function ReplaceToken$(s$,t$,r$) + Local l$,p,c + Repeat + p=FindToken(s,t,p) + If Not p Exit + s=s[..p-1]+r$+s[p+Len(t)-1..] + p:+Len r$ + Forever + Return s +End Function + +Function FindEOC(s$,p) 'return position of next space or command separator + Local q,r + q=Instr(s," ",p) + If q=0 q=Len s+1 + r=Instr(s,";",p) + If r And r",p) + If r And r = convert to bool operator & | + Local p,q,r,t + + While True + t=FindToken(s$,"And",p) + If t=0 Exit + q=Len(s$) + r=FindToken(s$,"Then",t) + If r And r",t);If r And r<=q p=q;Continue + s$=s[..t-1]+"&"+s[t+2..] + Wend + p=0 + While True + t=FindToken(s$,"Or",p) + If t=0 Exit + q=Len(s$) + r=FindToken(s$,"Then",t) + If r And r",t);If r And r<=q p=q;Continue + s$=s[..t-1]+"|"+s[t+1..] + Wend + Return s$ +End Function + +Function Convert$(s$) 'substring of source line before rem and between any string literals + Local p,q,r,c,n$ +' replace : command separator + s$=Replace(s$,":",";") +'.label->#label + p=Instr(Trim(s),".") + If p=1 + p=Instr(s,".") + s="#"+s[p..] + EndIf +' replace . type specifier + p=1 + Repeat + p=Instr(s,".",p+1) + If Not p Exit + c=s[p] + If c>47 And c<58 Continue 'ignore if decimal point + s=s[..p-1]+":"+TypePrefix$+s[p..] + Forever +' replace \ field separator + s$=Replace(s$,"\",".") +' update BASIC API name changes + s$=ReplaceToken(s,"freesound","Release") + s$=ReplaceToken(s,"channelpan","SetChannelPan") + s$=ReplaceToken(s,"filepos","StreamPos") + s$=ReplaceToken(s,"seekfile","SeekStream") + s$=ReplaceToken(s,"channelvolume","SetChannelVolume") + s$=ReplaceToken(s,"readbytes","ReadBank") + s$=ReplaceToken(s,"writebytes","WriteBank") + s$=ReplaceToken(s,"mousewait","WaitMouse") +' hidepointer -> hidemouse showpointer -> showmouse + s$=ReplaceToken(s,"hidepointer","HideMouse") + s$=ReplaceToken(s,"showpointer","ShowMouse") +' replace boolean operators + s$=ReplaceToken(s,"xor","~~") + s$=ReplaceToken(s,"stop","DebugStop") + +Rem + s$=ReplaceToken(s,"and","&") + s$=ReplaceToken(s,"or","|") + s$=ReplaceToken(s,"chr$","chr") + + s$=ReplaceToken(s,"line","DrawLine") + s$=ReplaceToken(s,"rect","DrawRect") + s$=ReplaceToken(s,"oval","DrawOval") + s$=ReplaceToken(s,"text","DrawText") + s$=ReplaceToken(s,"color","SetColor") + s$=ReplaceToken(s,"clscolor","SetCLSColor") +EndRem + s$=ReplaceToken(s,"str","String") + s$=ReplaceToken(s,"read","ReadData") + s$=ReplaceToken(s,"data","DefData") + s$=ReplaceToken(s,"restore","RestoreData") + s$=ReplaceToken(s,"keydown","VKeyDown") + s$=ReplaceToken(s,"keyhit","VKeyHit") + s$=ReplaceToken(s,"getkey","WaitKey") + s$=ReplaceToken(s,"readpixel","dev.blitz3d.ReadPixel") 'ReadPixelBuffer") + s$=ReplaceToken(s,"writepixel","dev.blitz3d.WritePixel") + s$=ReplaceToken(s,"graphicswidth","dev.blitz3d.GraphicsWidth") + s$=ReplaceToken(s,"graphicsheight","dev.blitz3d.GraphicsHeight") + + s$=ReplaceToken(s,"min","fmin") + s$=ReplaceToken(s,"max","fmax") + +' delete obj -> obj.remove + p=0 + Repeat + p=FindToken(s,"delete",p) + If Not p Exit + If Lower(s[p+5..p+10])=" each" s=s[..p+5]+"Each "+TypePrefix$+Trim(s[p+10]);Continue + If Lower(s[p+5..p+11])=" first" s=s[..p+5]+"First "+TypePrefix$+Trim(s[p+11]);Continue + If Lower(s[p+5..p+10])=" last" s=s[..p+5]+"Last "+TypePrefix$+Trim(s[p+10]);Continue + q=Instr(s," ",p+7) + If q=0 q=Len s+1 + r=Instr(s,";",p+7) + If r And r HandleFromObject(obj) object.blah(handle) -> HandleToObject(handle) + + s$=ReplaceToken(s,"handle","HandleFromObject") + p=0 + Repeat + p=FindToken(s,"object",p) + If p=0 Exit + q=Instr(s,":",p) + If q=0 Exit + r=Instr(s,"(",q) + If r=0 Exit + n$=s[q..r-1] + c=1 + q=r + While c + If r>Len(s) Exit + If s[r]="(" c:+1 + If s[r]=")" c:-1 + r:+1 + Wend + s=s[..p-1]+n$+"(HandleToObject"+s[q-1..r-1]+")"+s[r..] + p=p+Len(n)+16 + Forever + +' replace New Type with New prefix_type + p=0 + Repeat + p=FindToken(s,"New",p) + If Not p Exit + s=s[..p+3]+TypePrefix$+s[p+3..] + p=p+1 + Forever +' replace Dim var(size) with Global var[size] + p=0 + Repeat + p=FindToken(s,"Dim",p) + If Not p Exit + s=s[..p-1]+"Global"+s[p+2..] + Repeat + p=Instr(s,"(",p) + If Not p Exit + s=s[..p-1]+"["+s[p..] + p=Instr(s,")",p) + If Not p Exit + s=s[..p-1]+"]"+s[p..] 'was +1 + Forever + s=IncrementArrays(s$) + Exit 'no multiple dim calls pre +' if not p exit + Forever +' replace function(param[4]) with function(param[]) + p=0 + Repeat + p=FindToken(s,"Function",p) + If Not p Exit + p=Instr(s,"(",p) + If Not p Exit + q=Instr(s,")",p) + If Not q Exit + n$=StripDims(s[p..q-1]) + s=s[..p]+n+s[q-1..] + Exit + Forever +' for b=each bob -> for b=eachin bob.list + p=0 + Repeat + p=FindToken(s,"Each",p) + If Not p Exit + q=FindEOC(s,p+6) + s=s[..p-1]+"EachIn "+Trim(s[p+4..q-1])+"_list"+s[q-1..] + p=q + Forever +' First class + p=0 + Repeat + p=FindToken(s,"First",p) + If Not p Exit + q=FindEOC(s,p+7) + n$=Trim(s[p+5..q-1]) + s=s[..p-1]+TypePrefix+n+"("+n+"_list.First())"+s[q-1..] + p=q+Len(n)*2+14 + Forever +' Last class + p=0 + Repeat + p=FindToken(s,"Last",p) + If Not p Exit + q=FindEOC(s,p+6) + n$=Trim(s[p+4..q-1]) + s=s[..p-1]+TypePrefix+n+"("+n+"_list.Last())"+s[q-1..] + p=q+Len(n)*2+13 + Forever +' Insert b Before|After bob -> b.InsertAfter(bob) + p=0 + Repeat + p=FindToken(s,"Insert",p) + If Not p Exit + q=Instr(s," ",p+7) + If Not q Exit + n$=Lower(s[q..]) + If n$[..6]="before" + n$="Before" + r=q+7 + ElseIf n$[..5]="after" + n$="After" + r=q+6 + Else + Exit + EndIf + c=FindEOC(s,r+1) + s=s[..p-1]+s[p+6..q-1]+".Insert"+n$+"("+s[r..c-1]+")"+s[c..] 'n$+ simon was here + p=c + Forever +' After b -> b.After() + p=0 + Repeat + p=FindToken(s,"After",p) + If Not p Exit + q=Instr(s," ",p+7) + If q=0 q=Len s+1 + r=Instr(s,";",p+7) + If r And r b.Before() + p=0 + Repeat + p=FindToken(s,"Before",p) + If Not p Exit + q=Instr(s," ",p+8) + If q=0 q=Len s+1 + r=Instr(s,";",p+8) + If r And r1 p=Instr(l$,"end rem") + If p=1 + inrem=0 + EndIf + Else +' parse strings and remarks + out$="" + in$=fixquotes(in$) + While in$ + q=Instr(in$,Chr(34)) + r=Instr(in$,";") + inrem=Instr(l$,"rem ") + If inrem And inrem Include "blah.bmx" + + If l$[..8]="include " Or l$[..8]="include"+Chr$(34) + out=ProcessInclude(out) + EndIf + + dest.AddLast out + + Next +End Function + +Function ReadTextFile:TList(file$) + Local f:TStream,n + Local txt:TList + txt=New TList + f=ReadStream(file) + If Not f Return Null + While Not Eof(f) + txt.AddLast ReadLine(f) + n:+1 + Wend + Return txt +End Function + +Function WriteTextFile(file$,txt:TList) + Local f:TStream,l$ + f=WriteStream(file) + If Not f Return + For l$=EachIn txt + WriteLine f,l + Next + CloseStream f +End Function + +Function WriteVKeyFile() + Local dest:TStream=WriteFile("bbvkey.bmx") + If Not dest Return + WriteLine dest,"' virtual key support for legacy Blitz apps" + WriteLine dest,"" + WriteLine dest,"Global VKEY[]=[.." + WriteLine dest,"0,KEY_ESCAPE,KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,KEY_0,.." + WriteLine dest,"KEY_MINUS,KEY_EQUALS,KEY_BACKSPACE,KEY_TAB,KEY_Q,KEY_W,KEY_E,KEY_R,KEY_T,.." + WriteLine dest,"KEY_Y,KEY_U,KEY_I,KEY_O,KEY_P,KEY_OPENBRACKET,KEY_CLOSEBRACKET,KEY_RETURN,.." + WriteLine dest,"KEY_LCONTROL,KEY_A,KEY_S,KEY_D,KEY_F,KEY_G,KEY_H,KEY_J,KEY_K,KEY_L,.." + WriteLine dest,"KEY_SEMICOLON,KEY_QUOTES,KEY_TILDE,KEY_LSHIFT,KEY_BACKSLASH,.." + WriteLine dest,"KEY_Z,KEY_X,KEY_C,KEY_V,KEY_B,KEY_N,KEY_M,KEY_COMMA,KEY_PERIOD,KEY_SLASH,.." + WriteLine dest,"KEY_RSHIFT,KEY_NUMMULTIPLY,KEY_ALT,KEY_SPACE,KEY_CAPSLOCK,.." + WriteLine dest,"KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,.." + WriteLine dest,"KEY_NUMLOCK,KEY_SCROLL,KEY_NUM7,KEY_NUM8,KEY_NUM9,KEY_NUMSUBTRACT,KEY_NUM4,.." + WriteLine dest,"KEY_NUM5,KEY_NUM6,KEY_NUMADD,KEY_NUM1,KEY_NUM2,KEY_NUM3,KEY_NUM0,.." + WriteLine dest,"KEY_NUMDECIMAL,KEY_NUMSLASH,KEY_F11,KEY_F12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,.." + WriteLine dest,"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,.." + WriteLine dest,"KEY_EQUALS,0,0,KEY_MEDIA_PREV_TRACK,0,0,0,0,0,0,0,0,KEY_MEDIA_NEXT_TRACK,0,0,.." + WriteLine dest,"KEY_ENTER,KEY_RCONTROL,0,0,KEY_VOLUME_MUTE,0,KEY_MEDIA_PLAY_PAUSE,0,.." + WriteLine dest,"KEY_MEDIA_STOP,0,0,0,0,0,0,0,0,0,KEY_VOLUME_DOWN,0,KEY_VOLUME_UP,0,.." + WriteLine dest,"KEY_BROWSER_HOME,KEY_DECIMAL,0,KEY_NUMDIVIDE,0,KEY_SCREEN,.." + WriteLine dest,"0,0,0,0,0,0,0,0,0,0,0,0,0,KEY_PAUSE,0,KEY_HOME,KEY_UP,KEY_PAGEUP,0,.." + WriteLine dest,"KEY_LEFT,0,KEY_RIGHT,0,KEY_END,KEY_DOWN,KEY_PAGEDOWN,KEY_INSERT,KEY_DELETE,.." + WriteLine dest,"0,0,0,0,0,0,0,KEY_LWIN,KEY_RWIN,0,0,0,0,0,0,0,0,KEY_BROWSER_SEARCH,.." + WriteLine dest,"KEY_BROWSER_FAVORITES,KEY_BROWSER_REFRESH,KEY_BROWSER_STOP,KEY_BROWSER_FORWARD,.." + WriteLine dest,"KEY_BROWSER_BACK,0,KEY_LAUNCH_MAIL,KEY_LAUNCH_MEDIA_SELECT]" + WriteLine dest,"" + WriteLine dest,"Function VKeyDown(key);return KeyDown(VKEY[key]);End Function" + WriteLine dest,"Function VKeyHit(key);return KeyHit(VKEY[key]);End Function" + WriteLine dest,"" + WriteLine dest,"'currently unsupported in BlitzMax" + WriteLine dest,"" + WriteLine dest,"Function Locate( x,y );return 0;End Function" + WriteLine dest,"Function MouseZSpeed();return 0;End Function" + WriteLine dest,"Function FreeBank(bank);return 0;End Function" + WriteLine dest,"Function LoopSound(sound);return 0;End Function" + WriteLine dest,"Function ChannelPitch(channel,hz);return 0;End Function" + WriteLine dest,"Function PlayCDTrack( track,mode=0 );return 0;End Function" + WriteLine dest,"Function SoundVolume( sound,volume# );return 0;End Function" + WriteLine dest,"" + CloseFile dest +End Function + +Function WriteBBTypeFile() + Local dest:TStream=WriteFile("bbtype.bmx") + If Not dest Return + WriteLine dest,"' BBType adds legacy Type functionality to BlitzMax Type" + WriteLine dest,"" + WriteLine dest,"Type TBBType" + WriteLine dest,"" + WriteLine dest," Field _list:TList" + WriteLine dest," Field _link:TLink" + WriteLine dest,"" + WriteLine dest," Method Add(t:TList)" + WriteLine dest," _list=t" + WriteLine dest," _link=_list.AddLast(self)" + WriteLine dest," End Method" + WriteLine dest,"" + WriteLine dest," Method InsertBefore(t:TBBType)" + WriteLine dest," _link.Remove" + WriteLine dest," _link=_list.InsertBeforeLink(self,t._link)" + WriteLine dest," End Method" + WriteLine dest,"" + WriteLine dest," Method InsertAfter(t:TBBType)" + WriteLine dest," _link.Remove" + WriteLine dest," _link=_list.InsertAfterLink(self,t._link)" + WriteLine dest," End Method" + WriteLine dest,"" + WriteLine dest," Method Remove()" + WriteLine dest," _list.remove self" + WriteLine dest," End Method" + WriteLine dest,"" + WriteLine dest,"End Type" + WriteLine dest,"" + WriteLine dest,"Function DeleteLast(t:TBBType)" + WriteLine dest," if t TBBType(t._list.Last()).Remove()" + WriteLine dest,"End Function" + WriteLine dest,"" + WriteLine dest,"Function DeleteFirst(t:TBBType)" + WriteLine dest," if t TBBType(t._list.First()).Remove()" + WriteLine dest,"End Function" + WriteLine dest,"" + WriteLine dest,"Function DeleteEach(t:TBBType)" + WriteLine dest," if t t._list.Clear()" + WriteLine dest,"End Function" + WriteLine dest,"" + WriteLine dest,"Function ReadString$(in:TStream)" + WriteLine dest," local length" + WriteLine dest," length=readint(in)" + WriteLine dest," if length>0 and length<1024*1024 return brl.stream.readstring(in,length)" + WriteLine dest,"End Function" + WriteLine dest,"" + WriteLine dest,"Function HandleToObject:Object(obj:Object)" + WriteLine dest," Return obj" + WriteLine dest,"End Function" + WriteLine dest,"" + WriteLine dest,"Function HandleFromObject(obj:Object)" + WriteLine dest," Local h=HandleToObject(obj)" + WriteLine dest," Return h" + WriteLine dest,"End Function" + WriteLine dest,"" + WriteLine dest,"" + CloseFile dest +End Function diff --git a/src/bmk/bmk_config.bmx b/src/bmk/bmk_config.bmx new file mode 100644 index 0000000..8801f0e --- /dev/null +++ b/src/bmk/bmk_config.bmx @@ -0,0 +1,159 @@ + +Strict + +Import BRL.MaxUtil + +Import Pub.MacOS + +Const ALL_SRC_EXTS$="bmx;i;c;m;cpp;cxx;cc;mm;h;hpp;hxx;hh;s;asm" + +Global opt_arch$ +Global opt_server$ +Global opt_outfile$ +Global opt_framework$ +Global opt_apptype$="console" +Global opt_debug=False +Global opt_threaded=False +Global opt_release=False +Global opt_configmung$="" +Global opt_kill=False +Global opt_username$="nobody" +Global opt_password$="anonymous" +Global opt_modfilter$="." +Global opt_all=False +Global opt_quiet=False +Global opt_verbose=False +Global opt_execute=False +Global opt_proxy$ +Global opt_proxyport +Global opt_traceheaders +Global opt_appstub$="brl.appstub" ' BaH 28/9/2007 + +Global opt_dumpbuild + +Global cfg_platform$ + +?MacOS + +cfg_platform="macos" +Global macos_version +Gestalt Asc("s")Shl 24|Asc("y")Shl 16|Asc("s")Shl 8|Asc("v"),macos_version + +?MacOsPPC +If is_pid_native(0) opt_arch="ppc" Else opt_arch="x86" + +?MacOsX86 +If is_pid_native(0) opt_arch="x86" Else opt_arch="ppc" + +?Win32 + +opt_arch="x86" +cfg_platform="win32" + +'Fudge PATH so exec sees our MinGW first! +Local mingw$=getenv_( "MINGW" ) +If mingw + Local path$=getenv_( "PATH" ) + If path + path=mingw+"\bin;"+path + putenv_ "PATH="+path + EndIf +EndIf + +?Linux + +opt_arch="x86" +cfg_platform="linux" + +? + +ChangeDir LaunchDir + +Function CmdError() + Throw "Command line error" +End Function + +Function ParseConfigArgs$[]( args$[] ) + + Local n + + If getenv_( "BMKDUMPBUILD" ) + opt_dumpbuild=1 + opt_quiet=True + EndIf + + For n=0 Until args.length + Local arg$=args[n] + If arg[..1]<>"-" Exit + Select arg[1..] + Case "a" + opt_all=True + Case "q" + opt_quiet=True + Case "v" + opt_verbose=True + Case "x" + opt_execute=True + Case "d" + opt_debug=True + opt_release=False + Case "r" + opt_debug=False + opt_release=True + Case "h" + opt_threaded=True + Case "k" + opt_kill=True + Case "z" + opt_traceheaders=True + Case "y" + n:+1 + If n=args.length CmdError + opt_proxy=args[n] + Local i=opt_proxy.Find(":") + If i<>-1 + opt_proxyport=Int( opt_proxy[i+1..] ) + opt_proxy=opt_proxy[..i] + EndIf + Case "g" + n:+1 + If n=args.length CmdError + opt_arch=args[n].ToLower() + Case "t" + n:+1 + If n=args.length CmdError + opt_apptype=args[n].ToLower() + Case "o" + n:+1 + If n=args.length CmdError + opt_outfile=args[n] + Case "f" + n:+1 + If n=args.length CmdError + opt_framework=args[n] + Case "s" + n:+1 + If n=args.length CmdError + opt_server=args[n] + Case "u" + n:+1 + If n=args.length CmdError + opt_username=args[n] + Case "p" + n:+1 + If n=args.length CmdError + opt_password=args[n] + Case "b" + n:+1 + If n=args.length CmdError + opt_appstub=args[n] + Default + CmdError + End Select + Next + + Return args[n..] + +End Function + + diff --git a/src/bmk/bmk_make.bmx b/src/bmk/bmk_make.bmx new file mode 100644 index 0000000..f6a253c --- /dev/null +++ b/src/bmk/bmk_make.bmx @@ -0,0 +1,395 @@ + +Strict + +Import "bmk_modutil.bmx" + +Rem +Experimental speedup hack by Mark! + +Should allow you to modify non-interface affecting code without triggering lots of recompiles. + +Works by determining whether blah.bmx's .i file physically changes after blah.bmx is compiled. + +If not, then anything importing blah.bmx may not need to be recompiled. + +Uses a new '.i2' file which is updated only when actual .i file content changes. +End Rem +Global EXPERIMENTAL_SPEEDUP + +Local t$=getenv_( "BMK_SPEEDUP" ) +If t EXPERIMENTAL_SPEEDUP=True + +Type TFile + + Field path$,time + + Function Create:TFile( path$,files:TList ) + Local f:TFile=New TFile + f.path=path + f.time=FileTime(path) + If files files.AddFirst f + Return f + End Function + +End Type + +Global cc_opts$ +Global bcc_opts$ +Global app_main$ +Global app_type$ +Global src_files:TList +Global obj_files:TList +Global lnk_files:TList +Global tmp_stack:TList +Global ext_files:TList + +Function Push( o:Object ) + tmp_stack.AddLast o +End Function + +Function Pop:Object() + Return tmp_stack.RemoveLast() +End Function + +Function FindFile:TFile( path$,files:TList ) + path=path.ToLower() + Local f:TFile + For f=EachIn files + If f.path.ToLower()=path Return f + Next +End Function + +Function MaxTime( files:TList ) + Local f:TFile,t + For f=EachIn files + If f.time>t t=f.time + Next + Return t +End Function + +Function FilePaths:TList( files:TList ) + Local f:TFile,p:TList=New TList + For f=EachIn files + p.AddLast f.path + Next + Return p +End Function + +Function AddList( src:TList,dst:TList ) + Local t:Object + For t=EachIn src + dst.AddLast t + Next +End Function + +Function BeginMake() + cc_opts=Null + bcc_opts=Null + app_main=Null + src_files=New TList + obj_files=New TList + lnk_files=New TList + tmp_stack=New TList + ext_files=New TList +End Function + +'returns mod interface file +Function MakeMod:TFile( mod_name$ ) + + Local path$=ModulePath(mod_name) + Local id$=ModuleIdent(mod_name) + Local src_path$=path+"/"+id+".bmx" + Local arc_path$=path+"/"+id+opt_configmung+".a" + Local iface_path$=path+"/"+id+opt_configmung+".i" + + mod_opts = New TModOpt ' BaH + + Local iface:TFile=FindFile( iface_path,src_files ) + If iface Return iface + + Assert Not FindFile( arc_path,lnk_files ) + + Local arc:TFile=TFile.Create( arc_path,Null ) + + If (mod_name+".").Find(opt_modfilter)=0 And FileType(src_path)=FILETYPE_FILE + + Push cc_opts + Push bcc_opts + Push obj_files + + cc_opts="" + cc_opts:+" -I"+CQuote(path) + cc_opts:+" -I"+CQuote(ModulePath("")) + + If opt_release cc_opts:+" -O2 -DNDEBUG" + If opt_threaded cc_opts:+" -DTHREADED" + + bcc_opts=" -g "+opt_arch + bcc_opts:+" -m "+mod_name$ + + If opt_quiet bcc_opts:+" -q" + If opt_verbose bcc_opts:+" -v" + If opt_release bcc_opts:+" -r" + If opt_threaded bcc_opts:+" -h" + + obj_files=New TList + + MakeSrc src_path,True + + If MaxTime( obj_files )>arc.time Or opt_all + If Not opt_quiet Print "Archiving:"+StripDir(arc_path) + CreateArc arc_path,FilePaths( obj_files ) + arc.time=FileTime(arc_path) + EndIf + + obj_files=TList(Pop()) + bcc_opts=String(Pop()) + cc_opts=String(Pop()) + EndIf + + Local src:TFile=MakeSrc( iface_path,False ) + + lnk_files.AddFirst arc + + Return src + +End Function + +'adds to obj_files +'returns input src file +Function MakeSrc:TFile( src_path$,buildit ) + + Local src:TFile=FindFile( src_path,src_files ) + If src Return src + + If FileType( src_path )<>FILETYPE_FILE Return + + src=TFile.Create( src_path,src_files ) + + Local src_file:TSourceFile=ParseSourceFile( src_path ) + If Not src_file Return + + Local main_file=(src_path=app_main) + + Local keep_opts:TModOpt = mod_opts ' BaH + + If main_file + If src_file.framewk + If opt_framework Throw "Framework already specified on commandline" + opt_framework=src_file.framewk + bcc_opts:+" -f "+opt_framework + MakeMod opt_framework + Else + If app_type="bmx" + For Local t$=EachIn EnumModules() + If t.Find("brl.")=0 Or t.Find("pub.")=0 + If t<>"brl.blitz" And t<>opt_appstub MakeMod t + EndIf + Next + EndIf + EndIf + Else If src_file.framewk + Throw "Framework must appear in main source file" + EndIf + + mod_opts = keep_opts ' BaH + + push cc_opts + Push CurrentDir() + + ChangeDir ExtractDir( src_path ) + + Local src_ext$=ExtractExt( src_path ).ToLower() + + If Match( src_ext,"bmx;i" ) + 'incbins + For Local inc$=EachIn src_file.incbins + Local time=FileTime( inc ) + If time>src.time src.time=time + Next + 'includes + For Local inc$=EachIn src_file.includes + Local inc_ext$=ExtractExt(inc).ToLower() + If Match(inc_ext,"bmx") + Local dep:TFile=MakeSrc(RealPath(inc),False) + If Not dep Continue + If dep.time>src.time src.time=dep.time + Else + Throw "Unrecognized Include file type: "+inc + EndIf + Next + + 'module imports + For Local imp$=EachIn src_file.modimports + Local dep:TFile=MakeMod(imp) + If Not dep Continue + cc_opts:+" -I"+CQuote(ExtractDir(dep.path)) + If dep.time>src.time src.time=dep.time + Next + + mod_opts = keep_opts ' BaH + + For Local imp$=EachIn mod_opts.ld_opts ' BaH + ext_files.AddLast TModOpt.setPath(imp, ExtractDir(src_path)) + Next + + 'quoted imports + For Local imp$=EachIn src_file.imports + If imp[0]=Asc("-") + ext_files.AddLast imp + Continue + EndIf + Local imp_ext$=ExtractExt(imp).ToLower() + If Match( imp_ext,"h;hpp;hxx;hh" ) + cc_opts:+" -I"+CQuote(RealPath(ExtractDir(imp))) + Else If Match( imp_ext,"o;a;lib" ) + ext_files.AddLast RealPath(imp) + Else If Match( imp_ext,ALL_SRC_EXTS ) + + Local dep:TFile=MakeSrc(RealPath(imp),True) + + If Not dep Or Not Match( imp_ext,"bmx;i" ) Continue + + If EXPERIMENTAL_SPEEDUP And Match( imp_ext,"bmx" ) + Local p$=ExtractDir( dep.path )+"/.bmx" + Local i_path$=p+"/"+StripDir( dep.path )+opt_configmung+".i2" + If FileType( i_path )=FILETYPE_FILE + Local i_time=FileTime( i_path ) + If i_time>src.time src.time=i_time + Else + If dep.time>src.time src.time=dep.time + EndIf + Else + If dep.time>src.time src.time=dep.time + EndIf + + Else + Throw "Unrecognized Import file type: "+imp + EndIf + Next + Else If Match( src_ext,"c;m;cpp;cxx;cc;mm;h;hpp;hxx;hh" ) + For Local inc$=EachIn src_file.includes + Local inc_ext$=ExtractExt(inc).ToLower() + If Not Match(inc_ext,"h;hpp;hxx;hh") + Continue + EndIf + Local path$=RealPath(inc) + Local dep:TFile=MakeSrc(path,False) + If dep And dep.time>src.time src.time=dep.time + If Not opt_traceheaders Continue + Local src$=StripExt(path)+".cpp" + If FileType(src)<>FILETYPE_FILE + src="" + EndIf + If Not src Continue + MakeSrc src,True + Next + EndIf + + If buildit And Match( src_ext,"bmx;c;m;cpp;cxx;cc;mm;s;asm" ) + + Local p$=ExtractDir( src_path )+"/.bmx" + + If opt_dumpbuild Or FileType( p )=FILETYPE_NONE + CreateDir p + 'Sys "mkdir "+p 'Windows no likey... + EndIf + + If FileType( p )<>FILETYPE_DIR Throw "Unable to create temporary directory" + + Local obj_path$=p+"/"+StripDir( src_path ) + If main_file obj_path:+"."+opt_apptype + obj_path:+opt_configmung+".o" + + If src.time>FileTime( obj_path ) Or opt_all + + If Not opt_quiet Print "Compiling:"+StripDir(src_path) + Select src_ext + Case "bmx" + Local opts$=bcc_opts + If main_file opts=" -t "+opt_apptype+opts + + CompileBMX src_path,obj_path,opts + + If EXPERIMENTAL_SPEEDUP + Local i_path$=StripExt( obj_path )+".i" + + If FileType( i_path )=FILETYPE_FILE + + Local i_path2$=i_path+"2",update=True + + If Not opt_all And FileType( i_path2 )=FILETYPE_FILE And src.time=FileTime( src.path ) + If FileSize( i_path )=FileSize( i_path2 ) + Local i_bytes:Byte[]=LoadByteArray( i_path ) + Local i_bytes2:Byte[]=LoadByteArray( i_path2 ) + If i_bytes.length=i_bytes2.length And memcmp_( i_bytes,i_bytes2,i_bytes.length )=0 + update=False + EndIf + EndIf + EndIf + If update CopyFile i_path,i_path2 + EndIf + EndIf + + Case "c","m","cpp","cxx","cc","mm" + CompileC src_path,obj_path,cc_opts + Case "s","asm" + Assemble src_path,obj_path + End Select + EndIf + Local obj:TFile=TFile.Create( obj_path,obj_files ) + lnk_files.AddFirst obj + EndIf + + ChangeDir String(Pop()) + cc_opts=String(Pop()) + + Return src + +End Function + +Function MakeApp:TFile( Main$,makelib ) + + app_main=Main + + cc_opts="" + cc_opts:+" -I"+CQuote(ModulePath("")) + + If opt_release cc_opts:+" -O2 -DNDEBUG" + If opt_threaded cc_opts:+" -DTHREADED" + + bcc_opts=" -g "+opt_arch + + If opt_quiet bcc_opts:+" -q" + If opt_verbose bcc_opts:+" -v" + If opt_release bcc_opts:+" -r" + If opt_threaded bcc_opts:+" -h" + + If opt_framework bcc_opts:+" -f "+opt_framework + + Local app_ext$=ExtractExt( app_main ).ToLower() + Select app_ext + Case "bmx" + app_type="bmx" + MakeMod "brl.blitz" + MakeSrc Main,True + MakeMod opt_appstub + Case "c","cpp","cxx","cc","mm" + app_type="c/c++" + If opt_framework MakeMod opt_framework + MakeSrc Main,True + Default + Throw "Unrecognized app source file extension:"+app_ext + End Select + + If MaxTime( lnk_files )>FileTime( opt_outfile ) Or opt_all + If Not opt_quiet Print "Linking:"+StripDir( opt_outfile ) + lnk_files=FilePaths( lnk_files ) + AddList ext_files,lnk_files + LinkApp opt_outfile,lnk_files,makelib + EndIf + + app_main="" + +End Function + diff --git a/src/bmk/bmk_modinfo.bmx b/src/bmk/bmk_modinfo.bmx new file mode 100644 index 0000000..7f2c99d --- /dev/null +++ b/src/bmk/bmk_modinfo.bmx @@ -0,0 +1,60 @@ + +Strict + +Import "bmk_modutil.bmx" +Import "bmk_util.bmx" + +Type TInfo + Field info:TList=New TList + + Method Find$( key$ ) + key=key.ToLower()+":" + For Local t$=EachIn info + If t.ToLower()[..Len(key)]=key Return t[Len(key)..].Trim() + Next + End Method + + Method ReadFromStream:TModInfo( stream:TStream ) + While Not stream.Eof() + Local t$=stream.ReadLine() + If Not t Return + info.AddLast t + Wend + End Method +End Type + +Type TModInfo Extends TInfo + + Field name$ + Field version# + Field modprivs$ + Field modserver$ + Field serverinfo:Object + + Function CreateFromModule:TModInfo( name$ ) + Local path$=ModuleInterface( name,"release."+cfg_platform+"."+opt_arch ) + If FileType(path)<>FILETYPE_FILE Return + Local src:TSourceFile=ParseSourceFile( path ) + If Not src Return + Local modinfo:TModInfo=New TModInfo + modinfo.name=name + modinfo.info=src.info + modinfo.info.AddFirst "Module: "+name + modinfo.version=Float( modinfo.Find( "Version" ) ) + modinfo.modserver=modinfo.Find( "ModServer" ) + Return modinfo + End Function + + Function CreateFromStream:TModInfo( stream:TStream ) + Local modinfo:TModInfo=New TModInfo + modinfo.ReadFromStream stream + modinfo.name=modinfo.Find( "Module" ) + If Not modinfo.name Return + modinfo.version=Float( modinfo.Find( "Version" ) ) + modinfo.modprivs=modinfo.Find( "ModPrivs" ) + modinfo.modserver=modinfo.Find( "ModServer" ) + Return modinfo + End Function + +End Type + diff --git a/src/bmk/bmk_modutil.bmx b/src/bmk/bmk_modutil.bmx new file mode 100644 index 0000000..aeab773 --- /dev/null +++ b/src/bmk/bmk_modutil.bmx @@ -0,0 +1,187 @@ + +Strict + +Import BRL.MaxUtil +Import BRL.TextStream + +Import "bmk_util.bmx" + +Type TSourceFile + Field ext$ 'one of: "bmx", "i", "c", "cpp", "m", "s", "h" + Field path$ + Field modid$ + Field framewk$ + Field info:TList=New TList + + Field modimports:TList=New TList + + Field imports:TList=New TList + Field includes:TList=New TList + Field incbins:TList=New TList + + Field declids:TList=New TList +End Type + +Function CharIsDigit( ch ) + Return ch>=Asc("0") And ch<=Asc("9") +End Function + +Function CharIsAlpha( ch ) + Return ch=Asc("_") Or (ch>=Asc("a") And ch<=Asc("z")) Or (ch>=Asc("A") And ch<=Asc("Z")) +End Function + +Function ValidSourceExt( ext$ ) + Select ext.ToLower() + Case "bmx","i" + Case "c","m","h" + Case "cpp","cxx","mm","hpp","hxx" + Case "s","asm" + Default + Return False + End Select + Return True +End Function + +Function ParseSourceFile:TSourceFile( path$ ) + + If FileType(path)<>FILETYPE_FILE Return + + Local ext$=ExtractExt( path ).ToLower() + If Not ValidSourceExt( ext ) Return + + Local file:TSourceFile=New TSourceFile + file.ext=ext + file.path=path + + Local str$=LoadText( path ) + + Local pos,in_rem,cc=True + + While pos-1 line=line[..n] + + If Not line Continue + + Local lline$=line.Tolower() + + If in_rem + If lline[..6]="endrem" Or lline[..7]="end rem" + in_rem=False + EndIf + Continue + Else If lline[..3]="rem" + in_rem=True + Continue + EndIf + + If lline[..1]="?" + Local t$=lline[1..].Trim() + + Local cNot + If t.StartsWith( "not " ) + cNot=True + t=t[4..].Trim() + EndIf + + Select t + Case "" + cc=True + Case "debug" + cc=opt_debug + Case "threaded" + cc=opt_threaded +?x86 + Case "x86" cc=opt_arch="x86" +?ppc + Case "ppc" cc=opt_arch="ppc" +? +?Win32 + Case "win32" cc=True + Case "win32x86" cc=opt_arch="x86" + Case "win32ppc" cc=opt_arch="ppc" +?Linux + Case "linux" cc=True + Case "linuxx86" cc=opt_arch="x86" + Case "linuxppc" cc=opt_arch="ppc" +?MacOS + Case "macos" cc=True + Case "macosx86" cc=opt_arch="x86" + Case "macosppc" cc=opt_arch="ppc" +? + Default + cc=False + End Select + If cNot cc=Not cc + Continue + EndIf + + If Not cc Continue + + If Not CharIsAlpha( lline[0] ) Continue + + Local i=1 + While i1 And val[0]=34 And val[val.length-1]=34 + qval=val[1..val.length-1] + EndIf + + Select key + Case "module" + file.modid=val.ToLower() + Case "framework" + file.framewk=val.ToLower() + Case "import" + If qval + file.imports.AddLast qval + Else + file.modimports.AddLast val.ToLower() + EndIf + Case "incbin" + If qval + file.incbins.AddLast qval + EndIf + Case "include" + If qval + file.includes.AddLast qval + EndIf + Case "moduleinfo" + If qval + file.info.AddLast qval + If mod_opts mod_opts.addOption(qval) ' BaH + EndIf + End Select + Case "c","m","h","cpp","cxx","hpp","hxx" + If line[..8]="#include" + Local val$=line[8..].Trim(),qval$,qext$ + If val.length>1 And val[0]=34 And val[val.length-1]=34 + qval=val[1..val.length-1] + EndIf + If qval + file.includes.AddLast qval + EndIf + EndIf + End Select + + Wend + + Return file + +End Function diff --git a/src/bmk/bmk_util.bmx b/src/bmk/bmk_util.bmx new file mode 100644 index 0000000..5c122d8 --- /dev/null +++ b/src/bmk/bmk_util.bmx @@ -0,0 +1,359 @@ + +Strict + +Import "bmk_config.bmx" + +'OS X Nasm doesn't work? Used to produce incorrect reloc offsets - haven't checked for a while +Const USE_NASM=False + +Const CC_WARNINGS=False'True + +Type TModOpt ' BaH + Field cc_opts:String = "" + Field ld_opts:TList = New TList + + Method addOption(qval:String) + If qval.startswith("CC_OPTS") Then + cc_opts:+ " " + qval[qval.find(":") + 1..].Trim() + ElseIf qval.startswith("LD_OPTS") Then + Local opt:String = qval[qval.find(":") + 1..].Trim() + + If opt.startsWith("-L") Then + opt = "-L" + CQuote(opt[2..]) + End If + + ld_opts.addLast opt + End If + End Method + + Method hasCCopt:Int(value:String) + Return cc_opts.find(value) >= 0 + End Method + + Method hasLDopt:Int(value:String) + For Local opt:String = EachIn ld_opts + If opt.find(value) >= 0 Then + Return True + End If + Next + Return False + End Method + + Function setPath:String(value:String, path:String) + Return value.Replace("%PWD%", path) + End Function + +End Type + +Global mod_opts:TModOpt ' BaH + +Function Match( ext$,pat$ ) + Return (";"+pat+";").Find( ";"+ext+";" )<>-1 +End Function + +Function HTTPEsc$( t$ ) + t=t.Replace( " ","%20" ) + Return t +End Function + +Function Sys( cmd$ ) + If opt_verbose + Print cmd + Else If opt_dumpbuild + Local p$=cmd + p=p.Replace( BlitzMaxPath()+"/","./" ) + WriteStdout p+"~n" + Local t$="mkdir " + If cmd.StartsWith( t ) And FileType( cmd[t.length..] ) Return + EndIf + Return system_( cmd ) +End Function + +Function CQuote$( t$ ) + If t And t[0]=Asc("-") Return t + For Local i=0 Until t.length + If t[i]=Asc(".") Continue + If t[i]=Asc("/") Continue +?Win32 + If t[i]=Asc("\") Continue +? + If t[i]=Asc("_") Or t[i]=Asc("-") Continue + If t[i]>=Asc("0") And t[i]<=Asc("9") Continue + If t[i]>=Asc("A") And t[i]<=Asc("Z") Continue + If t[i]>=Asc("a") And t[i]<=Asc("z") Continue + Return "~q"+t+"~q" + Next + Return t +End Function + +Function Ranlib( dir$ ) + ' +?MacOS + If macos_version>=$1040 Return +? + ' + For Local f$=EachIn LoadDir( dir ) + Local p$=dir+"/"+f + Select FileType( p ) + Case FILETYPE_DIR + Ranlib p + Case FILETYPE_FILE + If ExtractExt(f).ToLower()="a" Sys "ranlib "+p + End Select + Next +End Function + +Function Assemble( src$,obj$ ) + DeleteFile obj + Local cmd$ +?MacOS + If opt_arch="ppc" + cmd="as -arch ppc" + Else + If USE_NASM + cmd="nasm -f macho" + Else + cmd="as -arch i386" + EndIf + EndIf + cmd:+" -W -o "+CQuote(obj)+" "+CQuote(src); +?Win32 + cmd$=CQuote(BlitzMaxPath()+"/bin/fasm")+" "+CQuote(src)+" "+CQuote(obj) +?Linux + cmd$=CQuote(BlitzMaxPath()+"/bin/fasm")+" -m32768 "+CQuote(src)+" "+CQuote(obj) +? + If Sys( cmd ) + Throw "Build Error: Failed to assemble "+src + EndIf +End Function + +Function CompileC( src$,obj$,opts$ ) + DeleteFile obj + + Local t$=getenv_( "BMK_CC_OPTS" ) + If t opts:+" "+t + + Local cmd$="gcc" + If ExtractExt(src)="cpp" Or ExtractExt(src)="cc" Or ExtractExt(src)="cxx" Or ExtractExt(src)="mm" + cmd="g++" + Else + If CC_WARNINGS opts:+" -Wimplicit-function-declaration" + EndIf + + If Not CC_WARNINGS opts:+" -w" + +?MacOS + If opt_arch="ppc" + opts:+" -arch ppc" + Else + opts:+" -arch i386" + EndIf + If macos_version>=$1070 'Lion? + opts:+" -mmacosx-version-min=10.4" '...can build for Tiger++ + Else If macos_version>=$1040 'Tiger? + opts:+" -mmacosx-version-min=10.3" '...can build for Panther++ + EndIf +?Win32 + If Not mod_opts Or Not mod_opts.hasCCopt("-march") + opts:+" -march=pentium" + EndIf + opts:+" -ffast-math" +?Linux + opts:+" -m32 -mfancy-math-387 -fno-strict-aliasing" +? + If mod_opts + If Not mod_opts.hasCCopt("-fexceptions") + opts:+" -fno-exceptions" + EndIf + opts:+ " " + mod_opts.cc_opts ' BaH + Else + opts:+" -fno-exceptions" + EndIf + + cmd:+opts+" -c -o "+CQuote(obj)+" "+CQuote(src) + + If Sys( cmd ) + Throw "Build Error: failed to compile "+src + EndIf +End Function + +Function CompileBMX( src$,obj$,opts$ ) + DeleteFile obj + + Local azm$=StripExt(obj)+".s" +?MacOs + Local cmd$=CQuote(BlitzMaxPath()+"/bin/bcc")+opts+" -o "+CQuote(azm)+" "+CQuote(src) +?Win32 + Local cmd$=CQuote(BlitzMaxPath()+"/bin/bcc")+opts+" -o "+CQuote(azm)+" "+CQuote(src) +?Linux + Local cmd$=CQuote(BlitzMaxPath()+"/bin/bcc")+opts+" -o "+CQuote(azm)+" "+CQuote(src) +? + If Sys( cmd ) + Throw "Build Error: failed to compile "+src + EndIf +?MacOs + If opt_arch="x86" + If Not USE_NASM + Local cmd$=CQuote(BlitzMaxPath()+"/bin/fasm2as")+" "+CQuote(azm) + If Sys( cmd ) Throw "Fasm2as failed - please contact BRL!" + EndIf + EndIf +? + Assemble azm,obj + +End Function + +Function CreateArc( path$ , oobjs:TList ) + DeleteFile path + Local cmd$,t$ +?Win32 + For t$=EachIn oobjs + If Len(cmd)+Len(t)>1000 + If Sys( cmd ) + DeleteFile path + Throw "Build Error: Failed to create archive "+path + EndIf + cmd="" + EndIf + If Not cmd cmd="ar -r "+CQuote(path) + cmd:+" "+CQuote(t) + Next +?MacOS + cmd="libtool -o "+CQuote(path) + For Local t$=EachIn oobjs + cmd:+" "+CQuote(t) + Next +?Linux + For Local t$=EachIn oobjs + If Len(cmd)+Len(t)>1000 + If Sys( cmd ) + DeleteFile path + Throw "Build Error: Failed to create archive "+path + EndIf + cmd="" + EndIf + If Not cmd cmd="ar -r "+CQuote(path) + cmd:+" "+CQuote(t) + Next +? + If cmd And Sys( cmd ) + DeleteFile path + Throw "Build Error: Failed to create archive "+path + EndIf +End Function + +Function LinkApp( path$,lnk_files:TList,makelib ) + DeleteFile path + + Local cmd$ + Local files$ + Local tmpfile$=BlitzMaxPath()+"/tmp/ld.tmp" +?MacOS + cmd="g++" + + If opt_arch="ppc" + cmd:+" -arch ppc" + Else + cmd:+" -arch i386 -read_only_relocs suppress" + EndIf + + If macos_version>=$1070 'Lion? + cmd:+" -mmacosx-version-min=10.4" '...can build for Tiger++ + Else If macos_version>=$1040 'Tiger? + cmd:+" -mmacosx-version-min=10.3" '...can build for Panther++ + EndIf + + cmd:+" -o "+CQuote( path ) + cmd:+" "+CQuote( "-L"+CQuote( BlitzMaxPath()+"/lib" ) ) + + If Not opt_dumpbuild cmd:+" -filelist "+CQuote( tmpfile ) + + For Local t$=EachIn lnk_files + If opt_dumpbuild Or (t[..1]="-") + cmd:+" "+t + Else + files:+t+Chr(10) + EndIf + Next + cmd:+" -lSystem -framework CoreServices -framework CoreFoundation" +?Win32 + cmd=CQuote(BlitzMaxPath()+"/bin/ld.exe")+" -s -stack 4194304" 'symbol stripping enabled + If opt_apptype="gui" cmd:+" -subsystem windows" + If makelib cmd:+" -shared" + + cmd:+" -o "+CQuote( path ) + cmd:+" "+CQuote( "-L"+CQuote( BlitzMaxPath()+"/lib" ) ) + + If makelib + Local imp$=StripExt(path)+".a" + Local def$=StripExt(path)+".def" + If FileType( def )<>FILETYPE_FILE Throw "Cannot locate .def file" + cmd:+" "+def + cmd:+" --out-implib "+imp + files:+"~n"+CQuote( BlitzMaxPath()+"/lib/dllcrt2.o" ) + Else + files:+"~n"+CQuote( BlitzMaxPath()+"/lib/crtbegin.o" ) + files:+"~n"+CQuote( BlitzMaxPath()+"/lib/crt2.o" ) + EndIf + + 'Unholy!!!!! + Local xpmanifest$ + For Local f$=EachIn lnk_files + Local t$=CQuote( f ) + If opt_dumpbuild Or (t[..1]="-" And t[..2]<>"-l") + cmd:+" "+t + Else + If f.EndsWith( "/win32maxguiex.mod/xpmanifest.o" ) + xpmanifest=t + Else + files:+"~n"+t + EndIf + EndIf + Next + + If xpmanifest files:+"~n"+xpmanifest + + cmd:+" "+CQuote( tmpfile ) + + files:+"~n-lgdi32 -lwsock32 -lwinmm -ladvapi32" + files:+" -lstdc++ -lgcc -lmingwex -lmingw32 -lmoldname -lmsvcrt -luser32 -lkernel32" + + If Not makelib + files:+" "+CQuote( BlitzMaxPath()+"/lib/crtend.o" ) + EndIf + + files="INPUT("+files+")" +?Linux + + cmd="g++" + cmd:+" -m32 -s -Os -pthread" + cmd:+" -o "+CQuote( path ) + cmd:+" "+CQuote( tmpfile ) + cmd:+" -L/usr/lib32" + cmd:+" -L/usr/X11R6/lib" + cmd:+" -L/usr/lib" + cmd:+" -L"+CQuote( BlitzMaxPath()+"/lib" ) + + For Local t$=EachIn lnk_files + t=CQuote(t) + If opt_dumpbuild Or (t[..1]="-" And t[..2]<>"-l") + cmd:+" "+t + Else + files:+" "+t + EndIf + Next + + files="INPUT("+files+")" +? + Local t$=getenv_( "BMK_LD_OPTS" ) + If t + cmd:+" "+t + EndIf + + Local stream:TStream=WriteStream( tmpfile ) + stream.WriteBytes files.ToCString(),files.length + stream.Close + + If Sys( cmd ) Throw "Build Error: Failed to link "+path + +End Function diff --git a/src/bmk/bmk_zap.bmx b/src/bmk/bmk_zap.bmx new file mode 100644 index 0000000..0301fbb --- /dev/null +++ b/src/bmk/bmk_zap.bmx @@ -0,0 +1,138 @@ + +Strict + +Import "bmk_modutil.bmx" +Import "bmk_bank.bmx" +Import "bmk_modinfo.bmx" + +Function Zap( path$,stream:TStream ) + + If Not path Return False + + Local name$=StripDir( path ) + + Local skip=False + + If name[..1]="." + skip=True + Else If name.ToLower().EndsWith( ".bak" ) + skip=True + EndIf + + If skip + stream.WriteLine "" + Return True + EndIf + + Local mode=FileMode( path ) + + Select FileType(path) + Case FILETYPE_NONE + Print "Error zapping file "+path + Return + Case FILETYPE_FILE + Local size=FileSize(path) + stream.WriteLine name + stream.WriteLine mode + stream.WriteLine size + Local from_stream:TStream=ReadStream(path) + CopyBytes from_stream,stream,size + from_stream.Close + Case FILETYPE_DIR + Local dir$[]=LoadDir( path ) + Local size=Len( dir ) + stream.WriteLine name + stream.WriteLine -mode + stream.WriteLine size + For Local t$=EachIn dir + If Not Zap( path+"/"+t,stream ) Return + Next + End Select + + Return True + +End Function + +Function Unzap( dir$,stream:TStream ) + + Local name$=stream.ReadLine() + If Not name Return True + + Local mode=Int( stream.ReadLine() ) + Local size=Int( stream.ReadLine() ) + + Local path$=dir+"/"+name + + If mode<0 + mode=-mode + CreateDir path + For Local k=0 Until size + If Not Unzap( path,stream ) Return + Next + Else + DeleteFile path + Local to_stream:TStream=WriteStream(path) + CopyBytes stream,to_stream,size + to_stream.Close + EndIf + + SetFileMode path,mode + Return True + +End Function + +Function ZapMod( name$,stream:TStream ) + + Local path$=ModuleInterface( name,"release."+cfg_platform+"."+opt_arch ) + + If FileType(path)<>FILETYPE_FILE + Print "Failed to find module" + Return + EndIf + + Local src:TSourceFile=ParseSourceFile( path ) + stream.WriteLine "Module: "+name + For Local t$=EachIn src.info + stream.WriteLine t + Next + stream.WriteLine "" + + Local bank:TBank=TBank.Create(0) + Local bank_stream:TStream=TBankStream.Create( bank ) + If Not Zap( ModulePath(name),bank_stream ) Throw "Failed to publish module" + bank_stream.Close + + bank=CompressBank( bank ) + bank_stream=TBankStream.Create( bank ) + CopyStream bank_stream,stream + bank_stream.Close + +End Function + +Function UnzapMod( stream:TStream ) + + Local modinfo:TModInfo=TModInfo.CreateFromStream( stream ) + + Local path$=ModulePath( modinfo.name ) + If Not CreateDir( path,True ) Throw "Unable to create module directory" + DeleteDir path,True + + Local bank:TBank=TBank.Create(0) + Local bank_stream:TStream=TBankStream.Create( bank ) + CopyStream stream,bank_stream + bank_stream.Close + + bank=UncompressBank( bank ) + bank_stream=TBankStream.Create( bank ) + If Not Unzap( ExtractDir(path),bank_stream ) + Print "Failed to Unzap module" + Return + EndIf + bank_stream.Close + +?MacOS + Ranlib path +? + Return True + +End Function diff --git a/src/bmk/macos.icns b/src/bmk/macos.icns new file mode 100644 index 0000000000000000000000000000000000000000..b712495f74576578fb88a0d112ae1a306af96e47 GIT binary patch literal 51417 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 literal 0 HcmV?d00001 diff --git a/src/docmods/bmxtoker.bmx b/src/docmods/bmxtoker.bmx new file mode 100644 index 0000000..3278e2c --- /dev/null +++ b/src/docmods/bmxtoker.bmx @@ -0,0 +1,400 @@ +Strict + +Import BRL.Map +Import BRL.Stream + +Private + +Function ReadTextStream:TStream( url:Object ) + + Local format,size,c,d,e + Local stream:TStream=ReadStream( url ) + If Not stream Return + + If Not stream.Eof() + c=stream.ReadByte() + size:+1 + If Not stream.Eof() + d=stream.ReadByte() + size:+1 + If c=$fe And d=$ff + format=TTextStream.UTF16BE + Else If c=$ff And d=$fe + format=TTextStream.UTF16LE + Else If c=$ef And d=$bb + If Not stream.Eof() + e=stream.ReadByte() + size:+1 + If e=$bf format=TTextStream.UTF8 + EndIf + EndIf + EndIf + EndIf + + If format Return TTextStream.Create( stream,format ) + + stream.Close + + Return ReadStream( url ) + +End Function + +Public + +Function TPrint( t$ ) + Local p + For Local i=0 Until t.length + If t[i]<256 Continue + 'unicode char! + StandardIOStream.WriteString t[p..i] + StandardIOStream.WriteString "&#"+t[i]+";" + p=i+1 + Next + Print t[p..] +End Function + +'----- Simple BMX Parser ----- + +Const T_EOF= -1 +Const T_EOL= 10 + +Const T_IDENT= $10001 +Const T_INTLIT= $10002 +Const T_FLOATLIT= $10003 +Const T_STRINGLIT= $10004 +Const T_STRINGERR= $10005 + +Const T_REM= $20001 +Const T_ENDREM= $20002 +Const T_FUNCTION= $20003 +Const T_ENDFUNCTION=$20004 +Const T_TYPE= $20005 +Const T_ENDTYPE= $20006 +Const T_CONST= $20007 +Const T_METHOD= $20008 +Const T_ENDMETHOD= $20009 +Const T_GLOBAL= $2000a +Const T_INCLUDE= $2000b ' Added by BaH - 01/06/2006 +Const T_IMPORT= $2000d +Const T_FIELD= $2000e +Const T_EXTENDS= $2000f +Const T_ABSTRACT= $20010 +Const T_FINAL= $20011 +Const T_MODULE= $2000c +Const T_MODULEINFO= $20012 + +Const T_DOTDOT= $30001 +Const T_ARRAYDECL= $30002 + +Const T_KEYWORD= $40000 + +Type TIntValue + Field value:Int +End Type + +Function IntValue:TIntValue( value ) + Local t:TIntValue=New TIntValue + t.value=value + Return t +End Function + +Function IsSpace( char ) + Return char<=Asc(" ") And char<>Asc("~n") +End Function + +Function IsAlpha( char ) + Return ( char>=Asc("a") And char<=Asc("z") ) Or ( char>=Asc("A") And char<=Asc("Z") ) +End Function + +Function IsNumeric( char ) + Return char>=Asc("0") And char<=Asc("9") +End Function + +Function IsAlphaNumeric( char ) + Return IsAlpha( char ) Or IsNumeric( char ) +End Function + +Type TBmxToker + + Field _filename:String ' Added by BaH - 25/05/2006 + + Field _spc + Field _pos,_line$ + Field _toke,_text$ + Field _stream:TStream + + Global _keywords:TMap + + Method Delete() + Close + End Method + + Method Bump() + + If _toke=T_EOF Return _toke + + If _pos>=_line.length + _pos=0 + If _stream.Eof() + _toke=T_EOF + _text="" + Return + EndIf + _line=_stream.ReadLine().Trim()+"~n" + EndIf + + Local from=_pos + While _pos<_line.length And IsSpace( _line[_pos] ) + _pos:+1 + Wend + + If _spc And _pos<>from + _text=_line[from.._pos] + _toke=Asc(" ") + Return _toke + EndIf + + from=_pos + Local char=_line[_pos] + _pos:+1 + + If char=Asc("'") + _pos=_line.length + _toke=T_EOL + Else If char=Asc("~n") + _toke=T_EOL + Else If isAlpha(char) Or char=Asc("_") + While IsAlphaNumeric(_line[_pos]) Or _line[_pos]=Asc("_") + _pos:+1 + Wend + _toke=T_IDENT + Local id$=_line[from.._pos].ToLower() + If id="end" And _line[_pos]=Asc(" ") And IsAlpha(_line[_pos+1]) + Local t_pos=_pos+2 + While IsAlphaNumeric(_line[t_pos]) Or _line[t_pos]=Asc("_") + t_pos:+1 + Wend + Local id$="end"+_line[_pos+1..t_pos].ToLower() + Local v:TIntValue=TIntValue( _keywords.ValueForKey( id ) ) + If v + _pos=t_pos + _toke=v.value + EndIf + EndIf + If _toke=T_IDENT + Local v:TIntValue=TIntValue( _keywords.ValueForKey( id ) ) + If v _toke=v.value + If _toke=T_REM + _text="" + Repeat + If _stream.Eof() + _pos=0 + _line="" + Return _toke + EndIf + _line=_stream.ReadLine().Trim()+"~n" + If _line[..6].ToLower()="endrem" Or _line[..7].ToLower()="end rem" Exit + _text:+_line + Forever + _pos=_line.length + Return _toke + Else + ' Complete lines if they continue onto other lines - BaH 03/09/2006 + If _toke = T_FUNCTION Or _toke = T_METHOD Or _toke = T_CONST Or _toke = T_GLOBAL Or _toke = T_FIELD Then + If _line.find("..") >= 0 Then + _line = getFullLine() + End If + End If + EndIf + EndIf + Else If IsNumeric(char) + While IsNumeric(_line[_pos]) + _pos:+1 + Wend + _toke=T_INTLIT + If _line[_pos]=Asc(".") + _pos:+1 + While IsNumeric(_line[_pos]) + _pos:+1 + Wend + _toke=T_FLOATLIT + EndIf + Else If char=Asc("~q") + While _line[_pos]<>Asc("~q") And _line[_pos]<>Asc("~n") + _pos:+1 + Wend + If _line[_pos]=Asc("~q") + _pos:+1 + _toke=T_STRINGLIT + Else + _toke=T_STRINGERR + EndIf + Else + _toke=char + EndIf + + _text=_line[from.._pos] + + Return _toke + + End Method + + ' Completes a line that takes up more than one actual line - BaH 03/09/2006 + Method getFullLine:String() + Local first:Int = True + Local fullline:String + Local line:String = _line + Local pos:Int = 0 + Local from:Int + + #loop + Repeat + + If Not first Then + If _stream.Eof() + pos=0 + line="" + Return line + EndIf + line = _stream.ReadLine().Trim()+"~n" + End If + + first = False + + If line.tolower().Trim() = "rem" Then + Repeat + If _stream.Eof() + pos=0 + line="" + Return fullline + EndIf + line = _stream.ReadLine().Trim()+"~n" + If line[..6].ToLower()="endrem" Or line[..7].ToLower()="end rem" Then + Continue loop + End If + Forever + End If + + pos = 0 + from = pos + While pos < line.length And IsSpace( line[pos] ) + pos:+1 + Wend + + from = pos + + Local char:Int = line[pos] + pos:+1 + While char <> Asc("~n") + + If char = Asc(".") And line[pos] = Asc(".") Then + fullline:+ line[from..pos-1] + Exit + End If + + If char = Asc("'") Then + Exit + End If + + char=line[pos] + pos:+1 + Wend + + If char = Asc("~n") Then + fullline :+ line[from..pos-1] + Exit + End If + + Forever + + Return fullline + End Method + + Method Curr() + Return _toke + End Method + + Method Text$() + Return _text + End Method + + Method Line$() + Return _line + End Method + + Method Parse$( toke ) + If Curr()<>toke Throw "Unexpected token" + Local t$=Text() + Bump + Return t + End Method + + Method CParse$( toke ) + If Curr()<>toke Return + Local t$=Text() + Bump + Return t + End Method + + Method ParseUntil$( toke ) + Local t$ + While Curr()<>toke + If Curr()=T_EOF Throw "Unexpected EOF" + t:+Text() + Bump + Wend + Bump + Return t + End Method + + Method Spaces( enable ) + If enable + _spc:+1 + Else + _spc:-1 + EndIf + End Method + + Method Close() + If _stream _stream.Close + _stream=Null + End Method + + Function CreateKeywords() + If _keywords Return + Function kw( id$,toke ) + _keywords.insert id,IntValue(toke) + End Function + _keywords=New TMap + kw "rem",T_REM + kw "endrem",T_ENDREM + kw "function",T_FUNCTION + kw "endfunction",T_ENDFUNCTION + kw "method",T_METHOD + kw "endmethod",T_ENDMETHOD + kw "const",T_CONST + kw "global",T_GLOBAL + kw "include",T_INCLUDE ' Added by BaH - 01/06/2006 + kw "import",T_IMPORT + kw "field",T_FIELD + kw "type",T_TYPE + kw "endtype",T_ENDTYPE + kw "extends",T_EXTENDS + kw "abstract",T_ABSTRACT + kw "final",T_FINAL + kw "module",T_MODULE + kw "moduleinfo",T_MODULEINFO + End Function + + Function Create:TBmxToker( url:Object ) + CreateKeywords + Local stream:TStream=ReadTextStream( url ) + If Not stream Throw "Unable to read stream: "+url.ToString() + Local t:TBmxToker=New TBmxToker + t._filename = url.ToString() ' Added by BaH - 25/05/2006 + t._stream=stream + t.Bump + Return t + End Function + +End Type diff --git a/src/docmods/dochistory.bmx b/src/docmods/dochistory.bmx new file mode 100644 index 0000000..ce84b6d --- /dev/null +++ b/src/docmods/dochistory.bmx @@ -0,0 +1,180 @@ + +Strict + +Import "docparser.bmx" + +Local docs:TDocs=ParseMods() + +docs.Sort + +DocModHistory docs + +End + +If AppArgs.length=2 + Select AppArgs[1].ToLower() + Case "sync" + SyncDocs docs + Case "history" + DocModHistory docs + End Select +Else + DocMods docs + SyncDocs docs +EndIf + +' spit out versions.doc format history + + +Function DocModHistory( docs:TDocs ) + + Local history$=LoadText("history120.txt") + +' Local stdio:TStream=StandardIOStream +' StandardIOStream=WriteFile( "commands.html" ) + + For Local t:TDocs=EachIn docs.kids + If t.kind<>T_MODULE Continue + For Local info$=EachIn t.infos + If info[info.length-8..]<>"Release~q" + If info[..8]="~qHistory" + Local h$="+ ("+t.ident+") "+info[10..info.length-1] + If Not Instr(history,h) TPrint h + EndIf + EndIf + Next + + Next + +' StandardIOStream.Close +' StandardIOStream=stdio + +End Function + + +Function ParseMods:TDocs() + + Local docs:TDocs=New TDocs + + Local mods:TList=EnumModules() + + For Local modid$=EachIn mods + + Local ident$=ModuleIdent( modid ) + Local modDir$=ModulePath( modid ) + Local bmxFile$=ModuleSource( modid ) + Local docDir$=modDir+"/doc" + + If FileType( modDir+"/"+ident+".bmx" )=FILETYPE_FILE + If FileType( docDir )<>FILETYPE_DIR + CreateDir docDir + If FileType( docDir )<>FILETYPE_DIR + TPrint "Failed to created directory:"+docDir + Continue + EndIf + EndIf + + Local toker:TBMXToker=TBMXToker.Create( bmxFile ) + + Local parser:TDocParser=TDocParser.WithToker( toker ) + + parser.Parse docs + + parser.Close + + EndIf + + Next + + Return docs + +End Function + +Function DocMods( docs:TDocs ) + + For Local t:TDocs=EachIn docs.kids + + If t.kind<>T_MODULE Continue + + ChangeDir ModulePath( t.ident.tolower() )+"/doc" 'linux fix for "BRL." case problem + + Local stdio:TStream=StandardIOStream + StandardIOStream=WriteFile( "commands.html" ) + + t.CacheTexts ' pre cache of text for output (allows us to use it for summaries etc) - BaH 03/09/2006 + t.EmitHtml + + StandardIOStream.Close + StandardIOStream=stdio + + Next +End Function + +Function SyncDocs( docs:TDocs ) + + Local comms:TList=New TList + Local index:TList=New TList + + Local stdio:TStream=StandardIOStream + StandardIOStream=WriteFile( BlitzMaxPath()+"/doc/bmxmods/navbar.html" ) + + TPrint "" + TPrint "" + TPrint "" + TPrint "" + + TPrint "Module reference
" + + For Local t:TDocs=EachIn docs.kids + + If t.kind<>T_MODULE Continue + + Local modid$=t.ident.ToLower() + Local modln$=modid.Replace(".","_") + Local moddesc$=t.bbdoc[6..].Trim() + Local i=moddesc.Find("~n") + If i<>-1 moddesc=moddesc[..i] + + Local url$="../../mod/"+modid.Replace(".",".mod/")+".mod/doc/commands.html" + + TPrint ""+moddesc+"
" + TPrint "
" + For Local p:TDocs=EachIn t.kids + ' kind is Type and is not doc'd and there are no doc'd kids + If p.kind = T_TYPE And (Not p.bbdoc And p.kids.count() = 0) Then + Continue + End If + Local turl$=url+"#"+p.ident + TPrint " "+p.ident+"
" + index.AddLast p.ident+":"+turl + Local i=p.proto.Find( " " ) + If i<>-1 comms.AddLast p.proto[i+1..].Trim()+"|"+turl[5..] + Next + TPrint "
" + + Next + + TPrint "
" + TPrint "Alphabetic index
" + + Local arr:Object[]=index.ToArray() + arr.Sort + For Local link$=EachIn arr + Local i=link.Find( ":" ) + If i=-1 Throw "chunks" + Local ident$=link[..i],url$=link[i+1..] + TPrint ""+ident+"
" + Next + + TPrint "" + + StandardIOStream.Close + StandardIOStream=WriteStream( BlitzMaxPath()+"/doc/bmxmods/commands.txt" ) + For Local t$=EachIn comms + TPrint t + Next + StandardIOStream.Close + StandardIOStream=stdio + +End Function + diff --git a/src/docmods/docmods.bmx b/src/docmods/docmods.bmx new file mode 100644 index 0000000..b972a6f --- /dev/null +++ b/src/docmods/docmods.bmx @@ -0,0 +1,157 @@ + +Rem + +Note: docmods is now only used to build 3rd party modules. + +BRL, MAXGUI and PUB mods are built by new makedocs. + +End Rem + +Strict + +Framework brl.basic + +Import "docparser.bmx" + +CreateDir BlitzMaxPath()+"/doc/bmxmods",True + +Local docs:TDocs=ParseMods() + +docs.Sort + +If AppArgs.length=2 And AppArgs[1].ToLower()="sync" + SyncDocs docs +Else + DocMods docs + SyncDocs docs +EndIf + +Function ParseMods:TDocs() + + Local docs:TDocs=New TDocs + + Local mods:TList=EnumModules() + + For Local modid$=EachIn mods + + If modid.StartsWith( "brl." ) Or modid.StartsWith( "pub." ) Or modid.StartsWith( "maxgui." ) Continue + + Local ident$=ModuleIdent( modid ) + Local modDir$=ModulePath( modid ) + Local bmxFile$=ModuleSource( modid ) + Local docDir$=modDir+"/doc" + + If FileType( modDir+"/"+ident+".bmx" )=FILETYPE_FILE + If FileType( docDir )<>FILETYPE_DIR + CreateDir docDir + If FileType( docDir )<>FILETYPE_DIR + Print "Failed to created directory:"+docDir + Continue + EndIf + EndIf + + Local toker:TBMXToker=TBMXToker.Create( bmxFile ) + + Local parser:TDocParser=TDocParser.WithToker( toker ) + + parser.Parse docs + + parser.Close + + EndIf + + Next + + Return docs + +End Function + +Function DocMods( docs:TDocs ) + + For Local t:TDocs=EachIn docs.kids + + If t.kind<>T_MODULE Continue + + ChangeDir ModulePath( t.ident.tolower() )+"/doc" 'linux fix for "BRL." case problem + + Local stdio:TStream=StandardIOStream + StandardIOStream=WriteFile( "commands.html" ) + + t.CacheTexts ' pre cache of text for output (allows us to use it for summaries etc) - BaH 03/09/2006 + t.EmitHtml + + StandardIOStream.Close + StandardIOStream=stdio + + Next +End Function + +Function SyncDocs( docs:TDocs ) + + Local comms:TList=New TList + Local index:TList=New TList + + Local stdio:TStream=StandardIOStream + StandardIOStream=WriteFile( BlitzMaxPath()+"/doc/bmxmods/navbar.html" ) + + TPrint "" + TPrint "" + TPrint "" + TPrint "" + + TPrint "Module reference
" + + For Local t:TDocs=EachIn docs.kids + + If t.kind<>T_MODULE Continue + + Local modid$=t.ident.ToLower() + Local modln$=modid.Replace(".","_") + Local moddesc$=t.bbdoc[6..].Trim() + Local i=moddesc.Find("~n") + If i<>-1 moddesc=moddesc[..i] + + Local url$="../../mod/"+modid.Replace(".",".mod/")+".mod/doc/commands.html" + + TPrint ""+moddesc+"
" + TPrint "
" + For Local p:TDocs=EachIn t.kids + ' kind is Type and is not doc'd and there are no doc'd kids + If p.kind = T_TYPE And (Not p.bbdoc And p.kids.count() = 0) Then + Continue + End If + Local turl$=url+"#"+p.ident + TPrint " "+p.ident+"
" + index.AddLast p.ident+":"+turl + Local i=p.proto.Find( " " ) + If i<>-1 comms.AddLast p.proto[i+1..].Trim()+"|"+turl[5..] + Next + TPrint "
" + + Next + + TPrint "
" + TPrint "Alphabetic index
" + + Local arr:Object[]=index.ToArray() + + arr.Sort + For Local link$=EachIn arr + Local i=link.Find( ":" ) + If i=-1 Throw "chunks" + Local ident$=link[..i],url$=link[i+1..] + TPrint ""+ident+"
" + Next + + TPrint "" + + StandardIOStream.Close + StandardIOStream=WriteStream( BlitzMaxPath()+"/doc/bmxmods/commands.txt" ) + For Local t$=EachIn comms + Print t + Next + StandardIOStream.Close + StandardIOStream=stdio + +End Function + diff --git a/src/docmods/docparser.bmx b/src/docmods/docparser.bmx new file mode 100644 index 0000000..ea4b0ec --- /dev/null +++ b/src/docmods/docparser.bmx @@ -0,0 +1,683 @@ +Strict + +Import BRL.StandardIO +Import BRL.MaxUtil + +Import "bmxtoker.bmx" + +Function FormatTags( t$ Var,bbTag$,htmlTag$ ) + Local i + Repeat + i=t.Find( bbTag,i ) + If i=-1 Exit + + If i And t[i-1]>32 + i:+1 + Continue + EndIf + + Local e + If i"+t[i+2..e]+"" + t=t[..i]+q+t[e+1..] + i:+q.length + + Else + e=i+1 + While eAsc("_") And Not IsAlphaNumeric(c) Exit + e:+1 + Wend + Local q$="<"+htmlTag+">"+t[i+1..e]+"" + t=t[..i]+q+t[e..] + i:+q.length + EndIf + Forever +End Function + +Function FindIdent( pre$,text$,i Var,e Var ) + + i=text.Find( pre,i ) + + If i=-1 Return False + + If i>0 And text[i-1]>32 Return False + + e=i+1 + While eAsc("_") And Not IsAlphaNumeric(c) Exit + e:+1 + Wend + Return True +End Function + +Function FormatText( t$ Var ) + + If t.length And IsAlphaNumeric( t[t.length-1] ) t:+"." + + Local i,e + + 'do links + i=0 + While FindIdent( "#",t,i,e ) + + While e"+id+"" + Else + ln=""+id+"" + EndIf + + t=t[..i]+ln+t[e..] + i:+ln.length + Wend + + FormatTags t,"@","b" + FormatTags t,"%","i" + FormatTags t,"&","pre" + + 'do tables + i=0 + Repeat + i=t.Find( "~n[",i ) + If i=-1 Exit + + Local i2=t.Find( "~n]",i+3 ) + If i2=-1 Exit + + Local q$=t[i+2..i2] + + q=q.Replace( "* ","" ) + q=q.Replace( " | ","" ) + + q="~n
"+q+"
~n" + + t=t[..i]+q+t[i2+2..] + i:+q.length + + Forever + + 'paras + t=t.Replace( "~n~n","~n

~n" ) + +End Function + +Type TDocs + + Field kind 'T_CONST, T_GLOBAL etc + Field ident$ 'identifier + Field proto$ 'Global/Function etc text + Field bbdoc$ 'rem/endrem bbdoc: text + Field infos:TList=New TList 'ModuleInfo's + Field imports:TList = New TList ' Added by BaH - 25/05/2006 + Field parent:TDocs ' Added by BaH - 25/05/2006 + Field textCache:TTextCache + + Field kids:TList=New TList + + Method Sort() + kids.Sort + For Local t:TDocs=EachIn kids + t.Sort + Next + End Method + + ' Added by BaH - 25/05/2006 + ' Checks for extra references to the same imports... + Method containsImport:Int(_file:String) + If imports.contains(_file) Then + Return True + End If + If parent <> Null Then + Return parent.containsImport(_file) + End If + + Return False + End Method + + Method Compare( with:Object ) + Local t:TDocs=TDocs(with) + + If kind=t.kind + If kind=T_MODULE + Return bbdoc.ToLower().Compare( t.bbdoc.ToLower() ) + EndIf + Return ident.ToLower().Compare( t.ident.ToLower() ) + EndIf + + If kind=T_MODULE Return -1 + If t.kind=T_MODULE Return 1 + + If kind=T_CONST Return -1 + If t.kind=T_CONST Return 1 + + If kind=T_GLOBAL Return -1 + If t.kind=T_GLOBAL Return 1 + + If kind=T_FIELD Return -1 + If t.kind=T_FIELD Return 1 + + If kind=T_METHOD Return -1 + If t.kind=T_METHOD Return 1 + + If kind=T_FUNCTION Return -1 + If t.kind=T_FUNCTION Return 1 + + If kind=T_TYPE Return -1 + If t.kind=T_TYPE Return 1 + + If kind=T_EOL Return -1 + If t.kind=T_EOL Return 1 + + Throw "OOps!" + + End Method + + Method CacheTexts() + If textCache = Null Then + textCache = TTextCache.Create(bbdoc, kind) + + If Not kids.IsEmpty() + Local tkind=0 + For Local doc:TDocs=EachIn kids + doc.CacheTexts() + Next + EndIf + + End If + + End Method + + Method EmitHtml$(summary:Int = False) + Local example$ + + Local stream:TStream=ReadStream( ident+".bmx" ) + If stream + While Not stream.Eof() + example:+stream.ReadLine()+"~n" + Wend + stream.Close + example=example.Trim() + EndIf + + If Not summary Then + Select kind + Case 0 + Case T_MODULE + TPrint ""+textCache.shortdesc+" reference" + TPrint "" + TPrint "" + Local n_consts + Local n_globals + Local n_functions + Local n_types + Local n_keywords:Int + For Local t:TDocs=EachIn kids + Select t.kind + Case T_CONST n_consts:+1 + Case T_GLOBAL n_globals:+1 + Case T_FUNCTION n_functions:+1 + Case T_TYPE + ' Type is doc'd or there are doc'd kids + If t.bbdoc Or t.kids.count() > 0 Then + n_types:+1 + End If + Case T_EOL n_keywords:+1 + End Select + Next + + TPrint "" + TPrint "" + If n_consts TPrint "" + If n_globals TPrint "" + If n_functions TPrint "" + If n_types TPrint "" + If n_keywords TPrint "" + + If Not infos.IsEmpty() + TPrint "" + EndIf + + Local t$=ModuleSource( ident.ToLower() ) + Local i=t.Find( "/mod/" ) + If i<>-1 + t="../../../.."+t[i..] + TPrint "" + EndIf + TPrint "
 "+ident+":ConstantsGlobalsFunctionsTypesKeywordsModinfoSource 
" + + Local stream:TStream=ReadStream( "intro.bbdoc" ) + + If stream + TPrint "

"+textCache.shortdesc+"

" + If textCache.longdesc TPrint textCache.longdesc.Trim() + Else + stream=ReadStream( "intro.html" ) + If Not stream + TPrint "

"+textCache.shortdesc+"

" + If textCache.longdesc TPrint textCache.longdesc.Trim() + EndIf + EndIf + + If stream + Local intro$ + While Not stream.Eof() + intro:+stream.ReadLine()+"~n" + Wend + Local i=intro.Find("") + If i<>-1 + intro=intro[i+6..] + i=intro.find("") + If i<>-1 intro=intro[..i] + EndIf + stream.close + intro=intro.Trim() + FormatText intro + TPrint intro + EndIf + + ' Show summaries + If Not kids.IsEmpty() + Local s:String = Null + Local tkind=0 + Local count:Int = 0 + For Local doc:TDocs=EachIn kids + ' kind is Type and is not doc'd and there are no doc'd kids + If doc.kind = T_TYPE And (Not doc.bbdoc And doc.kids.count() = 0) Then + Continue + End If + + s = "" + + If kind<>T_TYPE And doc.kind<>tkind + count = 0 + If tkind <> 0 Then + If tkind = T_CONST Or tkind = T_GLOBAL Or tkind = T_EOL Then + TPrint "" + End If + TPrint "
" + End If + Select doc.kind + Case T_CONST TPrint "

Constants Summary

" + s + "" + Case T_GLOBAL TPrint "

Globals Summary

" + s + "" + Case T_FUNCTION TPrint "

Functions Summary

" + s + Case T_TYPE TPrint "

Types Summary

" + s + Case T_EOL TPrint "

Keywords Summary

" + s + "" + Default + Continue + End Select + tkind=doc.kind + EndIf + count:+ 1 + If count > 1 Then + If tkind = T_CONST Or tkind = T_GLOBAL Or tkind = T_EOL Then + TPrint ", " + End If + End If + doc.EmitHtml(True) + Next + If s Then + TPrint "" + End If + EndIf + + Default + TPrint "" + TPrint "" + If textCache.returns + TPrint "" + EndIf + If textCache.shortdesc + TPrint "" + EndIf + If textCache.longdesc + TPrint "" + EndIf + If example + TPrint "" + EndIf + TPrint "
"+proto+"
Returns"+textCache.returns+"
Description"+textCache.shortdesc+"
Information"+textCache.longdesc+"
Example
"+example+"
" + End Select + + If kind = T_TYPE Then + ' Show summaries + If Not kids.IsEmpty() + Local s:String = Null + Local tkind=0 + Local count:Int = 0 + For Local doc:TDocs=EachIn kids + s = "" + .. + "" + End If + TPrint "
" + If doc.kind<>tkind + count = 0 + If tkind <> 0 Then + If tkind = T_CONST Or tkind = T_GLOBAL Or tkind = T_EOL Or tkind = T_FIELD Then + TPrint "
" + End If + Select doc.kind + Case T_CONST TPrint s + "Constants Summary" + Case T_FIELD TPrint s + "Fields Summary" + Case T_GLOBAL TPrint s + "Globals Summary" + Case T_FUNCTION TPrint s + "Functions Summary" + Case T_METHOD TPrint s + "Methods Summary" + Default + Continue + End Select + tkind=doc.kind + EndIf + count:+ 1 + If count > 1 Then + If tkind = T_CONST Or tkind = T_GLOBAL Or tkind = T_FIELD Then + TPrint ", " + End If + End If + doc.EmitHtml(True) + Next + If s Then + TPrint "" + End If + EndIf + End If + + If Not kids.IsEmpty() + Local tkind=0 + For Local doc:TDocs=EachIn kids + ' kind is Type and is not doc'd and there are no doc'd kids + If doc.kind = T_TYPE And (Not doc.bbdoc And doc.kids.count() = 0) Then + Continue + End If + + If kind<>T_TYPE And doc.kind<>tkind + TPrint "Constants" + Case T_GLOBAL TPrint " id=globalsdet>Globals" + Case T_FUNCTION TPrint " id=functionsdet>Functions" + Case T_TYPE TPrint " id=typesdet>Types" + Case T_FIELD TPrint ">Fields" + Case T_METHOD TPrint ">Methods" + Case T_EOL TPrint ">Keywords" + End Select + TPrint "" + tkind=doc.kind + EndIf + doc.EmitHtml + If kind<>T_TYPE TPrint "
" + Next + EndIf + Else + Select kind + Case T_CONST, T_GLOBAL, T_FIELD, T_EOL + TPrint "" + ident + "" + Case T_FUNCTION, T_METHOD, T_TYPE + TPrint "" + ident + "" + If textCache.shortdesc + TPrint textCache.shortdesc + Else + TPrint " " + EndIf + TPrint "" + End Select + End If + + + + Select kind + Case T_MODULE + If Not infos.IsEmpty() + TPrint "

Module Information

" + TPrint "" + For Local t$=EachIn infos + t=t[1..t.length-1] + If Not t Continue + Local i=t.Find(":") + If i=-1 Continue + Local key$=t[..i].Trim() + Local val$=t[i+1..].Trim() + TPrint "" + Next + EndIf + TPrint "" + End Select + + End Method + +End Type + +Type TDocParser + + Field toker:TBMXToker + + Method Close() + If Not toker Return + toker.Close + toker=Null + End Method + + ' Added by BaH - 25/05/2006 + Method processImport(parent:TDocs, txt:String) + + If txt.find(".bmx") > 0 Then + + Local importFile:String = ExtractDir(toker._filename) + "/" + txt[1..txt.length-1] + Local _file:String = StripDir(importFile).toLower() + + If Not parent.containsImport(_file) Then + If FileType(importFile) = 1 Then ' exists ! + parent.imports.AddLast(_file) + Local _toker:TBMXToker=TBMXToker.Create(importFile) + Local _parser:TDocParser=TDocParser.WithToker(_toker) + _parser.Parse parent + _parser.Close + End If + End If + End If + + End Method + + Method Parse( parent:TDocs ) + + If Not toker Throw "closed" + + While toker.Curr()<>T_EOF + + If toker.Curr()=T_ENDTYPE + If parent.kind=T_TYPE Return + toker.Bump + Continue + EndIf + + ' Added by BaH - 25/05/2006, modified 01/06/2006 + If toker.Curr()=T_IMPORT Or toker.Curr()=T_INCLUDE Then + If parent.kind=T_MODULE + If toker.Bump()=T_STRINGLIT + processImport(parent, Toker.Text()) + End If + End If + End If + + If toker.Curr()=T_MODULEINFO + If parent.kind=T_MODULE + If toker.Bump()=T_STRINGLIT + parent.infos.AddLast toker.Text() + EndIf + EndIf + toker.Bump + Continue + EndIf + + Local skip:Int = False + Local kind:Int + Local bbdoc$ + + If toker.Curr()<>T_REM + kind = toker.Bump() + + ' Fix to stop fields / methods etc appearing at top of docs if type not doc'd. + If kind = T_TYPE Then + skip = True + Else + Continue + End If + EndIf + + If Not skip Then + If toker.Text()[..6]<>"bbdoc:" + toker.Bump + Continue + EndIf + bbdoc = toker.Text() + + + kind=toker.Bump() + If kind<>T_CONST And kind<>T_GLOBAL And kind<>T_FUNCTION And kind<>T_METHOD.. + And kind<>T_MODULE And kind<>T_TYPE And kind<>T_FIELD And kind<>T_EOL + toker.Bump + Continue + EndIf + End If + + Local ident$,proto$ + If kind=T_EOL + Local i=bbdoc.Find( "keyword:" ) + If i=-1 Continue + ident=bbdoc[i+8..].Replace( "~q","" ) + proto="Keyword "+ident.Trim() + Else + proto=toker.Line() + If toker.Bump()<>T_IDENT Continue + ident=toker.Text() + If toker.Bump()=Asc(".") And toker.Bump()=T_IDENT + ident:+"."+toker.Text() + EndIf + toker.Bump + Select kind + Case T_CONST,T_GLOBAL + Local i=proto.Find( "=" ) + If i<>-1 proto=proto[..i] + Case T_FUNCTION,T_METHOD + Local i=proto.Find( "=" ) + While i<>-1 And proto.Find( ")",i+1 )<>-1 + i=proto.Find( "=",i+1 ) + Wend + If i<>-1 proto=proto[..i] + + + i=proto.toLower().Find( "nodebug" ) + While i<>-1 And proto.Find( ")",i+1 )<>-1 + i=proto.toLower().Find( "nodebug",i+1 ) + Wend + If i<>-1 proto=proto[..i] + + End Select + EndIf + + 'got a valid Doc! + Local docs:TDocs=New TDocs + docs.kind=kind + docs.ident=ident.Trim() + docs.proto=proto.Trim() + docs.bbdoc=bbdoc.Trim() + docs.parent = parent ' Added by BaH - 25/05/2006 + + parent.kids.AddLast docs + + If kind=T_MODULE Or kind=T_TYPE + Parse docs + EndIf + + Wend + + End Method + + Function WithToker:TDocParser( toker:TBmxToker ) + + Local t:TDocParser=New TDocParser + t.toker=toker + Return t + + End Function + +End Type + +Type TTextCache + Field returns:String + Field keyword:String + Field shortdesc:String + Field longdesc:String + Field parameters:TList = New TList + + Function Create:TTextCache(bbdoc:String, kind:Int) + Local this:TTextCache = New TTextCache + + Local d:String = bbdoc + + Local i=d.Find("~n") + If i=-1 i=d.length + this.shortdesc=d[6..i].Trim() + d=d[i+1..].Trim() + If kind<>T_MODULE + FormatText this.shortdesc + EndIf + + If d.Find( "returns:" )=0 + Local i=d.Find("~n") + If i=-1 i=d.length + this.returns=d[8..i].Trim() + d=d[i+1..].Trim() + FormatText this.returns + Else If d.Find( "keyword:" )=0 + Local i=d.Find("~n") + If i=-1 i=d.length + this.keyword=d[8..i].Trim() + d=d[i+1..].Trim() + FormatText this.keyword + EndIf + While d.Find("parameter:") = 0 + Local i=d.Find("~n") + If i=-1 i=d.length + Local param:String=d[10..i].Trim() + d=d[i+1..].Trim() + FormatText param + this.parameters.addLast(param) + Wend + If d.Find( "about:" )=0 + this.longdesc=d[6..].Trim() + FormatText this.longdesc + EndIf + + Return this + End Function +End Type \ No newline at end of file diff --git a/src/fasm2as/fasm2as.bmx b/src/fasm2as/fasm2as.bmx new file mode 100644 index 0000000..1184d0b --- /dev/null +++ b/src/fasm2as/fasm2as.bmx @@ -0,0 +1,245 @@ + +Strict + +Framework brl.basic + +Import Pub.StdC + +Try + +ChangeDir LaunchDir + +If AppArgs.length<>2 And AppArgs.length<>3 + Print "Command line error" + End +EndIf + +Local in:TStream=CreateBankStream( LoadBank( AppArgs[1] ) ),out:TStream + +If AppArgs.length=3 + out=WriteStream( AppArgs[2] ) +Else + out=WriteStream( AppArgs[1] ) +EndIf + +If Not out Throw "Failed to create output stream" + +While Not in.Eof() + + Local line$=in.ReadLine() + + Local t_line$=line + + If line And line[0]=9 + Local i=line.Find( Chr(9),1 ) + If i<>-1 + Local sz + Local op$=line[1..i] + Local args$=line[i+1..] + Select op + Case "db" + op=".byte" + If args[..1]="~q" And args[args.length-3..]="~q,0" + op=".asciz" + args="~q"+args[1..args.length-3]+"~q" + EndIf + Case "dw" + op=".short" + Case "dd" + op=".long" + Case "extrn" + op=".reference" + Case "public" + op=".globl" + Case "align" + op=".align" + Case "file" + If args[..1]="~q" And args[args.length-1..]="~q" + args=args[1..args.length-1] + EndIf + Local r:TStream=ReadStream( args ),n + If Not r Throw "Unable to open file "+args + Local buf:Byte[64] + Repeat + Local n=r.Read( buf,64 ) + If Not n Exit + Local t$="~t.byte~t"+buf[0] + For Local i=1 Until n + t:+","+buf[i] + Next + WriteString out,t + WriteString out,"~n" + Forever + r.Close + op="" + Case "section" + op="."+args + args="" + Default + Local i=args.Find( "," ) + If i=-1 + sz=FixArg( args,op ) + If sz And op="call" args="*"+args + Else + Local arg2$=args[..i] + Local arg1$=args[i+1..] + Local sz1=FixArg( arg1 ) + Local sz2=FixArg( arg2 ) + sz=sz1 + If sz2 sz=sz2 + Select op + Case "lea" + sz=0 + Case "movzx","movsx" + Local s$ + Select sz + Case 1 + s="b" + Case 2 + s="w" + Case 0 + Select arg1 + Case "%al","%bl","%cl","%dl" + s$="b" + Case "%ax","%bx","%cx","%dx","%bp","%sp","%si","%di" + s$="w" + End Select + End Select + op=op[..4]+s+"l" + sz=0 + Case "fdiv","fsub" + If arg1="%st(0)" And arg2[..3]="%st" op=op[..4]+"r" + Case "fdivp","fsubp" + If arg1="%st(0)" And arg2[..3]="%st" op=op[..4]+"rp" + Case "fdivr","fsubr" + If arg1="%st(0)" And arg2[..3]="%st" op=op[..4] + Case "fdivrp","fsubrp" + If arg1="%st(0)" And arg2[..3]="%st" op=op[..4]+"p" + End Select + args=arg1+","+arg2 + EndIf + Local fpsz + If op And op[0]=Asc("f") + Select op + Case .. + "fild","fist","fistp","ficom","ficomp",.. + "fiadd","fisub","fimul","fidiv","fidivr" + Default + fpsz=True + End Select + EndIf + Select sz + Case 1 + op:+"b" + Case 2 + op:+"w" + Case 4 + If fpsz op:+"s" Else op:+"l" + Case 8 + If fpsz op:+"l" Else op:+"q" + End Select + End Select + If op line=Chr(9)+op+Chr(9)+args Else line="" + EndIf + EndIf + + WriteString out,line + WriteString out,"~n" + +Wend + +in.Close + +Catch ex:Object + + WriteStdout "fasm2as failed: "+ex.ToString()+"~n" + + exit_ -1 + +End Try + +End + +Function RegSize( r$ ) + Select r + Case "eax","ebx","ecx","edx","esi","edi","ebp","esp" + Return 4 + End Select +End Function + +Function FixArg( arg$ Var,op$="" ) + + If Not arg.EndsWith( "]" ) + If arg[0]=Asc("_") + If op[0]<>Asc("j") And op<>"call" arg="$"+arg + Else If (arg[0]>=48 And arg[0]<=57) Or arg[0]=Asc("-") + arg="$"+arg + Else + If arg[..2]="st" arg="st("+arg[2..]+")" + arg="%"+arg + EndIf + Return 0 + EndIf + + Local sz + + If arg.StartsWith( "[" ) + arg=arg[1..arg.length-1] + Else If arg.StartsWith( "byte [" ) + arg=arg[6..arg.length-1] + sz=1 + Else If arg.StartsWith( "word [" ) + arg=arg[6..arg.length-1] + sz=2 + Else If arg.StartsWith( "dword [" ) + arg=arg[7..arg.length-1] + sz=4 + Else If arg.StartsWith( "qword [" ) + arg=arg[7..arg.length-1] + sz=8 + Else + Throw "Error" + EndIf + + arg=arg.Replace( "+-","-" ) + arg=arg.Replace( "-","+-" ) + + Local base$,index$,scale$,disp$ + For Local t$=EachIn arg.Split( "+" ) + If t.Contains("*") + If index Throw "Error" + Local bits$[]=t.Split( "*" ) + If bits.length<>2 Throw "Error" + index=bits[0] + scale=bits[1] + Else If RegSize( t ) + If base + If index Throw "Error" + index=t + scale="1" + Else + base=t + EndIf + Else + If disp + disp=(disp+"+"+t).Replace( "+-","-" ) + Else + disp=t + EndIf + EndIf + Next + + If base And index + arg=disp+"(%"+base+",%"+index+","+scale+")" + Else If base + arg=disp+"(%"+base+")" + Else If index + arg=disp+"(,%"+index+","+scale+")" + Else If disp + arg=disp + Else + Throw "Error!" + EndIf + + Return sz +End Function diff --git a/src/makedocs/bbdoc.bmx b/src/makedocs/bbdoc.bmx new file mode 100644 index 0000000..e0947a4 --- /dev/null +++ b/src/makedocs/bbdoc.bmx @@ -0,0 +1,202 @@ + +Strict + +Import "parse.bmx" + +'still pretty ugly - could probably be done in a few lines with Bah.RegEx! + +Type TBBLinkResolver + + Method ResolveLink$( link$ ) Abstract + +End Type + +Private + +'finds a 'span' style tag starting at index i - eg: @bold or @{this is bold} +' +'returns bit after tag, fills in b and e with begin and end of range. +Function FindBBTag$( text$,tag$,i,b Var,e Var ) + Repeat + i=text.Find( tag,i ) + If i=-1 Return + If i=0 Or text[i-1]<=32 Or text[i-1]=Asc(">") Exit + i:+tag.length + Forever + b=i + i:+1 + If i=text.length Return + Local t$ + If text[i]=Asc("{") + i:+1 + While iAsc("}") + i:+1 + Wend + t=text[b+2..i] + If i"+t+"" + text=text[..b]+t+text[e..] + i=b+t.length + Forever +End Function + +Public + +Function BBToHtml2$( text$,doc:TBBLinkResolver ) + Local i + + 'newlines + text=text.Replace( "\~n","
~n" ) + + 'paras + text=text.Replace( "~n~n","~n

~n" ) + + 'tabs + text=text.Replace( "~n~t","~n   " ) + + 'headings + i=0 + Local hl=1 + Repeat + i=text.Find( "~n+",i ) + If i=-1 Exit + + Local i2=text.Find( "~n",i+2 ) + If i2=-1 Exit + + Local q$=text[i+2..i2] + q=""+q+"" + + If hl=1 hl=2 + + text=text[..i]+q+text[i2..] + i:+q.length + Forever + + 'tables + i=0 + Repeat + i=text.Find( "~n[",i ) + If i=-1 Exit + + Local i2=text.Find( "~n]",i+2 ) + If i2=-1 Exit + + Local q$=text[i+2..i2] + + If q.Find( " | " )=-1 'list? + q=q.Replace( "~n*","

  • " ) + q="
      "+q+"
    " + Else + q=q.Replace( "~n*","
  • "+key+""+val+"
    " ) + q=q.Replace( " | ","" ) + q="~n
    "+q+"
    ~n" + EndIf + + text=text[..i]+q+text[i2+2..] + i:+q.length + Forever + + 'quotes + i=0 + Repeat + i=text.Find( "~n{",i ) + If i=-1 Exit + + Local i2=text.Find( "~n}",i+2 ) + If i2=-1 Exit + + Local q$=text[i+2..i2] + + q="
    "+q+"
    " + + text=text[..i]+q+text[i2+2..] + i:+q.length + Forever + + 'links + i=0 + Repeat + Local b,e + Local t$=FindBBTag( text,"#",i,b,e ) + If Not t Exit + + t=doc.ResolveLink( t ) + text=text[..b]+t+Text[e..] + i=b+t.length + Forever + + 'span tags + FormatBBTags text,"@","b" + + FormatBBTags text,"%","i" + + 'escapes + i=0 + Repeat + i=text.Find( "~~",i ) + If i=-1 Or i=text.length-1 Exit + + Local r$=Chr( text[i+1] ) + Select r + Case "<" r="<" + Case ">" r=">" + End Select + + text=text[..i]+r+text[i+2..] + i:+r.length + Forever + + Return text + +End Function + +'bbdoc to html conversion +Function BBToHtml$( text$,doc:TBBLinkResolver ) + + text=text.Replace( "~r~n","~n" ) + + Local out$,i + + 'preformatted code... + Repeat + Local i1=text.Find( "~n{{",i ) + If i1=-1 Exit + + Local i2=text.Find( "~n}}",i+3 ) + If i2=-1 Exit + + out:+BBToHtml2( text[i..i1],doc ) + + out:+"
    "+text[i1+3..i2].Trim()+"
    " + + i=i2+3 + Forever + + out:+BBToHtml2( text[i..],doc ) + + Return out + +End Function + diff --git a/src/makedocs/docnode.bmx b/src/makedocs/docnode.bmx new file mode 100644 index 0000000..30c5a93 --- /dev/null +++ b/src/makedocs/docnode.bmx @@ -0,0 +1,69 @@ + +Strict + +Import BRL.Map + +Type TDocNode + + Field id$ 'eg: BRL.Audio + Field path$ 'eg: Modules/Audio/Audio" + Field kind$ 'eg: "Module", "Function", "Type" etc + + Field proto$ 'eg: Function LoadImage(...) + Field bbdoc$ 'eg: Load an image (shortdesc?) + Field returns$ 'eg: A new image + Field about$ 'eg: blah etc blah (longdesc?) + Field params:TList 'eg: [x - the x coord, y - the y coord] + + Field docDir$ 'eg: ../mod/brl.mod/max2d.mod/doc + Field example$ 'eg: LoadImage.bmx (path) + + Field children:TList=New TList + + Function ForPath:TDocNode( path$ ) + + Return TDocNode( _pathMap.ValueForKey( path ) ) + + End Function + + Function Create:TDocNode( id$,path$,kind$ ) + + Local t:TDocNode=TDocNode( _pathMap.ValueForKey( path ) ) + + If t + If t.kind<>"/" And t.path<>path Throw "ERROR: "+t.kind+" "+kind + If t.path = path Return t + Else + t=New TDocNode + _pathMap.Insert path,t + EndIf + + t.id=id + t.path=path + t.kind=kind + + Local q:TDocNode=t + + Repeat + If path="/" Exit + path=ExtractDir( path ) + Local p:TDocNode=TDocNode( _pathMap.ValueForKey( path ) ) + If p + p.children.AddLast q + Exit + EndIf + p=New TDocNode + p.id=StripDir(path) + p.path=path + p.kind="/" + p.children.AddLast q + _pathMap.Insert path,p + q=p + Forever + + Return t + End Function + + Global _pathMap:TMap=New TMap + +End Type diff --git a/src/makedocs/docstyle.bmx b/src/makedocs/docstyle.bmx new file mode 100644 index 0000000..76a4fa6 --- /dev/null +++ b/src/makedocs/docstyle.bmx @@ -0,0 +1,191 @@ + +Strict + +Import BRL.MaxUtil + +Import "bbdoc.bmx" + +Import "docnode.bmx" + +Global BmxDocDir$=BlitzMaxPath()+"/docs/html" + +Global NodeKinds$[]=[ "/","Module","Type" ] + +Global LeafKinds$[]=[ "Const","Field","Global","Method","Function","Keyword" ] + +Global AllKinds$[]=NodeKinds+LeafKinds + +Type TDocStyle Extends TBBLinkResolver + + Field html$ + Field doc:TDocNode + Field children:TMap + Field docURL$ + Field absDocDir$ 'where output doc goes + Field relRootDir$ 'relative path to root doc dir + + Global commands:TMap=New TMap + + Method NodeIsleaf( node:TDocNode ) + For Local t$=EachIn LeafKinds + If t=node.kind Return True + Next + End Method + + Method FindNode:TDocNode( node:TDocNode,id$ ) + + If node.id.ToLower()=id Return node + + If node.path.Tolower().EndsWith( "/"+id ) Return node + + For Local t:TDocNode=EachIn node.children + Local p:TDocNode=FindNode( t,id ) + If p Return p + Next + End Method + + Method NodeURL$( node:TDocNode ) + If node.kind="Topic" + Return node.path+".html" + Else If NodeIsLeaf( node ) + Return ExtractDir( node.path )+"/index.html#"+node.id + Else If node.path<>"/" + Return node.path+"/index.html" + Else + Return "/index.html" + EndIf + End Method + + Method ResolveLink$( link$ ) + Local id$=link.ToLower() + + Local node:TDocNode=FindNode( doc,id ) + + If Not node + node=FindNode( TDocNode.ForPath( "/" ),id ) + EndIf + + If Not node + Print "Error: Unable to resolve link '"+link+"'" + Return link + EndIf + + Local url$=nodeURL( node ) + + 'optimize links... + If url.StartsWith( docURL+"#" ) + url=url[ docURL.length.. ] + Else If url.StartsWith( doc.path+"/" ) + url="~q"+url[ doc.path.length+1.. ]+"~q" + Else + url="~q"+relRootDir+url+"~q" + EndIf + Return ""+link+"" + End Method + + Method EmitDoc( node:TDocNode ) + + Print "Building: "+node.id + + html="" + doc=node + children=New TMap + docURL=NodeURL( doc ) + absDocDir=BmxDocDir+ExtractDir( docURL ) + relRootDir="" + + Local p$=ExtractDir( docURL ) + While p<>"/" + If relRootDir relRootDir:+"/" + relRootDir:+".." + p=ExtractDir( p ) + Wend + If Not relRootDir relRootDir="." + + CreateDir absDocDir,True + + If doc.docDir CopyDir doc.docDir,absDocDir + + If docURL.EndsWith( "/index.html" ) + Local intro$=absDocDir+"/index.bbdoc" + If FileType( intro )<>FILETYPE_FILE intro$=absDocDir+"/intro.bbdoc" + If FileType( intro )=FILETYPE_FILE + Local t$=LoadText( intro ) + If t.find( "commands.html" )<>-1 + Print "Error: Document contains 'commands.html'" + EndIf + doc.about=t+doc.about + EndIf + EndIf + + For Local t:TDocNode=EachIn doc.children + + Local list:TList=TList( children.ValueForKey( t.kind ) ) + If Not list + list=New TList + children.Insert t.kind,list + EndIf + + list.AddLast t + + 'update commands.txt + Select t.kind + Case "Keyword" + commands.Insert t.id+" : "+t.bbdoc,NodeURL( t ) + Case "Const","Global","Function","Type" ',"Module" + Local i=t.proto.Find( " " ) + If i<>-1 commands.Insert t.proto[i+1..].Trim()+" : "+t.bbdoc,NodeURL( t ) + End Select + Next + + EmitHeader + + For Local t$=EachIn NodeKinds + EmitLinks t + Next + + For Local t$=EachIn LeafKinds + EmitLinks t + Next + + For Local t$=EachIn LeafKinds + EmitDecls t + Next + + EmitFooter + + html=BBToHtml( html,Self ) + + SaveText html,BmxDocDir+docURL + + For Local t$=EachIn NodeKinds + EmitNodes t + Next + + End Method + + Method EmitNodes( kind$ ) + Local list:TList=TList( children.ValueForKey( kind ) ) + If Not list Return + For Local t:TDocNode=EachIn list + EmitDoc t + Next + End Method + + Method Emit( t$ ) + html:+t+"~n" + End Method + + Method ChildList:TList( kind$ ) + Return TList( children.ValueForKey( kind ) ) + End Method + + Method EmitHeader() Abstract + + Method EmitFooter() Abstract + + Method EmitLinks( kind$ ) Abstract + + Method EmitDecls( kind$ ) Abstract + +End Type diff --git a/src/makedocs/fredborgstyle.bmx b/src/makedocs/fredborgstyle.bmx new file mode 100644 index 0000000..8fe86f6 --- /dev/null +++ b/src/makedocs/fredborgstyle.bmx @@ -0,0 +1,128 @@ + +Strict + +Import "docstyle.bmx" + +Type TFredborgStyle Extends TDocStyle + + Method EmitHeader() + + 'emit HTML header + Emit ""+doc.id+"" + Emit "" + Emit "" + + 'emit title bar + Emit "" + Emit "" + + 'emit links to summaries + For Local t$=EachIn allKinds + If t="/" Or Not ChildList( t ) Continue + + Emit "" + + Next + + 'emit link to module source + If doc.kind="Module" + Local t$=ModuleSource( doc.id.ToLower() ),i=t.Find( "/mod/" ) + If i<>-1 + t=relRootDir+"/../.."+t[i..] + Emit "" + EndIf + EndIf + + 'end title bar + Emit "
     "+doc.id+":"+t+"sSource 


    " + + 'emit about + If doc.about Emit doc.about+"

    " + + End Method + + Method EmitFooter() + + 'emit HTML footer + Emit "" + + End Method + + Method EmitLinks( kind$ ) + + Local list:TList=ChildList( kind ) + If Not list Return + + 'emit anchor: _Const, _Function etc... + + If kind="/" + + Emit "" + + For Local t:TDocNode=EachIn list + + Emit "" + + Next + + Emit "
    #{"+t.id+"}
    " + + Else + + Emit "" + Emit "

    "+kind+"s

    " + + Emit "" + + For Local t:TDocNode=EachIn list + + Emit "" + + Next + + Emit "
    #{"+t.id+"}"+t.bbdoc+"
    " + EndIf + + End Method + + Method EmitDecls( kind$ ) + + Local list:TList=ChildList( kind ) + If Not list Return + + Emit "

    "+kind+" reference

    " + + For Local t:TDocNode=EachIn list + + Emit "" + + Emit "

    " + Emit "" + + If t.returns + Emit "" + EndIf + + If t.bbdoc + Emit "" + EndIf + + If t.about + Emit "" + EndIf + + If t.example + Local p$=t.example + Local link$="Example" + Local code$=LoadText( absDocDir+"/"+p ).Trim() + code="~n{{~n"+code+"~n}}~n" + Emit "" + EndIf + + Emit "
    "+t.proto+"
    Returns"+t.returns+"
    Description"+t.bbdoc+"
    Information"+t.about+"
    "+link+""+code+"
    " + + Next + + End Method + +End Type diff --git a/src/makedocs/makedocs.bmx b/src/makedocs/makedocs.bmx new file mode 100644 index 0000000..81a2a10 --- /dev/null +++ b/src/makedocs/makedocs.bmx @@ -0,0 +1,286 @@ + +Rem + +Build new style docs. + +Only builds official docs and brl, maxgui and pub modules. + +Calls 'docmods' first, which builds 3rd party modules. + +End Rem + +Strict + +Framework BRL.Basic + +Import "docnode.bmx" + +Import "fredborgstyle.bmx" + +system_ "~q"+BlitzMaxPath()+"/bin/docmods~q" + +Local style:TDocStyle=New TFredborgStyle + +DeleteDir BmxDocDir,True + +CopyDir BlitzMaxPath()+"/docs/src",BmxDocDir + +Local root:TDocNode=TDocNode.Create( "BlitzMax Help","/","/" ) +root.about=LoadText( BmxDocDir+"/index.html" ) + +DocMods + +DocBBDocs "/" + +style.EmitDoc TDocNode.ForPath( "/" ) + +Local t$ +For Local kv:TKeyValue=EachIn TDocStyle.commands + t:+String( kv.Key() )+"|/docs/html"+String( kv.Value() )+"~n" +Next + +Local p$=BlitzMaxPath()+"/doc/bmxmods/commands.txt" +If FileType( p )=FILETYPE_FILE t:+LoadText( p ) + +SaveText t,BmxDocDir+"/Modules/commands.txt" + +Cleanup BmxDocDir + +'***** + +Function Cleanup( dir$ ) + For Local e$=EachIn LoadDir( dir ) + Local p$=dir+"/"+e + Select FileType( p ) + Case FILETYPE_DIR + Cleanup p + Case FILETYPE_FILE + If ExtractExt( e )="bbdoc" + DeleteFile p + Else If e.ToLower()="commands.html" + DeleteFile p + EndIf + End Select + Next +End Function + +Function DocMods() + + For Local modid$=EachIn EnumModules() + + If Not modid.StartsWith( "brl." ) And Not modid.StartsWith( "pub." ) And Not modid.StartsWith("maxgui.") Continue + + Local p$=ModuleSource( modid ) + Try + docBmxFile p,"" + Catch ex$ + Print "Error:"+ex + End Try + Next + +End Function + +Function DocBBDocs( docPath$ ) + + Local p$=BmxDocDir+docPath + + For Local e$=EachIn LoadDir( p ) + + Local q$=p+"/"+e + + Select FileType( q ) + Case FILETYPE_FILE + Select ExtractExt( e ) + Case "bbdoc" + Local id$=StripExt( e ) + If id="index" Or id="intro" Continue + + Local path$=(docPath+"/"+id).Replace( "//","/" ) + Local node:TDocNode=TDocNode.Create( id,path,"/" ) + + node.about=LoadText( q ) + End Select + Case FILETYPE_DIR + DocBBDocs docPath+"/"+e + End Select + Next + +End Function + +Function docBmxFile( filePath$,docPath$ ) + + If FileType( filePath )<>FILETYPE_FILE + Print "Error: Unable to open '"+filePath+"'" + Return + EndIf + + Local docDir$=ExtractDir( filePath )+"/doc" + If FileType( docDir )<>FILETYPE_DIR docDir="" + + Local inrem,typePath$,section$ + + Local bbdoc$,returns$,about$,keyword$,params:TList + + Local text$=LoadText( filepath ) + + For Local line$=EachIn text.Split( "~n" ) + + line=line.Trim() + Local tline$=line.ToLower() + + Local i + Local id$=ParseIdent( tline,i ) + + If id="end" id:+ParseIdent( tline,i ) + + If it.ToLower() Continue + kind=t + proto=line + id=ParseIdent( line,i ) + Exit + Next + EndIf + + If kind + + Local path$ + + Select kind + Case "Type" + If Not docPath Throw "No doc path" + If typePath Throw "Type path already set" + typePath=docPath + docPath:+"/"+id + path=docPath + Case "Module" + If docPath Throw "Doc path already set" + If bbdoc.FindLast( "/" )=-1 + bbdoc="Other/"+bbdoc + EndIf + docPath="/Modules/"+bbdoc + path=docPath + Local i=bbdoc.FindLast( "/" ) + bbdoc=bbdoc[i+1..] + Default + If Not docPath Throw "No doc path" + path=docPath+"/"+id + End Select + + Local i=proto.Find( ")=" ) + If i<>-1 + proto=proto[..i+1] + If id.StartsWith( "Sort" ) proto:+" )" 'lazy!!!!! + EndIf + i=proto.Find( "=New" ) + If i<>-1 + proto=proto[..i] + EndIf + + Local node:TDocNode=TDocNode.Create( id,path,kind ) + + node.proto=proto + node.bbdoc=bbdoc + node.returns=returns + node.about=about + node.params=params + + If kind="Module" node.docDir=docDir + + Local tmpExampleFilePath$ = CasedFileName(docDir+"/"+id+".bmx") + If docDir And FileType( tmpExampleFilePath )=FILETYPE_FILE + node.example=StripDir(tmpExampleFilePath) + EndIf + + EndIf + + bbdoc="" + + EndIf + Next + +End Function diff --git a/src/makedocs/parse.bmx b/src/makedocs/parse.bmx new file mode 100644 index 0000000..e76f500 --- /dev/null +++ b/src/makedocs/parse.bmx @@ -0,0 +1,61 @@ + +Strict + +Function IsAlphaChar( char ) + Return (char>=Asc("A") And char<=Asc("Z")) Or (char>=Asc("a") And char<=Asc("z")) Or (char=Asc("_")) +End Function + +Function IsDecChar( char ) + Return (char>=Asc("0") And char<=Asc("9")) +End Function + +Function IsIdentChar( char ) + Return IsAlphaChar( char ) Or IsDecChar( char ) +End Function + +Function IsHexChar( char ) + Return IsDecChar( char ) Or (char>=Asc("A") And char<=Asc("F")) Or (char>=Asc("a") And char<=Asc("f")) +End Function + +Function IsBinChar( char ) + Return (char>=Asc("0") And char<=Asc("1")) +End Function + +Function IsPunctChar( char ) + Select char + Case Asc("."),Asc(","),Asc(";"),Asc(":" ),Asc("!"),Asc("?") Return True + End Select +End Function + +Function ParseWS$( t$,i Var ) + Local i0=i + While iAsc("~q") Return + i:+1 + Local i0=i + While iAsc("~q" ) + i:+1 + Wend + Local q$=t[i0..i] + If i + + + + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + bmx + + CFBundleTypeIconFile + MaxIDE.icns + CFBundleTypeRole + Editor + LSIsAppleDefaultForType + + + + CFBundleExecutable + MaxIDE + CFBundleIconFile + MaxIDE.icns + CFBundlePackageType + APPL + + diff --git a/src/maxide/bmxlogo.png b/src/maxide/bmxlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..16dcc443f5d4a856c2a01efd9b4257e1fb658c66 GIT binary patch literal 9126 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 literal 0 HcmV?d00001 diff --git a/src/maxide/default.language.ini b/src/maxide/default.language.ini new file mode 100644 index 0000000..d521702 --- /dev/null +++ b/src/maxide/default.language.ini @@ -0,0 +1,307 @@ +[LanguageDefinition] + +LanguageID = English (English) +LanguageVersion = v1.07 +LanguageAuthor = BRL + +; Toolbar Tips +tb_new = "New" +tb_open = "Open" +tb_close = "Close" +tb_save = "Save" +tb_cut = "Cut" +tb_copy = "Copy" +tb_paste = "Paste" +tb_find = "Find" +tb_build = "Build" +tb_buildrun = "Build and Run" +tb_step = "Step" +tb_stepin = "Step In" +tb_stepout = "Step Out" +tb_stop = "Stop" +tb_home = "Home" +tb_back = "Back" +tb_forward = "Forward" +tb_continue = "Continue" + +; Tabs +tab_help = "Help" +tab_output = "Output" +tab_locked:%1 = "build:%1" + +; Navbar Tabs +navtab_home = "Home" +navtab_code = "Code" +navtab_debug = "Debug" + +; Navigation Tree Nodes +navnode_help = "Help" +navnode_projects = "Projects" +navnode_addproject = "Add Project" +navnode_moduleindex = "Index" +navnode_thirdpartymods = "Third party modules" + +; Misc Buttons +btn_ok = "OK" +btn_cancel = "Cancel" +btn_close = "Close" + +; Find Requester +find_window_title = "Find" +find_label_find = "Find" +find_btn_find = "Find" +find_notification_cannotfind = "Could not find '%1'." + +; Find & Replace Requester +replace_window_title = "Find and Replace" +replace_label_find = "Find" +replace_label_replacewith = "Replace With" +replace_btn_replace = "Replace" +replace_btn_replaceall = "Replace All" +replace_btn_findnext = "Find Next" + +; About Requester +about_window_title = "About MaxIDE" +about_label_bccver = "BCC Version" +about_label_bmxpath = "BlitzMax Path" +about_label_mingwpath = "MinGW Path" +about_label_fasmver = "FASM Version" +about_label_gccver = "GCC Version" +about_label_gplusplusver = "G++ Version" +about_error_unavailable = "Unavailable" +about_error_notapplicable = "n/a" + +; Goto Requester +goto_window_title = "Goto Line" +goto_label_linenum = "Line Number" +goto_btn_goto = "Goto" + +; Command Line Requester +cmdline_window_title = "Program Command Line" +cmdline_label_cmdline = "Program command line" + +; Progress Requester +progress_window_title = "Progress Requester" + +; Options Requester +options_window_title = "Options" +options_optionstab = "Options" +options_editortab = "Editor" +options_toolstab = "Tools" + +options_options_label_language = "Language" +options_options_btn_showtoolbar = "Show Toolbar" +options_options_btn_autorestore = "Open Files at Startup" +options_options_btn_autocaps = "Auto Capitalize" +options_options_btn_syntaxhighlight = "Syntax Highlighting" +options_options_btn_bracketmatching = "Enable .bmx Bracket Matching" +options_options_btn_autobackup = "Auto Backup" +options_options_btn_autoindent = "Auto Indent" +options_options_btn_autohideoutput = "Hide Output When Process Completes" +options_options_btn_useexternalbrowser = "Use External Help Browser" +options_options_btn_osshortcuts = "Use OS Specific Shortcut Keys" +options_options_btn_sortcodeviewnodes = "Sort Nodes in Code View" + +options_editor_label_background = "Background" +options_editor_itemlabel_tabsize = "Tab Size:" +options_editor_label_plaintext = "Plain Text" +options_editor_label_remarks = "Remarks" +options_editor_label_strings = "Strings" +options_editor_label_keywords = "Keywords" +options_editor_label_numbers = "Numbers" +options_editor_label_matchings = "Matchings" + +options_tools_label_output = "Output" +options_tools_label_navbar = "Navbar" + +options_font_desc_user = "User Defined Font" +options_font_desc_guidefault = "GUI Default Font" +options_font_desc_monospaced = "Monospaced Font" +options_font_desc_sansserif = "Sans Serif Font" +options_font_desc_serif = "Serif Font" +options_font_desc_script = "Script Font" + +; Text Style Control +txtstyle_underline = "Underline" +txtstyle_normal = "Normal" +txtstyle_bold = "Bold" +txtstyle_italic = "Italic" +txtstyle_bolditalic = "Bold + Italic" + +; Project Manager +projman_window_title = "Project Manager" +projman_btn_addproj = "Add Project" +projman_btn_delproj = "Remove Project" +projman_btn_moveup = "Move Up" +projman_btn_movedn = "Move Down" +projman_btn_properties = "Properties" + +; Project Requester +project_window_title = "Project Properties" +project_group_details = "Project Details" +project_label_name = "Name" +project_defaultname = "New Project" +project_label_path = "Path" +project_group_svn = "Subversion Control" +project_label_url = "URL" +project_label_username = "Username" +project_label_password = "Password" +project_btn_checkout = "Check Out Project" +project_btn_update = "Update Project" +project_btn_commit = "Commit Project" +project_requestfolder_title = "Select the project folder" + +; Syncmods Requester +syncmods_window_title = "Synchronize Modules" +syncmods_label_username = "Username" +syncmods_label_password = "Password" +syncmods_label_proxyserver = "Proxy Server" +syncmods_process_label = "Synchronize Modules" +syncmods_btn_sync = "Sync" + +; Search Requester +search_window_title = "Find In Files" +search_label_find = "Find" +search_label_filetypes = "File Types" +search_type_bmaxfiles = "BlitzMax Files" +search_type_codefiles = "Code Files" +search_type_allfiles = "All Files (Warning: Includes Binary Files)" +search_label_searchpath = "Search Path" +search_btn_searchsubfolders = "Search sub-directories" +search_btn_startsearch = "Search" +search_btn_stopsearch = "Stop Search" +search_requestfolder_title = "Choose a folder to search..." +search_msg_complete = "Search complete! Found %1 instance(s)!" +search_msg_searchingdir = "Searching: %1" +search_error_pathnotfound = "The specified path does not exist!" +search_error_pathisfile = "The path entered is for a file, please enter the path of a directory." +search_error_nosearchstring = "You must enter a search string to find!" +search_safetynotification = "There have been %1 instances found.\n\nWould you like to continue?" + +; Misc Notifications / Requesters +request_restart = "MaxIDE would like to restart." +request_closeall = "MaxIDE needs to close all documents before continuing." +request_openfile = "Open File" +request_saveas_title = "Save As" +request_importbb_title = "Import .bb file" +request_savechanges = "Save changes to %1?" +notify_demo_featureunavailable = "This feature is unavailable in the demo version of BlitzMax." + +; SVN Messages +svn_notification_nodehostnotfound = "Node host not found." +svn_notification_nodeprojectnotfound = "Node project not found." +svn_msg_updating = "Updating %1" +svn_msg_committing = "Committing %1" +svn_msg_checkingout = "Checking out %1 to %2" + +; Output Messages +output_error_compileerror = "Compile Error\n\n%1" +output_notification_stillbusy = "Current process still busy %1." +output_notification_processfailure = "Process failure with %1." +output_notification_processfailure = "Process failed to start %1." +output_msg_rebuildingdocs = "Rebuilding documentation..." +output_msg_buildingmods = "Building Modules" +output_msg_converting = "Converting %1" +output_msg_processcomplete = "Process complete" +output_msg_processterminated = "Process terminated" +output_msg_debugfailure = "Process debug failure. Cannot write:\n%1" + +; Loading Errors +loaderror_failed = "Failed to open '%1'." +loaderror_docsnotfound = "Documentation not found.\n\nWould you like to rebuild documentation now?" + +; Misc +status_line_char = "Line: %1 Char: %2" +msg_loading = "Loading %1" +msg_highlightingcode = "Highlighting Code" + +; Menus +menu_file = "&File" +menu_file_new = "&New" +menu_file_open = "&Open..." +menu_file_open_recent = "Open &Recent" +menu_file_closetab = "&Close Tab" +menu_file_closealltabs = "Close All Tabs" +menu_file_closeothertabs = "Close Other Tabs" +menu_file_save = "&Save" +menu_file_saveas = "Save &As" +menu_file_saveall = "Save A&ll" +menu_file_nexttab = "&Next Tab" +menu_file_prevtab = "&Previous Tab" +menu_file_importbb = "Import BB Project..." +menu_file_ideoptions = "IDE Options" +menu_file_projectmanager = "&Project Manager" +menu_file_print = "Print" +menu_file_exit = "Exit" + +menu_edit = "&Edit" +menu_edit_undo = "&Undo" +menu_edit_redo = "&Redo" +menu_edit_cut = "Cu&t" +menu_edit_copy = "&Copy" +menu_edit_paste = "&Paste" +menu_edit_selectall = "Select &All" +menu_edit_blockindent = "Block Indent" +menu_edit_blockoutdent = "Block Outdent" +menu_edit_find = "&Find..." +menu_edit_findnext = "Find Next" +menu_edit_replace = "&Replace..." +menu_edit_gotoline = "&Goto Line..." +menu_edit_findinfiles = "Find in F&iles" + +menu_program = "&Program" +menu_program_build = "&Build" +menu_program_buildandrun = "Build and &Run" +menu_program_commandline = "Command &Line" +menu_program_step = "&Step" +menu_program_stepin = "Step &In" +menu_program_stepout = "Step &Out" +menu_program_terminate = "&Terminate" +menu_program_buildoptions = "Build Options" +menu_program_buildoptions_quick = "Quick Build" +menu_program_buildoptions_debug = "Debug Build" +menu_program_buildoptions_threaded = "Threaded Build" +menu_program_buildoptions_guiapp = "Build GUI App" +menu_program_lockbuildfile = "&Lock Build File" +menu_program_unlockbuildfile = "&Unlock Build File" +menu_program_syncmods = "Synchronize Modules..." +menu_program_buildmods = "Build &Modules" +menu_program_rebuildallmods = "Rebuild &All Modules" +menu_program_rebuilddocs = "&Rebuild Documentation" + +menu_help = "&Help" +menu_help_home = "&Home" +menu_help_back = "&Back" +menu_help_forward = "&Forward" +menu_help_quickhelp = "&Quick Help" +menu_help_aboutmaxide = "&About MaxIDE" + +; Popup Menus +popup_edit = "Edit" +popup_edit_quickhelp = "Quick Help" +popup_edit_cut = "Cut" +popup_edit_copy = "Copy" +popup_edit_paste = "Paste" +popup_edit_selectall = "Select All" +popup_edit_blockindent = "Block Indent" +popup_edit_blockoutdent = "Block Outdent" +popup_edit_find = "Find" +popup_edit_findnext = "Find Next" +popup_edit_replace = "Replace" +popup_edit_goto = "Goto Line" + +popup_output = "Edit" +popup_output_cut = "Cut" +popup_output_copy = "Copy" +popup_output_paste = "Paste" +popup_output_stop = "Stop" + +popup_nav_proj = "Proj" +popup_nav_proj_refresh = "Refresh" +popup_nav_proj_clean = "Clean" +popup_nav_proj_findinfiles = "Find in Files" +popup_nav_proj_explore = "Explore" +popup_nav_proj_shell = "Shell" +popup_nav_proj_svnupdate = "Update Version" +popup_nav_proj_svncommit = "Commit Version" +popup_nav_proj_properties = "Properties" \ No newline at end of file diff --git a/src/maxide/makeicons/Back.png b/src/maxide/makeicons/Back.png new file mode 100644 index 0000000000000000000000000000000000000000..c70a451a96ac36af9f06201d22ae38092fdc718c GIT binary patch literal 533 zcmV+w0_y#VP)LC;^mUHxVrG|5Zo#;ykQpo94gB@ud? zTmJ;RGyo6E$}{9AHGZH(Z3z8qgY$eZ2!7e2-Z(#-RW;(>G49>EN$Mgfp5>?(H9}%^ zprI|HD!!roTA-LOFg?nVB`J2sDLzLr-tWdZKA2)YoiCs1LHSt&<^UCiXj-{hUBPk< zXB>=Wuqn~h25dtpV5ssjxNMj)+V7s5K< z7=@a}Co9EBUeJioOW5rj*Xi)MKzmw2>mi)8sL{e01EEOVfaK%8KwWF3P$NxH!X+uD z5WroYrU$-!^A0E0ARG+PDvM7acJO61!CsUh8fO^A8TKbBqKVifLz+#Iq%$e|8{?;W zQ6D~j#!p_n;_bbJA3S=>f99{!zy7oPckj?RD=?C2tz|boFiH;Bx9A>7-Twb?z^d{S X-?1Wu3ItJO00000NkvXXu0mjf_T1^t literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Build-Run.png b/src/maxide/makeicons/Build-Run.png new file mode 100644 index 0000000000000000000000000000000000000000..f03f3efd971cec8fc47da3df9097e7c0a16d9549 GIT binary patch literal 795 zcmV+$1LXXPP)K@VAr7C$^9u6spj8=IKO_xvv`W}k=f>V?1N zgHhi|VJQym+rN#@18~v_kls!Zxg6|@5~5ZQE9>u(`{_VhTPG9QX$ipbYTwc5p(S>7 z7GitXDtJO%&28BE`=@0jGwnFyX3PL@17TGVoi5)GWLPmzz-6;SV3ez1jjn^)@)F&@ zS47n2A;h(Q^cULOAdF4Ylph%ZXLp9cVcpP&4~mJ=%HeTrJe`k~Jc2&zAkBxnjIcX6 z3a*9Mb{&FiE?*+ zhvEb^t!Jet!(CR6+RCQj+O6Pq|4_rrOC&WV2hk^Q;YxlkYO+tmWVc|T_Zwn7%YN{P z3bj)Uo5Gu%mU%L%eS%7(9B$VfM!yb2<6We<=2WtD*MaP_z|BG?q9>Q&4iSWUjE_5z z3NuPeGEtZ>Lt|77KKX8@6g}>}Tf!l@ccp+Z7zfK%;ar{`7c;8hGK^u$qCi~iVLv>{ zA^mLw6Vb;)SSlw9d5te~1HRX7`O6TLXFLo)(T(N-|JFq>%%fBm{&Vzi&s}PoEDz1n zsRRcWJ-?erOurMrS*@sfQ^$m@OE_^JCDK^Z?IGUR8bWJpMnzHXy7kRQ5Z?&h$@s5d Z|34P8-Kw}dt3?0+002ovPDHLkV1m1`aA^Pl literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Build.png b/src/maxide/makeicons/Build.png new file mode 100644 index 0000000000000000000000000000000000000000..f9b8d2fcf9762be9c15bdf9097d323842cca35d5 GIT binary patch literal 630 zcmV-+0*U>JP)QN8GgBN2=`~wW};Kc)fi4kwyJ!$mR6b}A4fYE3$2Ggdb8e(Iu zO2JUXGObI?cA@R=?2fxz6`G1{`I0Z0e3LhC^WOWG2qAEoqWfJW)2~D#@j&zhoPGI1 z1O~%cE);QfY)rTY4uwKkn16#+#sGu6V@xIAibyzuk>L@}F<6W#>;+TcS z_8^@EeLjcvripU3g3;G0@?S=JJh=NvYulx&4<{WqnoGAY_i#MEa!DL)hpd2ZsxY-a z_dc3GW$;D|VBZ0W_7DJ81~CjkfwcA!pSErC})l$r+ZP8SXj4xze#079f~7r_49dcHVZdGcy?JYlaHfNi*R|p;;`VU#Y_P9|f4oWI)#qMYz0tCvp4DXP1>M zyz_xW&mOpD2_C(?Ka8a!^h^^sf7_6~eghoK95~1RqH^)-Vir8xQY5@A`Hy~AvN`a| zMOb@$Pb4hdD*VA_D}v1IMMa{t$9~5ZA*ED$j#-42wY$kI3nAgc+#F~WuWiqnh#k)b zL)R(J4)~5uqyj3tyC;*CB? zHI56F3~%}_-R`-{3P~2L`2dm#Jl9s%dTb%>$F8T#h6+71dtNEzmzIVw=#tU7E|dGc zc9$$3(;PT4nuzo2IHrQ@SVQTO#7szkhDvU)IJ#5NH4Z002ovPDHLk FV1fiYeAfT~ literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Copy.png b/src/maxide/makeicons/Copy.png new file mode 100644 index 0000000000000000000000000000000000000000..8d7751bfd7fd26262aa703d23db9292b9173b2b9 GIT binary patch literal 1284 zcmX9;drVVz6#m_|x5aX?<}B4799W(lBtm8`jH!mAgVvxzg+9`!y=6$zZ8Jr@IXM|{KY^ArAV431)%k$)|1oV zuLZjmxmf@lUIu`^5&$>h$0+~~iU4?q0q~v)0M3CkyZYsR>fub8G_UN>0xv;`$;%-L zXtkdy^NjZsu|qr2pyPDZ(poC=(t34-{vSez*!ItR=J?hNoKrmG#w=cI6PYRDvbFQG zuf@T^C>c#iwNC8o;5L46kn8gvV)W_s)eG=8{2WxwArDRp@Q0`c-nx{|l*F;MKK!*f zIy~IkF(c-sca;w~$0s7G@l6L1vCc=@UAs)zoen{AdC<$LS49`4lmN%O@aC-J{tX6l zr<_o`yi=*8mL$o8u`XSarhPS5Ht!9oG*xlQv{()q4Guph-PKi2`)ht~NL>BV5P#+M zy~Tz~M4-2`(>hBh3AE+-(8ry>0K9)@th)s(RBJvLDKV8w>GC{}wZ7gy$K`HySA3@v z@)nvaPFIB1-RpKv{~Fl>Gt(uYXE~-)DqY@}w8|e&thHX+zM7Y=^Yt>no(@4Eb#?O{ zpGfO*);V6>X4o-lR+$kIt%%MJt75W zS~WA&tgA>@sxaz4HRU$z51~e@_ma$~mxCYyyNVP;joKeo%^35E(vIM@J$G|YIp!~U z5Khuq0XAvJFr`vupLjyvHfVe|S((1B0|f2`;et{4aoQ({uwdLS@XWh8l@09))`ks? zD-}I9=aS+zHpfU?2{IHVHn>QIakW4gzvd7%MqUL2h|&QXV|-9F$NZ%K)iq=<{VEbR zo`Ln3;P@~mh1+oYaPiUe&%UY}8+1W=!0$-;EnkBF{ItEz8oq|%yzSZqBYSeSje z?j~C#S+COHDxZA{jdDgLZ*DyL-huzW1a0C8AU8gNQd!F|8F{HN4s3iiNJpF&Njr_b z1gF!7Ek&|LLOsZkC8N>ggh+HWJATg|sye%q6dhr9xqpk*gp8DwAe;GXGbjy)G;2c* z!=dFJf{U}IKmy*@I%oJPFc?@-{U~Xt=(-ImO-$maq_HAcIXg}a(x#;U5?sbgX+=e{wkTBLVv&dC{F zqv+fg%C{yDBPaLt#jB+3X~+~!LsppMFh8S;A?YE9gfS!8g^7}f^!Mq`0?=rRXp9K$ zhx2~A+7`CT#LW@%1p4e_$J(!*M73`GgyTicDWq-xCVSXf7I*jNC~>u#rEm?9tnQVf z32%)$d40K-g!oGLSmgm7rUGJd!AX`Z>18WTYd6QoL3rPS-GhIe#wff_4> sJosuwh{fh5n&JKqzd97p)xi=y1RU}s!cUKA0uBZ;*Kd;D%D@c&0f3kj$^ZZW literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Cut.png b/src/maxide/makeicons/Cut.png new file mode 100644 index 0000000000000000000000000000000000000000..8e41a700c0143441773bf6711175354f602b2ec6 GIT binary patch literal 382 zcmV-^0fGLBP)I(bJk#ynZa%L+(!k7Vb}w~!JoLdRfL%y|{quf2enYSCUvt;v zJ<))yqc0`luzx47FVQW)@94T7An_hWAfbKi_ zF*KZxQcYc@7Sd+RH&yIOkL*!&y6KhNrLY^u0X*^;^Aul$uL4|>(IGryf$vLS7iO+GU*aRC` c{9n)g2bGIV?Z{3Yi~s-t07*qoM6N<$f&}26+yDRo literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Find.png b/src/maxide/makeicons/Find.png new file mode 100644 index 0000000000000000000000000000000000000000..ca4b46f1ffb2e103078032100d1c1b39ff0e3ab8 GIT binary patch literal 461 zcmV;;0W$uHP)Qhi zjV*{IS8gOBX*dlYvDnAL(?=-*PGT#`)-6h9npuSQW-Su z$*DeDv=P;Fbq`HJl5+ChxQrO50B2AJ&)Go{_z!y#oaamH|g9@%<@PY z7oBlpu?Tkr8t=XK(!JT8Ac`X%?|g-L-B5b8&dL+P@oUdDE1gzQinjRm%`Nwxw$#W- zN{45p{?Y7sX47+zs>kO@hLW)aWhHHOaK}jwdF;nCzet0qE+&y1|6lM8;#yauV; literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Forward.png b/src/maxide/makeicons/Forward.png new file mode 100644 index 0000000000000000000000000000000000000000..8a53374559d1854da6c5abec71f389ca18f1df46 GIT binary patch literal 546 zcmV+-0^R+IP)u$op3 zMUa}~?T62Q0A~wGvlD-2EAB-##vx91RoYn`SHJS6);z4ow9ijzk`Tv^ayaJslSlF! z>}%fl8`~+BsPOhsR8vD1i5j!vBjwOa&{afpMbnTd)PzYf;(A>0;^|{q*bJjjoS@1I zZxqT{yp1Uf6Y?R(dYo}sUob8vlvTk*Vv=Tpmm2GvTYe5Jg0(tjXcnwk3Lj6$!#eOT zMu+Jgo=8X314N0Y?th?L4IbQIB|9A~7EqLBXjb8QAqufBA+HQ80U`nJTsfs;jG>?k zxh0BPT=oZS?H%xH?b*WS!SNZrtWTC7)9K};-JDK0qkR;v4|Dd@oZWVp(ZvOovcyTk zcWc=Ev@-|I7jRp>`?%wEYn8qB7Y_3QuV1doKYwRQvq3vOx)F3|*wtu4+WU4RXr`&n k+nzV;8~$(im--9(3Dn~oE@WntT>t<807*qoM6N<$f>TZTMF0Q* literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Go.png b/src/maxide/makeicons/Go.png new file mode 100644 index 0000000000000000000000000000000000000000..5b6b736b18e9a672dd42fe40a4a9938ef6fb7ded GIT binary patch literal 373 zcmV-*0gC>KP)d;vZ04q#0UDm5SEH`mg2m zP7+d*1AXA&QlNxd)Rm`OUI{clA zrp@E06P(S@mB`Yptd>Y@>N>tr0?&4_N|x(LtX7qp z4U8}tZ#qWNNIrAeIUt4a)IRO<+2ZF`(OQYaK@+kDO8OB+!X z#=n`&jAoLUC^2y$@h@=YQUxVjT^V%eV_=qiO9%E)s=U$kh$*lyM!~I~+IrskNoO7>4MCjGzZW}(WEK*Mp1}J>{ zjz=?7^j{DcP+QA#-vyP=F6eQ7hq^*s0BzhtAQ-@9=0+Q+Yb7o~xqb)6$S{&g1Mzqi za`raQKTpqz6m~nV3d;-haC(yRPj+dqa6rZ56Z&ylp`+t6z5lRJ+fQHeJk#Ij1i<#r zE80Ii;)hQvzqrchKdE$jPG5^9dj0OB0OIe&Z6R`Z6Ul^uP$&plmV~Np=ciVyg|o^9 z4z8{8So+xuiO+-qhVE@4;Q;))-`PxPS+*eCwjs$f1|wm#Hg0-w@5O%pQY1~&u=^nE z_Q@7f>svgZ$z;4kxm*r!-tJ+3HYK&)j-o2O0<&Sl2s%m08g~KBM$ZxJ=G&;Zs%W`8&_y75#OQkYW z6XWgeSkBOx8g&!Rripr;RdVUYJEf>sw$mx=inz^ZC_tK~$<=9Yyi-1(Pn=0F_6Id7 VehTqz(`^6%002ovPDHLkV1jhu4mU%|#^c^}_VD*TSZj~gKO0}z=*SpM-Z(j&yR`X0$p2yU~5-JM;Ow|5}A z+^0WIVA@F{jaxWB@)(i9xC$82(J2n`HeqdwbUOA6dYFnIr4kj(RXumzHh%f_J!WU; z^k#r?kgbu|Y;jfPvjz*y(kRhpU z$nfEICSk@NpyFHxoE6j>XMDxIi2Do!^=cCf*RI2yNa8{wfwN=dDCR8`7%C-tp0jXq zH-Y6p@1XdVx0c@^Q<(8{+^Z`#KCjVU!$V|vOo#1<-)sD{S4xR=IgXXuD(2>AdZ$7N ziPIw^2yxh73;9576>J?{k>|yH4#)uCN~Pcn(%~pr0AjHLBvUCQ%_PI*`B9kZ6dx{N zvX42Lf<3zpDMT;5+6(f@6?#St9yR;U7oSgi^wpU-O8+yw{LW-FzD&0V39_YofIYM!_jt^QLj5xuQ|T0yY&3&Qm|sB%=6khkNI4VVZpMp z+H#At%_5h}YO8#5{7*shzMyu~>{cd2Zqp;rYmwu6JZ~KXuL~`Dwe;c`#jN6>D-1nH zF!p^lc(({hvVJRymPMP{jD8wB>NMe7;-geiiMY572t$YNX+>>bH#EtWtMj5K@kxxo Y0nYAPn7HEA_5c6?07*qoM6N<$f)MtyaR2}S literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Open.png b/src/maxide/makeicons/Open.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb468c3f80c3cdb90c37337276966cb0dd40c47 GIT binary patch literal 1415 zcmWkuc~Db#5dP&bq~u{z(C|Pp5tO2GypUE!q)D6*K@st2L9xvXn1F4^V68>vN(crJ z)Kowdr6yKI>UiK$(ZNb0pxmKV3$}6uFK9s!ktF1J4S#(5&CcxZ?0&Pm#R>6iJ=}fW z0RSGcG5pOghG#$~x$Hn@l*L6KWyEaT3joDy1~_o4awdY9#@oOH;QG;Q+om#?&rFTk zya9lNivf@+0I*^%9tL0^0>Hm00ISphcuS89n*^>@M=YPWb^qTfb`0YXLU3HbVs*iQ zNb3jzWF~!$d*AQbR&4nF3Mj=h1)5*E18NWM>76tiIm$#MgH`=adbocHfk=y|e4gi= z(pFR)lKZ6(eMmOLaFSL23+Jb;_B3=S${%DQtFtCa>9|V~09WudKTKHfNK27OEY|<1 z4YgD)+;`um*ONb`d=`a->+h6uV`-9H~*8Ud?V#3o^kS=VpJ>^qoi*o z#Jai9`~`v{G1K?-ELTyHp`k7lx3>gjsAAe$S*x?`#h!yP-V#t zPAUz}Y8!up36%^XhkcJ!btX~T2R!i6;q!VtRTL*7#A?PkJwmPYE4N56CtNG-MfVdu zHtz&9ZU%!~Q!laDvf9b#Mr=t++&%?y1)vDlCn%N5GqxHI`5w1cFJJ}Am$CzvajHs4eZ1(d?8F2zmC`~ZLJyUv47^(sSlI_-2TAKIY1Ode#?FxP<_N)Uy-wv z@^K^qC-h7t)?JZY)LZ)RLMt#5Y z3Q9v}#e3eKUhE(<3#b|l*-`IUP<{k%%pFTpc{vUGhwYt!5B19q3~;&N)C{=vg>?JTu42}AB#0}TUy-Lq%=%Rs(OR#9AGvbJ?YP<NceCuyM@{-E@A4krRs4lUj=mZtRQalI%IOGBK^|)4U`$@q>^RzpzSL^c5})@1 z9MNnj_Ns+f%365G-w_p;EQz`{wSm>#Zr93B&rM&ojNKsAQ8e2$aF6^Dlg*|eD`6ie z%b9|@Rpl+$UE^EwSgUP|S_kM~Q;jrNy(vR|*fF`u<_C9judTnsj(OiFve-{khI~;q zAz+mHVk0SD_X?A}O_RLF2R`LMar*2BbU- zI#YKNE`jOENR>mwBL$qpxvm8NI55GxJHkLBaAn~H2al2!nk<$;M4Xv`|EdGNZ?_njOk0uM literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Paste.png b/src/maxide/makeicons/Paste.png new file mode 100644 index 0000000000000000000000000000000000000000..56eebd1916a7de8046ac9045fbc212fe1e05b938 GIT binary patch literal 1299 zcmW+$drVVz6#n&I+CqDQDp+noXay0F4qLz|4tfg#R$PR_2*YPG>qfIUqB1f9I18p)(f&u68l=9m4UAt(xix47SW z98^$TiaW=2;0(f(n?z6u;dISWdWw_|IcpEho6SWOzK6N{IZC=Ii*x3Mux(tQpm32#yFZ>kR`%ZQTh&yJ!tAIZIDs2b zpQ;+rv438A*Q!VEv|t+Lq}A)7H%}hTmv}T>o1(>dA_sU%(!?n2R)lnVGR7-oBm_|@McS*1QoPk(ztz^Upri07Znv>yxh+&Anwu>=GwIY;ZgPV6_Ny>EmP=LNY&ZxLRo{MG^`Hif>qFo;aFl`=D<6|TjO9Q)q(=~rlrtfJa` z8?(G7%3nCV8o$7?wi*BDEWQb4m`r-KQ1Ck*SFE+$r{UTT<9FfpADUN1qv4Bs9rXxr zw$eMZuuUcmCxZH)tBb2&J&nrY-F$;3JIE6A^-u5)Nuf1se4KF(?`F__i@^_h>Mai- z@dPeo)NF_iNq8Qf5gFM$(qrrm&%7JqfY z>iT;WFLKkbG26jN7@l-$$evaYv2b8K*gdrbvtA2TC`qo3JIQf9YggMYfg$cF<5qz( z4-bOdera!OuZxbuw{{ZB`~o8@AY$)Un3k7uc7Pe}< wo)r?fbujgFT(Csx`GGc?{ErKmKLI(;06QWUI;5F*=qCk0Rz`OE?KHLVKcZY9Jpcdz literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Save.png b/src/maxide/makeicons/Save.png new file mode 100644 index 0000000000000000000000000000000000000000..2e45ce29e7c7b3b8d43a4362027c27c6662a4312 GIT binary patch literal 1181 zcmV;O1Y-M%P)wDQHdZSBqSC{ED(ZSB^E6E6JW(Z0Ew=Ogv1(&{{TBGh@egC zG-;lVi63zi+hfoBa%S>yQvzMUmCnrgo^!wZopZi>EpX29e~&9cjb@V{HagI5 zXox2!AWH1Y#l9Dy)!4`U>$ipH=PyO~`$KM74p#_hd$)|1Ze#zTi%L~Lvkt2CKDW?U zu5|vk*#+pp9CCp%3`Y)(j)7*Y1J`w6+xBzjz@m3(w&TEYU3i}R&!C<;K($iGeyxt$ zkwL*8%t4=Q5WNTev7P@m`#qSZc{ONlZ;x-4%J})gA{KvpgyrH20xES!?~--Mbm+!q zJDu;q=o(iy>Zn%Z>zh?vo0`V%&NiIH+(p3Mk7r)m?eBj)7{}jvLx`A$wJK;C1ip`K zJ|8*T#tGIpx3F5bv2kExy*9!|eTa>!LuO)aPsd}jr&S-$u!qWCC5pdDUm$EXt^JIi{OoA#}P??3eXd@+^K$b0<2d>Kuf!l@Aeu8J~ODN=0W6(h0V2ndm z6=;PUk>jH~pQ2FE;8`YZB_N=u=yPx;(<+gOa9F_zuH%DEEa3eQK0(tMj6p>qj0*z= zbXv{GajYMsGaSK_IbxwtmJx&o2@^zsOA2BWlpPC5Iu4G9gDA7Hq__Yjr4n0)sp;v{ z)51L{#4P61i4tRQiD5#N5sxv%1UX_H_%eMnIEc}=b6HZM0z_gNp0!r95jotEhrZ=N zl3ijnfm~dHtPs0I96}<2Y(hplA(3AKw=D#LeHm1s{Njj-w|0waCHicH(KykSUD?X2{XFJGF=5X`& z+wiGu+~M#=hVcH02LWs_W~Ole+aK`N*I$BD^VU{Zv9t03PdB$Q8kw-@KnNab7OBgP z5*am$g$x);Mmm!e9SG0Di1xjC>qE@V&QqX+^kfDssUerjN6H}O!D6HW9=yTU%gnP8`ij{SB-D(eJ)tTHg?NpY-~2Mvayaz vc@L#}9S5}%N~IFIbg_p(=k`*j|7H9ITAEHZ;aOFY00000NkvXXu0mjfUu`Et literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Step-In.png b/src/maxide/makeicons/Step-In.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f8e46a39ae4bc78068c41c778ae64cdc141af1 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^LLfE=8<1S$XD11y1WQ~aN`mv#O3D+9QW?BLd~{t> z%hVe-I-dhdvL$)DyBq+6ZHE+A07Wu9T^vIy=4_p0$aO%0$Mx()nbOPMtubdVZnO>Z z;u4EqIKkwvz$V*y&zOzb`{yj!`>a(gnqAb>A=*>t_vsk7&5VL8?{#;4=%08s$8b{a z*6O{pj=i|Hm@#C2-Iolu$p0s{-#i~t^(3u?p~dj^-ow|Uo&oJ-@O1TaS?83{1OQ5g BPw)T$ literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/Step-Out.png b/src/maxide/makeicons/Step-Out.png new file mode 100644 index 0000000000000000000000000000000000000000..87d5c3bd06ea4121415fe17cf2ec55674ec39e5c GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&V!3HE>w#^4|1WQ~aN`mv#O3D+9QW?BLd~{t> z%hVe-I-dhdvL$)DyBq+6ZHE+A07ddWT^vIy=4_p8$k$-N;kwZL)r#41Jq3pD?zTd0 zjEg58xo~N|Nqu!CW0w+>WTE33nMJ~aeWqQro=VSs{b%9T+~XaB8ZRQ$F21YWK2hq+ znT(!^rKe^0_H2oM+Z8x}_UCO9Rvye1&HueWzEl6(?NY?pcKj9hX`WDZR9(kz~S<6vP|h^dF4Md7B8;5 zB*{K&ne*kK<&!2P@2XzF(@=J0?Zj1fUV?)6yiL5ER`mMXx@_sG>ND8-Ggsc_`V`k^ n`zA`xyI#Ea)^zE=KjeMaJ*b>IQ~zrO&=v+yS3j3^P6h3nG}zh{cFDsue8T5wgpc76_1yzvlpc1r1rMs2bbEO1 zbd{hfG@PZ1)^RmYt}-)QylAn!7Bc^c>(v#ZucS!|nbKeQ0o&(* UbxF$f_y7O^07*qoM6N<$g5bNPxBvhE literal 0 HcmV?d00001 diff --git a/src/maxide/makeicons/makeicons.bmx b/src/maxide/makeicons/makeicons.bmx new file mode 100644 index 0000000..c8b68e8 --- /dev/null +++ b/src/maxide/makeicons/makeicons.bmx @@ -0,0 +1,62 @@ + +Strict + +Local n +Repeat + Local t$ + ReadData t$ + If Not t Exit + n:+1 +Forever + +Local sz=24 + +Local p:TPixmap + +RestoreData Here +Local x=0 + +Repeat + Local t$ + ReadData t + If Not t Exit + + If t<>" " + Local q:TPixmap=LoadPixmap( t+".png" ) + If Not p + Local rgb=0'q.readpixel( 0,0 ) + p=TPixmap.Create( n*sz,sz,PF_RGBA8888 ) + For Local y=0 Until sz + For Local x=0 Until n*sz + p.WritePixel x,y,rgb + Next + Next + EndIf + If q.width>sz Or q.height>sz + q=ResizePixmap( q,sz,sz ) + EndIf + Local cx=(sz-q.width)/2 + Local cy=(sz-q.height)/2 + p.paste q,x+cx,cy + EndIf + x:+sz +Forever + +SavePixmapPNG p,"../toolbar.png" + +Local i:TImage=LoadImage(p) + +Graphics 640,480,0 +DrawPixmap p,0,0 + +DrawImage i,0,100 +Flip +WaitKey + +#Here +DefData "New","Open","Close","Save"," " +DefData "Cut","Copy","Paste","Find"," " +DefData "Build","Build-Run","Step","Step-In","Step-Out","Stop"," " +DefData "Home","Back","Forward" +DefData "Go","" + diff --git a/src/maxide/maxicons.o b/src/maxide/maxicons.o new file mode 100644 index 0000000000000000000000000000000000000000..dc11fcfbad84f46dfea8231e6a941082f440d2ac GIT binary patch literal 5612 zcmeI0PiP$X6~~_(6>Fz%YMa(nV9Ippp)OdGJ&K8pq}Z|@s@AGSW4pb%>)jb?>F(@y zW+Yjm2c3NAsnB9Nx3r|81s96ki{%D%Dy1Hb3&DbBC93!kFond>gQ1Y==ly2?DYcwH z4~5`2`}_0${NDS%_uHM{?40lG6Qu`V7RhJ*eM4asmW(v-=FyEaeUnk?bF@<;>kRVN zumALI9Yy-(1^N+@l@}RT`0Qu=1LId1|6xSE_IpgcS>$`r&;OUcPYV4a`*Tn|adNr+ z6uF0=U+&^(8nDPqi`AmW7e$;l!}tsMcQpAH z%^wu`sE7Xsa+`LF)+a5;)TBd-QJbNa9NEf>)}F@2WPN>I)|>0{;a}g9{{DU$7#NU! z`}WC!0|(^Lp+j=`@L_QrM~)mhB9|^*l9`zqnVOoCv9U2(`AH_Tvzh!{9!U1jRQ~+n z19|ZN`|`8j{hkOe&8H=_G-q98E;1LHtFkON@saz;edIoJAGt0|kSE9!m;`x(oIz6L zDe@F~iXcUv!DsMUY9hPzF7aI)xh!%e6BC}!1%nF)7Yr^K3?wkPU~s|Ug24rYfe8i| z3@#X4Ft}ha(81t>!3Bd01{Vx&h724oI9$b%Wu^j181+hMDGj-%3>Kf@K7o9k`d~3- zKU39-#~nApgPpAbJGzHU)M{Dj&hU`W7_PzT*M zs$#N7!bq>%NDWfrrC>|JkrK~rt>T~tDL7KaL1JdRS(Jh$1xpHulz`?d0G5=6DMl%R z6fAsb_+d{{u%ucKgGIj$x(mp_kbxlsLk5Nn3>g^A*9iR$hZf?rMhY#h^qet@i@nd=N-oW zT(Q`gwe|O{4L-(;rLHU(*zWrKhEU!An^9;SHE7iruN!z^tl=IQil_UApN zLCYE(+_na{o0ipVHm&BnO2+?tgV6T2wT*a?R!{}{wPwNSRcDsuI@McN;r6XtK+^cS zrr9)0jIMC|Hk!gMH86S;>Rohey3A^tm4#cYs~!Cy%Z-jzmRV?7cUD(#=Ij4{+tO&N z%L)aHg-SOeqi?S%s6)06{U6r~ch=O8##bcRTdH59WowE&C)H<#=GxjCBl7G{SEm-L ze-?eh?+f)H*T+cC$@cDB}>vYtU ziQ|aIvjk_-^XC+v8>q$e=M>KJ&-io7({7>%bLyyC2i3v0pmi{H_|`$x<@1B5%jXA8 zFRuqnm(LH94&ORBdU-u4I(++JXyfS}fAulCcdvB|zsHQrV(g*+={~(;?x%gSM{I7h zdY9FIntigL8?SQuKU4oYzO!eP{<6F#SLn~l_n1w~1^F)gH2tg0mEP|E;G_Je+`dt_ zN28X0uALH*qYq!A74dENM3_Nx4au~eWw&^TAYaAjd3?CQ;%Fd<|-BDds#or`Z~G=a*B7< zF3+c(jgXbnWxR!*DBW+1$k+LvX(dA}0xF&FUblAo4ru(9YqhGq=!H?G9!xmLhDIFQ z3rh8JC77FVE?s=}=*x~B#l@gptkwf>!ddnr=atDXSmVVg@@iMB%Qg;zXu??tgOgEd z-m4X(qqRyYtVeY}K3b~RP8Oru(Bd)2t`&od??v(D-e|ZgbbL*hy&$f{%R2xKjVP!_ zSTA`|R1Z&wrTI$imEwgEI6rv#gkuNA8q?E_MzvBZ#w6m{@p1#vQYCPYJNB>%Qud-! zSZSz!@`CSIN|j>Oo~l;j*Jp~??CG=T#)o@N6jvNBMDcXs*FR;F#~qDS7>~TtLP#El zD~yNUj~B?>E6;|N#Y)wi^P)*x4KeNL&Rt`Xf;;amdR4os{e)ADrh~=$kG#;a7b>Sq zT7n73FIFSZFJUiKPZK*38n%nl!)ZtZLfeS4!jo3o>~ zdF^M~8$R^?N>HvZMMG!mwOTzmdZr%4VZ9nT-7P(#8~RCG+E}>CW`4(8zDV+XF;BcS z_R`f8qsK-^OC|qU*>~(}F_>E@&auniZ0#_mXy>#ehpyP9IzHUaXtMhbbg$Tb=I6w3 au>C%Bubk@0^c|$qUg>>=Asc("0") And c<=Asc("9") Continue + If c>=Asc("a") And c<=Asc("z") Continue + If c>=Asc("A") And c<=Asc("Z") Continue + Exit + Next + token$=l[..i] + help$="" + anchor$="" + q=l.findlast("|") + If q>=0 + help=l[..q] + anchor=l[q+1..] + EndIf + qh.AddCommand token,help,anchor + Next + Return qh + End Function +End Type + +Const TOOLSHOW=1 +Const TOOLREFRESH=2 +Const TOOLNEW=3 +Const TOOLOPEN=4 +Const TOOLCLOSE=5 +Const TOOLSAVE=6 +Const TOOLHELP=7 +Const TOOLUNDO=8 +Const TOOLREDO=9 +Const TOOLCUT=10 +Const TOOLCOPY=11 +Const TOOLPASTE=12 +Const TOOLQUICKSAVE=13 +Const TOOLSAVEAS=14 +Const TOOLGOTO=15 +Const TOOLFIND=16 +Const TOOLFINDNEXT=17 +Const TOOLREPLACE=18 +Const TOOLBUILD=19 +Const TOOLRUN=20 +Const TOOLLOCK=21 +Const TOOLUNLOCK=22 +Const TOOLSELECT=23 +Const TOOLSELECTALL=24 +Const TOOLINDENT=25 +Const TOOLOUTDENT=26 +Const TOOLACTIVATE=27 +Const TOOLNAVIGATE=28 +Const TOOLNEWVIEW=29 +Const TOOLMENU=30 +Const TOOLPRINT=31 +Const TOOLERROR=32 +Const TOOLOUTPUT=32 + +Type TTool + Method Invoke(command,argument:Object=Null) + End Method +End Type + +Type TRequester + + Const STYLE_OK% = 1, STYLE_CANCEL% = 2 + Const STYLE_DIVIDER% = 4, STYLE_STATUS% = 8 + Const STYLE_RESIZABLE% = 16, STYLE_STDBORDER% = 32 + Const STYLE_MODAL% = 64 + + Field host:TCodePlay + Field window:TGadget,ok:TGadget,cancel:TGadget,divider:TGadget + Field centered, modal + + Method initrequester(owner:TCodeplay,label$,w=260,h=60,flags=STYLE_OK|STYLE_CANCEL|STYLE_DIVIDER,oktext$="{{btn_ok}}") + + host=owner + If (flags&STYLE_MODAL) Then flags:|STYLE_STDBORDER + + If (flags & (STYLE_CANCEL|STYLE_OK)) Then h:+32;If (flags&STYLE_DIVIDER) Then h:+12 + + Local windowflags% = WINDOW_TITLEBAR|WINDOW_HIDDEN|WINDOW_CLIENTCOORDS + If (flags & STYLE_STATUS) Then windowflags:|WINDOW_STATUS + If (flags & STYLE_RESIZABLE) Then windowflags:|WINDOW_RESIZABLE + If Not (flags & STYLE_STDBORDER) Then windowflags:|WINDOW_TOOL + + window=CreateWindow(label,0,0,w,h,host.window,windowflags) + + If (flags & STYLE_RESIZABLE) Then + If (flags & STYLE_STDBORDER) Then SetMaxWindowSize(window,ClientWidth(Desktop()),ClientHeight(Desktop())) + SetMinWindowSize(window,w,h) + EndIf + + If (flags & STYLE_OK) Then + + ok=CreateButton(oktext,ClientWidth(window)-101,ClientHeight(window)-32,95,26,window,BUTTON_OK) + SetGadgetLayout(ok,EDGE_CENTERED,EDGE_ALIGNED,EDGE_CENTERED,EDGE_ALIGNED) + + If (flags & STYLE_CANCEL) Then + cancel=CreateButton("{{btn_cancel}}",6,ClientHeight(window)-32,95,26,window,BUTTON_CANCEL) + SetGadgetLayout(cancel,EDGE_ALIGNED,EDGE_CENTERED,EDGE_CENTERED,EDGE_ALIGNED) + EndIf + + Else + If (flags & STYLE_CANCEL) Then + cancel=CreateButton("{{btn_close}}",ClientWidth(window)-101,ClientHeight(window)-32,95,26,window,BUTTON_CANCEL) + SetGadgetLayout(cancel,EDGE_CENTERED,EDGE_ALIGNED,EDGE_CENTERED,EDGE_ALIGNED) + EndIf + EndIf + + If (flags & STYLE_DIVIDER) And (flags & (STYLE_OK|STYLE_CANCEL)) Then + divider = CreateLabel( "", 6, ClientHeight(window)-42, ClientWidth(window)-12, 4, window, LABEL_SEPARATOR ) + SetGadgetLayout(divider,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_CENTERED,EDGE_ALIGNED) + EndIf + + If (flags & STYLE_MODAL) Then modal = True Else modal = False + + End Method + + Method Show() + Local x,y,w,h,win:TGadget + If Not centered + win=host.window + w=GadgetWidth(window) + h=GadgetHeight(window) + x=GadgetX(win)+(GadgetWidth(win)-w)/2 + y=GadgetY(win)+(GadgetHeight(win)-h)/2 + SetGadgetShape window,x,y,w,h + centered=True + EndIf + host.HookRequester Self + ShowGadget window + ActivateGadget window + PollSystem + End Method + + Method Hide() + EnableGadget host.window + HideGadget window + host.UnhookRequester Self + host.SelectPanel host.currentpanel + End Method + Method IsModal() + Return modal + EndMethod + Method Poll() + End Method +End Type + +Rem +Type TProgressRequester Extends TRequester + Field message$,value + Field showing + Field label:TGadget + Field progbar:TGadget + + Method Show() 'returns false if cancelled + showing=True + Super.Show + End Method + + Method Hide() + showing=False + Super.Hide() + End Method + + Method Open(title$) + SetGadgetText window,title + Show + End Method + + Method Update(msg$,val) + If msg$<>message + message=msg + If label FreeGadget label + label=CreateLabel(message,8,8,260,20,window) + EndIf + If showing And (val&$fc)<>(value&$fc) 'only update every 4 percent + UpdateProgBar( progbar,val/100.0 ) + PollSystem + EndIf + value=val + End Method + + Function Create:TProgressRequester(host:TCodePlay) + Local progress:TProgressRequester + progress=New TProgressRequester + progress.initrequester(host,"{{progress_window_title}}",280,128,STYLE_CANCEL) + progress.progbar=CreateProgBar( 8,32,260,20,progress.window ) + Return progress + End Function +End Type +EndRem + +Type TPanelRequester Extends TRequester + Field tabber:TGadget + Field panels:TGadget[] + Field index + + Method InitPanelRequester(owner:TCodeplay,label$,w=280,h=128) + InitRequester owner,label,w,h,STYLE_OK|STYLE_CANCEL|STYLE_STDBORDER|STYLE_MODAL + tabber=CreateTabber(6,6,w-12,h-12,window) + SetGadgetLayout tabber,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED + End Method + + Method SetPanelIndex(i) + HideGadget panels[index] + index=i + ShowGadget panels[index] + SelectGadgetItem tabber,index + End Method + + Method SetPanel(panel:TGadget) + Local i + For Local p:TGadget = EachIn panels + If p=panel SetPanelIndex i;Exit + i:+1 + Next + End Method + + Method AddPanel:TGadget(name$) + Local panel:TGadget + panel=CreatePanel(0,0,ClientWidth(tabber),ClientHeight(tabber),tabber) + SetGadgetLayout panel,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED + HideGadget panel + AddGadgetItem tabber,name,GADGETITEM_LOCALIZED + panels=panels[..panels.length+1] + panels[panels.length-1]=panel + Return panel + End Method + + Method RemovePanel(panel) + End Method + +End Type + +Type TAboutRequester Extends TRequester + + Global pixLogo:TPixmap + + Field pnlLogo:TGadget, lblTitle:TGadget, lblSubtitle:TGadget + Field lblLeftAligned:TGadget[], lblRightAligned:TGadget[] + Field hypBlitz:TGadget + + Method PopulateText() + + Local strHeadings$[], strValues$[] + + strHeadings:+["{{about_label_bccver}}:"] + strValues:+[BCC_VERSION] + + strHeadings:+[""] + strValues:+[""] + + strHeadings:+["{{about_label_bmxpath}}:"] + ?Win32 + strValues:+[BlitzMaxPath().Replace("/","\")] + ?Not Win32 + strValues:+[BlitzMaxPath()] + ? + + ?Win32 + strHeadings:+["{{about_label_mingwpath}}:"] + If getenv_("MINGW") Then + strValues:+[getenv_("MINGW")] + Else + strValues:+[LocalizeString("{{about_error_unavailable}}")] + EndIf + ? + + strHeadings:+[""] + strValues:+[""] + + ?Not MacOS + strHeadings:+["{{about_label_fasmver}}:"] + strValues:+[GetFASM()] + ? + + strHeadings:+["{{about_label_gccver}}:"] + strValues:+[GetGCC()] + + strHeadings:+["{{about_label_gplusplusver}}:"] + strValues:+[GetGpp()] + + PopulateColumns( strHeadings, strValues ) + + EndMethod + + Function GetProcessOutput$(cmd$, flags$ = "") + + Local version$ + + ?Win32 + cmd:+".exe" + ? + + cmd=Quote(cmd) + If flags Then cmd:+" "+flags + + Local process:TProcess = CreateProcess(cmd,HIDECONSOLE) + + If process + Local bytes:Byte[] + Local tmpTimeout:Int = MilliSecs() + 500 + Repeat + Delay 10 + bytes=process.pipe.ReadPipe() + If bytes + version:+String.FromBytes(bytes,bytes.length) + EndIf + Until (Not process.status()) Or (MilliSecs() > tmpTimeout) + process.Close() + Return version.Trim().Replace("~r","") + EndIf + + Return LocalizeString("{{about_error_unavailable}}") + + EndFunction + + Method GetFASM$() + ?Not MacOS + Local tmpSections$[] = GetProcessOutput(BlitzMaxPath()+"/bin/fasm").Split("~n")[0].Split(" ") + Return tmpSections[tmpSections.length-1] + ? + EndMethod + + Method GetGCC$() + ?Win32 + If Not getenv_("MinGW") Then Return LocalizeString("{{about_error_notapplicable}}") + ? + Return GetProcessOutput("gcc", "-dumpversion").Split("~n")[0] + EndMethod + + Method GetGpp$() + ?Win32 + If Not getenv_("MinGW") Then Return LocalizeString("{{about_error_notapplicable}}") + ? + Return GetProcessOutput("g++", "-dumpversion").Split("~n")[0] + EndMethod + + Method PopulateColumns( strHeadings$[], strValues$[] ) + + strHeadings = strHeadings[..lblLeftAligned.length] + strValues = strValues[..lblRightAligned.length] + + For Local i:Int = 0 Until lblLeftAligned.length + LocalizeGadget( lblLeftAligned[i], strHeadings[i] ) + Next + + For Local i:Int = 0 Until lblRightAligned.length + SetGadgetText( lblRightAligned[i], strValues[i] ) + SetGadgetToolTip( lblRightAligned[i], strValues[i] ) + Next + + EndMethod + + Method Show() + PopulateText() + Super.Show() + EndMethod + + Method Poll() + Select EventSource() + Case window + If EventID()=EVENT_WINDOWCLOSE + Hide() + EndIf + Case cancel + If EventID()=EVENT_GADGETACTION + Hide() + EndIf + Default + Return 0 + End Select + Return 1 + End Method + + Function Create:TAboutRequester(host:TCodePlay) + + Local abt:TAboutRequester = New TAboutRequester + abt.initrequester(host,"{{about_window_title}}",420,255,STYLE_CANCEL|STYLE_DIVIDER|STYLE_MODAL) + + Local win:TGadget = abt.window, w = ClientWidth(abt.window)-12, h = ClientHeight(abt.window) + + abt.pnlLogo = CreatePanel(w-(64-6),0,64,64,win) + SetGadgetLayout abt.pnlLogo, EDGE_CENTERED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED + + 'abt.pnlLogo = CreatePanel(0,0,64,64,win) + 'SetGadgetLayout abt.pnlLogo, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED, EDGE_CENTERED + + If Not pixLogo Then pixLogo = LoadPixmapPNG("incbin::bmxlogo.png") + SetGadgetPixmap abt.pnlLogo, pixLogo, PANELPIXMAP_CENTER + + Local y = 12 + + abt.lblTitle = CreateLabel("MaxIDE "+IDE_VERSION,6,y,w,18,win,LABEL_LEFT) + SetGadgetFont abt.lblTitle, LookupGuiFont( GUIFONT_SYSTEM, 12, FONT_BOLD ) + SetGadgetLayout abt.lblTitle, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED + y:+19 + + abt.lblSubtitle = CreateLabel("Copyright Blitz Research Ltd.",6,y,w,22,win,LABEL_LEFT) + SetGadgetFont abt.lblSubtitle, LookupGuiFont( GUIFONT_SYSTEM, 10, FONT_ITALIC ) + SetGadgetLayout abt.lblSubtitle, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED + + y = 64 + + SetGadgetLayout( CreateLabel("",6,y,w,4,win,LABEL_SEPARATOR), EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED ) + + y:+(4+6) + + Local tmpGadget:TGadget + + For y = y Until (255-21) Step 22 + + tmpGadget = CreateLabel("",6,y,135,22,win,LABEL_LEFT) + SetGadgetLayout( tmpGadget, EDGE_ALIGNED, EDGE_RELATIVE, EDGE_ALIGNED, EDGE_CENTERED ) + abt.lblLeftAligned:+[tmpGadget] + + tmpGadget = CreateLabel("",135+6,y,w-135,22,win,LABEL_LEFT) + SetGadgetLayout( tmpGadget, EDGE_RELATIVE, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED ) + DelocalizeGadget tmpGadget + abt.lblRightAligned:+[tmpGadget] + + Next + + abt.hypBlitz = CreateHyperlink("http://www.blitzbasic.com/",6,(h-28),200,26,win,LABEL_LEFT) + SetGadgetLayout abt.hypBlitz, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED + + Return abt + + EndFunction + + Function Spacer( height:Int, inpout:Int Var ) + inpout:+height+6 + Return height + EndFunction + +EndType + + +Type TCmdLineRequester Extends TRequester + Field label:TGadget,textfield:TGadget + + Method Poll() + Select EventSource() + Case window + If EventID()=EVENT_WINDOWCLOSE + Hide + EndIf + Case ok + If EventID()=EVENT_GADGETACTION + host.SetCommandLine TextFieldText(textfield) + Hide + EndIf + Case cancel + If EventID()=EVENT_GADGETACTION + SetGadgetText textfield,host.GetCommandLine() + Hide + EndIf + Default + Return 0 + End Select + Return 1 + End Method + + Method Show() + SetGadgetText textfield,host.GetCommandLine() + Super.Show() + ActivateGadget textfield + End Method + + Function Create:TCmdLineRequester(host:TCodePlay) + Local cmd:TCmdLineRequester = New TCmdLineRequester + cmd.initrequester(host,"{{cmdline_window_title}}",260,60,STYLE_OK|STYLE_CANCEL|STYLE_DIVIDER|STYLE_MODAL) + cmd.label=CreateLabel("{{cmdline_label_cmdline}}:",6,8,260,20,cmd.window) + cmd.textfield=CreateTextField(6,30,ClientWidth(cmd.window)-12,21,cmd.window) + Return cmd + End Function +End Type + +Type TGotoRequester Extends TRequester + Field linenumber:TGadget + + Method Show() + Super.Show() + ActivateGadget linenumber + End Method + + Method Poll() + Local line,data,text$ + Select EventSource() + Case linenumber + If EventID() = EVENT_GADGETACTION + text = GadgetText(linenumber) + If text And (Int(text) <> text) Then SetGadgetText linenumber, Int(text) + EndIf + Case window + If EventID()=EVENT_WINDOWCLOSE + Hide + EndIf + Case ok + line=Int(GadgetText(linenumber)) + Hide + host.activepanel.Invoke TOOLGOTO,String(line) + Case cancel + Hide + Default + Return 0 + End Select + Return 1 + End Method + + Function Create:TGotoRequester(host:TCodePlay) + Local seek:TGotoRequester = New TGotoRequester + seek.initrequester(host,"{{goto_window_title}}",260,66,STYLE_OK|STYLE_CANCEL|STYLE_DIVIDER|STYLE_MODAL,"{{goto_btn_goto}}") + CreateLabel("{{goto_label_linenum}}:",6,8+4,114,20,seek.window) + seek.linenumber=CreateTextField(150,8,ClientWidth(seek.window)-(150+6),21,seek.window) + SetGadgetFilter( seek.linenumber, IntegerFilter ) + Return seek + End Function + + Function IntegerFilter:Int( event:TEvent, context:Object ) + Select event.id + Case EVENT_KEYCHAR + If event.data >= Asc("0") And event.data <= Asc("9") Return True + If event.data = 8 Or event.data = 127 Or event.data = 13 Return True + Return False + Case EVENT_KEYDOWN + Return True + EndSelect + EndFunction + +End Type + +Type TColor + Field red,green,blue + + Method Set(rgb) + red=(rgb Shr 16)&$FF + green=(rgb Shr 8)&$FF + blue=rgb&$FF + End Method + + Method ToString$() + Return ""+red+","+green+","+blue + End Method + + Method FromString(s$) + Local p=s.Find(",")+1 + If Not p Return + Local q=s.Find(",",p)+1 + If Not q Return + red=Int(s[..p-1]) + green=Int(s[p..q-1]) + blue=Int(s[q..]) + End Method + + Method Request() + If RequestColor(red,green,blue) + red=RequestedRed() + green=RequestedGreen() + blue=RequestedBlue() + Return True + EndIf + End Method +End Type + +Type TTextStyle + + Field label:TGadget,panel:TGadget,combo:TGadget + Field underline:TGadget, color:TColor + Field flags + + Method Set(rgb,bolditalic) + color.set(rgb) + flags=bolditalic + End Method + + Method Format(textarea:TGadget,pos,length,emphasise:Int = False) + Local tmpFlags:Int = flags + If emphasise Then tmpFlags:|TEXTFORMAT_BOLD + FormatTextAreaText textarea,color.red,color.green,color.blue,tmpFlags,pos,length + End Method + + Method ToString$() + Return ""+color.red+","+color.green+","+color.blue+","+flags + End Method + + Method FromString(s$) + Local p,q,r + p=s.Find(",")+1;If Not p Return + q=s.Find(",",p)+1;If Not q Return + r=s.Find(",",q)+1;If Not r Return + color.red=Int(s[..p-1]) + color.green=Int(s[p..q-1]) + color.blue=Int(s[q..r-1]) + flags=Int(s[r..]) + End Method + + Method Poll() + Select EventSource() + Case panel + If EventID()=EVENT_MOUSEDOWN + Return color.Request() + EndIf + Case combo + flags=(flags&~3)|SelectedGadgetItem(combo) + Return True + Case underline + If EventData() Then flags:|TEXTFORMAT_UNDERLINE Else flags:&~TEXTFORMAT_UNDERLINE + Return True + End Select + End Method + + Method Refresh() + SetPanelColor panel,color.red,color.green,color.blue + SelectGadgetItem combo,flags&3 + SetButtonState(underline,(flags&TEXTFORMAT_UNDERLINE <> 0)) + End Method + + Function Create:TTextStyle(name$,xpos,ypos,window:TGadget) + Local s:TTextStyle + s=New TTextStyle + s.color=New TColor + s.label=CreateLabel(name,xpos,ypos+4,90,24,window) + s.panel=CreatePanel(xpos+94,ypos,24,24,window,PANEL_BORDER|PANEL_ACTIVE) + SetPanelColor s.panel,255,255,0 + s.combo=CreateComboBox(xpos+122,ypos,96,24,window) + s.underline=CreateButton("{{txtstyle_underline}}",xpos+226,ypos,ClientWidth(window)-(xpos+220),24,window,BUTTON_CHECKBOX) + AddGadgetItem s.combo,"{{txtstyle_normal}}",GADGETITEM_LOCALIZED + AddGadgetItem s.combo,"{{txtstyle_bold}}",GADGETITEM_LOCALIZED + AddGadgetItem s.combo,"{{txtstyle_italic}}",GADGETITEM_LOCALIZED + AddGadgetItem s.combo,"{{txtstyle_bolditalic}}",GADGETITEM_LOCALIZED + Return s + End Function +End Type + +Type TGadgetStyle + + Global fntLibrary:TGUIFont[] = [TGuiFont(Null), LookupGuiFont(GUIFONT_SYSTEM), LookupGuiFont(GUIFONT_MONOSPACED), .. + LookupGuiFont(GUIFONT_SANSSERIF), LookupGuiFont(GUIFONT_SERIF), .. + LookupGuiFont(GUIFONT_SCRIPT) ] + + Field label:TGadget,fpanel:TGadget,bpanel:TGadget,fcombo:TGadget,fbutton:TGadget + Field font_name$, font_size:Double + Field fg:TColor, bg:TColor + Field font_type = GUIFONT_SYSTEM, font:TGUIFont = fntLibrary[font_type] + + Method Apply(gadget:TGadget) + SetGadgetFont gadget,font + SetGadgetColor gadget,bg.red,bg.green,bg.blue,True + SetGadgetColor gadget,fg.red,fg.green,fg.blue,False + End Method + + Method Set(fg_rgb,bg_rgb,ftype,fname$="",fsize=0) + fg.set(fg_rgb) + bg.set(bg_rgb) + If Not fntLibrary[ftype] Then + font_name=fname + font_size=fsize + Else + font_name=FontName(fntLibrary[ftype]) + font_size=FontSize(fntLibrary[ftype]) + EndIf + font_type=ftype + End Method + + Method ToString$() + Return font_name+","+font_size+","+fg.ToString()+","+bg.ToString()+","+font_type + End Method + + Function GetArg$(a$ Var) + Local p = a.Find(",") + If p=-1 Then p=a.length + Local r$ = a$[..p] + a$=a$[p+1..] + Return r$ + End Function + + Method FromString(s$) + font_name=GetArg(s$) + font_size=Double(GetArg(s$)) + fg.red=Int(GetArg(s$)) + fg.green=Int(GetArg(s$)) + fg.blue=Int(GetArg(s$)) + bg.red=Int(GetArg(s$)) + bg.green=Int(GetArg(s$)) + bg.blue=Int(GetArg(s$)) + font_type=Int(GetArg(s$)) + If fntLibrary[font_type] Then + font_name=FontName(fntLibrary[font_type]) + font_size=FontSize(fntLibrary[font_type]) + EndIf + End Method + + Method Poll() + Local f:TGUIFont + Select EventSource() + Case fpanel + If EventID()=EVENT_MOUSEDOWN + Return fg.Request() + EndIf + Case bpanel + If EventID()=EVENT_MOUSEDOWN + Return bg.Request() + EndIf + Case fcombo, fbutton + If EventSource() = fcombo Then + If Not (EventData() < 0) Then + font_type = EventData() + f = fntLibrary[font_type] + EndIf + Else + font_type = 0 + SelectGadgetItem fcombo, font_type + EndIf + If Not f Then f=RequestFont(font) + If f + font_name=FontName(f) + font_size=FontSize(f) + Return True + EndIf + End Select + End Method + + Method Refresh() + font=fntLibrary[font_type] + If Not font Then font=LoadGuiFont(font_name,font_size) + SetPanelColor fpanel,fg.red,fg.green,fg.blue + SetPanelColor bpanel,bg.red,bg.green,bg.blue + SelectGadgetItem fcombo, font_type + Local tmpFloatText$[] = String(font_size).Split(".") + If tmpFloatText.length > 1 Then tmpFloatText[1] = tmpFloatText[1][..Min(2,tmpFloatText[1].length)] + SetGadgetText fbutton,font_name+" : "+".".Join(tmpFloatText)+"pt" + End Method + + Function Create:TGadgetStyle(name$,xpos,ypos,window:TGadget) + Local s:TGadgetStyle + s=New TGadgetStyle + s.fg=New TColor + s.bg=New TColor + s.label=CreateLabel(name,xpos,ypos+LABELOFFSET,66,50,window) + s.fpanel=CreatePanel(xpos+68,ypos,24,24,window,PANEL_BORDER|PANEL_ACTIVE) + s.bpanel=CreatePanel(xpos+96,ypos,24,24,window,PANEL_BORDER|PANEL_ACTIVE) + s.fbutton=CreateButton("..",xpos+122,ypos+30,ClientWidth(window)-(xpos+128),24,window) + s.fcombo=CreateComboBox(xpos+122,ypos,ClientWidth(window)-(xpos+128),24,window) + AddGadgetItem s.fcombo, "{{options_font_desc_user}}", GADGETITEM_DEFAULT|GADGETITEM_LOCALIZED + AddGadgetItem s.fcombo, "{{options_font_desc_guidefault}}", GADGETITEM_LOCALIZED + AddGadgetItem s.fcombo, "{{options_font_desc_monospaced}}", GADGETITEM_LOCALIZED + AddGadgetItem s.fcombo, "{{options_font_desc_sansserif}}", GADGETITEM_LOCALIZED + AddGadgetItem s.fcombo, "{{options_font_desc_serif}}", GADGETITEM_LOCALIZED + AddGadgetItem s.fcombo, "{{options_font_desc_script}}", GADGETITEM_LOCALIZED + Return s + End Function +End Type + +Const NORMAL=0 +Const COMMENT=1 +Const QUOTED=2 +Const KEYWORD=3 +Const NUMBER=4 +Const MATCHING=5 + +Type TOptionsRequester Extends TPanelRequester +' panels + Field optionspanel:TGadget,editorpanel:TGadget,toolpanel:TGadget +' settings + Field showtoolbar,restoreopenfiles,autocapitalize,syntaxhighlight,autobackup,autoindent,hideoutput + Field bracketmatching, externalhelp,systemkeys,sortcode + Field tabsize,language$ + Field editfontname$,editfontsize,editcolor:TColor + Field outputfontname$,outputfontsize,outputcolor:TColor +' states + Field editfont:TGUIFont +' gadgets + Field languages:TGadget + Field tabbutton:TGadget + Field editpanel:TGadget,editbutton:TGadget + Field buttons:TGadget[11] + Field styles:TTextStyle[] + Field textarea:TGadget + Field outputstyle:TGadgetStyle + Field navstyle:TGadgetStyle + Field dirty + Field undo:TBank + + Method Show() + RefreshGadgets() + Super.Show() + EndMethod + + Method Snapshot() + If Not undo undo=CreateBank(8192) + Local stream:TStream=CreateBankStream(undo) + write stream + stream.close + End Method + + Method Restore() + If Not undo Return + Local stream:TStream=CreateBankStream(undo) + Read stream + stream.close + End Method + + Method SetDefaults() + language=DEFAULT_LANGUAGEPATH + bracketmatching=True + showtoolbar=True + restoreopenfiles=True + autocapitalize=True + syntaxhighlight=True + autobackup=True + autoindent=True + tabsize=4 + editfontname$=FontName(TGadgetStyle.fntLibrary[GUIFONT_MONOSPACED]) + editfontsize=FontSize(TGadgetStyle.fntLibrary[GUIFONT_MONOSPACED]) + editcolor.set( $334455 ) + styles[NORMAL].set( $ffffff,0 ) + styles[COMMENT].set( $bbeeff,0 ) + styles[QUOTED].set( $00ff66,0 ) + styles[KEYWORD].set( $ffff00,0 ) + styles[NUMBER].set( $40ffff,0 ) + styles[MATCHING].set( $ff4040,TEXTFORMAT_BOLD ) + outputstyle.set(0,-1,GUIFONT_MONOSPACED) + navstyle.set(0,-1,GUIFONT_SYSTEM) + RefreshGadgets + End Method + + Method Write(stream:TStream) + stream.WriteLine "[Options]" + stream.WriteLine "language="+language + stream.WriteLine "showtoolbar="+showtoolbar + stream.WriteLine "restoreopenfiles="+restoreopenfiles + stream.WriteLine "autocapitalize="+autocapitalize + stream.WriteLine "syntaxhighlight="+syntaxhighlight + stream.WriteLine "bracketmatching="+bracketmatching + stream.WriteLine "autobackup="+autobackup + stream.WriteLine "autoindent="+autoindent + stream.WriteLine "tabsize="+tabsize + stream.WriteLine "editfontname="+editfontname + stream.WriteLine "editfontsize="+editfontsize + stream.WriteLine "editcolor="+editcolor.ToString() + stream.WriteLine "normal_style="+styles[NORMAL].ToString() + stream.WriteLine "comment_style="+styles[COMMENT].ToString() + stream.WriteLine "quote_style="+styles[QUOTED].ToString() + stream.WriteLine "keyword_style="+styles[KEYWORD].ToString() + stream.WriteLine "number_style="+styles[NUMBER].ToString() + stream.WriteLine "matched_style="+styles[MATCHING].ToString() + stream.WriteLine "console_style="+outputstyle.ToString() 'Renamed from 'output_style' to bump users to default monospace font. + stream.WriteLine "navi_style="+navstyle.ToString() 'Renamed from 'nav_style' to bump users to default treeview font. + stream.WriteLine "hide_output="+hideoutput + stream.WriteLine "external_help="+externalhelp + stream.WriteLine "system_keys="+systemkeys + stream.WriteLine "sort_code="+sortcode + End Method + + Method Read(stream:TStream) + Local f$,p,a$,b$,t + While Not stream.Eof() + f$=stream.ReadLine() + If f$="" Or (f$[..1]="[" And f$<>"[Options]") Exit + p=f.find("=") + a$=f[..p] + b$=f[p+1..] + t=Int(b) + Select a$ + Case "showtoolbar" showtoolbar=t + Case "restoreopenfiles" restoreopenfiles=t + Case "autocapitalize" autocapitalize=t + Case "syntaxhighlight" syntaxhighlight=t + Case "bracketmatching" bracketmatching=t + Case "autobackup" autobackup=t + Case "autoindent" autoindent=t + Case "tabsize" tabsize=t + Case "editfontname" editfontname=b + Case "editfontsize" editfontsize=t + Case "editcolor" editcolor.FromString(b) + Case "normal_style" styles[NORMAL].FromString(b) + Case "comment_style" styles[COMMENT].FromString(b) + Case "quote_style" styles[QUOTED].FromString(b) + Case "keyword_style" styles[KEYWORD].FromString(b) + Case "number_style" styles[NUMBER].FromString(b) + Case "matched_style" styles[MATCHING].FromString(b) + Case "console_style" outputstyle.FromString(b) 'Renamed from 'output_style' to bump users to default monospace font. + Case "navi_style" navstyle.FromString(b) 'Renamed from 'nav_style' to bump users to default treeview font. + Case "hide_output" hideoutput=t + Case "external_help" externalhelp=t + Case "system_keys" systemkeys=t + Case "sort_code" sortcode=t + Case "language" + Try + Local tmpLanguage:TMaxGUILanguage = LoadLanguage(host.FullPath(b)) + If tmpLanguage Then + language = b + SetLocalizationLanguage tmpLanguage + host.RefreshMenu() + EndIf + Catch excn:Object + EndTry + End Select + Wend + RefreshGadgets + End Method + + Method RefreshGadgets() + Local rgb:TColor,flags + editfont=LoadGuiFont(editfontname,editfontsize) + + 'Language Loading / Enumeration + ClearGadgetItems languages + AddGadgetItem languages, "English (English) [Embedded]", GADGETITEM_DEFAULT,-1,"",DEFAULT_LANGUAGEPATH + For Local tmpFile$ = EachIn LoadDir( host.bmxpath+"/cfg", True ) + Local tmpPath$ = host.bmxpath+"/cfg/"+tmpFile + If FileType(tmpPath) = FILETYPE_FILE And tmpFile.ToLower().EndsWith(".language.ini") Then + If tmpPath = host.FullPath(language) Then flags = GADGETITEM_DEFAULT Else flags = 0 + AddGadgetItem languages, tmpFile.Split(".")[0], flags, -1, "", "$BMXPATH/cfg/"+tmpFile + EndIf + Next + + SetButtonState buttons[0],showtoolbar + SetButtonState buttons[1],restoreopenfiles + SetButtonState buttons[2],autocapitalize + SetButtonState buttons[3],syntaxhighlight + SetButtonState buttons[4],bracketmatching + SetButtonState buttons[5],autobackup + SetButtonState buttons[6],autoindent + SetButtonState buttons[7],hideoutput + SetButtonState buttons[8],externalhelp + SetButtonState buttons[9],systemkeys + SetButtonState buttons[10],sortcode + SelectGadgetItem tabbutton,Min(Max(tabsize/2-1,0),7) + SetPanelColor editpanel,editcolor.red,editcolor.green,editcolor.blue + SetGadgetText editbutton,editfontname+" : "+editfontsize + "pt" + For Local i:Int = 0 Until styles.length + styles[i].Refresh + Next + LockTextArea textarea + SetTextAreaColor textarea,editcolor.red,editcolor.green,editcolor.blue,True + SetGadgetFont textarea,editfont + styles[NORMAL].format(textarea,0,TEXTAREA_ALL) + styles[COMMENT].format(textarea,0,12) + styles[MATCHING].format(textarea,24,1) + styles[NUMBER].format(textarea,25,3) + styles[NUMBER].format(textarea,31,1) + styles[MATCHING].format(textarea,32,1) + styles[NUMBER].format(textarea,36,1) + styles[KEYWORD].format(textarea,39,5) + styles[QUOTED].format(textarea,46,10) + UnlockTextArea textarea + outputstyle.Refresh + navstyle.Refresh + dirty=True + End Method + + Method Poll() + Local font:TGUIFont,refresh,processed = 1 + For Local i:Int = 0 Until styles.length + refresh:|styles[i].Poll() + Next + refresh:|outputstyle.Poll() + refresh:|navstyle.Poll() + Select EventID() + Case EVENT_GADGETACTION, EVENT_WINDOWCLOSE + Select EventSource() + Case buttons[0];showtoolbar=ButtonState(buttons[0]);dirty=True + Case buttons[1];restoreopenfiles=ButtonState(buttons[1]) + Case buttons[2];autocapitalize=ButtonState(buttons[2]);dirty=True + Case buttons[3];syntaxhighlight=ButtonState(buttons[3]);dirty=True + Case buttons[4];bracketmatching=ButtonState(buttons[4]) + Case buttons[5];autobackup=ButtonState(buttons[5]) + Case buttons[6];autoindent=ButtonState(buttons[6]) + Case buttons[7];hideoutput=ButtonState(buttons[7]) + Case buttons[8];externalhelp=ButtonState(buttons[8]) + Case buttons[9];systemkeys=ButtonState(buttons[9]);dirty=2 + Case buttons[10];sortcode=ButtonState(buttons[10]);dirty=3 + Case tabber;SetPanelIndex SelectedGadgetItem(tabber) + Case ok + Hide() + Select dirty + Case 1 + host.RefreshAll + Case 2 + host.Restart + End Select + dirty=False + SnapShot() + Case window + If EventID()=EVENT_WINDOWCLOSE + Restore() + dirty=False + Hide() + EndIf + Case cancel + Restore() + dirty=False + Hide() + Case tabbutton + tabsize=(SelectedGadgetItem(tabbutton)+1)*2 + refresh=True + Case editpanel + If EventID()=EVENT_MOUSEDOWN + refresh=editcolor.Request() + EndIf + Case editbutton + font=RequestFont(editfont) + If font + editfontname=FontName(font) + editfontsize=FontSize(font) + refresh=True + EndIf + Case languages + If EventData() > 0 Then + language = String(GadgetItemExtra(languages,EventData())) + SetLocalizationLanguage TMaxGUILanguage(LoadLanguage(host.FullPath(language))) + Else + language = DEFAULT_LANGUAGEPATH + SetLocalizationLanguage defaultLanguage + EndIf + host.RefreshMenu() + Default + processed = 0 + EndSelect + Case EVENT_MOUSEDOWN + Select EventSource() + Case editpanel + If EventID()=EVENT_MOUSEDOWN + editcolor.Request() + refresh=True + EndIf + Default + processed = 0 + EndSelect + EndSelect + If refresh Then RefreshGadgets() + Return processed + End Method + + Method InitOptionsRequester(host:TCodePlay) + Local w:TGadget + InitPanelRequester(host,"{{options_window_title}}",380,430) +' init values + editcolor=New TColor +' init gadgets + optionspanel=AddPanel("{{options_optionstab}}") + editorpanel=AddPanel("{{options_editortab}}") + toolpanel=AddPanel("{{options_toolstab}}") + + SetGadgetShape( tabber, GadgetX(tabber), GadgetY(tabber)+32, GadgetWidth(tabber), GadgetHeight(tabber)-32 ) + + w=window + CreateLabel("{{options_options_label_language}}:",6,6+4,80,24,w) + languages = CreateComboBox(90,6,ClientWidth(w)-96,26,w) + + w=optionspanel + + buttons[0]=CreateButton("{{options_options_btn_showtoolbar}}",6,6,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[1]=CreateButton("{{options_options_btn_autorestore}}",6,34,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[2]=CreateButton("{{options_options_btn_autocaps}}",6,60,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[3]=CreateButton("{{options_options_btn_syntaxhighlight}}",6,86,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[4]=CreateButton("{{options_options_btn_bracketmatching}}",6,112,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[5]=CreateButton("{{options_options_btn_autobackup}}",6,138,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[6]=CreateButton("{{options_options_btn_autoindent}}",6,164,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[7]=CreateButton("{{options_options_btn_autohideoutput}}",6,190,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[8]=CreateButton("{{options_options_btn_useexternalbrowser}}",6,216,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[9]=CreateButton("{{options_options_btn_osshortcuts}}",6,242,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + buttons[10]=CreateButton("{{options_options_btn_sortcodeviewnodes}}",6,268,ClientWidth(w)-12,26,w,BUTTON_CHECKBOX) + + w=editorpanel + CreateLabel("{{options_editor_label_background}}:",6,6+4,90,24,w) + editpanel=CreatePanel(100,6,24,24,w,PANEL_BORDER|PANEL_ACTIVE) + editbutton=CreateButton("..",128,6,ClientWidth(w)-134,24,w) + + tabbutton=CreateComboBox(128,36,ClientWidth(w)-134,24,w) + For Local i=1 To 8 + AddGadgetItem tabbutton,"{{options_editor_itemlabel_tabsize}} "+(i*2),GADGETITEM_LOCALIZED + Next + + styles=New TTextStyle[6] + styles[NORMAL]=TTextStyle.Create("{{options_editor_label_plaintext}}:",6,66,w) + styles[COMMENT]=TTextStyle.Create("{{options_editor_label_remarks}}:",6,96,w) + styles[QUOTED]=TTextStyle.Create("{{options_editor_label_strings}}:",6,126,w) + styles[KEYWORD]=TTextStyle.Create("{{options_editor_label_keywords}}:",6,156,w) + styles[NUMBER]=TTextStyle.Create("{{options_editor_label_numbers}}:",6,186,w) + styles[MATCHING]=TTextStyle.Create("{{options_editor_label_matchings}}:",6,216,w) + + textarea=CreateTextArea(6,250,ClientWidth(w)-12,ClientHeight(w)-256,w,TEXTAREA_READONLY) + SetGadgetText textarea,"'Sample Code~n~nresult = ((2.0 * 4) + 1)~nPrint( ~qResult: ~q + result )~n" + + w=toolpanel + outputstyle=TGadgetStyle.Create("{{options_tools_label_output}}: ",6,6,w) + navstyle=TGadgetStyle.Create("{{options_tools_label_navbar}}: ",6,66,w) + + SetDefaults() + SetPanel optionspanel + + SnapShot + End Method + + Function Create:TOptionsRequester(host:TCodePlay) + Local o:TOptionsRequester + o=New TOptionsRequester + o.InitOptionsRequester host + Return o + End Function + +End Type + +Type TFindRequester Extends TRequester + Field findterm:TGadget + + Method ShowFind(term$="") + If term SetGadgetText(findterm,term) + Super.Show() + ActivateGadget findterm + End Method + + Method Poll() + Local find$,data + Select EventSource() + Case window + If EventID()=EVENT_WINDOWCLOSE + Hide() + EndIf + Case ok + If EventID() = EVENT_GADGETACTION + If TextFieldText(findterm) + find=TextFieldText(findterm) + Hide() + host.activepanel.Invoke TOOLFINDNEXT,find + Else + Notify LocalizeString("{{search_error_nosearchstring}}"), True + ActivateGadget findterm + EndIf + EndIf + Case cancel + If EventID() = EVENT_GADGETACTION Then Hide() + Default + Return 0 + End Select + Return 1 + End Method + + Function Create:TFindRequester(host:TCodePlay) + Local seek:TFindRequester + seek=New TFindRequester + seek.initrequester(host,"{{find_window_title}}",280,66,STYLE_OK|STYLE_CANCEL|STYLE_DIVIDER|STYLE_MODAL,"{{find_btn_find}}") + CreateLabel("{{find_label_find}}:",6,12,82,24,seek.window) + seek.findterm=CreateTextField(88,8,ClientWidth(seek.window)-(88+6),21,seek.window) + Return seek + End Function +End Type + +Type TReplaceRequester Extends TRequester + Field findterm:TGadget,replaceterm:TGadget + Field findnext:TGadget,replaceit:TGadget,replaceall:TGadget + + Method Show() + Super.Show() + ActivateGadget findterm + End Method + + Method Poll() + Local find$,Replace$ + Select EventSource() + Case window + If EventID()=EVENT_WINDOWCLOSE + Hide() + EndIf + Case cancel + If EventID() = EVENT_GADGETACTION Then Hide + Case ok + If EventID() = EVENT_GADGETACTION Then + If TextFieldText(findterm) Then + find=TextFieldText(findterm) + host.activepanel.Invoke TOOLFINDNEXT,find + Else + Notify LocalizeString("{{search_error_nosearchstring}}"), True + ActivateGadget findterm + EndIf + EndIf + Case replaceit + If EventID() = EVENT_GADGETACTION Then + Replace=TextFieldText(replaceterm) + If host.activepanel.Invoke(TOOLREPLACE,Replace) + host.activepanel.Invoke TOOLFINDNEXT,find + EndIf + EndIf + Case replaceall + If EventID() = EVENT_GADGETACTION Then + find=TextFieldText(findterm) + Replace=TextFieldText(replaceterm) + host.activepanel.Invoke TOOLREPLACE,find+Chr(0)+Replace + EndIf + Default + Return 0 + End Select + Return 1 + End Method + + Function Create:TReplaceRequester(host:TCodePlay) + Local x,y + Local seek:TReplaceRequester + seek=New TReplaceRequester + seek.initrequester(host,"{{replace_window_title}}",380,80,STYLE_OK|STYLE_CANCEL|STYLE_DIVIDER,"{{replace_btn_findnext}}") + + y=11 + CreateLabel( "{{replace_label_find}}:",6,y+4,88,24,seek.window ) + seek.findterm=CreateTextField( 96,y,168,21,seek.window ) + + y:+32 + CreateLabel( "{{replace_label_replacewith}}:",6,y+4,88,24,seek.window ) + seek.replaceterm=CreateTextField( 96,y,168,21,seek.window ) + + x=ClientWidth(seek.window)-102 + y=8 + seek.replaceit=CreateButton("{{replace_btn_replace}}",x,y,96,26,seek.window) + seek.replaceall=CreateButton("{{replace_btn_replaceall}}",x,y+32,96,26,seek.window) + + Return seek + End Function +End Type + +Type TEventHandler Extends TTool + Method OnEvent() Abstract +End Type + +Type TToolPanel Extends TEventHandler + Field name$,path$ + Field panel:TGadget + Field index + Field active + + Method Show() + End Method +End Type + +Type TView + Field node:TGadget + Field state +End Type + +Type TNode Extends TTool + Const HIDESTATE=0 + Const CLOSEDSTATE=1 + Const OPENSTATE=2 + + Field name$,sortname$ + Field parent:TNode + Field kids:TList=New TList + Field views:TView[] +' activate program + Field target:TTool + Field action + Field argument:Object + + Method SortKids( ascending=True ) + Local term:TLink=kids._head + Repeat + Local link:TLink=kids._head._succ + Local sorted=True + Repeat + Local succ:TLink=link._succ + If succ=term Exit + Local cc=TNode(link._value).sortname.Compare( TNode(succ._value).sortname ) + If (cc>0 And ascending) Or (cc<0 And Not ascending) + Local link_pred:TLink=link._pred + Local succ_succ:TLink=succ._succ + link_pred._succ=succ + succ._succ=link + succ._pred=link_pred + link._succ=succ_succ + link._pred=succ + succ_succ._pred=link + sorted=False + Else + link=succ + EndIf + Forever + If sorted Return + term=link + Forever + End Method + + Method FindArgument:TNode(arg:Object) + Local n:TNode,r:TNode,a$ + If arg.Compare(argument)=0 Return Self + a$=(String(arg)).ToLower() + If a And a=(String(argument)).toLower() Return Self + For n=EachIn kids + r=n.FindArgument(arg) + If r Return r + Next + End Method + + ?Debug + Method Dump(indent$="") + Local n:TNode + Print indent+name + indent:+"~t" + For n=EachIn kids + n.Dump indent + Next + End Method + ? + + Method IsHidden() + Local v:TView + If Not parent Return False + For v=EachIn parent.views + If v.state=OPENSTATE Return False + Next + Return True + End Method + + Method SetAction(tool:TTool,cmd,arg:Object=Null) + target=tool + action=cmd + argument=arg + End Method + + Method Hide(v:TView=Null) 'null means hide in all views + For Local n:TNode = EachIn kids + n.hide v + Next + If v + If v.node FreeTreeViewNode v.node;v.node=Null + Else + For v=EachIn views + If v.node FreeTreeViewNode v.node;v.node=Null + Next + EndIf + End Method + + Method Detach() + Hide() + If parent parent.kids.remove Self;parent=Null + End Method + + Method FreeKids() + For Local n:TNode = EachIn kids + n.free + Next + End Method + + Method Free() + FreeKids() + Detach() + target=Null;argument=Null;views=Null + End Method + + Method Invoke(command,arg:Object=Null) + Select command + Case TOOLACTIVATE + If target Return target.Invoke(action,argument) + End Select + End Method + + Method Find:TNode(treeviewnode:TGadget,view=0) + Local n:TNode,r:TNode + Local v:TView + v=getview(view) + If v And v.node=treeviewnode Return Self + For n=EachIn kids + r=n.Find(treeviewnode,view) + If r Return r + Next + End Method + + Method SetNode(treeviewnode:TGadget,view=0) + Local v:TView = getview(view) + v.node=treeviewnode + open view + End Method + + Method HighLight(view=-1) + Local v:TView + If view=-1 + For view=0 Until views.length + HighLight view + Next + Return + EndIf + v=GetView(view) + If v.node SelectTreeViewNode v.node + End Method + + Method Open(view=-1) + Local v:TView + If view=-1 + For view=0 Until views.length + Open view + Next + Return + EndIf + v=GetView(view) + If v.state<>OPENSTATE + v.state=OPENSTATE + RefreshView view +' If v.node ExpandTreeViewNode v.node + EndIf + End Method + + Method Close(view=0) + Local v:TView = GetView(view) + If v.state<>CLOSEDSTATE + v.state=CLOSEDSTATE +' If v.node CollapseTreeViewNode v.node + EndIf + End Method + + Method GetState(view=0) + Return GetView(view).state + End Method + + Method GetView:TView(view=0) + If view>=views.length + views=views[..view+1] + EndIf + If Not views[view] views[view] = New TView + Return views[view] + End Method + + Method GetIndex() + Local node:TNode + Local i + If parent + For node=EachIn parent.kids + If node=Self Return i + i:+1 + Next + EndIf + End Method + + Method Refresh() + For Local i:Int = 0 Until views.length + RefreshView i + Next + End Method + + Method RefreshView(view=0) + Local n:TNode,quick,nodeToOpen:TGadget + Local v:TView,vv:TView + Local node + If parent And parent.getstate(view)=CLOSEDSTATE quick=True + v=getview(view) + If v.node And parent + If GadgetText(v.node) <> name Then + ModifyTreeViewNode v.node,name + LocalizeGadget(v.node,name,"") + EndIf + If v.state=OPENSTATE nodeToOpen = v.node;quick = False + Else + If parent And name + vv=parent.getview(view) + If vv.node + v.node=InsertTreeViewNode(GetIndex(),name,vv.node) + If v.state=HIDESTATE v.state=CLOSEDSTATE + If vv.state=OPENSTATE nodeToOpen = vv.node + quick=False + EndIf + EndIf + EndIf + If quick Return + If Not kids Return + For n=EachIn kids + n.RefreshView view + Next + If nodeToOpen Then ExpandTreeViewNode nodeToOpen + End Method + + Method NodeAfter:TNode(node:TNode) + Local link:TLink + If node link=kids.FindLink(node) + If link link=link.NextLink() + If link Return TNode(link.Value()) + End Method + + Method Sync(snap:TNode) + Local snapkid:TNode + Local currentkid:TNode + Local kid:TNode + Local t:TNode + Local link:TLink + + If snap.name<>name Return + If kids.Count() currentkid=TNode(kids.First()) + For snapkid=EachIn snap.kids +' if same name in list + kid=currentkid + While kid + If kid.name=snapkid.name Exit + kid=NodeAfter(kid) + Wend +' then remove entries in front + If kid + While currentkid<>kid + t=currentkid + currentkid=NodeAfter(currentkid) + t.free() + Wend + EndIf +' if same name sync else insert + If currentkid And currentkid.name=snapkid.name 'merge values if same name + currentkid.Sync snapkid + currentkid=NodeAfter(currentkid) + Else + snapkid.detach + If currentkid + link=kids.FindLink(currentkid) + kids.InsertBeforeLink snapkid,link + Else + kids.AddLast snapkid + EndIf + snapkid.parent=Self + EndIf + Next +' remove any entries at end + While currentkid + t=currentkid + currentkid=NodeAfter(currentkid) + t.free() + Wend + Refresh() + End Method + + Method SetName(n$) + name=n + End Method + + Method AddNode:TNode(name$) + Local v:TNode + v=New TNode + v.setname name + Append v + Return v + End Method + + Method Append(v:TNode) + v.parent=Self + kids.AddLast v + End Method + + Function CreateNode:TNode(name$) + Local node:TNode + node=New TNode + node.setname name + Return node + End Function + +End Type + +Type THelpPanel Extends TToolPanel + + Field host:TCodePlay + Field htmlview:TGadget + + Method AddLink:TNode(parent:TNode,name$,href$) + Local n:TNode + If parent + n=parent.AddNode(name) + Else + n=host.helproot + EndIf + If href href=RealPath(href) + n.SetAction(Self,TOOLNAVIGATE,href) + Return n + End Method + + Method ImportLinks( node:TNode,path$ ) + + Local t$=path+"/index.html" + If FileType( t )<>FILETYPE_FILE Return + + node=AddLink( node,StripDir( path ),t ) + + Local map:TMap=New TMap + + 'scan for html files + For Local e$=EachIn LoadDir( path ) + If e="index.html" Continue + Local p$=path+"/"+e + Select FileType( p ) + Case FILETYPE_DIR + ImportLinks node,p + Case FILETYPE_FILE + If Not e.StartsWith( "_" ) And ExtractExt( e ).Tolower()="html" + map.Insert StripExt( e ),p + EndIf + End Select + Next + + 'scan for anchors in index.html... + ' + 'note: anchors must be quote enclosed and of simple form + Local c$=CacheAndLoadText( t ),i + Repeat + i=c.Find( "",i ) + If i2=-1 Exit + Local q$=c[i..i2] + If q.StartsWith( "_" ) Continue + map.Insert q,t+"#"+q + i=i2+1 + Forever + + For Local kv:TKeyValue=EachIn map + AddLink node,String( kv.Key() ),String( kv.Value() ) + Next + + End Method + + Method SyncDocs() + host.helproot.FreeKids + + ImportLinks Null,host.bmxpath+"/docs/html" + + Local link:TNode + For Local m$=EachIn EnumModules() + If m.StartsWith( "brl." ) Or m.StartsWith( "pub." ) Or m.StartsWith("maxgui.") Continue + Local p$=ModulePath( m )+"/doc/commands.html" + If FileType( p )<>FILETYPE_FILE Continue + If Not link link=AddLink( host.helproot,"{{navnode_thirdpartymods}}","" ) + AddLink link,m,p + Next + + link=AddLink( host.helproot,"{{navnode_moduleindex}}","" ) + + If FileType( host.bmxpath+"/docs/html/Modules/commands.txt" )=FILETYPE_FILE + Local comm$=CacheAndLoadText( host.bmxpath+"/docs/html/Modules/commands.txt" ) + For Local line$=EachIn comm.Split( "~n" ) + Local bits$[]=line.Split( "|" ) + If bits.length<>2 Continue + Local i=bits[0].Find( " : " ) + If i<>-1 bits[0]=bits[0][..i] + AddLink link,bits[0],host.bmxpath+bits[1] + Next + EndIf + + host.helproot.Refresh + End Method + + Method Invoke(command,argument:Object=Null) + Local href$ + If Not htmlview Return + Select command + Case TOOLCUT + GadgetCut htmlview + Case TOOLCOPY + GadgetCopy htmlview + Case TOOLPASTE + GadgetPaste htmlview + Case TOOLSHOW + ActivateGadget htmlview + host.SetTitle + Case TOOLNAVIGATE + href$=String(argument) + If href Go href + Case TOOLPRINT + GadgetPrint htmlview + End Select + End Method + + Method OnEvent() + Local url$,p,t$ + If EventSource()=htmlview + Select EventID() + Case EVENT_GADGETACTION 'NAVIGATEREQUEST + url$=String( EventExtra() ) + If url[..5]="http:" + OpenURL url + Else + p=url.findlast(".") + If p>-1 + t$=url[p..].tolower() + If t$=".bmx" + If url.Find( "file://" )=0 + url=url[7..] + ?Win32 + url=url[1..] + ? + EndIf + url=url.Replace("%20"," ") + Local source:TOpenCode=host.OpenSource(url) + If source source.MakePathTemp + Else + url=url.Replace("\","/") + url=url.Replace("user/index","user/welcome") + url=url.Replace("lang/index","lang/welcome") + url=url.Replace("mods/index","mods/welcome") + Go url$ + EndIf + EndIf + EndIf + End Select + EndIf + End Method + + Method Go(url$,internal=False) + Local node:TNode + + If host.options.externalhelp And Not internal + PollSystem + OpenURL url + MinimizeWindow host.window + PollSystem + Return + EndIf + + HtmlViewGo htmlview,url + host.SelectPanel Self + node=host.helproot.FindArgument(RealPath(url)) + If node + node.Highlight +' Else +' print "node not found" + EndIf + ActivateGadget htmlview + End Method + + Method Home() + Go host.bmxpath+HOMEPAGE,True + End Method + + Method Forward() + HtmlViewForward htmlview + End Method + + Method Back() + HtmlViewBack htmlview + End Method + + Function Create:THelpPanel(host:TCodePlay) + Local root,style + Local p:THelpPanel = New THelpPanel + p.host=host + p.name="{{tab_help}}" + codeplay.addpanel(p) + style=HTMLVIEW_NONAVIGATE 'HTMLVIEW_NOCONTEXTMENU + p.htmlview=CreateHTMLView(0,0,ClientWidth(p.panel),ClientHeight(p.panel),p.panel,style) + SetGadgetLayout p.htmlview,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED +' p.Home + p.SyncDocs + Return p + End Function + +End Type + +Type TSearchResult + + Field filepath$ + Field char%, line% + Field linestring$ + + Method AddToListbox( pGadget:TGadget ) + AddGadgetItem pGadget, "[" + line + ", " + char + "] " + filepath, 0, -1, StripWhitespace(linestring,char), Self + EndMethod + + Method Set:TSearchResult(pFilePath$,pChar%,pLine%,pLineString$) + filepath = pFilePath + char = pChar + line = pLine + linestring = pLineString + Return Self + EndMethod + + Function StripWhitespace$(pString$,pChar%) + If pString.length < pChar Then Return pString + Local outString$ + For Local i:Int = 0 Until pString.length + Select pString[i] + Case Asc(" "), Asc("~t"), Asc("~n"), Asc("~r") + If outString And Not outString.EndsWith(" ") Then outString:+" " + Default + outString:+pString[i..i+1] + EndSelect + Next + Return outString + EndFunction + +EndType + +Type TSearchRequester Extends TRequester + + Const strSearchText$ = "{{search_btn_startsearch}}", strStopSearchText$ = "{{search_btn_stopsearch}}" + + Global strFileExts$[][] = [["bmx"],filetypes.Split(","),String[](Null)] + + Field findbox:TGadget,typebox:TGadget,pathbox:TGadget,pathbutton:TGadget,pathsubdir:TGadget,results:TGadget + Field lstSearchResults:TList = New TList + + Field safetyCount% = -1, safetyThreshold = 500, safetyResetCount% = 0 + + Method Poll() + Local id:Int = EventID() + Local data:Int = EventData() + Select EventSource() + Case results + Select id + Case EVENT_GADGETACTION + Local tmpSearchResult:TSearchResult = TSearchResult(EventExtra()) + If tmpSearchResult Then + host.DebugSource( tmpSearchResult.filepath, tmpSearchResult.line, tmpSearchResult.char ) + 'Hide() + EndIf + EndSelect + Case pathbutton + If EventID()=EVENT_GADGETACTION + Local tmpString$ = RequestDir( LocalizeString("{{search_requestfolder_title}}"),GadgetText(pathbox)) + If tmpString Then SetGadgetText(pathbox,tmpString) + EndIf + Case window + If EventID()=EVENT_WINDOWCLOSE Then Hide() + Case findbox + If EventID() = EVENT_GADGETACTION Then + If GadgetText(findbox) Then EnableGadget(ok) Else DisableGadget(ok) + EndIf + Case ok + If EventID()=EVENT_GADGETACTION + If safetyCount < 0 Then StartSearch() Else safetyCount = -2 + EndIf + Case cancel + If EventID()=EVENT_GADGETACTION Then Hide() + End Select + End Method + + Method Hide() + safetyCount = -2 + Super.Hide() + EndMethod + + Method ShowWithPath( pPath$ ) + If pPath Then SetGadgetText( pathbox, pPath ) + Show() + ActivateGadget( findbox ) + EndMethod + + Method StartSearch() + + PollSystem() + + Select FileType(RealPath(GadgetText(pathbox))) + Case FILETYPE_NONE + Notify LocalizeString("{{search_error_pathnotfound}}"),True + ActivateGadget(pathbox) + Return + Case FILETYPE_FILE + Notify LocalizeString("{{search_error_pathisfile}}"),True + ActivateGadget(pathbox) + Return + EndSelect + + If Not GadgetText(findbox) Then + Notify LocalizeString("{{search_error_nosearchstring}}"),True + ActivateGadget(findbox);Return + EndIf + + safetyResetCount = 0;safetyCount = 0 + LocalizeGadget ok, strStopSearchText;ClearGadgetItems results + SearchPath( GadgetText(pathbox), strFileExts[SelectedGadgetItem(typebox)], GadgetText(findbox).ToLower(), ButtonState(pathsubdir) ) + LocalizeGadget ok, strSearchText;safetyCount = -1 + SetStatusText window, LocalizeString("{{search_msg_complete}}").Replace("%1",CountGadgetItems(results)) + + EndMethod + + Method SearchPath(pPath$,pFileType$[],pString$,pRecurse% = True) + + pPath$ = RealPath(pPath) 'Make sure we are using a real path + + Local tmpSearchDir$[] = LoadDir(pPath,True) 'Load directors contents into string array + If Not tmpSearchDir Then Return 'Return if the directory is invalid + tmpSearchDir.Sort() 'Sort the contents alphabetically + + SetStatusText window, LocalizeString("{{search_msg_searchingdir}}").Replace("%1", pPath) 'And let user know which directory is being searched + + Local tmpFullPath$ + + For Local tmpItem$ = EachIn tmpSearchDir + + tmpFullPath = pPath + "/" + tmpItem + + Select FileType(tmpFullPath) + Case FILETYPE_NONE;Continue 'Skip item if, for whatever reason, it doesn't exist + Case FILETYPE_FILE 'If file, then check extension and search if valid + If Not pFileType + SearchFile(tmpFullPath,pString) + Else + Local tmpExt$ = ExtractExt(tmpFullPath).ToLower$() + For Local tmpValidExt$ = EachIn pFileType + If tmpExt = tmpValidExt Then SearchFile(tmpFullPath,pString) + Next + EndIf + Case FILETYPE_DIR 'If folder, then we might have to search recursively + If pRecurse Then SearchPath(tmpFullPath,pFileType,pString,pRecurse) + EndSelect + + If Not ShouldContinue() Then Return + + Next + + PollSystem();If PeekEvent() Then host.Poll() 'Let the system update as we could be searching a while + + EndMethod + + Method SearchFile(pPath$,pString$) + Local tmpText$ = CacheAndLoadText( pPath ), tmpLines$[], tmpFindPos%, tmpCharCount%, tmpLineNo% + Local tmpStringLength% = pString.length, tmpChunkLines$[], tmpPrevLines$ + + If tmpText Then + tmpLines = tmpText.Split("~n") + tmpText = tmpText.ToLower() + tmpFindPos = tmpText.Find(pString) + While ShouldContinue() And tmpFindPos > -1 + tmpChunkLines = tmpText[..tmpFindPos].Split("~n") + tmpPrevLines = "~n".Join(tmpChunkLines[..tmpChunkLines.length-1]) + tmpLineNo:+(tmpChunkLines.length)-1 + Local tmpSearchResult:TSearchResult = New TSearchResult.Set(pPath,tmpFindPos-tmpPrevLines.length,tmpLineNo+1,tmpLines[tmpLineNo]) + tmpSearchResult.AddToListbox(results);safetyCount:+1 + tmpCharCount:+tmpFindPos+tmpStringLength + tmpText = tmpText[tmpFindPos+tmpStringLength..] + tmpFindPos = tmpText.Find(pString) + Wend + EndIf + EndMethod + + Method ShouldContinue() + If safetyCount < 0 Then Return False + If safetyCount >= safetyThreshold Then + If Confirm( LocalizeString("{{search_safetynotification}}").Replace("%1",(safetyResetCount*safetyThreshold)+safetyCount) ) Then + safetyCount = 0 + safetyResetCount:+1 + Else + safetyCount = -1 + Return False + EndIf + EndIf + Return True + EndMethod + + Function Create:TSearchRequester(host:TCodePlay) + Local search:TSearchRequester = New TSearchRequester + search.initrequester(host,"{{search_window_title}}",440,280,STYLE_CANCEL|STYLE_DIVIDER|STYLE_OK|STYLE_STATUS|STYLE_RESIZABLE,strSearchText) + DisableGadget(search.ok) + + SetGadgetLayout(CreateLabel("{{search_label_find}}:",6,8+4,95,24,search.window),EDGE_ALIGNED,EDGE_CENTERED,EDGE_ALIGNED,EDGE_CENTERED) + search.findbox=CreateTextField(103,8,ClientWidth(search.window)-(103+6),21,search.window);SetGadgetLayout(search.findbox,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_CENTERED) + + + SetGadgetLayout(CreateLabel("{{search_label_filetypes}}:",6,42,95,24,search.window),EDGE_ALIGNED,EDGE_CENTERED,EDGE_ALIGNED,EDGE_CENTERED) + search.typebox=CreateComboBox(103,38,ClientWidth(search.window)-(103+6),24,search.window);SetGadgetLayout(search.typebox,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_CENTERED) + + AddGadgetItem( search.typebox, "{{search_type_bmaxfiles}}",GADGETITEM_DEFAULT|GADGETITEM_LOCALIZED,-1,"*.bmx" ) + AddGadgetItem( search.typebox, "{{search_type_codefiles}}",GADGETITEM_LOCALIZED,-1,fileTypes ) + AddGadgetItem( search.typebox, "{{search_type_allfiles}}",GADGETITEM_LOCALIZED,-1,"*") + + SetGadgetLayout(CreateLabel("{{search_label_searchpath}}:",6,72,95,48,search.window),1,0,1,0) + search.pathbox=CreateTextField(103,68,ClientWidth(search.window)-(103+6+30+6),21,search.window);SetGadgetLayout(search.pathbox,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_CENTERED) + search.pathbutton=CreateButton("..",ClientWidth(search.window)-(34+6),65,34,26,search.window);SetGadgetLayout(search.pathbutton,EDGE_CENTERED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_CENTERED) + SetGadgetText(search.pathbox, CurrentDir()) + + search.pathsubdir=CreateButton("{{search_btn_searchsubfolders}}",103,98,ClientWidth(search.window)-(103+6),20,search.window,BUTTON_CHECKBOX);SetGadgetLayout(search.pathsubdir,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_CENTERED) + SetButtonState(search.pathsubdir,True) + + search.results=CreateListBox(6,128,ClientWidth(search.window)-12,280-(128+6),search.window);SetGadgetLayout(search.results,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED) + + Return search + End Function +End Type + + +Type TProjectRequester Extends TRequester + Field projects:TProjects + Field listbox:TGadget + Field add:TGadget + Field remove:TGadget + Field props:TGadget + Field moveup:TGadget + Field movedown:TGadget + Field Current:TProjectFolderNode + + Method Invoke(command,arg:Object=Null) + Select command + Case TOOLACTIVATE + Refresh + End Select + End Method + + Method SetCurrent(i) + If i=-1 + DisableGadget remove + DisableGadget moveup + DisableGadget movedown + DisableGadget props + Current=Null + Else + Current=TProjectFolderNode(GadgetItemExtra(listbox,i)) + If Current + EnableGadget remove + EnableGadget props + EnableGadget moveup + EnableGadget movedown + EndIf + EndIf + End Method + + Method Poll() + Local index + Select EventSource() + Case window + If EventID()=EVENT_WINDOWCLOSE Then Hide() + Case add + If EventID() = EVENT_GADGETACTION Then + projects.NewProject + Refresh + EndIf + Case remove + If EventID() = EVENT_GADGETACTION Then + projects.RemoveProject SelectedGadgetItem(listbox) + Refresh + EndIf + Case cancel + If EventID() = EVENT_GADGETACTION Then Hide + Case props + If EventID() = EVENT_GADGETACTION And Current + host.projectprops.Open(Current) + EndIf + Case listbox + If EventID()=EVENT_GADGETSELECT + SetCurrent SelectedGadgetItem(listbox) 'EventData() + ElseIf EventID()=EVENT_GADGETACTION + SetCurrent SelectedGadgetItem(listbox) + host.projectprops.Open(Current) + EndIf + Case moveup + If EventID()=EVENT_GADGETACTION Then + index=projects.MoveProject(SelectedGadgetItem(listbox),-1) + Refresh + SelectGadgetItem listbox,index + SetCurrent(index) + EndIf + Case movedown + If EventID()=EVENT_GADGETACTION Then + index=projects.MoveProject(SelectedGadgetItem(listbox),1) + Refresh + SelectGadgetItem listbox,index + SetCurrent(index) + EndIf + End Select + End Method + + Method Refresh() + ClearGadgetItems listbox + For Local node:TNode = EachIn projects.kids + If TFolderNode(node)'node.argument + AddGadgetItem listbox,node.name,0,-1,"",node + EndIf + Next + SetCurrent -1 + End Method + + Method Open(projnode:TProjects) + projects=projnode + Refresh + Show + End Method + + Function Create:TProjectRequester(host:TCodePlay) + Local x,y + Local proj:TProjectRequester = New TProjectRequester + + proj.initrequester(host,"{{projman_window_title}}",400,168,STYLE_CANCEL|STYLE_DIVIDER|STYLE_MODAL) + proj.listbox=CreateListBox( 6,8,244,154,proj.window ) + + x=ClientWidth(proj.window)-144 + proj.add=CreateButton("{{projman_btn_addproj}}",x,8,138,26,proj.window) + proj.remove=CreateButton("{{projman_btn_delproj}}",x,40,138,26,proj.window) + + proj.moveup=CreateButton("{{projman_btn_moveup}}",x,72,138,26,proj.window) + proj.movedown=CreateButton("{{projman_btn_movedn}}",x,104,138,26,proj.window) + + proj.props=CreateButton("{{projman_btn_properties}}",x,136,138,26,proj.window) + + DisableGadget proj.remove + DisableGadget proj.moveup + DisableGadget proj.movedown + DisableGadget proj.props + Return proj + End Function +End Type + +Type TProjectProperties Extends TRequester + Field proj:TProjectFolderNode + Field newproj:Int = False 'If 'True' then cancel/close deletes project. + Field localname:TGadget + Field localpath:TGadget + Field pathbutton:TGadget + Field path:TGadget + Field user:TGadget + Field password:TGadget + Field checkout:TGadget +' Field update:TGadget +' Field commit:TGadget + Field poprequester:TRequester 'hack for restoring to projectmanager requester + Field dirty + + Method Invoke(command,arg:Object=Null) + Select command + Case TOOLACTIVATE + Refresh + End Select + End Method + + Method Tidy() + newproj = False + If dirty + proj.Set GadgetText(localname),GadgetText(localpath),GadgetText(path),GadgetText(user),GadgetText(password) + dirty=False + EndIf + End Method + + Method Poll() + If (EventID() <> EVENT_GADGETACTION) And (EventID() <> EVENT_WINDOWCLOSE) Then Return + Select EventSource() + Case localname,localpath,path,user,password + dirty=True + Case pathbutton + Local dir$=RequestDir(LocalizeString("{{project_requestfolder_title}}")) + If dir + If dir[dir.length-1..]="/" 'fltk hack + dir=dir[..dir.length-1] + EndIf + SetGadgetText localpath,dir + If GadgetText(localname)="" + SetGadgetText localname,StripDir(dir) + EndIf + dirty=True + EndIf + Case checkout + Tidy() + Hide() + proj.CheckoutVersion() +Rem + Case commit + Tidy + Hide + proj.CommitVersion + Case update + Tidy + Hide + proj.UpdateVersion +EndRem + Case ok + Tidy() + Hide() + Case cancel + Hide() + Case window + If EventID()=EVENT_WINDOWCLOSE + Hide() + EndIf + End Select + End Method + + Method Hide() + If proj And newproj Then proj.Free() + EnableGadget host.window + HideGadget window + host.UnhookRequester Self'poprequester + If poprequester poprequester.Show + End Method + + Method Refresh() + SetGadgetText localname,proj.name + SetGadgetText localpath,proj.path + SetGadgetText path,proj.svnpath + SetGadgetText user,proj.svnuser + SetGadgetText password,proj.svnpass + End Method + + Method Open(projnode:TProjectFolderNode, newproject:Int = False) + newproj=newproject + proj=projnode + Refresh() + Show() + End Method + + Function Create:TProjectProperties(host:TCodePlay) + Local proj:TProjectProperties = New TProjectProperties + proj.initrequester(host,"{{project_window_title}}",480,250,STYLE_OK|STYLE_CANCEL|STYLE_DIVIDER|STYLE_MODAL) + proj.modal = True + + Local projectdetails:TGadget = CreatePanel(6,8,ClientWidth(proj.window)-12,85,proj.window,PANEL_GROUP,"{{project_group_details}}") + + Local i,n,y + y=4 + + CreateLabel("{{project_label_name}}:",6,y+4,72,24,projectdetails) + proj.localname=CreateTextField(88,y,ClientWidth(projectdetails)-(88+6),21,projectdetails) +' proj.pathbutton=CreateButton("..",434,y,34,28,projectdetails) + y:+30 + + CreateLabel("{{project_label_path}}:",8,y+4,72,24,projectdetails) + proj.localpath=CreateTextField(88,y,ClientWidth(projectdetails)-(88+34+6+6),21,projectdetails) + proj.pathbutton=CreateButton("..",ClientWidth(projectdetails)-(34+6),y-3,34,26,projectdetails) + y:+30 + + + Local svnbox:TGadget = CreatePanel(6,101,ClientWidth(proj.window)-12,144,proj.window,PANEL_GROUP,"{{project_group_svn}}") + y=4 + CreateLabel("{{project_label_url}}:",8,y+LABELOFFSET,72,24,svnbox) + proj.path=CreateTextField(88,y,ClientWidth(svnbox)-92,21,svnbox) + y:+30 + CreateLabel("{{project_label_username}}:",8,y+LABELOFFSET,72,24,svnbox) + proj.user=CreateTextField(88,y,ClientWidth(svnbox)-92,21,svnbox) + y:+30 + CreateLabel("{{project_label_password}}:",8,y+LABELOFFSET,72,24,svnbox) + proj.password=CreateTextField(88,y,ClientWidth(svnbox)-92,21,svnbox,TEXTFIELD_PASSWORD) + y:+30 + proj.checkout=CreateButton("{{project_btn_checkout}}",ClientWidth(svnbox)-154,ClientHeight(svnbox)-32,150,28,svnbox) +' proj.update=CreateButton("{{project_btn_update}}",180,y+10,150,28,svnbox) +' proj.commit=CreateButton("{{project_btn_commit}}",340,y+10,150,28,svnbox) + y:+40 + + Return proj + End Function +End Type + +Function GetInfo$(a$ Var) + Local p,r$ + p=a.Find("|")+1 + If p=0 p=a.length+1 + r$=a$[..p-1] + a$=a$[p..] + Return r$ +End Function + +Type TFolderNode Extends TNode + Field owner:TNode + Field path$ + Field scanned + Field version + Field foldertype + + Const PROJECTFOLDER=0 + Const DIRECTORYFOLDER=1 + Const FILEFOLDER=2 + + Method FindFolderFromPath:TFolderNode(dir$) + Local result:TFolderNode + If path=dir Return Self + For Local folder:TFolderNode = EachIn kids + result=folder.FindFolderFromPath(dir) + If result Return result + Next + End Method + + Method SetName(n$) + If version Then n:+"("+version+")" + Super.SetName( n ) + Refresh + End Method + + Method SetVersion(ver) + version=ver + SetName StripDir(path) + End Method + + Method Write(stream:TStream) + Local isopen + If GetState()=OPENSTATE isopen=True + If version Or isopen + stream.WriteLine "proj_data="+path+"|"+isopen+"|"+version+"|" + EndIf + For Local folder:TFolderNode = EachIn kids + folder.Write(stream) + Next + End Method + + Method ProjectHost:TCodePlay() + Local n:TNode = Self + While n + If TProjects(n) Return TProjects(n).host + n=n.parent + Wend + End Method + + Method ProjectNode:TProjectFolderNode() + Local n:TNode = Self + While n + If TProjectFolderNode(n) Return TProjectFolderNode(n) + n=n.parent + Wend + End Method + + Method RunSVN(cmd$,about$,refresh) + + Local host:TCodePlay = ProjectHost() + If Not host Notify LocalizeString("{{svn_notification_nodehostnotfound}}");Return + + Local project:TProjectFolderNode = ProjectNode() + If Not project Notify LocalizeString("{{svn_notification_nodeprojectnotfound}}");Return + + If project.svnuser + cmd:+" --username "+project.svnuser + If project.svnpass cmd:+" --password "+project.svnpass + EndIf + + If refresh + host.execute cmd,about,MENUREFRESH,True,Self + Else + host.execute cmd,about,0,0,Self + EndIf + End Method + + Method UpdateVersion() + Local cmd$=svncmd+" update" + cmd:+" "+quote(path) + RunSVN cmd,LocalizeString("{{svn_msg_updating}}").Replace("%1",path),True + End Method + + Method CommitVersion() + Local cmd$=svncmd+" commit" + cmd:+" -m ~qmy comment~q" + cmd:+" "+quote(path) + RunSVN cmd,LocalizeString("{{svn_msg_committing}}").Replace("%1",path),False + End Method + + Method Open(view=-1) + Update(True) + Super.Open view + End Method + + Method AddFileNode:TNode(file$) + Local n:TNode + Local ext$ + If (","+FileTypes+",").Contains(","+ExtractExt(file).toLower()+",") Then + n=AddNode(StripDir(file)) + n.SetAction(owner,TOOLOPEN,file) + ext=ExtractExt(file$).ToLower() + n.sortname=ext+n.name + Return n + EndIf + End Method + + Method AddFolderNode:TNode(path$) + Local n:TFolderNode = TFolderNode.CreateFolderNode(path,DIRECTORYFOLDER) + n.owner = owner + n.sortname=" "+n.name + Append n + Return n + End Method + + Method Scan(o:TNode) + Local p$ + Local flist:TList = New TList + + owner=o + + For Local f$ = EachIn LoadDir(path,True) + If f[..1] = "." Then Continue + p$=path+"/"+f + Select FileType(p$) + Case FILETYPE_FILE + AddFileNode p$ + Case FILETYPE_DIR + AddFolderNode p$ + End Select + Next + + SortKids + scanned = True + + End Method + + Method ScanKids() + For Local f:TFolderNode = EachIn kids + f.owner = owner + f.Update(False) + Next + End Method + + Method Rescan() + scanned = False + Update() + EndMethod + + Method Update( alwaysScanKids:Int = False ) + If Not scanned Then + FreeKids() + Scan owner + EndIf + If alwaysScanKids Or Not IsHidden() Then ScanKids() + Refresh() + End Method + + Method Invoke(command,argument:Object=Null) + Local host:TCodePlay + Local cmd,p + Local line$ + + host=ProjectHost() + If Not host Notify LocalizeString("{{svn_notification_nodehostnotfound}}");Return + + Select command + Case TOOLOUTPUT + line$=String(argument) + p=line.find(" revision ") + If p>-1 + SetVersion Int(line[p+10..]) + EndIf +' If line[..12]="At revision " +' DebugLog "TOOLOUTPUT:"+line + Return + Case TOOLERROR + line$=String(argument) +' DebugLog "TOOLERROR:"+line + Return + Case TOOLMENU + cmd=Int(String(argument)) + Select cmd + Case 0 'special toolmenu-command=0 fired by rightbutton node context + Highlight + Local menu:TGadget + menu=host.projects.projmenu + PopupWindowMenu host.window,menu,Self + Case MENUREFRESH + Rescan() + Case MENUBROWSE + OpenURL RealPath(path) + Case MENUSHELL + Local cd$=CurrentDir() + ChangeDir RealPath(path) +?MacOS + host.execute "/bin/bash","Shell Terminal" +?Linux + host.execute "/bin/bash","Shell Terminal" +?Win32 + host.execute "cmd","Shell Terminal - Type Exit To End" +? + ChangeDir cd + Case MENUUPDATE + UpdateVersion + Case MENUCOMMIT + CommitVersion +' Case MENUPROPS +' host.projectprops.Open(Self) + Case MENUFINDINFILES + host.searchreq.ShowWithPath( RealPath(path) ) + End Select + End Select + End Method + + Function CreateFolderNode:TFolderNode(path$,foldertype) + Local n:TFolderNode = New TFolderNode +' n.host=host + n.SetName( StripDir(path) ) + n.path = path + n.foldertype = foldertype + Return n + End Function +End Type + +Type TProjectFolderNode Extends TFolderNode + + Field owner:TProjects + Field svnpath$,svnuser$,svnpass$,svnversion + Field svnerr$ + + Method CheckoutVersion() 'to do - needs to move old version to temp? + Local cmd$ = svncmd+" checkout" + cmd:+" "+quote(svnpath) + cmd:+" "+quote(path) + RunSVN cmd,LocalizeString("{{svn_msg_checkingout}}").Replace("%1",svnpath).Replace("%2",path),True + End Method + + Function Crypt$(a$) + Local b$,c + For Local i:Int = 0 Until a.length + c=a[i] + If c>31 c:~((i*-5)&31) + b:+Chr(c&255) + Next + Return b + End Function + + Method ToString$() + Local prj$ + Local isopen + If GetState()&OPENSTATE isopen=True + prj=name+"|"+path+"|"+svnpath+"|"+svnuser+"|"+crypt(svnpass)+"|"+isopen+"|"+version + Return prj + End Method + + Method Write(stream:TStream) + stream.WriteLine "proj_node="+ToString() + For Local folder:TFolderNode = EachIn kids + folder.Write(stream) + Next + End Method + + Method FromString(info$) + Local n$ = GetInfo(info) + If Not n Then n = "Unknown" + SetName( n ) + path=GetInfo(info) + If path path=owner.host.FullPath(path) + svnpath=GetInfo(info) + svnuser=GetInfo(info) + svnpass=GetInfo(info) + Scan(owner) + Local isopen,vers + isopen=Int(GetInfo(info)) + If isopen + Open + EndIf + vers=Int(GetInfo(info)) + If vers + SetVersion vers + EndIf + End Method + + Method Invoke(command,argument:Object=Null) + Local cmd + Select command + Case TOOLMENU + cmd=Int(String(argument)) + Select cmd + Case MENUPROPS + Return owner.host.projectprops.Open(Self) + End Select + End Select + Return Super.Invoke(command,argument) + End Method + + Method Set(n$,p$,s$,user$,pass$) + path=owner.host.FullPath(p) + setname n + svnpath=s + svnuser=user + svnpass=pass + Rescan() + owner.host.projectreq.Refresh() + End Method + + Function CreateProjectNode:TProjectFolderNode(projects:TProjects,info$) + Local n:TProjectFolderNode = New TProjectFolderNode + n.owner=projects + n.FromString(info) + n.foldertype=PROJECTFOLDER + Return n + End Function +End Type + +Type TProjects Extends TNode + Field host:TCodePlay + Field addproj:TNode + Field projmenu:TGadget + Field projmenuprops:TGadget + + Method RemoveProject(index) + Local node:TNode + If index<0 Or index>=kids.Count() Return + node=TNode(kids.ValueAtIndex(index)) + If node node.Free + Refresh + End Method + + Method MoveProject(index,dir) + Local node:TNode + Local link:TLink + If index<0 Or index>=kids.Count() Return index + node=TNode(kids.ValueAtIndex(index)) + If node + addproj.Detach + node.Hide + link=kids.FindLink(node) + If dir>0 + If link link=link._succ + If link + kids.Remove node + kids.InsertAfterLink node,link + index:+1 + EndIf + Else + If link link=link._pred + If link + kids.Remove node + kids.InsertBeforeLink node,link + index:-1 + EndIf + EndIf + Append addproj + Refresh + EndIf + Return index + End Method + + Method NewProject() + addproj.Detach + Local proj:TProjectFolderNode = TProjectFolderNode.CreateProjectNode(Self,LocalizeString("{{project_defaultname}}")) +' proj.scan(Self) + Append proj + Append addproj + host.projectprops.Open(proj, True) + Refresh + End Method + + Method AddProject(data:TList) + Local project:TProjectFolderNode + Local folder:TFolderNode + For Local info$ = EachIn data + If Not project + addproj.Detach + project=TProjectFolderNode.CreateProjectNode(Self,info) + Append project + Append addproj + Refresh + Else + Local path$ + Local popen + Local pversion + path=GetInfo(info) + popen=Int(GetInfo(info)) + pversion=Int(GetInfo(info)) + folder=project.FindFolderFromPath(path) + If folder + folder.SetVersion pversion + folder.ReScan() + If popen Then folder.Open() + EndIf + EndIf + Next + End Method + + Method Write(stream:TStream) + For Local project:TProjectFolderNode = EachIn kids + project.Write(stream) + Next + End Method + + Method Invoke(command,argument:Object=Null) + Select command + Case TOOLNEW + NewProject + Case TOOLOPEN + host.OpenSource String(argument) + End Select + End Method + + Function CreateProjects:TProjects(host:TCodePlay) + Local p:TProjects = New TProjects + p.SetName("{{navnode_projects}}") + p.host=host + p.addproj=p.AddNode("{{navnode_addproject}}") + p.addproj.SetAction p,TOOLNEW + + p.projmenu=CreateMenu("{{popup_nav_proj}}",0,Null) + CreateMenu "{{popup_nav_proj_refresh}}",MENUREFRESH,p.projmenu + CreateMenu "{{popup_nav_proj_findinfiles}}",MENUFINDINFILES,p.projmenu + CreateMenu "{{popup_nav_proj_explore}}",MENUBROWSE,p.projmenu + CreateMenu "{{popup_nav_proj_shell}}",MENUSHELL,p.projmenu + CreateMenu "",0,p.projmenu + CreateMenu "{{popup_nav_proj_svnupdate}}",MENUUPDATE,p.projmenu + CreateMenu "{{popup_nav_proj_svncommit}}",MENUCOMMIT,p.projmenu + CreateMenu "",0,p.projmenu + p.projmenuprops=CreateMenu("{{popup_nav_proj_properties}}",MENUPROPS,p.projmenu) + host.projectreq.projects=p + Return p + End Function +End Type + +Type TByteBuffer Extends TStream + Field bytes:Byte[] + Field readpointer + + Method Read( buf:Byte Ptr,count ) + If count>readpointer count=readpointer + If Not count Return + MemCopy buf,bytes,count + readpointer:-count + If readpointer MemMove bytes,Varptr bytes[count],readpointer + Return count + End Method + + Method ReadLine$() + For Local i:Int = 0 Until readpointer + If bytes[i]=10 Or bytes[i] = 0 Then + Local tmpBytes:Byte[] = New Byte[i+1] + If i And bytes[i-1] = 13 Then i:-1 + Read(tmpBytes,tmpBytes.length) + Return String.FromBytes(tmpBytes, i) + EndIf + Next + EndMethod + + Method WriteFromPipe( pipe:TPipeStream ) + Local n,m,count = pipe.ReadAvail() + n=readpointer+count + If n>bytes.length + m=Max(bytes.length*1.5,n) + bytes=bytes[..m] + EndIf + pipe.Read( Varptr bytes[readpointer], count ) + readpointer=n + Return count + EndMethod + + Method Write( buf:Byte Ptr,count ) + Local n,m + n=readpointer+count + If n>bytes.length + m=Max(bytes.length*1.5,n) + bytes=bytes[..m] + EndIf + MemCopy Varptr bytes[readpointer],buf,count + readpointer=n + Return count + End Method + + Method LineAvail() + For Local i:Int = 0 Until readpointer + If bytes[i]=10 Return True + Next + End Method + + Method FlushBytes:Byte[]() + Local res:Byte[] = bytes[..readpointer] + readpointer = 0 + Return res + End Method +End Type + +Type TObj + Field addr$,sync,refs,syncnext + Method ShouldSync( pDebugTree:TDebugTree ) + If sync < pDebugTree.sync Then pDebugTree.QueueSync( Self ) + EndMethod + Method HasSynced( pSync% ) + sync = pSync;syncnext = False + EndMethod +End Type + +Type TVar Extends TNode + + Field owner:TDebugTree + Field obj:Object + + Method Free() + If TObj(obj) owner.RemoveObj TObj(obj) + obj=Null + Super.Free() + End Method + + Method SetVarName(n$) + Local p + name=n +' if object ref set addr$ field + If name.find("$=")=-1 And name.find( ":String=" )=-1 And name.find(")=$")=-1 + p=name.find("=$") + If p<>-1 + If TObj(obj) Then + If TObj(obj).addr <> name[p+2..] Then + TDebugTree.RemoveObj TObj(obj) + Else + TObj(obj).refs:-1 + EndIf + EndIf + obj=TDebugTree.AddObj(name[p+2..]) + 'Request object dump if we are visible now that + 'we have updated our own object pointer. + If Not IsHidden() Then Request() + Return + EndIf + p=name.find("=Null") + If p<>-1 + FreeKids + TDebugTree.RemoveObj TObj(obj) + obj=Null + EndIf + EndIf + End Method + + Method AddVar(name$) + Local v:TVar=New TVar + v.owner=owner + Append v + v.setvarname name + End Method + + Method SetValue(val:TVar) + Local v:TVar,w:TVar,i,kidsarray:Object[] +' if this is a reference to same object refresh values + If obj And obj=val.obj + If kids.IsEmpty() + For v=EachIn val.kids + AddVar v.name + Next + Else + kidsarray = kids.ToArray() + For v=EachIn val.kids + If i")+1 + Local r=f.Find(",")+1 + If p And q And r + file=f[..p-1] + line=Int(f[p..r-1]) + column=Int(f[r..q-1]) + EndIf + obj=Self + End Method + + Method Request() + For Local kid:TVar = EachIn kids + kid.Request() + Next + EndMethod + +End Type + +Type TDebugTree Extends TVar + Global sync + Global objmap:TMap = CreateMap() + Field host:TCodePlay + Field instack:TList + Field inscope:TScope + Field invar:TVar + Field infile$ + Field inexception$ + Field firststop + Field cancontinue + + Method Reset() +' host.SetMode host.DEBUGMODE + SetStack( New TList ) + ClearMap objmap + instack=Null + inscope=Null + invar=Null + infile="" + inexception="" + sync=0 + firststop=True + cancontinue=False + End Method + + Function AddObj:TObj(addr$) + Local o:TObj = TObj(MapValueForKey( objmap, addr )) + If o Then + o.refs:+1 + Else + o=New TObj + o.addr=addr + o.refs=1 + MapInsert objmap, addr, o + EndIf + Return o + End Function + + Function FindObj:TObj(addr$) + Return TObj(MapValueForKey( objmap, addr )) + End Function + + Function RemoveObj(obj:TObj) ':TObj + If obj Then + obj.refs:-1 + If Not obj.refs Then MapRemove objmap, obj.addr + EndIf + End Function + + Method SyncVars() + sync:+1 + For Local tmpVar:TVar = EachIn kids + tmpVar.Request() + Next + End Method + + Method QueueSync( pObj:TObj ) + If Not pObj Then Return + 'Sync as soon as the debug pipe is clear + '(see TOuputPanel.SendDumpRequests()). + pObj.syncnext = True + EndMethod + + Method SetStack(list:TList) + Local openscope:TScope + Local s:TScope + Local count,i + + count=kids.count() 'root.varlist.count() + For Local scope:TScope = EachIn list + If i>=count + Append scope 'root.Append scope + s=scope + Else + s=TScope(kids.ValueAtIndex(i)) +' simon was here + If s.name=scope.name + s.SetScope scope + scope.Free + Else + While kids.count()>i + s=TScope(kids.Last()) + s.free + Wend + Append scope + s=scope + count=i+1 + EndIf + + EndIf + If firststop + If host.IsSourceOpen(s.file) openscope=s + Else + openscope=s + EndIf + i:+1 + Next + While kids.count()>i + s=TScope(kids.Last()) + s.free + Wend + If list.IsEmpty() Return + If Not openscope openscope=TScope(list.First()) + If openscope SelectScope openscope,True + Refresh + firststop=False + End Method + + Method SelectScope(scope:TScope,open) + If Not scope Return + host.SetMode host.DEBUGMODE ' simon was here, smoved from reset + If scope.file host.DebugSource scope.file,scope.line,scope.column + scope.Open() +' If open +' SelectTreeViewNode scope.node +' scope.open +' EndIf + End Method + + Method ProcessError$(line$) + Local p + + While p < line.length + If line[p]=$3E Then p:+1 Else Exit '">" + Wend + + If p = line.length Return + If p Then line = line[p..] + + If Not line.StartsWith("~~>") Return line + line=line[2..] + + If invar + If line="}" + SetValue invar 'root + invar.Free + invar=Null + Else +' If Not invar.name +' invar.name=line +' Else + invar.AddVar line +' EndIf + EndIf + Return + EndIf + + If instack + If line="}" + + SetStack instack + instack=Null + inscope=Null + 'Request first object dumps, and bump sync count + SyncVars + If inexception + Notify inexception + inexception="" + EndIf + Return + EndIf + + If infile + If line="Local " + Else + inscope=New TScope +' Print "inscope.line="+line + inscope.name=line + inscope.owner=Self + instack.AddLast inscope + EndIf + If inscope inscope.setfile Self,infile + infile="" + Return + EndIf + + If line.StartsWith("@") And line.Contains("<") + infile=line[1..] + Else + If inscope inscope.AddVar line + EndIf + + Return + EndIf + + If line.StartsWith("Unhandled Exception:") + inexception=line + host.output.WritePipe "t" + cancontinue=False + Return + EndIf + + If line="StackTrace{" + instack=New TList + Return + EndIf + + If line="Debug:" Or line="DebugStop:" + host.output.WritePipe "t" + If Not cancontinue Then + cancontinue=True + host.RefreshToolbar() + EndIf + Return + EndIf + + If line.StartsWith("ObjectDump@") + p=line.find("{",11) + If p=-1 Return line + line=line[11..p] + invar=New TVar + invar.obj=FindObj(line) + invar.owner=Self + Return + EndIf + + End Method + + Function CreateDebugTree:TDebugTree(host:TCodePlay) + Local d:TDebugTree = New TDebugTree + d.owner=d + d.SetName "{{navtab_debug}}" + d.host=host + d.Open + Return d + End Function +End Type + +Type TNodeView + Field owner:TNavBar + Field root:TNode + Field treeview:TGadget + Field index + + Method NewView() + Local n:TNode,hnode:TGadget + hnode=SelectedTreeViewNode(treeview) + n=root.Find(hnode,index) + If n And n.parent owner.AddView n + End Method + + Method OnEvent() + Local n:TNode = root.Find(TGadget(EventExtra()),index) + If Not n Return 'probably an eventgadgetselect -1 Notify("could not find in root");Return + + Select EventID() + Case EVENT_GADGETSELECT + n.invoke(TOOLSELECT) + Case EVENT_GADGETACTION + n.invoke(TOOLACTIVATE) + Case EVENT_GADGETMENU + n.invoke(TOOLMENU,Self) + Case EVENT_GADGETOPEN + n.open index + Case EVENT_GADGETCLOSE + n.close index + End Select + End Method +End Type + +Type TNavBar Extends TEventHandler + Field host:TCodePlay + Field tabber:TGadget + Field viewlist:TList=New TList + Field selected:TNodeView + Field navmenu:TGadget + + Method SelectedView() + If selected Return selected.index + End Method + + Method SelectView(index) + Local n:TNodeView + If index>=viewlist.count() Return + n=TNodeView(viewlist.ValueAtIndex(index)) + If Not n Print "selectview failed";Return + If n<>selected + If selected HideGadget selected.treeview + selected=n + EndIf + ShowGadget n.treeview + SelectGadgetItem tabber,index + End Method + + Method AddView(node:TNode) + Local n:TNodeView + Local index,root:TGadget + For n=EachIn viewlist + If n.root=node SelectView n.index;Return + Next + n=New TNodeView + n.owner=Self + n.root=node + n.treeview=CreateTreeView(0,0,ClientWidth(tabber),ClientHeight(tabber),tabber) + host.options.navstyle.Apply n.treeview + SetGadgetLayout n.treeview,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED + HideGadget n.treeview + + n.index=viewlist.Count() + viewlist.AddLast n + + AddGadgetItem tabber,node.name,GADGETITEM_LOCALIZED + root=TreeViewRoot(n.treeview) + node.setnode root,n.index + SelectView n.index + Return n.index + End Method + + Method OnEvent() + If EventSource()=tabber + SelectView SelectedGadgetItem(tabber) + End If + If selected And EventSource()=selected.treeview + selected.OnEvent + EndIf + End Method + + Method Refresh() + For Local view:TNodeView = EachIn viewlist + host.options.navstyle.Apply view.treeview + Next + End Method + + Method Invoke(command,argument:Object=Null) + If command=TOOLREFRESH Refresh() + If command=TOOLNEWVIEW And selected selected.NewView + End Method + + Function CreateNavMenu:TGadget() + Local edit:TGadget = CreateMenu("&Nav",0,Null) + CreateMenu "&New View",MENUNEWVIEW,edit + Return edit + End Function + + Function Create:TNavBar(host:TCodePlay, parent:TGadget) ',root:TNode) + Local n:TNavBar = New TNavBar + n.host=host + n.tabber=CreateTabber(0,0,ClientWidth(parent),ClientHeight(parent),parent) + SetGadgetLayout(n.tabber,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED) +' n.AddView root + n.navmenu=CreateNavMenu() + Return n + End Function + +End Type + +Type TOutputPanel Extends TToolPanel 'used build and run + + Field host:TCodePlay + Field output:TGadget + + Field process:TProcess + Field pipe:TStream + + Field wpipe:TTextStream + + Field user$,cmdline$,err$,post$ + Field errbuffer:TByteBuffer + Field outputmenu:TGadget + Field posttool:TTool + + Method ClearDumpRequests() + For Local o:TObj = EachIn MapValues(host.debugtree.objmap) + o.HasSynced(o.sync) + Next + EndMethod + + Method SendDumpRequests() + For Local o:TObj = EachIn MapValues(host.debugtree.objmap) + If o.syncnext Then + If o.addr <> "00000000" Then WritePipe "d"+o.addr + o.HasSynced( host.debugtree.sync ) + EndIf + Next + EndMethod + + Method Clear() + If Not output Open() + SetGadgetText output,"" + End Method + + Method WriteAscii(mess$) + If Not output Open() + AddTextAreaText output,mess.Replace("~0","") + End Method + + Method Write(mess$) + If Not output Open() + AddTextAreaText output,mess.Replace("~0","") + End Method + + Method Execute(cmd$,mess$="",exe$="",home=True,owner:TTool=Null) + If Not output Open() + If Not mess$ mess$=cmd$ + err$="" + post$=exe + posttool=owner + host.SelectPanel Self + host.debugtree.Reset + + If process And ProcessStatus(process) + Delay 500 + If ProcessStatus(process) + Notify LocalizeString("{{output_notification_stillbusy}}").Replace("%1",cmdline) + Return + EndIf + EndIf + cmd=cmd.Trim() + process=CreateProcess(cmd$,HIDECONSOLE) + If Not process Then Notify LocalizeString("{{output_notification_processfailure}}").Replace("%1",cmd);Return + If Not process.status() Then Notify LocalizeString("{{output_notification_failedstart}}").Replace("%1",cmd);process=Null;Return + pipe=Process.pipe + wpipe=TTextStream.Create(pipe,TTextStream.UTF8) + + cmdline=cmd + If home Clear + Write( mess+"~n" ) + errbuffer = New TByteBuffer + host.RefreshToolbar + + End Method + + Method WritePipe(l$) + Try + If pipe pipe.WriteLine(l) + Catch ex:TStreamWriteException + Write LocalizeString("{{output_msg_debugfailure}}~n").Replace("%1",l) + Stop + EndTry + End Method + + Method Go() + WritePipe "r" + host.debugtree.cancontinue = False + host.SelectPanel Self + host.RefreshToolbar() + End Method + + Method StepOver() + ClearDumpRequests() + WritePipe "s" + End Method + + Method StepIn() + ClearDumpRequests() + WritePipe "e" + End Method + + Method StepOut() + ClearDumpRequests() + WritePipe "l" + End Method + + Method Stop() + If Not process Return + process.Terminate() + FlushPipes process.pipe,process.err + process.Close() + process=Null + Write LocalizeString("~n{{output_msg_processterminated}}~n") + host.DebugExit() + Close() + End Method + + Method Wait() + While process And process.status() + PollSystem + Wend + End Method + + Method Invoke(command,argument:Object=Null) + Select command + Case TOOLSHOW + host.SetTitle() + If output ActivateGadget output + Case TOOLCLOSE + host.RemovePanel Self + output=Null + Case TOOLCUT + GadgetCut output + Case TOOLCOPY + GadgetCopy output + Case TOOLPASTE + GadgetPaste output + Case TOOLSELECTALL + If output SelectTextAreaText output + Case TOOLREFRESH + host.options.outputstyle.apply output + End Select + End Method + + Method Close() + host.SelectPanel host.activepanel + End Method + + Method Escape() + Stop + Close + End Method + + Function outputfilter(event:TEvent,context:Object) + Local out:TOutputPanel=TOutputPanel(context) + If Not out Return + Select event.id + Case EVENT_KEYDOWN + If event.data=27 + out.Escape() + Return 0 + EndIf + Case EVENT_KEYCHAR +' Print "output_keychar "+event.data + out.writechar(event.data) + End Select + Return 1 + End Function + + Method OnEvent() + If EventSource()=output + If EventID()=EVENT_GADGETMENU + PopupWindowMenu host.window,outputmenu + EndIf + EndIf +' Case EVENT_TIMERTICK + If Not process Return + + ReadPipes process.pipe,process.err + + If Not process.status() + process.terminate + FlushPipes process.pipe,process.err + process.close() + process = Null + Write LocalizeString("~n{{output_msg_processcomplete}}~n") + host.DebugExit + host.SelectPanel Self + + If err + host.ParseError err + Else + If post$ + Local menuaction=Int(post) + If menuaction + host.OnMenu menuaction,posttool +' Else +' Execute post$,"","",False,0 + EndIf + Else + If host.options.hideoutput Close() + EndIf + EndIf + EndIf + + End Method + + Method FlushPipes(pipe:TPipeStream,errpipe:TPipeStream) + ReadPipes(pipe,errpipe) + Local bytes:Byte[] = errbuffer.flushbytes() + If bytes + Local line$=String.FromBytes(bytes,Len bytes) + line=line.Replace(Chr(13),"") + If line<>">" Write line + EndIf + End Method + + Method ReadPipes(pipe:TPipeStream,errpipe:TPipeStream) + Local status + Local bytes:Byte[],line$ + + bytes=pipe.ReadPipe() + If bytes + line$=String.FromBytes(bytes,Len bytes) + line=line.Replace(Chr(13),"") + Write line + EndIf + + If errpipe.ReadAvail() Then + errbuffer.WriteFromPipe(errpipe) + Else + SendDumpRequests() + EndIf + +' If bytes Write String.FromBytes(bytes,bytes.length) + While errbuffer.LineAvail() + line$=errbuffer.ReadLine() + line=host.debugtree.ProcessError(line) + If line + Write line+"~n" + err:+line+"~n" + EndIf + Wend + + End Method + + Method WriteChar(char) + Local pipe:TPipeStream + + If Not process Return + pipe=process.pipe + If char=3 'CTRL-C + Stop() + EndIf + If char=13 'ENTER +' Write Chr(10) + pipe.WriteLine user$ + user="" + EndIf + If char=8 And user.length 'DELETE +' Local pos=TextAreaLen(output) +' If pos SetTextAreaText output,"",pos-1,1,TEXTAREA_CHARS + user=user[..user.length-1] + EndIf + If char>31 +' Write Chr(char) + user:+Chr(char) + EndIf + End Method + + Method Open() + If output Then + codeplay.SelectPanel Self + Return + EndIf + codeplay.addpanel(Self) + output=CreateTextArea(0,0,ClientWidth(panel),ClientHeight(panel),panel,TEXTAREA_WORDWRAP) + DelocalizeGadget output + SetGadgetLayout output,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED + SetGadgetFilter output,outputfilter,Self + SetGadgetText output," " 'simon was here + host.options.outputstyle.apply output + End Method + + Function CreateOutputMenu:TGadget() + Local edit:TGadget = CreateMenu("{{popup_output}}",0,Null) + CreateMenu "{{popup_output_cut}}",MENUCUT,edit + CreateMenu "{{popup_output_copy}}",MENUCOPY,edit + CreateMenu "{{popup_output_paste}}",MENUPASTE,edit + CreateMenu "",0,edit + CreateMenu "{{popup_output_stop}}",MENUSTOP,edit + Return edit + End Function + + Function Create:TOutputPanel(host:TCodePlay) + Local o:TOutputPanel = New TOutputPanel + o.host=host + o.name="{{tab_output}}" + o.outputmenu=CreateOutputMenu() +' o.Open + Return o + End Function + +End Type + +Type TCodeNode Extends TNode + Field owner:TOpenCode + Field pos,count + 'Field groups:TMap=New TMap + + Method Invoke(command,argument:Object=Null) + Select command + Case TOOLACTIVATE + owner.ShowPos(pos) + End Select + End Method + + Method Sync(snap:TNode) + If snap.name<>name SetName(snap.name) + Local n:TCodeNode = TCodeNode(snap) + If n pos=n.pos;count=n.count + Super.Sync(snap) + End Method + + Method SetName(n$) + Local p = n.find("'") + If p<>-1 n=n[..p] + name=n.Trim() +' If owner.host.options.sortcode + sortname=n + End Method + + Method Free() + owner = Null + Super.Free() + End Method + + Method AddCodeNode:TCodeNode(n$,p0,p1) + + Local t$ + Local i:Int = n.find(" ") 'if space then group + + If i>0 + t=n[..i] + n=n[i+1..] +Rem + p=TNode(groups.ValueForKey(t)) + If Not p + p=AddNode(t+"s") + p.Open + groups.insert t,p + EndIf +EndRem + EndIf + + Local c:TCodeNode = New TCodeNode + c.owner=owner + c.setname n$ + c.pos=p0 + c.count=p1-p0 + Append(c) + + Return c + + End Method +End Type + +Type TDiff + Field pos,count,del$,add$,pos0,count0,pos1,textchange = True +End Type + +Type TOpenCode Extends TToolPanel + + Global msgHighlightingStatus$ = "Highlighting" + + Field host:TCodePlay + Field textarea:TGadget + Field dirty=True + Field filesrc$,cleansrc$,cleansrcl$ + Field Current:TDiff + Field undolist:TList=New TList + Field redolist:TList=New TList + Field helpcmd$,helpstring$ + Field seek$ + Field cursorpos,cursorlen,cursorline + Field oldpos,oldlen + Field isbmx,isc,iscpp,ishtml + Field deferpos = -1 + Field tidyqueue1 = -1, tidyqueue2 = -1 + Field editmenu:TGadget + Field codenode:TCodeNode + Field dirtynode,uc + + Function RefreshHighlightingMsg() + msgHighlightingStatus = LocalizeString("{{msg_highlightingcode}}") + EndFunction + + Function IsNotAlpha(c) + If c<48 Return True + If c>=58 And c<65 Return True + If c>=91 And c<95 Return True + If c>=96 And c<97 Return True + If c>=123 Return True + End Function + + Function WordAtPos$(a$,p) + Local c,q,r,n + ' string literal + q=a.findlast(EOL$,a.length-p) + If q=-1 q=0 + For q=q To p-1 + If a[q]=34 Then + n=Not n + r=q + EndIf + Next + If n + q=a.Find("~q",r+1)+1 + If q=0 q=a.length + Return a[r..q] + EndIf + ' alphanumeric + p=Min(p,a.length-1) 'simon was here - crash when checking at last char + For p=p Until 0 Step -1 'simon was here unto->to + If IsNotAlpha(a$[p]) Continue + Exit + Next + For q=p-1 To 0 Step -1 + If IsNotAlpha(a$[q]) Exit + Next + For r=p To a.length-1 + If IsNotAlpha(a$[r]) Exit + Next + Return a[q+1..r] + End Function + + Function FirstDiff(s0$,s1$) + Local n = Min(s0.length,s1.length) + For Local i:Int = 0 Until n + If s0[i]<>s1[i] Return i + Next + Return n + End Function + + Function LastDiff(s0$,s1$) + Local n = Min(s0.length,s1.length) + Local i = s0.length-1 + Local j = s1.length-1 + While n>0 + If s0[i]<>s1[j] Exit + i:-1;j:-1;n:-1 + Wend + Return i+1 + End Function + + Method parsebmx(n:TCodeNode) + Local src$,line,col + Local p,p1,r,t,m,f,l,e + + src=cleansrcl + p1=src.length + p=-1;r=-1;t=-1;m=-1;f=-1;l=-1 + While pn.pos+n.count) + If Not TCodeNode(n.parent) + If n.parent.parent + n = TCodeNode(n.parent.parent) + Else + n = Null + EndIf + Else + n=TCodeNode(n.parent) + EndIf + Wend + If t= ClientWidth(textarea) ) Then + SelectTextAreaText textarea,line,1,TEXTAREA_LINES + Else + SelectTextAreaText textarea,tmpCharLineStart,tmpLine.length-i,TEXTAREA_CHARS + EndIf + EndMethod + + Method ShowPos(pos) + host.SelectPanel( Self ) + HighlightLine( TextAreaLine(textarea,pos) ) + UpdateCursor() + ActivateGadget( textarea ) + End Method + + Method Debug(line,column) + HighlightLine( line-1 ) + UpdateCursor() + End Method + + Method Edit() + SelectTextAreaText( textarea,cursorpos,0,TEXTAREA_CHARS ) + ActivateGadget( textarea ) + UpdateStatus() + End Method + + Method UpdateStatus() + Local c = cursorpos+cursorlen + If cursorline Then c:-TextAreaChar(textarea,cursorline-1) + host.SetStatus helpstring+"~t~t"+LocalizeString("{{status_line_char}}").Replace("%1",cursorline).Replace("%2",(c+1)) + End Method + + Method UpdateCursor() + oldpos=cursorpos + oldlen=cursorlen + cursorpos=TextAreaCursor(textarea,TEXTAREA_CHARS) + cursorlen=TextAreaSelLen(textarea,TEXTAREA_CHARS) + If cursorpos<>oldpos Or cursorlen<>oldlen + Local l = TextAreaLine(textarea,cursorpos)+1 + If l<>cursorline And dirtynode + GetNode().Refresh + dirtynode=False + If (deferpos>=0) UpdateCode 'SetCode cleansrc + EndIf + cursorline=l + UpdateStatus() + BracketMatching(cleansrcl) + If (tidyqueue1 >= 0 Or tidyqueue2 >= 0) Then UpdateCode() + PollSystem + EndIf + End Method + +' tdiff - pos del$ add$ + + Method CalcDiff:TDiff(src$) + Local d:TDiff + If src.length<>cleansrc.length + d=New TDiff + d.pos0=cursorpos + d.count0=cursorlen + d.pos=oldpos + d.count=oldlen + If cursorlen And oldlen 'block modified + d.del=cleansrc[oldpos..oldpos+oldlen] + d.add=src[oldpos..cursorpos+cursorlen] + d.pos1=oldpos + Else + If cursorpos<=oldpos And cursorlen<=oldlen 'backspace + d.del=cleansrc[cursorpos..cursorpos+cleansrc.length-src.length] + d.pos1=cursorpos + Else 'insert + d.del=cleansrc[oldpos..oldpos+oldlen] + d.add=src[oldpos..cursorpos+cursorlen] + d.pos1=oldpos + EndIf + EndIf + Else + If cursorpos>oldpos 'overwrite + d=New TDiff + d.pos0=cursorpos + d.count0=cursorlen + d.pos=oldpos + d.count=oldlen + d.del=cleansrc[oldpos..cursorpos] + d.add=src[oldpos..cursorpos] + d.pos1=oldpos + If d.del = d.add Then d.textchange=False + EndIf + EndIf + Return d + End Method + + Method UpdateCode(makeundo=True) + Local cpos + Local src$ = TextAreaText(textarea) + Local d:TDiff = CalcDiff(src) + If d + If makeundo And d.textchange + undolist.AddLast d + redolist.Clear + EndIf + SetCode src,d + If d.textchange Then dirtynode=True + EndIf + If (deferpos >= 0) Or (tidyqueue1 >= 0) Or (tidyqueue2 >= 0) Then SetCode src + End Method + + Method Undo() + Local d:TDiff + If undolist.IsEmpty() Return + d=TDiff(undolist.RemoveLast()) + redolist.AddLast d + SetTextAreaText textarea,d.del,d.pos1,d.add.length + SelectTextAreaText(textarea,d.pos,d.count) + SetCode TextAreaText(textarea),d + UpdateCursor + End Method + + Method Redo() + Local d:TDiff + If redolist.IsEmpty() Return + d=TDiff(redolist.RemoveLast()) + undolist.AddLast d + SetTextAreaText textarea,d.add,d.pos,d.del.length + SelectTextAreaText(textarea,d.pos0,d.count0) + UpdateCursor + SetCode TextAreaText(textarea),d + End Method + + Method RefreshStyle() + Local rgb:TColor + Local src$ + Local charwidth + charwidth=host.options.editfont.CharWidth(32) + SetTextAreaTabs textarea,host.options.tabsize*charwidth + SetMargins textarea,4 + SetTextAreaFont textarea,host.options.editfont + rgb=host.options.editcolor + SetTextAreaColor textarea,rgb.red,rgb.green,rgb.blue,True + rgb=host.options.styles[0].color + SetTextAreaColor textarea,rgb.red,rgb.green,rgb.blue,False + src=cleansrc + cleansrc="" + cleansrcl="" + cursorpos=0 + SetCode(src) + End Method + + Function IsntAlphaNumeric(c) 'lowercase test only + If c<48 Return True + If c>=58 And c<95 Return True + If c=96 Return True + If c>=123 Return True + End Function + + Function IsntAlphaNumericOrQuote(c) 'lowercase test only + If c=34 Return False + If c<48 Return True + If c>=58 And c<95 Return True + If c=96 Return True + If c>=123 Return True + End Function + + Function IsCode(src$,p) + Local n + Local l = src.FindLast(EOL$,src.length-p) + If l=-1 l=0 + Local q = src.Find("'",l) + If q<>-1 And q

    =0 + p=src.Find("#",pos) + If p=-1 Exit + q=p + While q>0 + q:-1 + c=src[q] + If c=13 Return p + If c=10 Return p + If c=32 Or c=9 Continue + Exit + Wend + If q<0 Return p + pos=p+1 + Wend + Return src.length + End Function + + Function FindToken(token$,src$,pos) 'lowercase src only! + Local p,c + Local n=token.length + While pos>=0 + p=src.Find(token,pos) + If p=-1 Exit + c=10 If p>0 c=src[p-1] + If isntalphanumeric(c) + If p+n"end " Return p + EndIf + EndIf + EndIf + pos=p+1 + Wend + Return src.length + End Function + + Function FindEndToken(token$,src$,pos,returnlast=False) 'if true returns first character after endtoken + Local p,q,e$,n + + p=pos + e$="end"+token + n=e.length + While p32 Return False + Next + Return True + End Function + +' rem and endrem must be first nonwhitespace on line - following funcs are for lowercase src only + + Function FindRem(src$,pos) + While pos0 + If pos>src.length Exit 'fixed endrem on lastline overrun + p=src.FindLast("rem",src.length-pos) + If p=-1 Exit + If isntalphanumeric(src[p+3]) And IsFirstCharOnLine(src,p) Return p + pos=p-1 + Wend + Return -1 + End Function + + Method IsRemmed(pos,src$) + Local p = FindPrevRem(src$,Min(pos+3,src.length)) + If p<0 Return + p=FindEndRem(src$,p) + If p<0 Or pos

    = 0) Or (tidyqueue1 >= 0) Or (tidyqueue2 >= 0)) + EndMethod + + Method ClearTidyQueue(start,endpos) + If start<=deferpos And deferpos < endpos Then deferpos = -1 + If start<=tidyqueue1 And tidyqueue1 < endpos Then tidyqueue1 = -1 + If start<=tidyqueue2 And tidyqueue2 < endpos Then tidyqueue2 = -1 + EndMethod + + Method SetCode(src$,diff:TDiff=Null) + Local same,i,p,startp,p1,q,r,a,t$,h$,lsrc$,r0,r1,cpos,autocap + Local style:TTextStyle[5],s:TTextStyle +' update dirty flag + CheckDirty src + same = Not ((diff) Or (src<>cleansrc)) + If same And Not (diff Or HasTidyQueue()) Then Return + If Not isbmx Or Not host.quickhelp Or Not host.options.syntaxhighlight + If Not same Then + cleansrc=src + cleansrcl=src.ToLower() + EndIf + Return + EndIf +' doit + autocap=host.options.autocapitalize + If same Then lsrc = cleansrcl Else lsrc=src.ToLower() + cpos=TextAreaCursor(textarea,TEXTAREA_CHARS) + LockTextArea textarea + style=host.options.styles +' calculate highlight region + + If diff + p=diff.pos + p1=p+diff.add.length + If Not diff.add.length Then + p:-diff.del.length + EndIf + ElseIf HasTidyQueue() + p=src.length + If (deferpos>=0) Then + p = Min(p,deferpos) + p1 = Max(p1,deferpos+1) + EndIf + If (tidyqueue1>=0) Then + p = Min(p,tidyqueue1) + p1 = Max(p1, tidyqueue1+1) + EndIf + If (tidyqueue2>=0) Then + p = Min(p,tidyqueue2) + p1 = Max(p1, tidyqueue2+1) + EndIf + Else + p=firstdiff(src,cleansrc) + p1=lastdiff(src,cleansrc) + EndIf + q=src.length-cleansrc.length + If p1-p"EndRem" SetTextAreaText textarea,"EndRem",r1-6,6 + If lsrc[r1-7..r1]="end rem" And src[r1-7..r1]<>"End Rem" SetTextAreaText textarea,"End Rem",r1-7,7 + EndIf + ClearTidyQueue(p,r1) + p=r1 + EndIf + EndIf +' if was remmed and now isn't move p1 down to nearest rem or endrem + If WasRemmed(p,lsrc) + r0=FindRem(lsrc,p) + r1=FindEndRem(lsrc,r0,True) + p1=Max(p1,Min(r0,r1)) + EndIf +' highlight code + ClearTidyQueue(p,p1) + + s=style[NORMAL] + If p1>p s.format(textarea,p,p1-p) + startp = p + + While p-1 And r-1 And r-1 And r=65 And a<91) Or (a>=97 And a<123) Or (a=95) + q=p+1 + While q=58 And a<65 Exit + If a>=91 And a<95 Exit + If a=96 Exit + If a>122 Exit + q:+1 + Wend + t$=src[p..q] + h$=host.quickhelp.token(t$) + If h$ + If h$<>t$ And autocap + If cpos

    q + SetTextAreaText textarea,h,p,h.length + Else + deferpos=q + EndIf + EndIf + s=style[KEYWORD] + If h$="Rem" And IsFirstCharOnLine(lsrc,p) ' Not (p>4 And lsrc[p-4..p]="end ") + If q<>cpos + q=FindEndRem(lsrc,p,True) + s=style[COMMENT] + Else + deferpos=q + EndIf + EndIf + s.format(textarea,p,q-p) + EndIf + p=q + Continue + EndIf +' numbers + If (p=0 Or IsntAlphaNumeric(lsrc[p-1])) And ((a>=$30 And a<$3A) Or (a=$24) Or (a=$25) Or (a=$2E)) '0-9, $, %, . + q=p+1 + Local hexed:Int = (a=$24), binaried:Int = (a=$25), dots:Int = (a=$2E) + Local valid:Int = Not(hexed Or binaried Or dots) + + While q<(p1) + a=lsrc[q] + q:+1 + If (a>=$30 And a<$3A) Then + valid = True + Continue '0-9 + EndIf + If hexed + If (a>=$61 And a<$67) Then 'a-f (only test lower as 'a' var is from lsrc) + valid = True + Continue + EndIf + EndIf + If (a=$2E) Then + 'Hex or Binary literals don't support decimal points + If Not (hexed Or binaried) Then + dots:+1 + 'Fix for slicing '..' syntax + If src[q-2] = $2E Then + dots:-2 + q:-2 + Exit + EndIf + 'End Fix + Continue + EndIf + EndIf + If Not IsntAlphaNumeric(a) Then valid = False + q:-1 + Exit + Wend + If valid And dots < 2 Then style[NUMBER].format(textarea,p,(q-p)) + 'Fix for slicing '..' syntax + If q currentbrackets[0] And cln1 <= currentbrackets[0]) Then + If currentbrackets[0]>-1 Then tidyqueue1 = currentbrackets[0] + EndIf + If Not(cln2 > currentbrackets[1] And cln1 <= currentbrackets[1]) Then + If currentbrackets[1]>-1 Then tidyqueue2 = currentbrackets[1] + EndIf + currentbrackets = Null + If Not alwaysfind Then Return + EndIf + + If host.options.bracketmatching And isbmx And Not IsRemmed(currentcharpos,lsrc) Then + + limit = Min(lsrc.length,currentcharpos+2) + + While currentcharpos >= 0 And currentcharpos < limit + If IsCode(lsrc,currentcharpos) Then + Select lsrc[currentcharpos] + Case Asc("(");otherchar = Asc(")");Exit + Case Asc("{");otherchar = Asc("}");Exit + Case Asc("[");otherchar = Asc("]");Exit + ' Negate char code to search backwards + Case Asc(")");otherchar = -Asc("(");Exit + Case Asc("}");otherchar = -Asc("{");Exit + Case Asc("]");otherchar = -Asc("[");Exit + EndSelect + EndIf + currentcharpos:+1 + Wend + + If otherchar Then + + absotherchar = (Abs otherchar) + currentchar = lsrc[currentcharpos] + + LockTextArea textarea + style[MATCHING].format(textarea, currentcharpos, 1) + currentbrackets = [currentcharpos,-1] + + othercharpos = currentcharpos+(otherchar/absotherchar) + + While othercharpos < lsrc.length And othercharpos >= 0 + + If IsCode(lsrc,othercharpos) Then + Select lsrc[othercharpos] + Case Asc(" "), Asc("~t") + 'Do nothing + Case Asc("'") + Exit + Case absotherchar + If check < 0 Then Exit Else check = 0 + If depth Then + depth:-1 + Else + style[MATCHING].format(textarea, othercharpos, 1) + currentbrackets[1] = othercharpos + UnlockTextArea textarea + Return + EndIf + Case Asc("~n") + If (otherchar/absotherchar) > 0 Then + If check = 2 Then check = 0 Else Exit + Else + If check < 0 Then Exit Else check = -2 + EndIf + Case Asc(".") + check:+1 + Default + If check < 0 Then Exit Else check = 0 + If lsrc[othercharpos] = lsrc[currentcharpos] Then depth:+1 + EndSelect + EndIf + + othercharpos:+(otherchar/absotherchar) + + Wend + + UnlockTextArea textarea + + EndIf + + EndIf + + EndMethod + + Method AutoIndent() + Local p,q + Local c = TextAreaCursor(textarea,TEXTAREA_CHARS) + Local n = TextAreaSelLen(textarea,TEXTAREA_CHARS) + If cc q=c + EndIf + SetTextAreaText textarea,EOL$+cleansrc[p..q],c,n + SelectTextAreaText textarea,c+1+q-p,0 + UpdateCursor + UpdateCode + End Method + + Method IndentCode() + Local a$ +' blockindent + Local p0 = TextAreaCursor(textarea,TEXTAREA_LINES) + Local p1 = TextAreaSelLen(textarea,TEXTAREA_LINES) +' v122: make sure the entire block is selected (start cursor pos may in the middle of the line) + SelectTextAreaText textarea , p0 , p1 , TEXTAREA_LINES + UpdateCursor + For Local i:Int = 0 Until p1 + a$="~t"+TextAreaText(textarea,p0+i,1,TEXTAREA_LINES) + SetTextAreaText textarea,a$,p0+i,1,TEXTAREA_LINES + Next + SelectTextAreaText textarea,p0,p1,TEXTAREA_LINES + UpdateCursor + UpdateCode + End Method + + Method OutdentCode() + Local a$,modified +' blockoutdent + Local p0 = TextAreaCursor(textarea,TEXTAREA_LINES) + Local p1 = TextAreaSelLen(textarea,TEXTAREA_LINES) +' v122: make sure the entire block is selected (start cursor pos may in the middle of the line) + SelectTextAreaText textarea , p0 , p1 , TEXTAREA_LINES + UpdateCursor + For Local i:Int = 0 Until p1 + a$=TextAreaText(textarea,p0+i,1,TEXTAREA_LINES) + If a[0]=9 a$=a$[1..];modified=True + SetTextAreaText textarea,a$,p0+i,1,TEXTAREA_LINES + Next + If Not modified + For Local i:Int = 0 Until p1 + a$=TextAreaText(textarea,p0+i,1,TEXTAREA_LINES) + If a[0]=32 a$=a$[1..] + SetTextAreaText textarea,a$,p0+i,1,TEXTAREA_LINES + Next + EndIf + SelectTextAreaText textarea,p0,p1,TEXTAREA_LINES + UpdateCursor + UpdateCode + End Method + + Function FilterKey(event:TEvent,context:Object) +' If event.id<>EVENT_KEYCHAR Return 1 + Local id=event.id + Local key=event.data + Local mods=event.mods + Local this:TOpenCode=TOpenCode(context) +?MacOS + If key=25 And mods=MODIFIER_SHIFT key=KEY_TAB +? + If id=EVENT_KEYCHAR And this And key=KEY_TAB And TextAreaSelLen( this.textarea,TEXTAREA_CHARS ) + Select mods + Case MODIFIER_NONE + this.IndentCode + Case MODIFIER_SHIFT + this.OutdentCode + End Select + Return 0 + EndIf + + If id=EVENT_KEYDOWN And key=KEY_ENTER And this And this.host.options.autoindent + this.AutoIndent() + Return 0 + EndIf + + Return 1 + End Function + + Method OnEvent() + Select EventSource() + Case textarea + Select EventID() + Case EVENT_GADGETMENU + PopupWindowMenu host.window,editmenu + Case EVENT_GADGETACTION + UpdateCode + Case EVENT_GADGETSELECT + UpdateCursor + End Select + End Select + End Method + + Method SetDirty( bool ) + If dirty=bool Return + dirty=bool + name=StripDir(path) + If (dirty) name:+"*" + If (host.lockedpanel=Self) name=LocalizeString("{{tab_locked:%1}}").Replace("%1",name) + host.RefreshPanel Self + PollSystem + End Method + + Method SetLocked( bool ) + Local locked:TOpenCode = TOpenCode(host.lockedpanel) + If locked And locked<>Self locked.SetLocked False + name=StripDir(path) + If (dirty) name:+"*" + If (bool) + name=LocalizeString("{{tab_locked:%1}}").Replace("%1",name) + host.lockedpanel=Self + Else + host.lockedpanel=Null + EndIf + host.RefreshPanel Self + End Method + + Method Help() + If Not host.quickhelp Return + Local p = TextAreaCursor(textarea,TEXTAREA_CHARS) + Local a$ = WordAtPos(cleansrc,p) + If a=helpcmd + Local l$ = host.quickhelp.link(a$) + If l + host.helppanel.go host.bmxpath+l$ + EndIf + Else + helpcmd=a$ + helpstring$=host.quickhelp.help(a$) + UpdateStatus 'host.setstatus helpstring$ + EndIf + End Method + + Method Find() + host.findreq.ShowFind WordAtPos(cleansrc,TextAreaCursor(textarea,TEXTAREA_CHARS)) + End Method + + Method FindNext(s$) + If s seek=s Else s=seek + Local p = TextAreaCursor(textarea,TEXTAREA_CHARS) + p:+TextAreaSelLen(textarea,TEXTAREA_CHARS) +' case insensitive + Local l$ = s.toLower() + p=cleansrcl.Find(l$,p) + If p=-1 p=cleansrcl.Find(l$) +' case sensitive +' p=cleansrc.Find(s$,p+1) +' if p=-1 p=cleansrc.Find(s$) + If p=-1 + Notify LocalizeString("{{find_notification_cannotfind}}").Replace("%1",s) + Return False + Else + SelectTextAreaText textarea,p,Len s,TEXTAREA_CHARS + UpdateCursor + Return True + EndIf + End Method + + Method ReplaceAll(s$,r$) + Local t$ = TextAreaText( textarea ).ToLower() + Local c = TextAreaCursor(textarea,TEXTAREA_CHARS),i,p + s = s.ToLower() + Repeat + Local i2=t.Find( s,i ) + If i2=-1 Exit + p:+i2-i + i=i2+s.length + SelectTextAreaText textarea,p,s.length + UpdateCursor + UpdateCode + SetTextAreaText textarea,r,p,s.length + If p0 + f$=r[..p] + r$=r[p+1..] + ReplaceAll f$,r$ + Else + p=TextAreaCursor(textarea,TEXTAREA_CHARS) + n=TextAreaSelLen(textarea,TEXTAREA_CHARS) + If Not n Return + SetTextAreaText textarea,r$,p,n + SelectTextAreaText textarea,p+r.length,0 + UpdateCursor + UpdateCode + EndIf + Return True + End Method + + Method ReadSource(path$) + Local src$ + src=CacheAndLoadText(path) + src=src.Replace(Chr(13),"") + src=src.Replace(Chr(11),"") + LockTextArea textarea + SetTextAreaText textarea,src + UnlockTextArea textarea + filesrc=TextAreaText(textarea) + cleansrc="" + cleansrcl="" + ActivateGadget textarea + End Method + + Method SaveSource(file$) + If host.options.autobackup + DeleteFile file+".bak" + RenameFile file,file+".bak" + EndIf + Local src$ = TextAreaText(textarea) + filesrc=src + src=src.Replace(Chr(13),Chr(10)) + src=src.Replace(Chr(11),"") + Local txt$ = src.Replace$(Chr(10),Chr(13)+Chr(10)) + + Try + SaveText txt,file + Catch exception:Object + Local err$=String(exception) + Notify "Save Error~n"+err + Return False + EndTry + + path=host.FullPath$(file) + dirty=True + SetDirty False + host.AddRecent(path$) + Return True + End Method + + Method BuildSource(quick,debug,threaded,gui,run) + Local cmd$,out$,arg$ + If isbmx Or isc Or iscpp + cmd$=quote(host.bmkpath) + cmd$:+" makeapp" + If run cmd$:+" -x" + If debug cmd$:+" -d" Else cmd$:+" -r" '-v + If threaded cmd$:+" -h" + If gui cmd$:+" -t gui" + If Not quick cmd$:+" -a" + If debug Or threaded + out=StripExt(host.FullPath(path)) + If debug out:+".debug" + If threaded out:+".mt" + cmd:+" -o "+quote(out$)+" " + EndIf + cmd$:+" "+quote(host.FullPath(path)) + If run + arg$=host.GetCommandLine() + If arg cmd$:+" "+arg + EndIf + host.execute cmd,"Building "+StripExt(StripDir(path)) ',exe$ + Else + If ishtml + host.helppanel.Go "file://"+path + Else +'see what the system shell thinks of the file + Local cd$=CurrentDir() + ChangeDir ExtractDir(path) + cmd=StripDir(path) + host.execute cmd,"Building "+cmd + ChangeDir cd + EndIf + EndIf +' print cmd + End Method + + Method Save() + Local file$ = path + If host.IsTempPath(path) + file=RequestFile(LocalizeString("{{request_saveas_title}}"),FileTypeFilters,True,"") + If file="" Return False + If ExtractExt(file)="" file=file+".bmx" + dirty=True + EndIf + If dirty SaveSource(file) + Return True + End Method +' common command interface + + Method Invoke(command,argument:Object=Null) + Local file$,ex$ + Local p,res + Select command + Case TOOLSHOW + host.SetCodeNode GetNode() + host.SetTitle path +' simon was here If textarea Edit + If textarea ActivateGadget textarea + + Case TOOLCLOSE + If dirty 'Or host.IsTempPath(path) + Invoke(TOOLSHOW) + p=Proceed(LocalizeString("{{request_savechanges}}").Replace("%1",name)) 'the current file? + If p=-1 Return True + If p=1 + If Not Save() Return True + EndIf + EndIf + If codenode Then + codenode.Free() + codenode=Null + EndIf + 'Added just in case MaxGUI driver doesn't handle properly. + SetGadgetFilter textarea,Null,Null + 'Seb gone. + host.RemovePanel Self + FreeGadget(editmenu) + Case TOOLSAVE + Save + Case TOOLQUICKSAVE + file=path + If dirty SaveSource(file) + Case TOOLSAVEAS + file=path + If host.IsTempPath(path) file$="" + file=RequestFile(LocalizeString("{{request_saveas_title}}"),FileTypeFilters,True,file) + If file="" Return + ex$=ExtractExt(file) + If ex$="" + file=file+".bmx" + isbmx=True + Else + isbmx=(ex.ToLower()="bmx") + ishtml=(ex.ToLower()="html") + isc=(ex.ToLower()="c") + iscpp=(ex.ToLower()="cpp" Or ex.ToLower()="cxx") + EndIf + SaveSource(file$) + RefreshStyle() + GetNode().Refresh + SetDirty False + host.SetTitle path + Case TOOLGOTO + Local line=Int(String(argument)) + SelectTextAreaText textarea,line-1,0,TEXTAREA_LINES + UpdateCursor + ActivateGadget textarea + Case TOOLFIND + Find + Case TOOLFINDNEXT + Return FindNext(String(argument)) + Case TOOLREPLACE + Return FindReplace(String(argument)) + Case TOOLBUILD + BuildSource host.quickenabled,host.debugenabled,host.threadedenabled,host.guienabled,False + Case TOOLRUN + BuildSource host.quickenabled,host.debugenabled,host.threadedenabled,host.guienabled,True + Case TOOLLOCK + SetLocked True + Case TOOLUNLOCK + SetLocked False + Case TOOLHELP + Help() + Case TOOLUNDO + Undo() + Case TOOLREDO + Redo() + Case TOOLREFRESH + RefreshStyle() + Case TOOLCUT + GadgetCut textarea + UpdateCursor() + UpdateCode() + Case TOOLCOPY + GadgetCopy textarea + Case TOOLPASTE + GadgetPaste textarea + UpdateCursor() + UpdateCode() + Case TOOLSELECTALL + SelectTextAreaText textarea + UpdateCursor() + Case TOOLINDENT + IndentCode() + Case TOOLOUTDENT + OutdentCode() + Case TOOLPRINT + GadgetPrint textarea + End Select + End Method + + Function CreateEditMenu:TGadget() + Local edit:TGadget = CreateMenu("{{popup_edit}}",0,Null) + CreateMenu "{{popup_edit_quickhelp}}",MENUQUICKHELP,edit + CreateMenu "",0,edit + CreateMenu "{{popup_edit_cut}}",MENUCUT,edit + CreateMenu "{{popup_edit_copy}}",MENUCOPY,edit + CreateMenu "{{popup_edit_paste}}",MENUPASTE,edit + CreateMenu "",0,edit + CreateMenu "{{popup_edit_selectall}}",MENUSELECTALL,edit + CreateMenu "",0,edit + CreateMenu "{{popup_edit_blockindent}}",MENUINDENT,edit + CreateMenu "{{popup_edit_blockoutdent}}",MENUOUTDENT,edit + CreateMenu "",0,edit + CreateMenu "{{popup_edit_find}}",MENUFIND,edit + CreateMenu "{{popup_edit_findnext}}",MENUFINDNEXT,edit + CreateMenu "{{popup_edit_replace}}",MENUREPLACE,edit + CreateMenu "{{popup_edit_goto}}",MENUGOTO,edit + Return edit + End Function + + Method MakePathTemp() +' prepends "." to file name with code borrowed from SaveAs + Local file$=ExtractDir(path)+"/."+StripDir(path) + SaveSource(file$) +' Refresh + GetNode().Refresh +' setdirty False + host.SetTitle path + End Method + + Function Create:TOpenCode(path$,host:TCodePlay) + Local code:TOpenCode + Local stream:TStream + Local isnew + If path + If FileType(path)<>FILETYPE_FILE + Return Null + EndIf + stream=ReadFile(path) + If Not stream +' Notify "Open could not read from "+path + Return Null + EndIf + CloseFile stream + Else + TEMPCOUNT:+1 + path=host.bmxpath+"/tmp/untitled"+TEMPCOUNT+".bmx" + isnew=True + EndIf + code=New TOpenCode + code.host=host + code.active=True + code.path=host.FullPath(path) + code.editmenu=CreateEditMenu() + codeplay.addpanel(code) + code.textarea=CreateTextArea(0,0,ClientWidth(code.panel),ClientHeight(code.panel),code.panel,0) + DelocalizeGadget code.textarea + SetGadgetFilter code.textarea,code.FilterKey,code + SetTextAreaText code.textarea,"~n" + SetGadgetLayout code.textarea,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED + code.RefreshStyle() + + If isnew + code.SaveSource path + code.filesrc="~n" + ActivateGadget code.textarea + Else + host.UpdateProgress "Reading Stream" + code.ReadSource(path) + EndIf + + If ExtractExt(path).toLower()="bmx" code.isbmx=True + If ExtractExt(path).toLower()="c" code.isc=True + If ExtractExt(path).toLower()="cpp" code.iscpp=True + If ExtractExt(path).toLower()="cxx" code.iscpp=True + If ExtractExt(path).toLower()="html" code.ishtml=True + If ExtractExt(path).toLower()="htm" code.ishtml=True + code.UpdateCode False + code.filesrc=TextAreaText(code.textarea) + Return code + End Function + +End Type + +Type TRect + Field x,y,w,h + + Method Set(xpos,ypos,width,height) + x=xpos;y=ypos;w=width;h=height + End Method + + Method ToString$() + Return ""+x+","+y+","+w+","+h + End Method + + Method FromString(s$) + Local p,q,r + p=s.Find(",")+1;If Not p Return + q=s.Find(",",p)+1;If Not q Return + r=s.Find(",",q)+1;If Not r Return + x=Int(s[..p-1]) + y=Int(s[p..q-1]) + w=Int(s[q..r-1]) + h=Int(s[r..]) + End Method + + Method IsOutside(tx,ty,tw,th) + If x+w<=tx Or y+h<=ty Return True + If x>=tx+tw Or y>=ty+th Return True + End Method + +End Type + +Type TCodePlay + + Const EDITMODE=1 + Const DEBUGMODE=2 + + Field bmxpath$,bmkpath$ + Field panels:TToolPanel[] + Field helppanel:THelpPanel + Field currentpanel:TToolPanel + Field output:TOutputPanel + Field lockedpanel:TToolPanel + Field activepanel:TToolPanel + + Field cmdlinereq:TCmdLineRequester + Field cmdline$ + 'Field syncmodsreq:TSyncModsRequester + Field gotoreq:TGotoRequester + Field findreq:TFindRequester + Field replacereq:TReplaceRequester + Field options:TOptionsRequester +' Field progress:TProgressRequester + Field activerequesters:TList = New TList + Field projectreq:TProjectRequester + Field projectprops:TProjectProperties + Field searchreq:TSearchRequester + Field aboutreq:TAboutRequester + + + Field eventhandlers:TList=New TList + Field window:TGadget,menubar:TGadget,toolbar:TGadget,client:TGadget,tabbar:TGadget + Field split:TSplitter + Field debugtree:TDebugTree + + Field root:TNode + Field helproot:TNode + Field projects:TProjects + Field coderoot:TNode + Field navbar:TNavBar + + Field mode + Field debugcode:TOpenCode + + Field quickenable:TGadget,quickenabled 'menu,state + Field debugenable:TGadget,debugenabled 'menu,state + Field threadedenable:TGadget,threadedenabled + Field guienable:TGadget,guienabled 'menu,state + Field quickhelp:TQuickHelp + Field running + Field recentmenu:TGadget + Field recentfiles:TList=New TList + Field recentmenus:TGadget[] + Field openlist:TList=New TList + Field openlock$ + Field projlist:TList=New TList + Field winsize:TRect=New TRect + Field winmax,tooly,splitpos,debugview,navtab + Field progress,splitorientation + +?MacOS + Method RanlibMods() + + Local cmd$=Quote( bmxpath+"/bin/bmk" )+" ranlibdir "+Quote( bmxpath+"/mod" ) + system_ cmd + + End Method +? + + Method CheckVersion$() + Local process:TProcess + Local bytes:Byte[] + Local cmd$,version$ + + cmd$=bmxpath+"/bin/bcc" + +?win32 + cmd:+".exe" +? + + cmd=Quote(cmd) + process=CreateProcess(cmd$,HIDECONSOLE) + If process + Repeat + Delay 10 + bytes=process.pipe.ReadPipe() + If bytes + version:+String.FromBytes(bytes,bytes.length) + EndIf + Until Not process.Status() + EndIf + + If version="" + Notify "Unable to determine BlitzMax version.~n~nPlease reinstall BlitzMax to repair this problem." + End + EndIf + + BCC_VERSION=version.Trim() + +' print "bcc version="+version + + If version.find("(expired)")>-1 'Demo timed out + Notify "This demo version of BlitzMax has expired.~n~nPlease visit www.blitzbasic.com to buy the full version." + End + EndIf + + If version.find("Demo Version")>-1 'Valid demo version + is_demo=True + Notify version+"~nTo purchase the full version please visit www.blitzbasic.com." + Notify ABOUTDEMO + EndIf + + End Method + + Method OpenProgress(message$) +' progress.Open message + DisableGadget window + SetStatus message + progress=-1 + End Method + + Method CloseProgress() +' progress.Hide + SetStatus "" + EnableGadget window + progress=0 + End Method + + Method UpdateProgress(message$,value=0) 'returns false if cancelled +' Return progress.Update(message,value) + If progress + If progress/5<>value/5 + SetStatus message+" "+value+"%" + progress=value +' Pollsystem + EndIf + EndIf + End Method + + Method FullPath$(path$) + If path[..8]="$BMXPATH" path=bmxpath+path[8..] + If Not path.Contains("::") Then path = RealPath(path) +?win32 + path=path.Replace("\","/") +? + Return path + End Method + + Method IsTempPath(path$) + If path[..bmxpath.length+5]=bmxpath+"/tmp/" Return True + End Method + + Method AddDefaultProj(p$) + Local projdata:TList = New TList + projdata.AddLast p + projlist.AddLast projdata + End Method + + Method ReadConfig() + Local stream:TStream + Local f$,p,a$,b$ +' defaults + Local wh=GadgetHeight(Desktop())-80'32 + Local ww=wh + Local wx=(GadgetWidth(Desktop())-ww)/2 + Local wy=(GadgetHeight(Desktop())-wh)/2 + winsize.set( wx,wy,ww,wh ) + quickenabled=False + debugenabled=True + threadedenabled=False + guienabled=True + splitpos=200;splitorientation = SPLIT_VERTICAL +' read ini + stream=ReadFile(bmxpath+"/cfg/ide.ini") + If Not stream + AddDefaultProj "Samples|samples" + If Not is_demo + AddDefaultProj "Modules Source|mod" + AddDefaultProj "BlitzMax Source|src" + EndIf + Return + EndIf + options.read(stream) + options.Snapshot + + Local projdata:TList + + While Not stream.Eof() + f$=stream.ReadLine() + p=f.find("=") + If p=-1 Continue + a$=f[..p] + b$=f[p+1..] + Select a$ + Case "ide_version" + + Case "file_recent" + recentfiles.addlast b$ + Case "file_open" + openlist.addlast b$ + Case "prg_quick" + quickenabled=Int(b$) + Case "prg_debug" + debugenabled=Int(b$) + Case "prg_threaded" + threadedenabled=Int(b$) + Case "prg_gui" + guienabled=Int(b$) + Case "cmd_line" + cmdline=b$ + Case "prg_locked" + openlock=b$ + Case "win_size" + winsize.FromString(b$) + Case "win_max" + winmax=Int(b$) + Case "split_position" + splitpos=Int(b$) + Case "split_orientation" + splitorientation=Int(b$) + Case "proj_node" + projdata=New TList + projdata.AddLast b + projlist.AddLast projdata + Case "proj_data" + If projdata projdata.AddLast b + 'Case "sync_state" + 'syncmodsreq.FromString b$ + End Select + Wend + stream.close() + End Method + + Method WriteConfig() + Local panel:TToolPanel + Local node:TNode + Local f$ + + Local stream:TStream = WriteFile(bmxpath+"/cfg/ide.ini") + If Not stream Return +' options + options.write(stream) +' defaults + stream.WriteLine "[Defaults]" + stream.WriteLine "ide_version="+IDE_VERSION$ + stream.WriteLine "prg_quick="+quickenabled + stream.WriteLine "prg_debug="+debugenabled + stream.WriteLine "prg_threaded="+threadedenabled + stream.WriteLine "prg_gui="+guienabled + stream.WriteLine "win_size="+winsize.ToString() + stream.WriteLine "win_max="+winmax + stream.WriteLine "split_position="+SplitterPosition(split) + stream.WriteLine "split_orientation="+SplitterOrientation(split) + stream.WriteLine "cmd_line="+cmdline + 'stream.WriteLine "sync_state="+syncmodsreq.ToString() + If lockedpanel stream.WriteLine "prg_locked="+lockedpanel.path + For f$=EachIn recentfiles + stream.WriteLine "file_recent="+f$ + Next + For Local panel:TToolPanel = EachIn panels + f$=panel.path + If f$ And Not IsTempPath(f$) stream.WriteLine "file_open="+f$ + Next + projects.write(stream) + stream.close + End Method + + Method CloseAll(dontask,inccurrent=True) 'returns true if successful + Local count, cancel + For Local panel:TToolPanel = EachIn panels + If TOpenCode(panel) And (inccurrent Or currentpanel <> panel) count:+1 + Next + If (Not count) Or dontask Or Confirm(LocalizeString("{{request_closeall}}")) + For Local panel:TToolPanel = EachIn panels[..] 'Use a copy of the original array for iterating. + If (inccurrent Or currentpanel <> panel) And panel.invoke(TOOLCLOSE) Then + cancel=True + Exit + EndIf + Next + Return Not cancel + EndIf + End Method + + Method Quit() + WriteConfig() + If CloseAll(True) running=False + End Method + + Method DebugExit() + If debugcode + debugtree.cancontinue = False + debugcode.Edit 'restore cursor etc. + debugcode=Null + EndIf + SetMode EDITMODE + RefreshToolbar() + End Method + + Method DebugSource(path$,line,column) + Local code:TOpenCode + path=FullPath(path) + code=OpenSource(path) + If Not code Then + Notify(LocalizeString("{{loaderror_failed}}").Replace("%1",path), True) + Return + EndIf + If debugcode And debugcode<>code Then debugcode.Edit() 'restore cursor etc. + debugcode=code + debugcode.debug(line,column) + ActivateWindow window + PollSystem + End Method + + Method SetMode(m) + If mode=m Return + ActivateWindow window + Select m + Case DEBUGMODE + navtab=navbar.SelectedView() + navbar.SelectView debugview + Case EDITMODE + navbar.SelectView navtab + End Select + mode=m + RefreshToolbar + End Method + + Method RefreshMenu() + TOpenCode.RefreshHighlightingMsg() + UpdateWindowMenu window + EndMethod + + Method RefreshToolbar() + Local i +' sourceedit buttons + If THelpPanel(CurrentPanel) + DisableGadgetItem toolbar,TB_CLOSE + Else + EnableGadgetItem toolbar,TB_CLOSE + EndIf + If TOpenCode(CurrentPanel) + EnableGadgetItem toolbar,TB_SAVE + For i=TB_CUT To TB_FIND + EnableGadgetItem toolbar,i + Next + Else + DisableGadgetItem toolbar,TB_SAVE + For i=TB_CUT To TB_FIND + DisableGadgetItem toolbar,i + Next + EndIf +' debug buttons + If mode = DEBUGMODE And debugtree.cancontinue Then + If GadgetItemIcon( toolbar, TB_BUILDRUN ) = TB_BUILDRUN Then + ModifyGadgetItem( toolbar, TB_BUILDRUN, "", GADGETITEM_LOCALIZED, TB_CONTINUE, "{{tb_continue}}" ) + EndIf + Else + If GadgetItemIcon( toolbar, TB_BUILDRUN ) <> TB_BUILDRUN Then + ModifyGadgetItem( toolbar, TB_BUILDRUN, "", GADGETITEM_LOCALIZED, TB_BUILDRUN, "{{tb_buildrun}}" ) + EndIf + EndIf + For i=TB_STEP To TB_STEPOUT + If mode=DEBUGMODE And debugtree.cancontinue Then + EnableGadgetItem toolbar,i + Else + DisableGadgetItem toolbar,i + EndIf + Next +' stop button + If output And output.process + EnableGadgetItem toolbar,TB_STOP + Else + DisableGadgetItem toolbar,TB_STOP + EndIf + End Method + + Method IsSourceOpen(path$) + Local p$ = FullPath(path) + For Local panel:TToolPanel = EachIn panels + If panel.path=p Return True + Next + End Method + + Method OpenSource:TOpenCode(path$) + Local code:TOpenCode + Local ext$,p$ + If path$="." + path$=RequestFile(LocalizeString("{{request_openfile}}"),FileTypeFilters ) + If path$="" Return + EndIf +' check if already open + p$=FullPath(path).ToLower() + For Local panel:TToolPanel = EachIn panels + If panel.path.ToLower()=p + SelectPanel panel + Return TOpenCode(panel) + EndIf + Next +' open based on extension +' Select ExtractExt(Upper(path$)) +' Case "BMX","TXT","BB","CPP","C","S","I","H","HTML","CSS","BAT","FS","VS","README","" + OpenProgress LocalizeString("{{msg_loading}}").Replace("%1",StripDir(path)) + code=TOpenCode.Create(path,Self) + If code + AddRecent code.path + EndIf + CloseProgress + If code + ActivateGadget code.textarea + code.GetNode().Refresh + EndIf + Return code +' end select + End Method + + Method AddRecent(path$) + For Local f$ = EachIn recentfiles + If f$=path$ recentfiles.Remove(f$);Exit + Next + recentfiles.AddFirst(path$) + RefreshRecentFiles + UpdateWindowMenu window + End Method + + Method RefreshRecentFiles() + Local n + For Local m:TGadget = EachIn recentmenus + FreeMenu m + Next + n=Min(recentfiles.count(),16) + recentmenus=New TGadget[n] + n=0 + For Local f$ = EachIn recentfiles + recentmenus[n]=CreateMenu(f$,MENURECENT+n,recentmenu) + n:+1 + If n=16 Exit + Next + End Method + + Method BuildModules(buildall) + Local cmd$,out$,exe$ + output.Stop + SaveAll + cmd$=quote(bmkpath) + cmd$:+" makemods " + + If buildall cmd$:+"-a " + If threadedenabled cmd:+"-h " + + Execute cmd,LocalizeString("{{output_msg_buildingmods}}") + End Method + + Method ImportBB() + Local f$ = RequestFile(LocalizeString("{{request_importbb_title}}"),"bb" ) + If Not f$ Return + Local cmd$ = Quote(bmkpath$) + cmd$:+" convertbb " + cmd$:+quote(FullPath(f$)) + Execute cmd,LocalizeString("{{output_msg_converting}}").Replace("%1",StripExt(StripDir(f$))) + output.wait + OpenSource(StripExt(f$)+".bmx") + End Method + + Method GetCommandLine$() + Return cmdline + End Method + + Method SetCommandLine(text$) + cmdline=text + End Method + + Method SetStatus(text$) + SetStatusText window,text + End Method + + Method Execute(cmd$,mess$="",post$="",home=True,tool:TTool=Null) + If Not output output=TOutputPanel.Create(Self) + output.execute cmd$,mess$,post$,home,tool + End Method + + Method SelectError(path$,column,line) + Local panel:TOpenCode,found + For panel=EachIn panels + If panel.path=path found=True;Exit + Next + If Not found panel=OpenSource(path) + If panel + SelectPanel panel + panel.Debug line,column + EndIf + End Method + + Method ParseError(err$) + Local mess$,file$,p,q + Local line,column +' bcc error + If err$[..13]="Compile Error" + err=err[14..] + p=err.find(EOL$) + If p=-1 p=err.length + mess=err[..p] + err=err[p+1..] + If err[..1]="[" + p=err.find("]") + If p=-1 p=err.length + file$=err[1..p] + p=file.find(";")+1 + If p=0 p=err.length + q=file.find(";",p)+1 + If q=0 q=err.length + line=Int(file[p..q-1]) + column=Int(file[q..]) + file=FullPath(file[..p-1]) + SelectError file,column,line + EndIf + Notify LocalizeString("{{output_error_compileerror}}").Replace("%1",mess) + SetStatus mess + Return + EndIf +' gcc error + err=err.Replace(EOL+" "," ") + While err + p=err.find(EOL) + If p=-1 p:+err.length 'equiv. to p=err.length-1 ;-) + mess=err[..p] + err=err[p+1..] + p=0 + Repeat + p=mess.Find(":",p)+1 + If p=0 Exit + q=mess.Find(":",p) + If q<>-1 + file=mess[..p-1] + line=Int(mess[p..q]) + If line + mess=mess[q+1..] + SelectError file,column,line + Notify LocalizeString("{{output_error_compileerror}}").Replace("%1",mess) + Return + EndIf + p=q+1 + EndIf + Forever + Wend + End Method + + Method AddPanel(tabpanel:TToolPanel) + Local panel:TGadget,index + index=CountGadgetItems(tabbar) + If panels.length<=index panels=panels[..index+1] + AddGadgetItem(tabbar,tabpanel.name$,GADGETITEM_DEFAULT|GADGETITEM_LOCALIZED) + + panel=CreatePanel(0,0,ClientWidth(tabbar),ClientHeight(tabbar),tabbar,0) 'name + SetGadgetLayout panel,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED + tabpanel.panel=panel + + tabpanel.index=index + panels[index]=tabpanel + SelectPanel tabpanel + AddHandler tabpanel + End Method + + Method AddHandler(handler:TEventHandler) + eventhandlers.addlast handler + End Method + + Method RemovePanel(tabpanel:TToolPanel) + Local p:TToolPanel[] + Local index + eventhandlers.remove tabpanel +' unset debugcode + If debugcode=tabpanel debugcode=Null +' activate next panel + If tabpanel=activepanel activepanel=helppanel + If tabpanel=lockedpanel lockedpanel=Null + If tabpanel=currentpanel + index=tabpanel.index+1 + If index>=panels.length index=panels.length-2 + SelectPanel panels[index] + EndIf +' remove from array + p=panels + panels=panels[..panels.length-1] + For index=tabpanel.index To panels.length-1 + panels[index]=p[index+1] + panels[index].index=index + Next +' remove gadget - simon come here, placing before remove needs fix in fltk + FreeGadget tabpanel.panel + RemoveGadgetItem tabbar,tabpanel.index + tabpanel.panel=Null + End Method + + Method HookRequester(req:TRequester) + If Not activerequesters.Contains(req) Then + If req.IsModal() Then + For Local tmpRequester:TRequester = EachIn activerequesters + DisableGadget tmpRequester.window + Next + DisableGadget window + EndIf + activerequesters.AddFirst(req) + EndIf + End Method + + Method UnhookRequester(req:TRequester) + If activerequesters.Contains(req) Then + activerequesters.Remove(req) + If req.IsModal() Then + For Local tmpRequester:TRequester = EachIn activerequesters + EnableGadget tmpRequester.window + Next + EnableGadget window + EndIf + EndIf + EndMethod + + Method SetTitle(title$="") + If title title=" - "+title + SetGadgetText window,"MaxIDE"+title + End Method + + Method SelectPanel(panel:TToolPanel) + Local curr:TToolPanel = currentpanel + currentpanel=panel + If curr And curr<>currentpanel + SelectGadgetItem tabbar,panel.index + ShowGadget panel.panel + If panel.active activepanel=panel + HideGadget curr.panel + RefreshToolbar + EndIf + currentpanel.Invoke TOOLSHOW + End Method + + Method RefreshPanel(panel:TToolPanel) 'call after a name change + ModifyGadgetItem( tabbar,panel.index,panel.name,GADGETITEM_LOCALIZED ) + End Method + + Function OutsideDesktop(winrect:TRect) + Local x,y,w,h + Local desk:TGadget = Desktop() + x=GadgetX(desk) + y=GadgetY(desk) + w=GadgetWidth(desk) + h=GadgetHeight(desk) + Return winrect.IsOutside(x,y,w,h) + End Function + + Method SetCodeNode(code:TNode) + Local node:TNode + If coderoot.kids.count() node=TNode(coderoot.kids.First()) + If node=code Return + If node node.Detach + If code + coderoot.Append code + coderoot.Refresh + coderoot.Open + code.Open + EndIf + End Method + + Method Initialize() + Local open:TOpenCode, splash:TGadget + Local dir$,nomods,pname$,p + Local stream:TStream + + Try + bmxpath=BlitzMaxPath() + Catch err$ + Notify "Unable to determine BlitzMax installation directory." + End + EndTry + + CreateDir bmxpath+"/tmp" + If FileType( bmxpath+"/tmp" )<>FILETYPE_DIR + Notify "Unable to create BlitzMax 'tmp' directory." + End + EndIf +?Win32 + CreateFile bmxpath+"/tmp/t.exe" + If FileType( bmxpath+"/tmp/t.exe" ) <> FILETYPE_FILE + Notify "Unable to write to BlitzMax installation directory.~n"+.. + "Please run MaxIDE as administrator, or reinstall BlitzMax to a different directory." + End + EndIf + DeleteFile bmxpath+"/tmp/t.exe" +? + bmkpath=bmxpath+"/bin/bmk" +?Win32 + bmkpath:+".exe" +? + dir$=bmxpath+"/mod" + If FileType(dir)=FILETYPE_NONE + If Not CreateDir(dir) + Notify "Failed to create %1 directory:~n%2".Replace("%1","Module").Replace("%2",dir) + End + EndIf + nomods=True + EndIf + dir$=bmxpath+"/tmp" + If FileType(dir)=FILETYPE_NONE + If Not CreateDir(dir) + Notify "Failed to create %1 directory:~n%2".Replace("%1","Temp").Replace("%2",dir) + End + EndIf + EndIf + dir$=bmxpath+"/cfg" + If FileType(dir)=FILETYPE_NONE + If Not CreateDir(dir) + Notify "Failed to create %1 directory:~n%2".Replace("%1","Config").Replace("%2",dir) + End + EndIf + EndIf + + CheckVersion() + + splash=CreateWindow("MaxIDE",200,200,400,140,Null,WINDOW_CLIENTCOORDS|WINDOW_HIDDEN|WINDOW_CENTER) + Local panel:TGadget = CreatePanel(0,0,ClientWidth(splash),ClientHeight(splash),splash,0) + SetPanelColor panel,255,255,255;SetPanelPixmap panel, LoadPixmapPNG("incbin::splash.png"), PANELPIXMAP_FIT2 + Local progress:TGadget = CreateProgBar(2,ClientHeight(panel)-22,ClientWidth(panel)-4,20,panel) + ShowGadget splash;PollSystem + + window=CreateWindow("MaxIDE",20,20,760,540,Null,WINDOW_TITLEBAR|WINDOW_RESIZABLE|WINDOW_STATUS|WINDOW_HIDDEN|WINDOW_ACCEPTFILES|WINDOW_MENU) + + ?Linux + SetGadgetPixmap(window, LoadPixmapPNG("incbin::window_icon.png"), GADGETPIXMAP_ICON ) + ? + + cmdlinereq=TCmdLineRequester.Create(Self) + 'syncmodsreq=TSyncModsRequester.Create(Self) + gotoreq=TGotoRequester.Create(Self) + findreq=TFindRequester.Create(Self) + replacereq=TReplaceRequester.Create(Self) + options=TOptionsRequester.Create(Self) +' progress=TProgressRequester.Create(Self) + projectreq=TProjectRequester.Create(Self) + projectprops=TProjectProperties.Create(Self) + searchreq=TSearchRequester.Create(Self) + aboutreq=TAboutRequester.Create(Self) + + UpdateProgBar progress, 0.1;PollSystem + + ReadConfig() + + toolbar=CreateToolbar("incbin::toolbar.png",0,0,0,0,window ) + + RemoveGadgetItem toolbar, TB_CONTINUE + + Rem + SetToolbarTips toolbar, ["{{tb_new}}","{{tb_open}}","{{tb_close}}","{{tb_save}}","","{{tb_cut}}","{{tb_copy}}","{{tb_paste}}","{{tb_find}}","",.. + "{{tb_build}}","{{tb_buildrun}}","{{tb_step}}","{{tb_stepin}}","{{tb_stepout}}","{{tb_stop}}","","{{tb_home}}","{{tb_back}}","{{tb_forward}}"] + End Rem + + If Not options.showtoolbar Then HideGadget toolbar + + If OutsideDesktop(winsize) + winsize.set(20,20,760,540) + EndIf + + UpdateProgBar progress, 0.2;PollSystem + + SetGadgetShape(window, winsize.x, winsize.y, winsize.w, winsize.h) + + client=window + + split=CreateSplitter(0,0,ClientWidth(client),ClientHeight(client),client,SPLIT_VERTICAL) + SetGadgetLayout(split,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED) + + tabbar=CreateTabber(0,0,ClientWidth(SplitterPanel(split,SPLITPANEL_MAIN)),ClientHeight(SplitterPanel(split,SPLITPANEL_MAIN)),SplitterPanel(split,SPLITPANEL_MAIN)) + SetGadgetLayout(tabbar,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED) + + debugtree=TDebugTree.CreateDebugTree(Self) + + root=TNode.CreateNode("{{navtab_home}}") + + helproot=root.AddNode("{{navnode_help}}") + + projects=TProjects.CreateProjects(Self) + root.Append projects + +' opencoderoot=root.AddNode("Open") + coderoot=TNode.CreateNode("{{navtab_code}}") + coderoot.Open() + + navbar=TNavBar.Create(Self,SplitterPanel(split,SPLITPANEL_SIDEPANE)) + + navbar.AddView root + navbar.AddView coderoot + debugview=navbar.AddView(debugtree) + navbar.SelectView 0 + + helproot.Open + projects.Open + + AddHandler navbar + + SetMode EDITMODE + + UpdateProgBar progress, 0.3;PollSystem + + quickhelp=TQuickHelp.LoadCommandsTxt(bmxpath) + + helppanel=THelpPanel.Create(Self) + + output=TOutputPanel.Create(Self) + + activepanel=helppanel + InitMenu + InitHotkeys + + RefreshAll + + UpdateProgBar progress, 0.4;PollSystem 'allow repaint + + Local mkdocs + If FileType( bmxpath+"/docs/html/User Guide/index.html" )<>FILETYPE_FILE + CreateDir bmxpath+"/docs/html" + CreateFile bmxpath+"/docs/html/index.html" + mkdocs=True + EndIf + + helppanel.Home() + + UpdateProgBar progress, 0.5;PollSystem + +' scan projects in projlist + For Local pdata:TList = EachIn projlist + projects.AddProject pdata + Next + + UpdateProgBar progress, 0.6;PollSystem + + Local tmpProgValue# = 0.6 + Local tmpProgStep# + +'open files from .ini restorelist + If options.restoreopenfiles + If Not openlist.IsEmpty() Then tmpProgStep = (0.3/openlist.Count()) + For Local f$=EachIn openlist + open=OpenSource(f$) + If open And f$=openlock open.SetLocked(True) + tmpProgValue:+tmpProgStep;UpdateProgBar progress,tmpProgValue + Next + EndIf + + tmpProgValue = 0.9 + If AppArgs.length > 1 Then tmpProgStep = (0.1/(AppArgs.length-1)) Else tmpProgValue = 1.0 + UpdateProgBar progress,tmpProgValue;PollSystem + +' open files specified in command line + For Local i:Int = 1 Until AppArgs.length + open=OpenSource(AppArgs[i]) + tmpProgValue:+tmpProgStep;UpdateProgBar progress,tmpProgValue;PollSystem + Next + + HideGadget splash;FreeGadget splash + PollSystem + + SetSplitterPosition(split,splitpos);SetSplitterOrientation(split,splitorientation) + + If winmax MaximizeWindow(window) + ShowGadget window + + PollSystem + running=True + + CreateTimer(TIMER_FREQUENCY) + + 'If nomods syncmodsreq.Show + +'build docs if not there + If mkdocs + If Confirm( LocalizeString("{{loaderror_docsnotfound}}") ) And CloseAll( False ) DocMods + EndIf + + End Method + + Method DocMods() + Local cmd$=quote(bmxpath+"/bin/makedocs") + execute cmd,LocalizeString("{{output_msg_rebuildingdocs}}"),MENUTRIGGERSYNCDOCS + ?MacOS + RanLibMods() + ? + End Method + + Method SyncDocs() + helppanel.SyncDocs() + quickhelp=TQuickHelp.LoadCommandsTxt(bmxpath) + helppanel.Home + End Method + + Method InitMenu() + Local menu:TGadget,file:TGadget,edit:TGadget,program:TGadget,tools:TGadget + Local help:TGadget,buildoptions:TGadget + Local buildmods:TGadget,buildallmods:TGadget,syncmods:TGadget,docmods:TGadget + + Local MENUMOD=MODIFIER_COMMAND + + If options.systemkeys + MENUMOD=MODIFIER_CONTROL + EndIf + + menu=WindowMenu(window) + + file=CreateMenu("{{menu_file}}",0,menu) + CreateMenu "{{menu_file_new}}",MENUNEW,file,KEY_N,MENUMOD + CreateMenu "{{menu_file_open}}",MENUOPEN,file,KEY_O,MENUMOD + recentmenu=CreateMenu("{{menu_file_open_recent}}",0,file) + CreateMenu "",0,file + CreateMenu "{{menu_file_closetab}}",MENUCLOSE,file,KEY_W,MENUMOD + CreateMenu "{{menu_file_closealltabs}}",MENUCLOSEALL,file,KEY_W,MENUMOD|MODIFIER_SHIFT + CreateMenu "{{menu_file_closeothertabs}}",MENUCLOSEOTHERS,file,KEY_W,MENUMOD|MODIFIER_ALT + CreateMenu "",0,file + CreateMenu "{{menu_file_save}}",MENUSAVE,file,KEY_S,MENUMOD + CreateMenu "{{menu_file_saveas}}",MENUSAVEAS,file,KEY_S,MENUMOD|MODIFIER_SHIFT + CreateMenu "{{menu_file_saveall}}",MENUSAVEALL,file + CreateMenu "",0,file + + If options.systemkeys +?MacOS + CreateMenu "{{menu_file_nexttab}}",MENUNEXT,file,KEY_RIGHT,MENUMOD + CreateMenu "{{menu_file_prevtab}}",MENUPREV,file,KEY_LEFT,MENUMOD +?Not MacOS + CreateMenu "{{menu_file_nexttab}}",MENUNEXT,file,KEY_RIGHT,MODIFIER_ALT + CreateMenu "{{menu_file_prevtab}}",MENUPREV,file,KEY_LEFT,MODIFIER_ALT +? + Else + CreateMenu "{{menu_file_nexttab}}",MENUNEXT,file,KEY_RIGHT,MENUMOD + CreateMenu "{{menu_file_prevtab}}",MENUPREV,file,KEY_LEFT,MENUMOD + EndIf + CreateMenu "",0,file + CreateMenu "{{menu_file_importbb}}",MENUIMPORTBB,file + CreateMenu "",0,file + CreateMenu "{{menu_file_ideoptions}}",MENUOPTIONS,file + CreateMenu "{{menu_file_projectmanager}}",MENUPROJECTMANAGER,file + CreateMenu "",0,file + CreateMenu "{{menu_file_print}}",MENUPRINT,file,KEY_P,MENUMOD +?Not MacOS + CreateMenu "",0,file + CreateMenu "{{menu_file_exit}}",MENUQUIT,file +? + edit=CreateMenu("{{menu_edit}}",0,menu) + CreateMenu "{{menu_edit_undo}}",MENUUNDO,edit,KEY_Z,MENUMOD +?MacOS + CreateMenu "{{menu_edit_redo}}",MENUREDO,edit,KEY_Z,MENUMOD|MODIFIER_SHIFT +?Not MacOS + CreateMenu "{{menu_edit_redo}}",MENUREDO,edit,KEY_Y,MENUMOD +? + CreateMenu "",0,edit + CreateMenu "{{menu_edit_cut}}",MENUCUT,edit,KEY_X,MENUMOD + CreateMenu "{{menu_edit_copy}}",MENUCOPY,edit,KEY_C,MENUMOD + CreateMenu "{{menu_edit_paste}}",MENUPASTE,edit,KEY_V,MENUMOD + CreateMenu "",0,edit + CreateMenu "{{menu_edit_selectall}}",MENUSELECTALL,edit,KEY_A,MENUMOD + CreateMenu "",0,edit + CreateMenu "{{menu_edit_blockindent}}",MENUINDENT,edit,KEY_CLOSEBRACKET,MENUMOD + CreateMenu "{{menu_edit_blockoutdent}}",MENUOUTDENT,edit,KEY_OPENBRACKET,MENUMOD + CreateMenu "",0,edit + CreateMenu "{{menu_edit_find}}",MENUFIND,edit,KEY_F,MENUMOD +?MacOS + CreateMenu "{{menu_edit_findnext}}",MENUFINDNEXT,edit,KEY_G,MENUMOD + CreateMenu "{{menu_edit_replace}}",MENUREPLACE,edit,KEY_H,MENUMOD + CreateMenu "{{menu_edit_gotoline}}",MENUGOTO,edit,KEY_L,MENUMOD +?Not MacOS + CreateMenu "{{menu_edit_findnext}}",MENUFINDNEXT,edit,KEY_F3 + CreateMenu "{{menu_edit_replace}}",MENUREPLACE,edit,KEY_H,MENUMOD + CreateMenu "{{menu_edit_gotoline}}",MENUGOTO,edit,KEY_G,MENUMOD +? + CreateMenu "",0,edit + CreateMenu "{{menu_edit_findinfiles}}",MENUFINDINFILES,edit,KEY_F,MENUMOD|MODIFIER_SHIFT + + program=CreateMenu("{{menu_program}}",0,menu) + CreateMenu "{{menu_program_build}}",MENUBUILD,program,KEY_B,MENUMOD + CreateMenu "{{menu_program_buildandrun}}",MENURUN,program,KEY_R,MENUMOD + CreateMenu "{{menu_program_commandline}}",MENUCOMMANDLINE,program + CreateMenu "",0,program + CreateMenu "{{menu_program_step}}",MENUSTEP,program,KEY_F9 + CreateMenu "{{menu_program_stepin}}",MENUSTEPIN,program,KEY_F10 + CreateMenu "{{menu_program_stepout}}",MENUSTEPOUT,program,KEY_F11 + CreateMenu "{{menu_program_terminate}}",MENUSTOP,program + buildoptions=CreateMenu("{{menu_program_buildoptions}}",0,program) + quickenable=CreateMenu("{{menu_program_buildoptions_quick}}",MENUQUICKENABLED,buildoptions) + debugenable=CreateMenu("{{menu_program_buildoptions_debug}}",MENUDEBUGENABLED,buildoptions) + If (FileType( BlitzMaxPath()+"/mod/brl.mod/blitz.mod/blitz_gc_ms.c" )=FILETYPE_FILE) .. + Or (FileType( BlitzMaxpath()+"/mod/brl.mod/blitz.mod/bdwgc" )=FILETYPE_DIR) + threadedenable=CreateMenu("{{menu_program_buildoptions_threaded}}",MENUTHREADEDENABLED,buildoptions) + EndIf + guienable=CreateMenu("{{menu_program_buildoptions_guiapp}}",MENUGUIENABLED,buildoptions) + CreateMenu "",0,program + CreateMenu "{{menu_program_lockbuildfile}}",MENULOCKBUILD,program + CreateMenu "{{menu_program_unlockbuildfile}}",MENUUNLOCKBUILD,program + CreateMenu "",0,program + 'syncmods=CreateMenu("{{menu_program_syncmods}}",MENUSYNCMODS,program) + 'CreateMenu "",0,program + buildmods=CreateMenu("{{menu_program_buildmods}}",MENUBUILDMODULES,program,KEY_D,MENUMOD) + buildallmods=CreateMenu("{{menu_program_rebuildallmods}}",MENUBUILDALLMODULES,program) + docmods=CreateMenu("{{menu_program_rebuilddocs}}",MENUDOCMODS,program) + + help=CreateMenu("{{menu_help}}",0,menu) + CreateMenu "{{menu_help_home}}",MENUHOME,help + CreateMenu "{{menu_help_back}}",MENUBACK,help + CreateMenu "{{menu_help_forward}}",MENUFORWARD,help + CreateMenu "{{menu_help_quickhelp}}",MENUQUICKHELP,help,KEY_F1 + CreateMenu "{{menu_help_aboutmaxide}}",MENUABOUT,help + + If quickenabled CheckMenu quickenable + If debugenabled CheckMenu debugenable + If threadedenabled CheckMenu threadedenable + If guienabled CheckMenu guienable + +?Win32 + Local mingw$=getenv_("MINGW") + If Not mingw + DisableMenu buildmods + DisableMenu buildallmods + EndIf +? +' If is_demo +' DisableMenu syncmods +' EndIf + + RefreshRecentFiles + UpdateWindowMenu window + End Method + + Method RunCode() + If mode=DEBUGMODE And debugtree.cancontinue + output.Go() + Return + EndIf + output.Stop() + SaveAll() + If lockedpanel + lockedpanel.invoke TOOLRUN + Else + activepanel.invoke TOOLRUN + EndIf + End Method + + Method BuildCode() + output.Stop() + SaveAll() + If lockedpanel + lockedpanel.invoke TOOLBUILD + Else + activepanel.invoke TOOLBUILD + EndIf + End Method + + + Method AddEventHotKey(key,mods,id,data) + SetHotKeyEvent key,mods,CreateEvent(id,Null,data) + End Method + + Method InitHotkeys() + AddEventHotKey KEY_F5,MODIFIER_NONE,EVENT_MENUACTION,MENURUN + AddEventHotKey KEY_F7,MODIFIER_NONE,EVENT_MENUACTION,MENUBUILD + End Method + + Method SaveAll() + For Local panel:TToolPanel = EachIn panels + panel.invoke TOOLQUICKSAVE + Next + End Method + + Method Restart() + If Confirm(LocalizeString("{{request_restart}}")) + Quit + EndIf + End Method + + Method RefreshAll() +' hide/show toolbar + If options.showtoolbar Then ShowGadget toolbar Else HideGadget toolbar +' refresh panels + For Local panel:TToolPanel = EachIn panels + panel.invoke TOOLREFRESH + Next +' refresh navbar + navbar.invoke TOOLREFRESH + End Method + + Method SnapshotWindow() + If WindowMaximized(window) + winmax=True + Else + If Not WindowMinimized(window) + winmax=False + winsize.x=GadgetX(window) + winsize.y=GadgetY(window) + winsize.w=GadgetWidth(window) + winsize.h=GadgetHeight(window) + EndIf + EndIf + options.showtoolbar = Not GadgetHidden(toolbar) + End Method + + Method OnMenu(menu,extra:Object=Null) + Local index + + Local tool:TTool = TTool(extra) + If tool + tool.invoke(TOOLMENU,""+menu) + Return + EndIf + + Select menu + Case MENUNEW + OpenSource "" + Case MENUOPEN + OpenSource "." + Case MENUCLOSE + currentpanel.invoke TOOLCLOSE + Case MENUCLOSEALL + CloseAll True + Case MENUCLOSEOTHERS + CloseAll True, False + Case MENUSAVE + currentpanel.invoke TOOLSAVE + Case MENUSAVEAS + currentpanel.invoke TOOLSAVEAS + Case MENUSAVEALL + SaveAll() + Case MENUPRINT + currentpanel.invoke TOOLPRINT + Case MENUQUIT + Quit() + + Case MENUGOTO + gotoreq.Show() + Case MENUFIND + currentpanel.invoke TOOLFIND + Case MENUFINDNEXT + currentpanel.invoke TOOLFINDNEXT + Case MENUREPLACE + replacereq.Show() + + Case MENUUNDO currentpanel.invoke TOOLUNDO + Case MENUREDO currentpanel.invoke TOOLREDO + + Case MENUCUT currentpanel.invoke TOOLCUT + Case MENUCOPY currentpanel.invoke TOOLCOPY + Case MENUPASTE currentpanel.invoke TOOLPASTE + Case MENUSELECTALL currentpanel.invoke TOOLSELECTALL + + Case MENUBUILD + BuildCode() + Case MENURUN + RunCode() + + Case MENUBUILDMODULES + If CheckDemo() BuildModules False + Case MENUBUILDALLMODULES + If CheckDemo() BuildModules True + 'Case MENUSYNCMODS + ' If CheckDemo() And CloseAll(False) syncmodsreq.Show + Case MENUDOCMODS + If CheckDemo() And CloseAll(False) DocMods + Case MENUTRIGGERDOCMODS + DocMods() + Case MENUTRIGGERSYNCDOCS + SyncDocs() + + Case MENUSTEP If output output.StepOver() + Case MENUSTEPIN If output output.StepIn() + Case MENUSTEPOUT If output output.StepOut() + Case MENUSTOP If output output.Stop() + + Case MENULOCKBUILD + activepanel.invoke TOOLLOCK + Case MENUUNLOCKBUILD + If lockedpanel lockedpanel.invoke TOOLUNLOCK + + Case MENUCOMMANDLINE cmdlinereq.Show + + Case MENUQUICKENABLED + If quickenabled + quickenabled=False + UncheckMenu quickenable + Else + quickenabled=True + CheckMenu quickenable + EndIf + UpdateWindowMenu window + + Case MENUDEBUGENABLED + If debugenabled + debugenabled=False + UncheckMenu debugenable + Else + debugenabled=True + CheckMenu debugenable + EndIf + UpdateWindowMenu window + + Case MENUTHREADEDENABLED + If threadedenabled + threadedenabled=False + UncheckMenu threadedenable + Else + threadedenabled=True + CheckMenu threadedenable + EndIf + UpdateWindowMenu window + + Case MENUGUIENABLED + If guienabled + guienabled=False + UncheckMenu guienable + Else + guienabled=True + CheckMenu guienable + EndIf + UpdateWindowMenu window + + Case MENUIMPORTBB + ImportBB + + Case MENUFINDINFILES + If activepanel Then searchreq.ShowWithPath( ExtractDir(activepanel.path) ) Else searchreq.Show() + + Case MENUPROJECTMANAGER + projectreq.Open projects + Case MENUSHOWCONSOLE + If output Then output.Open() + + Case MENUOPTIONS + options.Show() + + Case MENUNEXT + If Not currentpanel Return + index=currentpanel.index+1 + If index=panels.length index=0 + SelectPanel panels[index] + + Case MENUPREV + If Not currentpanel Return + index=currentpanel.index-1 + If index<0 index=panels.length-1 + SelectPanel panels[index] + + Case MENUQUICKHELP + currentpanel.invoke TOOLHELP + + Case MENUHOME + helppanel.Home() + SelectPanel helppanel + Case MENUBACK + helppanel.Back() + SelectPanel helppanel + Case MENUFORWARD + helppanel.Forward() + SelectPanel helppanel + Case MENUABOUT + aboutreq.Show() + 'Notify (ABOUT.Replace( "{bcc_version}",BCC_VERSION )) + Case MENUINDENT + currentpanel.invoke TOOLINDENT + Case MENUOUTDENT + currentpanel.invoke TOOLOUTDENT + + Case MENUNEWVIEW + navbar.invoke TOOLNEWVIEW + + End Select + + If menu>=MENURECENT + Local f:String = String(recentfiles.ValueAtIndex(menu-MENURECENT)) + If f$ OpenSource f$ + EndIf + End Method + + Method poll() + + Local src:TGadget + Local event = WaitEvent() + + If Not activerequesters.IsEmpty() + Select event + Case EVENT_MENUACTION + src = ActiveGadget() + If src And (GadgetClass(src) = GADGET_TEXTFIELD) Then + Select EventData() + Case MENUSELECTALL + ActivateGadget(src) + Case MENUCOPY + GadgetCopy(src) + Case MENUPASTE + GadgetPaste(src) + Case MENUCUT + GadgetCut(src) + EndSelect + Return + EndIf + src = Null + Case EVENT_MOUSEENTER,EVENT_MOUSELEAVE,EVENT_GADGETLOSTFOCUS + Return + End Select + 'DebugLog CurrentEvent.ToString() + For Local activerequester:TRequester = EachIn activerequesters + If activerequester.Poll() Then Return + Next + EndIf + + For Local handler:TEventHandler = EachIn eventhandlers + handler.OnEvent() + Next + + src = TGadget(EventSource()) + + Select event + Case EVENT_GADGETACTION + Select EventSource() + Case toolbar + Select EventData() + Case TB_NEW OpenSource "" + Case TB_OPEN OpenSource "." + Case TB_CLOSE currentpanel.invoke TOOLCLOSE + Case TB_SAVE currentpanel.invoke TOOLSAVE + Case TB_CUT currentpanel.invoke TOOLCUT + Case TB_COPY currentpanel.invoke TOOLCOPY + Case TB_PASTE currentpanel.invoke TOOLPASTE + Case TB_FIND currentpanel.invoke TOOLFIND + Case TB_BUILD BuildCode + Case TB_BUILDRUN RunCode + Case TB_STEP If output output.stepover + Case TB_STEPIN If output output.stepin + Case TB_STEPOUT If output output.stepout + Case TB_STOP If output output.Stop + Case TB_HOME helppanel.Home;SelectPanel helppanel + Case TB_BACK helppanel.Back;SelectPanel helppanel + Case TB_FORWARDS helppanel.Forward;SelectPanel helppanel + End Select + + Case tabbar + Local index = EventData() + If index>=0 And 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

    B84#`ic2h45EX`hoG_0Q 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�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%0AOJt`sEc@JhEEm1 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 Z4gdxqKfmimb@l)N002ovPDHLkV1j=}oyPzG literal 0 HcmV?d00001 -- 2.30.2