Code archives/BlitzPlus Gui/CodeArea gadget
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
This is not 100% working, but it's pretty good. | |||||
SuperStrict Import maxgui.maxgui Import "TextAreaUndo.bmx" Import "Lexer.bmx" Type TCodeArea Extends TProxyGadget Field textarea:TGadget Field undocontext:TUndoContext Field undostate:TUndoState Field lexer:TLexer=New TLexer Method CleanUp() RemoveHook(EmitEventHook,EventHook,Self) undocontext.Free() Super.CleanUp() EndMethod Method ClearUndos() undocontext.Flush() EndMethod Method ReplaceText:Int(pos:Int,length:Int,text$,units:Int) Local result:Int Local l:Int Local p:Int Local line1:Int Local line2:Int result=proxy.ReplaceText(pos,length,text,units) l=text.length p=pos line1=TextAreaLine(textarea,p) line2=TextAreaLine(textarea,p+l) lexer.FormatText(textarea,line1,1+line2-line1) ClearUndos() Return result End Method Method AddText:Int(text$) Local result:Int Local l:Int Local p:Int Local line1:Int Local line2:Int l=text.length p=TextAreaText(textarea).length result=proxy.AddText(text) line1=TextAreaLine(textarea,p) line2=TextAreaLine(textarea,p+l) lexer.FormatText(textarea,line1,1+line2-line1) ClearUndos() Return result End Method Function Create:TCodeArea(x:Int,y:Int,width:Int,height:Int,group:TGadget,style:Int=0) Local codearea:TCodeArea codearea=New TCodearea codearea.textarea=CreateTextArea(x,y,width,height,group) codearea.undocontext=TUndoContext.Create(codearea.textarea) SetGadgetFilter(codearea.textarea,Filter,codearea) codearea.setproxy(codearea.textarea) AddHook(EmitEventHook,EventHook,codearea) Return codearea EndFunction Method Undo() Local start:Int Local stop:Int Local line1:Int Local line2:Int If undostate start=undostate.undopos stop=start+undostate.undolen EndIf undocontext.Undo() line1=Min(TextAreaLine(textarea,start),TextAreaCursor(textarea,TEXTAREA_LINES)) line2=Max(TextAreaLine(textarea,stop),TextAreaCursor(textarea,TEXTAREA_LINES)+TextAreaSelLen(textarea,TEXTAREA_LINES)) lexer.FormatText(textarea,line1,1+line2-line1) EndMethod Method Redo() Local start:Int Local stop:Int Local line1:Int Local line2:Int If undostate start=undostate.undopos stop=start+undostate.undolen EndIf undocontext.Redo() line1=Min(TextAreaLine(textarea,start),TextAreaCursor(textarea,TEXTAREA_LINES)) line2=Max(TextAreaLine(textarea,stop),TextAreaCursor(textarea,TEXTAREA_LINES)+TextAreaSelLen(textarea,TEXTAREA_LINES)) lexer.FormatText(textarea,line1,1+line2-line1) EndMethod 'Handles undo states Function EventHook:Object(id:Int,data:Object,context:Object) Local codearea:TCodeArea=TCodeArea(context) Local event:TEvent=TEvent(data) Select event.id Case EVENT_GADGETSELECT If codearea.undocontext.Current If Not codearea.undocontext.disabled If codearea.undocontext.Current.text=TextAreaText(codearea.textarea) codearea.undocontext.Current.selpos=TextAreaCursor(codearea.textarea) codearea.undocontext.Current.sellen=TextAreaSelLen(codearea.textarea) codearea.undocontext.Current.removetext=TextAreaText(codearea.textarea,codearea.undocontext.Current.selpos,codearea.undocontext.Current.sellen) EndIf EndIf EndIf Case EVENT_GADGETACTION If Not codearea.undocontext.disabled Local change:Int Local start:Int Local stop:Int Local line1:Int Local line2:Int Local n:Int Local s:String change=event.data start=TextAreaCursor(codearea.textarea)-1 If change<0 start=start+change stop=start+Abs(change) line1=TextAreaLine(codearea.textarea,start) line2=TextAreaLine(codearea.textarea,stop) codearea.lexer.FormatText codearea.textarea,line1,1+line2-line1 If codearea.undocontext.Current If codearea.undocontext.Current.text=TextAreaText(codearea.textarea) Return Null EndIf If Not codearea.undocontext.disabled codearea.undostate=codearea.undocontext.createundostate() EndIf EndIf EndSelect Return data EndFunction 'Handles block indent Function Filter:Int(event:TEvent,context:Object) Local codearea:TCodeArea=TCodeArea(context) Local line1:Int Local line2:Int Local n:Int,s:String Select event.id Case EVENT_KEYDOWN Case EVENT_KEYCHAR If event.data=KEY_TAB If TextAreaSelLen(codearea.textarea) 'Don't lock the textarea gadget, it will cause errors. line1=TextAreaCursor(codearea.textarea,TEXTAREA_LINES) line2=line1+TextAreaSelLen(codearea.textarea,TEXTAREA_LINES) If event.mods=MODIFIER_SHIFT For n=line1 To line2-1 s$=TextAreaText(codearea.textarea,n,1,TEXTAREA_LINES) If s.length>1 If Left(s,1).Trim()="" SetTextAreaText codearea.textarea,"",TextAreaChar(codearea.textarea,n),1 EndIf EndIf Next Else For n=line1 To line2-1 SetTextAreaText codearea.textarea," ",n,0,TEXTAREA_LINES Next EndIf Local char1:Int Local char2:Int char1=TextAreaChar(codearea.textarea,line1) char2=TextAreaChar(codearea.textarea,line2) Local do:Int=0 If Right(TextAreaText(codearea.textarea,char1,char2-char1,TEXTAREA_CHARS),1)="~n" char2:-1 EndIf SelectTextAreaText(codearea.textarea,char1,char2-char1,TEXTAREA_CHARS) codearea.undostate=codearea.undocontext.CreateUndoState() codearea.undostate.undopos=TextAreaChar(codearea.textarea,line1) Return 0 EndIf EndIf EndSelect Return 1 EndFunction EndType |
Comments
| ||
Lexer.bmx:SuperStrict Import maxgui.drivers Type TLexer Field BACKGROUNDCOLOR_R:Int=224 Field BACKGROUNDCOLOR_G:Int=224 Field BACKGROUNDCOLOR_B:Int=224 Field FORMATCOLOR_PLAIN_R:Int=0 Field FORMATCOLOR_PLAIN_G:Int=0 Field FORMATCOLOR_PLAIN_B:Int=0 Field FORMATSTYLE_PLAIN:Int=0 Field FORMATCOLOR_STRING_R:Int=0 Field FORMATCOLOR_STRING_G:Int=128 Field FORMATCOLOR_STRING_B:Int=0 Field FORMATSTYLE_STRING:Int=TEXTFORMAT_ITALIC Field FORMATCOLOR_COMMENT_R:Int=128 Field FORMATCOLOR_COMMENT_G:Int=0 Field FORMATCOLOR_COMMENT_B:Int=128 Field FORMATSTYLE_COMMENT:Int=0 Field FORMATCOLOR_COMMAND_R:Int=0 Field FORMATCOLOR_COMMAND_G:Int=0 Field FORMATCOLOR_COMMAND_B:Int=255 Field FORMATSTYLE_COMMAND:Int=TEXTFORMAT_BOLD Field keywords:String[] Field lkeywords:String[] Method AddKeyword(word:String) Local count:Int count=keywords.length keywords=keywords[..count+1] lkeywords=lkeywords[..count+1] keywords[count]=word lkeywords[count]=word.tolower() EndMethod Method FormatText(gadget:TGadget,position:Int=0,length:Int=TEXTAREA_ALL) Local index:Int Local commentline:Int LockTextArea gadget If length=TEXTAREA_ALL length=TextAreaLen(gadget,TEXTAREA_LINES)-1-position For index=position To position+length Rem 'Check for Rem s$=Lower(TextAreaText(gadget,index,1,TEXTAREA_LINES)) s=Trim(s) If Not commentline If Left(s,3)="rem" c$=Mid(s,4,1) commentline=True If lb>=48 And lb<=57 commentline=False If lb>=65 And lb<=90 commentline=False If lb>=97 And lb<=122 commentline=False EndIf EndIf If commentline If Left(s,6)="endrem" c$=Mid(s,4,1) commentline=False If lb>=48 And lb<=57 commentline=True If lb>=65 And lb<=90 commentline=True If lb>=97 And lb<=122 commentline=True FormatTextAreaText(gadget,128,0,128,0,index,1,TEXTAREA_LINES) Continue EndIf EndIf EndRem If commentline FormatTextAreaText(gadget,128,0,128,0,index,1,TEXTAREA_LINES) Else FormatLine gadget,index EndIf Next UnlockTextArea gadget EndMethod Method FormatLine(gadget:TGadget,position:Int) Local s:String Local startpos:Int Local pos:Int Local ss:String Local pos2:Int Local stringoffset:Int Local firstpart:String Local lastpart:String Local space:String Local n:Int FormatTextAreaText(gadget,FORMATCOLOR_PLAIN_R,FORMATCOLOR_PLAIN_G,FORMATCOLOR_PLAIN_B,FORMATSTYLE_PLAIN,position,1,TEXTAREA_LINES) s$=Lower(TextAreaText(gadget,position,1,TEXTAREA_LINES)) startpos=TextAreaChar(gadget,position) 'Remove Comments pos=Instr(s,"'") If pos FormatTextAreaText(gadget,FORMATCOLOR_COMMENT_R,FORMATCOLOR_COMMENT_G,FORMATCOLOR_COMMENT_B,FORMATSTYLE_COMMENT,startpos+pos-1,s.length-pos) s=Left(s,pos) EndIf 'Strings ss$=s pos=Instr(ss,Chr(34)) stringoffset=0 While pos pos2=Instr(ss,Chr(34),pos+1) If pos2 FormatTextAreaText(gadget,FORMATCOLOR_STRING_R,FORMATCOLOR_STRING_G,FORMATCOLOR_STRING_B,FORMATSTYLE_STRING,startpos+pos+stringoffset-1,pos2-pos+1) firstpart$=Left(s,pos+stringoffset-1) lastpart$=Right(s,s.length-pos2-stringoffset) s$=firstpart space$="" For n=1 To ss.length-firstpart.length-lastpart.length-2 space$:+"?" Next s=s+Chr(34)+space+Chr(34)+lastpart Else Exit EndIf ss=Right(ss,ss.length-pos2) pos=Instr(ss,Chr(34)) stringoffset:+pos2 Wend 'Format Keywords Local text:String Local ltext:String Local leftokay:Int Local rightokay:Int Local lc:String Local lb:Int Local rc:String Local rb:Int Local r:Int,g:Int,b:Int Local length:Int For n=0 To keywords.length-1 text=keywords[n] ltext=lkeywords[n] pos=Instr(s,ltext) While pos 'Is word isolated? If pos>0 lc$=Mid(s,pos-1,1) lb=Asc(lc) leftokay=True If lb>=48 And lb<=57 leftokay=False If lb>=65 And lb<=90 leftokay=False If lb>=97 And lb<=122 leftokay=False Else leftokay=True EndIf 'Check the right side If leftokay If pos+text.length-1<=s.length rc$=Mid(s,pos+text.length,1) rb=Asc(rc) rightokay=True If rb>=48 And rb<=57 rightokay=False If rb>=65 And rb<=90 rightokay=False If rb>=97 And rb<=122 rightokay=False Else rightokay=True EndIf If rightokay SetTextAreaText gadget,text,startpos+pos-1,text.length r=FORMATCOLOR_COMMAND_R g=FORMATCOLOR_COMMAND_G b=FORMATCOLOR_COMMAND_B position=startpos+pos-1 length=text.length FormatTextAreaText(gadget,r,g,b,FORMATSTYLE_COMMAND,position,length) EndIf EndIf pos=Instr(s,ltext,pos+text.length) Wend Next EndMethod EndType |
| ||
TextAreaUndo.bmx:SuperStrict Import maxgui.drivers Type TUndoContext Field link:TLink Field gadget:TGadget Field undostates:TList=New TList Field Current:TUndoState Field change:Int Field disabled:Int Method CreateUndoState:TUndostate(force:Int=0) If Current Current.clearafter Local undostate:TUndoState=New TUndoState undostate.context=Self undostate.link=undostates.addlast(undostate) undostate.selpos=TextAreaCursor(gadget) undostate.sellen=TextAreaSelLen(gadget) undostate.update() undostate.force=force Current=undostate Return undostate EndMethod Method CanUndo:TUndoState(undostate:TUndoState=Null) If undostate=Null undostate=Current If undostate If undostate.link.prevlink() Local prevundostate:TUndoState=TUndoState(undostate.link.prevlink().value()) If prevundostate.force=-1 Return TUndoState(prevundostate.link.prevlink().value()) Else Return prevundostate EndIf EndIf EndIf Return Null EndMethod Method CanRedo:TUndoState(undostate:TUndoState=Null) If undostate=Null undostate=Current If undostate If undostate.link.nextlink() Local nextundostate:TUndoState=TUndoState(undostate.link.nextlink().value()) If nextundostate.force=1 Return TUndoState(nextundostate.link.nextlink().value()) Else Return nextundostate EndIf EndIf EndIf Return Null EndMethod Method Undo:Int() If Current If canundo() Current.undo Current=canundo() Return True EndIf EndIf Return False EndMethod Method Redo:Int() Local undostate:TUndoState=CanRedo() If undostate undostate.redo Current=undostate Return True EndIf Return False EndMethod Method Flush() For Local undostate:TUndoState=EachIn undostates undostate.kill Next undostates.clear Current=Null createundostate EndMethod Method Free() flush undostates=Null gadget=Null RemoveLink link link=Null EndMethod Function Create:TUndoContext(gadget:TGadget) Local undocontext:TUndoContext=New TUndoContext undocontext.gadget=gadget undocontext.createundostate Return undocontext EndFunction EndType Type TUndostate Field link:TLink Field context:TUndoContext Field text$ Field undotext$ Field redotext$ Field selpos:Int Field sellen:Int Field undolen:Int Field undopos:Int Field change:Int Field force:Int Field removetext$ Field removeposition:Int Field addtext$ Field addposition:Int Method Update() text$=TextAreaText(context.gadget) selpos=TextAreaCursor(context.gadget) sellen=TextAreaSelLen(context.gadget) removetext=TextAreaText(context.gadget,selpos,sellen) Local lastundostate:TUndoState=context.canundo(Self) If lastundostate change=text.length-lastundostate.text.length change=change+lastundostate.sellen Else change=text.length EndIf If change<0 undopos=selpos undolen=-change If lastundostate undotext=Mid(lastundostate.text,undopos+1,-change) EndIf Else undopos=selpos-change undolen=change If lastundostate undotext=Mid(text,undopos+1,change) EndIf EndIf EndMethod Method Undo() Local start:Int=TextAreaCursor(context.gadget) Local prevundostate:TUndoState=context.canundo(Self) Local nextundostate:TUndoState=context.canredo(Self) 'LockTextArea context.gadget If change>0 SetTextAreaText context.gadget,"",undopos,undolen Else SetTextAreaText context.gadget,undotext,undopos,0 EndIf 'UnlockTextArea context.gadget prevundostate:TUndoState=context.canundo(Self) If prevundostate SetTextAreaText context.gadget,prevundostate.removetext,prevundostate.selpos,0 SelectTextAreaText context.gadget,prevundostate.selpos,prevundostate.sellen Else SelectTextAreaText context.gadget,0,0 EndIf Local stop:Int=TextAreaCursor(context.gadget) Local size:Int=Abs(start-stop)+TextAreaSelLen(context.gadget) context.disabled=True EmitEvent CreateEvent(EVENT_GADGETACTION,context.gadget,size) context.disabled=False EndMethod Method Redo() Local start:Int=TextAreaCursor(context.gadget) Local prevundostate:TUndoState=context.canundo(Self) Local nextundostate:TUndoState=context.canredo(Self) Local l:Int LockTextArea context.gadget If change<0 SetTextAreaText context.gadget,"",undopos,undolen Else If prevundostate l=prevundostate.sellen SetTextAreaText context.gadget,undotext,undopos,l EndIf UnlockTextArea context.gadget SelectTextAreaText context.gadget,selpos,sellen Local stop:Int=TextAreaCursor(context.gadget) Local size:Int=start-stop context.disabled=True EmitEvent CreateEvent(EVENT_GADGETACTION,context.gadget,size) context.disabled=False EndMethod Method ClearAfter() Local sublink:TLink Local nextsublink:TLink sublink=link.nextlink() While sublink nextsublink=sublink.nextlink() TUndoState(sublink.value()).kill sublink=nextsublink Wend EndMethod Method Kill() If link.prevlink() context.Current=TUndostate(link.prevlink().value()) Else context.Current=Null EndIf RemoveLink link link=Null EndMethod EndType |
| ||
Example:SuperStrict Import "CodeArea.bmx" Local window:TGadget Local menu:TGadget Local codearea:TCodeArea 'Create window window=CreateWindow("My Window",130,20,800,600,,15|WINDOW_ACCEPTFILES) menu=CreateMenu("Edit",0,WindowMenu(window)) CreateMenu("Undo",0,menu,KEY_Z,MODIFIER_COMMAND) CreateMenu("Redo",0,menu,KEY_Z,MODIFIER_COMMAND|MODIFIER_OPTION) UpdateWindowMenu(window) 'Create CodeArea gadget codearea=TCodeArea.Create(0,0,ClientWidth(window),ClientHeight(window),window) SetGadgetLayout codearea,1,1,1,1 SetGadgetFont(codearea,LoadGuiFont("Courier",9)) ActivateGadget codearea 'Add keywords codearea.lexer.AddKeyWord("If") codearea.lexer.AddKeyWord("EndIf") codearea.lexer.AddKeyWord("Next") codearea.lexer.AddKeyWord("Next") codearea.lexer.AddKeyWord("For") codearea.lexer.AddKeyWord("To") codearea.lexer.AddKeyWord("Local") codearea.lexer.AddKeyWord("Global") codearea.lexer.AddKeyWord("KeyHit") 'Add some text AddTextAreaText codearea,"if keyhit(KEY_ESCAPE)~n" AddTextAreaText codearea," ~n" AddTextAreaText codearea,"endif~n" 'Main loop While WaitEvent() Print CurrentEvent.ToString()+" "+EventSourceHandle() Select EventID() Case EVENT_MENUACTION Select GadgetText(TGadget(EventSource())) Case "Undo" codearea.Undo() Case "Redo" codearea.Redo() EndSelect Case EVENT_WINDOWCLOSE End Case EVENT_APPTERMINATE End End Select Wend |
Code Archives Forum