March 05, 2021, 07:57:03 AM

Author Topic: [bmx] CodeArea gadget by JoshK [ 1+ years ago ]  (Read 681 times)

Offline BlitzBot

[bmx] CodeArea gadget by JoshK [ 1+ years ago ]
« on: June 29, 2017, 12:28:40 AM »
Title : CodeArea gadget
Author : JoshK
Posted : 1+ years ago

Description : This is not 100% working, but it's pretty good.

Code :
Code: BlitzMax
  1. SuperStrict
  2.  
  3. Import maxgui.maxgui
  4. Import "TextAreaUndo.bmx"
  5. Import "Lexer.bmx"
  6.  
  7. Type TCodeArea Extends TProxyGadget
  8.        
  9.         Field textarea:TGadget
  10.         Field undocontext:TUndoContext
  11.         Field undostate:TUndoState
  12.         Field lexer:TLexer=New TLexer
  13.        
  14.         Method CleanUp()
  15.                 RemoveHook(EmitEventHook,EventHook,Self)
  16.                 undocontext.Free()
  17.                 Super.CleanUp()
  18.         EndMethod
  19.        
  20.         Method ClearUndos()
  21.                 undocontext.Flush()
  22.         EndMethod
  23.        
  24.         Method ReplaceText:Int(pos:Int,length:Int,text$,units:Int)
  25.                 Local result:Int
  26.                 Local l:Int
  27.                 Local p:Int
  28.                 Local line1:Int
  29.                 Local line2:Int
  30.                 result=proxy.ReplaceText(pos,length,text,units)
  31.                 l=text.length
  32.                 p=pos
  33.                 line1=TextAreaLine(textarea,p)
  34.                 line2=TextAreaLine(textarea,p+l)
  35.                 lexer.FormatText(textarea,line1,1+line2-line1)
  36.                 ClearUndos()
  37.                 Return result
  38.         End Method
  39.        
  40.         Method AddText:Int(text$)
  41.                 Local result:Int
  42.                 Local l:Int
  43.                 Local p:Int
  44.                 Local line1:Int
  45.                 Local line2:Int
  46.                 l=text.length
  47.                 p=TextAreaText(textarea).length
  48.                 result=proxy.AddText(text)
  49.                 line1=TextAreaLine(textarea,p)
  50.                 line2=TextAreaLine(textarea,p+l)
  51.                 lexer.FormatText(textarea,line1,1+line2-line1)
  52.                 ClearUndos()
  53.                 Return result
  54.         End Method
  55.        
  56.         Function Create:TCodeArea(x:Int,y:Int,width:Int,height:Int,group:TGadget,style:Int=0)
  57.                 Local codearea:TCodeArea
  58.                 codearea=New TCodearea
  59.                 codearea.textarea=CreateTextArea(x,y,width,height,group)               
  60.                 codearea.undocontext=TUndoContext.Create(codearea.textarea)
  61.                 SetGadgetFilter(codearea.textarea,Filter,codearea)
  62.                 codearea.setproxy(codearea.textarea)
  63.                 AddHook(EmitEventHook,EventHook,codearea)
  64.                 Return codearea
  65.         EndFunction
  66.        
  67.         Method Undo()
  68.                 Local start:Int
  69.                 Local stop:Int
  70.                 Local line1:Int
  71.                 Local line2:Int
  72.                
  73.                 If undostate
  74.                         start=undostate.undopos
  75.                         stop=start+undostate.undolen
  76.                 EndIf
  77.                 undocontext.Undo()
  78.                 line1=Min(TextAreaLine(textarea,start),TextAreaCursor(textarea,TEXTAREA_LINES))
  79.                 line2=Max(TextAreaLine(textarea,stop),TextAreaCursor(textarea,TEXTAREA_LINES)+TextAreaSelLen(textarea,TEXTAREA_LINES))
  80.                 lexer.FormatText(textarea,line1,1+line2-line1)
  81.         EndMethod
  82.  
  83.         Method Redo()
  84.                 Local start:Int
  85.                 Local stop:Int
  86.                 Local line1:Int
  87.                 Local line2:Int
  88.                
  89.                 If undostate
  90.                         start=undostate.undopos
  91.                         stop=start+undostate.undolen
  92.                 EndIf          
  93.                 undocontext.Redo()
  94.                 line1=Min(TextAreaLine(textarea,start),TextAreaCursor(textarea,TEXTAREA_LINES))
  95.                 line2=Max(TextAreaLine(textarea,stop),TextAreaCursor(textarea,TEXTAREA_LINES)+TextAreaSelLen(textarea,TEXTAREA_LINES))
  96.                 lexer.FormatText(textarea,line1,1+line2-line1)         
  97.         EndMethod
  98.        
  99.         'Handles undo states
  100.         Function EventHook:Object(id:Int,data:Object,context:Object)
  101.                 Local codearea:TCodeArea=TCodeArea(context)
  102.                 Local event:TEvent=TEvent(data)
  103.                 Select event.id
  104.                         Case EVENT_GADGETSELECT
  105.                                 If codearea.undocontext.Current
  106.                                         If Not codearea.undocontext.disabled
  107.                                                 If codearea.undocontext.Current.text=TextAreaText(codearea.textarea)
  108.                                                         codearea.undocontext.Current.selpos=TextAreaCursor(codearea.textarea)
  109.                                                         codearea.undocontext.Current.sellen=TextAreaSelLen(codearea.textarea)
  110.                                                         codearea.undocontext.Current.removetext=TextAreaText(codearea.textarea,codearea.undocontext.Current.selpos,codearea.undocontext.Current.sellen)
  111.                                                 EndIf
  112.                                         EndIf
  113.                                 EndIf                  
  114.                         Case EVENT_GADGETACTION
  115.                                 If Not codearea.undocontext.disabled
  116.                                         Local change:Int
  117.                                         Local start:Int
  118.                                         Local stop:Int
  119.                                         Local line1:Int
  120.                                         Local line2:Int
  121.                                         Local n:Int
  122.                                         Local s:String
  123.                                         change=event.data
  124.                                         start=TextAreaCursor(codearea.textarea)-1
  125.                                         If change<0 start=start+change
  126.                                         stop=start+Abs(change)
  127.                                         line1=TextAreaLine(codearea.textarea,start)
  128.                                         line2=TextAreaLine(codearea.textarea,stop)
  129.                                         codearea.lexer.FormatText codearea.textarea,line1,1+line2-line1
  130.                                         If codearea.undocontext.Current
  131.                                                 If codearea.undocontext.Current.text=TextAreaText(codearea.textarea) Return Null
  132.                                         EndIf
  133.                                         If Not codearea.undocontext.disabled
  134.                                                 codearea.undostate=codearea.undocontext.createundostate()
  135.                                         EndIf
  136.                                 EndIf
  137.                 EndSelect
  138.                 Return data
  139.         EndFunction
  140.        
  141.         'Handles block indent
  142.         Function Filter:Int(event:TEvent,context:Object)
  143.                 Local codearea:TCodeArea=TCodeArea(context)
  144.                 Local line1:Int
  145.                 Local line2:Int
  146.                 Local n:Int,s:String
  147.                
  148.                 Select event.id
  149.                 Case EVENT_KEYDOWN
  150.                 Case EVENT_KEYCHAR
  151.                         If event.data=KEY_TAB
  152.                                 If TextAreaSelLen(codearea.textarea)
  153.                                         'Don't lock the textarea gadget, it will cause errors.
  154.                                         line1=TextAreaCursor(codearea.textarea,TEXTAREA_LINES)
  155.                                         line2=line1+TextAreaSelLen(codearea.textarea,TEXTAREA_LINES)
  156.                                         If event.mods=MODIFIER_SHIFT
  157.                                                 For n=line1 To line2-1
  158.                                                         s$=TextAreaText(codearea.textarea,n,1,TEXTAREA_LINES)
  159.                                                         If s.length>1
  160.                                                                 If Left(s,1).Trim()=""
  161.                                                                         SetTextAreaText codearea.textarea,"",TextAreaChar(codearea.textarea,n),1
  162.                                                                 EndIf
  163.                                                         EndIf
  164.                                                 Next
  165.                                         Else
  166.                                                 For n=line1 To line2-1
  167.                                                         SetTextAreaText codearea.textarea,"     ",n,0,TEXTAREA_LINES
  168.                                                 Next
  169.                                         EndIf
  170.                                         Local char1:Int
  171.                                         Local char2:Int
  172.                                         char1=TextAreaChar(codearea.textarea,line1)
  173.                                         char2=TextAreaChar(codearea.textarea,line2)
  174.  
  175.                                         Local do:Int=0
  176.  
  177.                                         If Right(TextAreaText(codearea.textarea,char1,char2-char1,TEXTAREA_CHARS),1)="~n"
  178.                                                 char2:-1
  179.                                         EndIf
  180.                                         SelectTextAreaText(codearea.textarea,char1,char2-char1,TEXTAREA_CHARS)
  181.                                        
  182.                                         codearea.undostate=codearea.undocontext.CreateUndoState()
  183.                                         codearea.undostate.undopos=TextAreaChar(codearea.textarea,line1)
  184.                                        
  185.                                         Return 0
  186.                                 EndIf
  187.                         EndIf
  188.                 EndSelect
  189.                 Return 1
  190.         EndFunction
  191.        
  192. EndType


Comments :


JoshK(Posted 1+ years ago)

 Lexer.bmx:
Code: [Select]
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



JoshK(Posted 1+ years ago)

 TextAreaUndo.bmx:
Code: [Select]
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



JoshK(Posted 1+ years ago)

 Example:
Code: [Select]
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


 

SimplePortal 2.3.6 © 2008-2014, SimplePortal