KUMOS/Client/Default Apps/TextEditor.sbi
2026-05-02 15:49:06 +02:00

217 lines
6.0 KiB
Plaintext

DeclareModule TextEditor
Declare Open(FileID.s, Path.s)
EndDeclareModule
Module TextEditor
EnableExplicit
#MaxEditors = 16
#ToolbarHeight = 35
Global Dim _Windows.i(#MaxEditors)
Global Dim _Editors.i(#MaxEditors)
Global Dim _SaveBtns.i(#MaxEditors)
Global Dim _FileIDs.s(#MaxEditors)
Global Dim _Paths.s(#MaxEditors)
Global Dim _Dirty.i(#MaxEditors)
Global Dim _Saving.i(#MaxEditors)
Global _Count.i = 0
Global _Loading_Slot.i = -1
Global _ClosingSlot.i = -1 ; slot awaiting confirm-close callback
Declare _FindByWindow(Window.i)
Declare _FindByEditor(Editor.i)
Declare _SetDirty(Slot.i, Dirty.i)
Declare _Save(Slot.i)
Declare _Remove(Slot.i)
Declare _ConfirmCloseCallback(Result.i)
Declare _ReadCallback(Success.i, DataString.s)
Declare _SaveCallback(Success.i, DataString.s)
Declare Handler_Resize()
Declare Handler_Save()
Declare Handler_Change()
Declare Handler_Close()
Procedure Open(FileID.s, Path.s)
If _Count >= #MaxEditors
Notify::Toast("Too many editors open.", Notify::#Warning)
ProcedureReturn
EndIf
Protected Slot.i = _Count
Protected W.i = 600
Protected H.i = 400
Protected Win.i = OpenWindow(#PB_Any, 120 + Slot * 20, 60 + Slot * 20, W, H,
FS::GetFilePart(Path),
#PB_Window_TitleBar | #PB_Window_SizeGadget | #PB_Window_SystemMenu)
Protected SaveBtn.i = ButtonGadget(#PB_Any, 5, 3, 80, #ToolbarHeight - 6, "Save")
Protected Editor.i = EditorGadget(#PB_Any, 0, #ToolbarHeight, W, H - #ToolbarHeight)
_Windows(Slot) = Win
_Editors(Slot) = Editor
_SaveBtns(Slot) = SaveBtn
_FileIDs(Slot) = FileID
_Paths(Slot) = Path
_Dirty(Slot) = #False
_Count + 1
BindGadgetEvent(SaveBtn, @Handler_Save())
BindGadgetEvent(Editor, @Handler_Change())
BindEvent(#PB_Event_SizeWindow, @Handler_Resize(), Win)
BindEvent(#PB_Event_CloseWindow, @Handler_Close(), Win)
Desktop::Register(FS::GetFilePart(Path), Win, "📄")
_Loading_Slot = Slot
FS::Read_(FileID, Path, @_ReadCallback())
EndProcedure
; Private procedures
Procedure.i _FindByWindow(Window.i)
Protected i.i
For i = 0 To _Count - 1
If _Windows(i) = Window : ProcedureReturn i : EndIf
Next
ProcedureReturn -1
EndProcedure
Procedure.i _FindByEditor(Editor.i)
Protected i.i
For i = 0 To _Count - 1
If _Editors(i) = Editor : ProcedureReturn i : EndIf
Next
ProcedureReturn -1
EndProcedure
Procedure _SetDirty(Slot.i, Dirty.i)
_Dirty(Slot) = Dirty
Protected Title.s = FS::GetFilePart(_Paths(Slot))
If Dirty : Title = "* " + Title : EndIf
SetWindowTitle(_Windows(Slot), Title)
EndProcedure
Procedure _Save(Slot.i)
If Not IsWindow(_Windows(Slot)) : ProcedureReturn : EndIf
If _Saving(Slot) : ProcedureReturn : EndIf
Protected Content.s = GetGadgetText(_Editors(Slot))
_Saving(Slot) = #True
DisableGadget(_SaveBtns(Slot), #True)
FS::Write(_FileIDs(Slot), _Paths(Slot), Content, @_SaveCallback())
EndProcedure
Procedure _Remove(Slot.i)
UnbindGadgetEvent(_SaveBtns(Slot), @Handler_Save())
UnbindGadgetEvent(_Editors(Slot), @Handler_Change())
UnbindEvent(#PB_Event_SizeWindow, @Handler_Resize(), _Windows(Slot))
UnbindEvent(#PB_Event_CloseWindow, @Handler_Close(), _Windows(Slot))
Desktop::Unregister(_Windows(Slot))
Protected i.i
For i = Slot To _Count - 2
_Windows(i) = _Windows(i + 1)
_Editors(i) = _Editors(i + 1)
_SaveBtns(i) = _SaveBtns(i + 1)
_FileIDs(i) = _FileIDs(i + 1)
_Paths(i) = _Paths(i + 1)
_Dirty(i) = _Dirty(i + 1)
_Saving(i) = _Saving(i + 1)
Next
_Windows(_Count - 1) = 0
_Editors(_Count - 1) = 0
_SaveBtns(_Count - 1) = 0
_FileIDs(_Count - 1) = ""
_Paths(_Count - 1) = ""
_Dirty(_Count - 1) = #False
_Saving(_Count - 1) = #False
_Count - 1
If _Loading_Slot = Slot : _Loading_Slot = -1 : EndIf
If _ClosingSlot = Slot : _ClosingSlot = -1 : EndIf
EndProcedure
; Called by Notify::Confirm — Result=1 means discard and close, 0 means cancel.
Procedure _ConfirmCloseCallback(Result.i)
If Result And _ClosingSlot >= 0
_Remove(_ClosingSlot)
EndIf
_ClosingSlot = -1
EndProcedure
; Callbacks
Procedure _ReadCallback(Success.i, DataString.s)
If _Loading_Slot < 0 : ProcedureReturn : EndIf
Protected Slot.i = _Loading_Slot
_Loading_Slot = -1
If Not IsWindow(_Windows(Slot)) : ProcedureReturn : EndIf
If Success
SetGadgetText(_Editors(Slot), DataString)
_SetDirty(Slot, #False)
Else
SetGadgetText(_Editors(Slot), "")
_SetDirty(Slot, #True)
EndIf
EndProcedure
Procedure _SaveCallback(Success.i, DataString.s)
Protected i.i
For i = 0 To _Count - 1
If _Saving(i)
_Saving(i) = #False
DisableGadget(_SaveBtns(i), #False)
If Success
_SetDirty(i, #False)
Else
Notify::Toast("Save failed — " + FS::GetFilePart(_Paths(i)), Notify::#Error)
EndIf
Break
EndIf
Next
EndProcedure
; Event handlers
Procedure Handler_Resize()
Protected Win.i = EventWindow()
Protected Slot.i = _FindByWindow(Win)
If Slot < 0 : ProcedureReturn : EndIf
ResizeGadget(_Editors(Slot), 0, #ToolbarHeight,
WindowWidth(Win), WindowHeight(Win) - #ToolbarHeight)
EndProcedure
Procedure Handler_Save()
Protected Slot.i = _FindByWindow(EventWindow())
If Slot >= 0 : _Save(Slot) : EndIf
EndProcedure
Procedure Handler_Change()
Protected Slot.i = _FindByEditor(EventGadget())
If Slot >= 0 And Not _Dirty(Slot)
_SetDirty(Slot, #True)
EndIf
EndProcedure
Procedure Handler_Close()
Protected Win.i = EventWindow()
Protected Slot.i = _FindByWindow(Win)
If Slot < 0 : ProcedureReturn : EndIf
If _Dirty(Slot)
_ClosingSlot = Slot
Notify::Confirm("Unsaved changes",
"Close " + Chr(34) + FS::GetFilePart(_Paths(Slot)) + Chr(34) + " without saving?",
@_ConfirmCloseCallback())
Else
_Remove(Slot)
EndIf
EndProcedure
EndModule
; IDE Options = SpiderBasic 3.10 (Windows - x86)
; CursorPosition = 69
; Folding = DA5
; iOSAppOrientation = 0
; AndroidAppCode = 0
; AndroidAppOrientation = 0
; EnableXP
; DPIAware
; CompileSourceDirectory