diff --git a/.XsEdit.xojo_uistate b/.XsEdit.xojo_uistate deleted file mode 100644 index 4fe0314..0000000 Binary files a/.XsEdit.xojo_uistate and /dev/null differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..820e718 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.xojo_uistate +.DS_Store +Builds - XsEdit.xojo_project +*.obsolete_* +XsEdit.debug.app +*.gz diff --git a/App.xojo_code b/App.xojo_code index b6e1662..e7ec314 100644 --- a/App.xojo_code +++ b/App.xojo_code @@ -1,6 +1,203 @@ #tag Class Protected Class App Inherits Application + #tag Event + Sub EnableMenuItems() + // + // FileClose + // + + if Keyboard.AsyncOptionKey then + FileClose.Text = "Close All" + FileClose.KeyboardShortcut = "cmd-opt-W" + else + FileClose.Text = "Close" + FileClose.KeyboardShortcut = "cmd-W" + end if + End Sub + #tag EndEvent + + #tag Event + Function HandleAppleEvent(theEvent As AppleEvent, eventClass As String, eventID As String) As Boolean + #pragma unused theEvent + + #if not TargetMacOS + #pragma unused eventClass + #pragma unused eventID + + #else + + if StrComp( eventClass, "aevt", 0 ) = 0 and StrComp( eventID, "rapp", 0 ) = 0 then + dim isVisibleWindow as boolean + dim lastWindowIndex as integer = WindowCount - 1 + for i as integer = 0 to lastWindowIndex + if Window( i ).Visible then + isVisibleWindow = true + exit + end if + next + + if not isVisibleWindow then + NewDocument + end if + + return true + end if + + #endif + End Function + #tag EndEvent + + #tag Event + Sub NewDocument() + dim w as new WndEditor + w.Show + End Sub + #tag EndEvent + + #tag Event + Sub OpenDocument(item As FolderItem) + dim editor as WndEditor + + // + // See if the first window is available + // + for i as integer = 0 to WindowCount - 1 + dim wnd as Window = Window( i ) + if wnd IsA WndEditor then + dim maybe as WndEditor = WndEditor( wnd ) + if editor is nil and maybe.MyDocument is nil and not maybe.ContentsChanged then + editor = maybe + // + // But keep going to see if the document is open elsewhere + // + + elseif maybe.MyDocument IsA FolderItem and maybe.MyDocument.NativePath = item.NativePath then + // + // It's already open + // + maybe.Show + return // Early exit + end if + end if + next i + + if editor is nil then + editor = new WndEditor + else + editor.Show + end if + + editor.OpenDocument item + End Sub + #tag EndEvent + + + #tag MenuHandler + Function EditPreferences() As Boolean Handles EditPreferences.Action + WndPreferences.ShowModal + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function FileNew() As Boolean Handles FileNew.Action + NewDocument + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function FileOpen() As Boolean Handles FileOpen.Action + dim dlg as new OpenDialog + dlg.PromptText = "Select a XojoScript document:" + dlg.Filter = DocumentTypes.XojoScript + + dim f as FolderItem = dlg.ShowModal + if f IsA FolderItem then + OpenDocument f + end if + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function HelpAbout() As Boolean Handles HelpAbout.Action + WndAbout.Show + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function HelpXojoScriptingLanguageDocs() As Boolean Handles HelpXojoScriptingLanguageDocs.Action + ShowURL "http://docs.xojo.com/index.php/Scripting_Language" + Return True + + End Function + #tag EndMenuHandler + + + #tag Method, Flags = &h0 + Sub CloseAllWindows() + for i as integer = WindowCount - 1 downto 0 + Window( i ).Close + next + + End Sub + #tag EndMethod + + + #tag Property, Flags = &h21 + Private mPrefs As XsEditPreferences + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if mPrefs is nil then + mPrefs = new XsEditPreferences( "com.mactechnologiesconsulting.xsedit" ) + end if + + return mPrefs + End Get + #tag EndGetter + Prefs As XsEditPreferences + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + dim f as FolderItem = App.ExecutableFile.Parent + + #if TargetMacOS then + f = f.Parent.Child( "Resources" ) + #else + f = f.Child( "Resources" ) + #endif + + return f + End Get + #tag EndGetter + ResourcesFolder As FolderItem + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return ResourcesFolder.Child( "Syntax Definitions" ).Child( "XojoScript.xml" ) + End Get + #tag EndGetter + SyntaxDefinitionFile As FolderItem + #tag EndComputedProperty + + #tag Constant, Name = kEditClear, Type = String, Dynamic = False, Default = \"&Delete", Scope = Public #Tag Instance, Platform = Windows, Language = Default, Definition = \"&Delete" #Tag Instance, Platform = Linux, Language = Default, Definition = \"&Delete" @@ -15,6 +212,14 @@ Inherits Application #Tag Instance, Platform = Linux, Language = Default, Definition = \"Ctrl+Q" #tag EndConstant + #tag Constant, Name = kViewHideToolbar, Type = String, Dynamic = False, Default = \"Hide Toolbar", Scope = Public + #tag EndConstant + + #tag Constant, Name = kViewShowToolbar, Type = String, Dynamic = False, Default = \"Show Toolbar", Scope = Public + #tag EndConstant + + #tag ViewBehavior + #tag EndViewBehavior End Class #tag EndClass diff --git a/Build Automation.xojo_code b/Build Automation.xojo_code index f9b6aee..3f0adde 100644 --- a/Build Automation.xojo_code +++ b/Build Automation.xojo_code @@ -2,13 +2,31 @@ Begin BuildStepList Linux Begin BuildProjectStep Build End + Begin CopyFilesBuildStep CopySyntaxDefinitionLinux + AppliesTo = 0 + Destination = 1 + Subdirectory = Syntax Definitions + FolderItem = Li4AT3RoZXIgRmlsZXMAU3ludGF4IERlZmluaXRpb24AWG9qb1NjcmlwdC54bWw= + End End Begin BuildStepList Mac OS X Begin BuildProjectStep Build End + Begin CopyFilesBuildStep CopySyntaxDefinitionMac + AppliesTo = 0 + Destination = 1 + Subdirectory = Syntax Definitions + FolderItem = Li4AT3RoZXIgRmlsZXMAU3ludGF4IERlZmluaXRpb24AWG9qb1NjcmlwdC54bWw= + End End Begin BuildStepList Windows Begin BuildProjectStep Build End + Begin CopyFilesBuildStep CopySyntaxDefinitionWin + AppliesTo = 0 + Destination = 1 + Subdirectory = Syntax Definitions + FolderItem = Li4AT3RoZXIgRmlsZXMAU3ludGF4IERlZmluaXRpb24AWG9qb1NjcmlwdC54bWw= + End End #tag EndBuildAutomation diff --git a/Classes/IDECommunicator.xojo_code b/Classes/IDECommunicator.xojo_code new file mode 100644 index 0000000..f10fb1f --- /dev/null +++ b/Classes/IDECommunicator.xojo_code @@ -0,0 +1,177 @@ +#tag Module +Protected Module IDECommunicator + #tag Method, Flags = &h1 + Protected Function FindIPCPath() As String + // Find a path for our temp file. + Dim parent As FolderItem + + // fix fb 3230 - njp - Sep 4, 2008 + Try + parent = Volume(0).Child("tmp") + Catch err As NilObjectException + parent = Nil + End Try + + If parent Is Nil Or Not parent.exists Or Not parent.IsWriteable Then + // more fixes for fb 3230 - njp - Sep 16, 2008 .. mostly becuase WUBI installed Ubuntu is really odd and Volume(0) fails + Try + parent = Volume(0).Child("var").Child("tmp") + Catch err As NilObjectException + parent = Nil + End Try + End If + + If parent Is Nil Or Not parent.exists Or Not parent.IsWriteable Then + // more fixes for fb 3230 - njp - Sep 16, 2008 .. mostly becuase WUBI installed Ubuntu is really odd and Volume(0) fails + Try + parent = SpecialFolder.Temporary + Catch err As NilObjectException + parent = Nil + End Try + End If + + If parent Is Nil Or Not parent.exists Or Not parent.IsWriteable Then + // more fixes for fb 3230 - njp - Sep 16, 2008 .. mostly becuase WUBI installed Ubuntu is really odd and Volume(0) fails + Try + parent = SpecialFolder.Home + Catch err As NilObjectException + parent = Nil + End Try + End If + + // fix fb 3230 - njp - Sep 4, 2008 + Try + Const ipcpath = "XojoIDE" + '#If DebugBuild + 'Return parent.Child("DEBUG"+ipcpath).ShellPath + '#Else + Return parent.Child(ipcpath).ShellPath + '#EndIf + Catch err As NilObjectException + Return "" + End Try + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ReportError(errorCode As Integer) + // A socket error has occurred. If you're using this module + // in your own code, you'll probably want to replace this + // with some error reporting mechanism that fits into your app. + + #If Not TargetHasGUI + stderr.WriteLine "Socket error: " + Str(errorCode) + #else + #pragma unused errorCode + #EndIf + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub SendScript(sourceCode As String, timeout as Integer = 7200) + // Send the given source to the IDE. + //Default timeout, 7200 ticks = 2 minutes + + Dim endTime As Integer = Ticks + timeout + Dim sock As IPCSocket + While Ticks < endTime + sock = New IPCSocket + sock.Path = FindIPCPath + sock.Connect + While Not sock.IsConnected And sock.LastErrorCode = 0 + sock.Poll + Wend + If sock.IsConnected Then Exit + App.DoEvents( 1000 ) + Wend + If sock = Nil Then + ReportError 0 + Return + Elseif sock.LastErrorCode <> 0 Then + ReportError sock.LastErrorCode + Return + End If + + sock.Write sourceCode + While sock.BytesLeftToSend > 0 + sock.Poll + If sock.LastErrorCode <> 0 Then + ReportError(sock.LastErrorCode) + Return + End If + Wend + + End Sub + #tag EndMethod + + + #tag Note, Name = About + == IDECommunicator + Provides a way to send IDE Script commands to Xojo from an external + app. + + === Usage === + To use this project, build it and then run IDECommunicator from the command line. + + To get usage info: + + ./IDECommunicator -? + + Usage: + -i command: Send a one-line script command to the IDE to run + -v : Display version info + -c : Display communication path + -? or -h: Display usage information + file : Send the script to the IDE to run + + If neither a file or -i is used then the script is read from stdin and send when Control-D + is pressed. + + === Use In Your Projects === + You can use the IDECommunicator module in your own applications by calling the + SendScript method and passing in the text of the script you want the IDE + to run. + + + #tag EndNote + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Classes/IDEEmulator.xojo_code b/Classes/IDEEmulator.xojo_code new file mode 100644 index 0000000..65e27b1 --- /dev/null +++ b/Classes/IDEEmulator.xojo_code @@ -0,0 +1,356 @@ +#tag Class +Protected Class IDEEmulator + #tag Method, Flags = &h1 + Protected Sub Beep() + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function BuildApp(buildType As Integer, reveal As Boolean = False) As String + #pragma unused buildType + #pragma unused reveal + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ChangeDeclaration(name As String, parameters As String, returnType As String, scope As Integer, implement As String) + #pragma unused name + #pragma unused parameters + #pragma unused returnType + #pragma unused scope + #pragma unused implement + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ConstantValue(name As String) As String + #pragma unused name + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ConstantValue(name As String, Assigns value As String) + #pragma unused name + #pragma unused value + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function CurrentBuildAppName() As String + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function CurrentBuildLocation() As String + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function CurrentBuildTarget() As String + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function DecryptItem(password As String) As Boolean + #pragma unused password + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub DoCommand(cmd As String) + #pragma unused cmd + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function DoShellCommand(cmd As String) As String + #pragma unused cmd + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function DoShellCommand(cmd As String, timeOut As Integer, ByRef resultCode As Integer) As String + #pragma unused cmd + #pragma unused timeOut + #pragma unused resultCode + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function EncryptItem(password As String) As Boolean + #pragma unused password + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function EndOfLine() As String + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub NewConsoleProject() + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub NewGUIProject() + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub NewWebProject() + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub OpenFile(filePath As String) + #pragma unused filePath + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ProjectItem() As String + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ProjectShellPath() As String + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function PropertyValue(name As String) As String + #pragma unused name + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub PropertyValue(name As String, Assigns value As String) + #pragma unused name + #pragma unused value + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub QuitIDE(saveChanges As Boolean) + #pragma unused saveChanges + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub RunScript(name As String) + #pragma unused name + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function SelectProjectItem(itemPath As String) As Boolean + #pragma unused itemPath + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub SelectWindow(index As Integer) + #pragma unused index + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub SelectWindow(windowTitle As String) + #pragma unused windowTitle + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ShowDialog(message As String, explanation As String, defaultButtonCaption As String) As String + #pragma unused message + #pragma unused explanation + #pragma unused defaultButtonCaption + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ShowDialog(message As String, explanation As String, defaultButtonCaption As String, CancelButtonCaption As String) As String + #pragma unused message + #pragma unused explanation + #pragma unused defaultButtonCaption + #pragma unused CancelButtonCaption + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ShowDialog(message As String, explanation As String, defaultButtonCaption As String, CancelButtonCaption As String, altButtonCaption As String) As String + #pragma unused message + #pragma unused explanation + #pragma unused defaultButtonCaption + #pragma unused CancelButtonCaption + #pragma unused altButtonCaption + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ShowDialog(message As String, explanation As String, defaultButtonCaption As String, CancelButtonCaption As String, altButtonCaption As String, icon As Integer) As String + #pragma unused message + #pragma unused explanation + #pragma unused defaultButtonCaption + #pragma unused CancelButtonCaption + #pragma unused altButtonCaption + #pragma unused icon + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ShowURL(url As String) + #pragma unused url + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub Speak(s As String) + #pragma unused s + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function SubLocation(baseLocation As String) As String + #pragma unused baseLocation + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function TypeOfCurrentLocation() As String + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function WindowCount() As Integer + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub WindowTitle(index As Integer) + #pragma unused index + End Sub + #tag EndMethod + + + #tag Property, Flags = &h1 + Protected BuildCurrentPlatform As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildLanguage As String + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildLinux As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildMacCocoa As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildMacMachOx86 As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildRegion As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildWebDebugPort As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildWebPort As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildWebProtocol As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected BuildWin32 As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Clipboard As String + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Location As String + #tag EndProperty + + #tag Property, Flags = &h1 + Protected SelLength As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected SelStart As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected SelText As String + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Text As String + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Dialogs/DlgInput.xojo_window b/Dialogs/DlgInput.xojo_window new file mode 100644 index 0000000..4ee5831 --- /dev/null +++ b/Dialogs/DlgInput.xojo_window @@ -0,0 +1,412 @@ +#tag Window +Begin Window DlgInput + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = False + Compatibility = "" + Composite = False + Frame = 8 + FullScreen = False + FullScreenButton= False + HasBackColor = False + Height = 200 + ImplicitInstance= True + LiveResize = True + MacProcID = 0 + MaxHeight = 32000 + MaximizeButton = True + MaxWidth = 32000 + MenuBar = 0 + MenuBarVisible = True + MinHeight = 200 + MinimizeButton = True + MinWidth = 300 + Placement = 0 + Resizeable = True + Title = "Input" + Visible = True + Width = 300 + Begin Label lblPrompt + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 40 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = True + Scope = 2 + Selectable = False + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + Text = "Untitled" + TextAlign = 0 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 10 + Transparent = False + Underline = False + Visible = True + Width = 560 + End + Begin PushButton btnAccept + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "OK" + Default = True + Enabled = True + Height = 22 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 195 + LockBottom = True + LockedInPosition= False + LockLeft = False + LockRight = True + LockTop = False + Scope = 2 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 158 + Underline = False + Visible = True + Width = 85 + End + Begin TextArea fldInput + AcceptTabs = False + Alignment = 0 + AutoDeactivate = True + AutomaticallyCheckSpelling= True + BackColor = &cFFFFFF00 + Bold = False + Border = True + DataField = "" + DataSource = "" + Enabled = True + Format = "" + Height = 84 + HelpTag = "" + HideSelection = True + Index = -2147483648 + Italic = False + Left = 20 + LimitText = 0 + LineHeight = 0.0 + LineSpacing = 1.0 + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = True + Mask = "" + Multiline = False + ReadOnly = False + Scope = 0 + ScrollbarHorizontal= False + ScrollbarVertical= True + Styled = False + TabIndex = 3 + TabPanelIndex = 0 + TabStop = True + Text = "" + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 62 + Underline = False + UseFocusRing = True + Visible = True + Width = 260 + End +End +#tag EndWindow + +#tag WindowCode + #tag Method, Flags = &h21 + Private Sub Show() + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function ShowModal(prompt As String) As String + lblPrompt.Text = prompt.Trim + super.ShowModal + + return Result + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ShowModalWithin(parentWindow As Window, prompt As String) As String + lblPrompt.Text = prompt.Trim + super.ShowModalWithin( parentWindow ) + + return Result + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private Result As String + #tag EndProperty + + +#tag EndWindowCode + +#tag Events btnAccept + #tag Event + Sub Action() + Result = fldInput.Text + self.Close + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Example Scripts/Included.xojo_script b/Example Scripts/Included.xojo_script new file mode 100644 index 0000000..bcb7179 --- /dev/null +++ b/Example Scripts/Included.xojo_script @@ -0,0 +1 @@ +print "included script" \ No newline at end of file diff --git a/Example Scripts/With Included.xojo_script b/Example Scripts/With Included.xojo_script new file mode 100644 index 0000000..57a3faf --- /dev/null +++ b/Example Scripts/With Included.xojo_script @@ -0,0 +1 @@ +print "before included" '#include ~ktekinay/Desktop/../Desktop/Included.xojo_script '#include ../Desktop/Included.xojo_script '#include /Users/ktekinay/Documents/../Desktop/Bozo/../Included.xojo_script print "after included" \ No newline at end of file diff --git a/FileTypes/DocumentTypes.xojo_filetypeset b/FileTypes/DocumentTypes.xojo_filetypeset new file mode 100644 index 0000000..a1d89b5 --- /dev/null +++ b/FileTypes/DocumentTypes.xojo_filetypeset @@ -0,0 +1,28 @@ +#tag FileTypeSet + #tag FileType + CodeName=XojoScript + Extension=.xojo_script;.rbs + Flags=&h5 + MacCreator= + MacType= + Name=text/script + UTIConformsTo= + Description= + MimeType= + Imported=True + #tag EndFileType + + #tag FileType + CodeName=Text + Extension=.asc;.ascii;.bas;.c;.class;.cp;.cpp;.h;.hp;.hpp;.i3;.java;.m2;.m3;.mak;.text;.txt + Flags=&h2 + MacCreator=???? + MacType=TEXT + Name=text/plain + UTIConformsTo= + Description= + MimeType= + Imported=True + #tag EndFileType + +#tag EndFileTypeSet diff --git a/Helpers/FolderItemAlias.xojo_code b/Helpers/FolderItemAlias.xojo_code new file mode 100644 index 0000000..5abee10 --- /dev/null +++ b/Helpers/FolderItemAlias.xojo_code @@ -0,0 +1,113 @@ +#tag Class +Protected Class FolderItemAlias + #tag Method, Flags = &h0 + Sub Constructor(f As FolderItem) + zSaveInfo = f.GetSaveInfo( nil ) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Operator_Convert() As FolderItem + return me.Resolve + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Operator_Convert(f As FolderItem) + if f IsA FolderItem then me.Constructor( f ) + + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function OriginalExists() As Boolean + dim f as FolderItem = me.Resolve + if f is nil then + return false + else + return f.Exists + end if + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Resolve() As FolderItem + if zSaveInfo.LenB = 0 then + return nil + else + dim f as new FolderItem( zSaveInfo ) + return f + end if + + End Function + #tag EndMethod + + + #tag Note, Name = Legal + This class was created by Kem Tekinay, MacTechnologies Consulting (ktekinay@mactechnologies.com). + It is copyright ©2012, all rights reserved. + + You may use this class AS IS at your own risk for any purpose. The author does not warrant its use + for any particular purpose and disavows any responsibility for bad design, poor execution, + or any other faults. + + The author does not actively support this class, although comments and recommendations + are welcome. + + You may modify code in this class as long as those modifications are clearly indicated + via comments in the source code. + + You may distribute this class, modified or unmodified, as long as any modifications + are clearly indicated, as noted above, and this copyright notice is not disturbed or removed. + + If you do make useful modifications, please let me know so I can include them in + future versions. + #tag EndNote + + + #tag Property, Flags = &h21 + Private zSaveInfo As String + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Helpers/FormatCodePreferences.xojo_code b/Helpers/FormatCodePreferences.xojo_code new file mode 100644 index 0000000..9ff75f8 --- /dev/null +++ b/Helpers/FormatCodePreferences.xojo_code @@ -0,0 +1,86 @@ +#tag Module +Protected Module FormatCodePreferences + #tag Note, Name = About + + This module is optional. If present, the FormatCode IDE Script will use the constants found here instead + of the ones in the script. + + The module does not need all of these constants, but must have at lease one or the script will fail. + + Most of the constants are booleans. The KeywordsTo... provide exceptions to the default CaseConversion. + For example, if you want all of your keywords as title case except for if/then/else, you would + set CaseConversion = "kTitleCase" and KeywordsToLowerCase to "if,then,else". (Spaces around the + keywords will be stripped by the script). + + The KeywordsTo... constants are comma-delimited strings. + #tag EndNote + + + #tag Constant, Name = AlignAs, Type = Boolean, Dynamic = False, Default = \"False", Scope = Protected + #tag EndConstant + + #tag Constant, Name = CaseConversion, Type = String, Dynamic = False, Default = \"kLowerCase", Scope = Protected + #tag EndConstant + + #tag Constant, Name = DoDebug, Type = Boolean, Dynamic = False, Default = \"False", Scope = Protected + #tag EndConstant + + #tag Constant, Name = KeywordsToLowerCase, Type = String, Dynamic = False, Default = \"if\x2C then\x2C end\x2C while\x2C wend\x2C select\x2C case\n", Scope = Protected + #tag EndConstant + + #tag Constant, Name = KeywordsToTitleCase, Type = String, Dynamic = False, Default = \"Integer\x2C Byte\x2C Short\x2C Int8\x2C Int16\x2C Int32\x2C Int64\x2C UInt8\x2C UInt16\x2C UInt32\x2C UInt64\x2C Double\x2C Single\x2C String\x2C Text\x2C Variant\n\nDictionary\x2C Date\n\nTrue\x2C False\n\nClose\n\nRaiseEvent\n\nBackgroundTasks\nNilObjectChecking\nStackOverflowChecking\nBoundsChecking\nDebugBuild", Scope = Protected + #tag EndConstant + + #tag Constant, Name = KeywordsToUpperCase, Type = String, Dynamic = False, Default = \"", Scope = Protected + #tag EndConstant + + #tag Constant, Name = PadComma, Type = Boolean, Dynamic = False, Default = \"True", Scope = Protected + #tag EndConstant + + #tag Constant, Name = PadOperators, Type = Boolean, Dynamic = False, Default = \"True", Scope = Protected + #tag EndConstant + + #tag Constant, Name = PadParensInner, Type = Boolean, Dynamic = False, Default = \"True", Scope = Protected + #tag EndConstant + + #tag Constant, Name = PadParensOuter, Type = Boolean, Dynamic = False, Default = \"False", Scope = Protected + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Helpers/M_FolderItem.xojo_code b/Helpers/M_FolderItem.xojo_code new file mode 100644 index 0000000..0c2832d --- /dev/null +++ b/Helpers/M_FolderItem.xojo_code @@ -0,0 +1,1448 @@ +#tag Module +Protected Module M_FolderItem + #tag Method, Flags = &h1 + Protected Sub BinaryContents(f As FolderItem, Assigns contents As String) + f.BinaryContents_MTC = contents + call f.Owner + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function BinaryContents(f As FolderItem, enc As TextEncoding = nil) As String + return f.BinaryContents_MTC( enc ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub BinaryContents_MTC(Extends f As FolderItem, Assigns contents As String) + // Sets the contents of a file as binary + + dim out as BinaryStream = BinaryStream.Create( f, true ) + + if out IsA BinaryStream then + out.Write( contents ) + end if + + if out.WriteError then + dim e as new IOException + e.ErrorNumber = out.LastErrorCode + raise e + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function BinaryContents_MTC(Extends f As FolderItem, enc As TextEncoding = nil) As String + // Returns the contents of a file as binary + + dim r() as string + dim bs as BinaryStream = BinaryStream.Open( f ) + if bs isA BinaryStream then + bs.Position = 0 + while not bs.EOF + r.Append bs.Read( 32000, enc ) + wend + bs = nil + end if + + return join( r, "" ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Clear_MTC(Extends f As FolderItem) + dim b as boolean = f.Clear_MTC + + #pragma unused b + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Clear_MTC(Extends f As FolderItem) As Boolean + dim r as boolean = true + + if f.Exists then + r = false // Assume we can't do it for a moment + dim bs as BinaryStream = BinaryStream.Open( f, true ) + if bs IsA BinaryStream and bs.LastErrorCode = 0 then + bs.Length = 0 + if bs.LastErrorCode = 0 then r = true + end if + end if + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function ContentsMatch(f1 As FolderItem, f2 As FolderItem) As Boolean + return f1.ContentsMatch_MTC( f2 ) // Will raise nilObjectException if either is nil + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ContentsMatch_MTC(Extends f1 As FolderItem, f2 As FolderItem) As Boolean + if f2 is nil then + raise new NilObjectException + + elseif f1.SameFolderItemAs_MTC( f2 ) then + return true + + elseif not f1.Exists or not f2.Exists then + return false + + elseif f1.Directory or f2.Directory then + return false + + elseif f1.Length <> f2.Length then + return false + + else + + const kChuckSize = 100000 + + dim b1, b2 as BinaryStream + b1 = BinaryStream.Open( f1 ) + b2 = BinaryStream.Open( f2 ) + + dim chunk1, chunk2 as string + while not b1.EOF + chunk1 = b1.Read( kChuckSize ) + chunk2 = b2.Read( kChuckSize ) + if StrComp( chunk1, chunk2, 0 ) <> 0 then + return false + end if + wend + + // If we get here... + return true + + // I tried this too, but much slower and not good for large files. + + 'dim h1 as new MD5Digest + 'dim h2 as new MD5Digest + 'dim b as BinaryStream + 'b = BinaryStream.Open( f1 ) + 'h1.Process b.Read( b.Length ) + 'b.Close + 'b = BinaryStream.Open( f2 ) + 'h2.Process b.Read( b.Length ) + 'b.Close + ' + 'return StrComp( h1.Value, h2.Value, 0 ) = 0 + + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub CopyTo_MTC(Extends source As FolderItem, destination As FolderItem, overwrite As Boolean = False) + // Works similarly to FolderItem.CopyFileTo, but allows you to force an overwrite. + + dim doStraightCopy as boolean = true + + if overwrite and source.Exists and destination.Directory and destination.Exists and _ + not SameFolderItem( source.Parent, destination ) then + + dim srcName as string = source.Name + dim fileInDest as FolderItem = destination.Child( srcName ) + if fileInDest <> nil and fileInDest.Exists then // We have to overwrite the file + + // Set up a temporary folder to copy the file to + dim tempFolderName as string = destination.UniqueNameInFolder_MTC( "TempCopy" ) + dim tempFolder as FolderItem = destination.GetFolder_MTC( tempFolderName ) + + if tempFolder <> nil and tempFolder.Directory then + source.CopyFileTo( tempFolder ) + if source.LastErrorCode = 0 then + dim tempSource as FolderItem = tempFolder.Child( srcName ) + if tempSource <> nil and tempSource.Exists then + tempSource.MoveTo_MTC( destination, overwrite ) + if tempSource.LastErrorCode = 0 and destination.Child( srcName ).Exists then + doStraightCopy = false + + // Delete the temp folder since there was no error + dim err as integer = tempFolder.DeleteRecursive_MTC() + #pragma unused err + end if // tempSource.LastErrorCode = 0 and ... + + end if // tempSource <> nil and ... + end if // source.LastErrorCode = 0 + end if // tempFolder <> nil and ... + + end if // fileInDest <> nil and ... + + end if // overwrite and source.Exists and ... + + // Either this is just a straight copy, or there was an error and source needs to reflect it + if doStraightCopy then + source.CopyFileTo( destination ) + end if // doStraightCopy + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function DeleteContentsOfFolder_MTC(Extends baseFolder As FolderItem) As Integer + // Recursively deletes the contents of a folder, but keeps the folder in place. + // If there is a problem anywhere along the way, returns an error code. + + dim errorCode as integer = 0 + + if not baseFolder.Exists or not baseFolder.Directory then + return errorCode + end if + + dim queue() as FolderItem = baseFolder.Items_MTC + while queue.Ubound <> -1 + + dim thisItem as FolderItem = queue.Pop + if thisItem.Directory then + dim subItems() as FolderItem = thisItem.Items_MTC + if subItems.Ubound <> -1 then + queue.Append thisItem + thisItem = nil + for each f as FolderItem in subItems + queue.Append f + next + end if + end if + + if thisItem <> nil and thisItem.Exists then + thisItem.Delete + errorCode = thisItem.LastErrorCode + if errorCode <> 0 then + exit while + end if + end if + + wend + + return errorCode + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function DeleteRecursive_MTC(Extends f As FolderItem) As Integer + // Deletes the contents of a folder, then the folder itself. + // If there is an error along the way, returns the code + + dim errorCode as integer = 0 + if not f.Exists then return errorCode + + errorCode = f.DeleteContentsOfFolder_MTC() + if errorCode = 0 then + f.Delete + errorCode = f.LastErrorCode + end if + + return errorCode + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function EndOfLineOf(f As FolderItem, defaultEOL As String = "", enc As TextEncoding = nil) As String + // Figures out the EOL in a file. + // If it can't find one, it returns the defaultEOL (nothing by default). + + // Assumes f is not nil, or throw an exception if it is. + + if enc is nil then enc = Encodings.UTF8 // Won't allow a nil encoding + + dim eol as string + dim tis as TextInputStream = TextInputStream.Open( f ) + if tis isA TextInputStream then + + dim l as string = tis.ReadLine // This will go past the EOL ... + dim backup as UInt64 = 3 + dim curPos as UInt64 = tis.PositionB + if backup > curPos then backup = curPos + tis.PositionB = curPos - backup // So back it up a bit + l = tis.Read( backup ) + l = l.ConvertEncoding( Encodings.UTF8 ) + + tis = nil // Done with this + + if l.LenB > 0 then + if l.RightB( 2 ) = EndOfLine.Windows then + eol = EndOfLine.Windows + else + dim char as string = l.RightB( 1 ) + dim charVal as integer = char.Asc + if charVal = 10 then + eol = EndOfLine.UNIX + elseif charVal = 13 then + eol = EndOfLine.Macintosh + end if + end if + end if // l.LenB > 0 + + end if // tis isA TextInputStream + + if eol.LenB = 0 then + eol = defaultEOL + else + eol = eol.ConvertEncoding( enc ) + end if + + return eol + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ExpandShellPath_MTC(path As String) As String + #if TargetLinux + + dim sh as new Shell + sh.Execute "readlink -f " + path + path = sh.Result + path = path.ConvertEncoding( Encodings.UTF8 ) + path = ReplaceLineEndings( path, "" ) + + path = ShellPathFromPOSIXPath_MTC( path ) // Get it back to a shell path + + #elseif TargetMacOS + + dim sh as new Shell + sh.Execute "echo " + path + path = sh.Result + path = path.ConvertEncoding( Encodings.UTF8 ) + path = ReplaceLineEndings( path, "" ) + + path = ShellPathFromPOSIXPath_MTC( path ) // Get it back to a shell path + + #endif + + return path + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Extension_MTC(Extends f As FolderItem) As String + dim ext as string + dim nme as string = f.Name + + dim parts() as string = nme.Split( "." ) + dim i as integer = parts.Ubound + + if i < 1 then + // No dot + elseif i = 1 and nme.Left( 1 ) = "." then + // Not a true extension + else + ext = parts( i ) + if ext.InStr( " " ) <> 0 then ext = "" // An extension can't have a space + end if + + return ext + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function GetFolderItemFromPOSIXPath_MTC(path As String) As FolderItem + path = ShellPathFromPOSIXPath_MTC( path ) + return GetFolderItemFromShellPath_MTC( path ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function GetFolderItemFromShellPath_MTC(path As String) As FolderItem + // Attempts to make a FolderItem from a path + + if path.LenB = 0 then return nil + + dim f as FolderItem + dim wd as FolderItem = SpecialFolder.CurrentWorkingDirectory + + try + f = GetFolderItem( path, FolderItem.PathTypeShell ) + catch err + f = nil + end try + + if f is nil then + #if not TargetWin32 + + try + f = GetFolderItem( ExpandShellPath_MTC( path ), FolderItem.PathTypeShell ) + catch + f = nil + end + + 'if ( f is nil or not f.Exists ) and path.Left( 2 ) = "~/" then + 'dim home as string = SpecialFolder.UserHome.ShellPath + 'if home.Right( 1 ) <> "/" then home = home + "/" + 'dim filePath as string = home + path.Mid( 3 ) + 'try + 'f = GetFolderItem( filePath, FolderItem.PathTypeShell ) + 'catch err + 'f = nil + 'end try + 'end if + + #endif + + end if + + if f is nil then + try + f = GetFolderItem( wd.ShellPath + path, FolderItem.PathTypeShell ) + catch err + f = nil + end try + end if + + return f + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function GetFolder_MTC(Extends parentFolder As FolderItem, name As String) As FolderItem + // Ensures that the given folder name exists in the given parent folder. + // If not, it creates it. + // Returns the resulting folder. + + // Will allow as call like: + // f = SpecialFolder.Documents.GetFolder_MTC( "folder1" ).GetFolder_MTC( "folder2" ) + + dim f as FolderItem = parentFolder.Child( name ) + if not f.Exists then + f.CreateAsFolder + end if + + return f + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function GetRelativeFolderItem_MTC(nativePath As String, relativeTo As FolderItem = Nil) As FolderItem + // Stolen from Jeremy Cowgar, which is why the paren spacing is wrong + + dim prefix as String = "" + + #if targetwin32 then + const pathSep = "\" + + // + // Maybe what is passed isn't actually a relative nativePath + // + + if nativePath.Mid( 2, 1 ) = ":" then + return GetFolderItem( nativePath, FolderItem.PathTypeNative ) + end if + + if nativePath.Left( 1 ) = pathSep then + relativeTo = GetFolderItem( SpecialFolder.CurrentWorkingDirectory.NativePath.Left( 3 ) ) + end if + + #else + const pathSep = "/" + + // + // Maybe what is passed isn't actually a relative nativePath + // + + if nativePath.Left( pathSep.Len ) = pathSep then + relativeTo = Volume( 0 ) + nativePath = nativePath.Mid( pathSep.Len + 1 ) + + // + // See if it's a home path + // + elseif nativePath.Left( pathSep.Len + 1 ) = ( "~" + pathSep ) or nativePath = "~" then + relativeTo = SpecialFolder.UserHome + nativePath = nativePath.Mid( pathSep.Len + 1 ) + + // + // See if it's another user's home folder + // + elseif nativePath.Left( 1 ) = "~" then + dim homePath as string = nativePath.NthField( pathSep, 1 ) + dim homePathShell as string = ShellPathFromPOSIXPath_MTC( homePath ) + + dim sh as new Shell + sh.Execute "cd " + homePathShell + " && pwd" + dim thisPath as string = sh.Result.DefineEncoding( Encodings.UTF8 ).Trim + if thisPath.Right( 1 ) = pathSep then + thisPath = thisPath.Left( thisPath.Len - pathSep.Len ) + end if + + relativeTo = GetFolderItem( thisPath, FolderItem.PathTypeNative ) + nativePath = nativePath.Mid( homePath.Len + pathSep.Len + 1 ) + + end if + + prefix = pathSep + #endif + + // + // OK, seems to be a relative nativePath + // + + if relativeTo = nil then + relativeTo = SpecialFolder.CurrentWorkingDirectory + end if + + nativePath = relativeTo.NativePath + pathSep + nativePath + + // + // We have to parse all the parts individually because the path may + // not exist + // + + dim newParts() as String + + dim pathParts() as String = nativePath.Split( pathSep ) + for i as Integer = 0 to pathParts.Ubound + dim p as String = pathParts( i ) + if p = "" then + // Can happen on Windows since it appends a pathSep onto the end of NativePath + // if relativeTo is a folder. + + elseif p = "." then + // Skip this nativePath component + + elseif p = ".." then + // Remove the last nativePath component from newParts + if newParts.Ubound <> -1 then + newParts.Remove newParts.Ubound + end if + + else + // Nothing special about this nativePath component + newParts.Append p + end if + next + + nativePath = prefix + Join( newParts, pathSep ) + + return GetFolderItem( nativePath, FolderItem.PathTypeNative ) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function Head(f As FolderItem, numOfParagraphs As Integer, eol As String = "", enc As TextEncoding = nil) As String + return f.Head_MTC( numOfParagraphs, eol, enc ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Head_MTC(Extends f As FolderItem, numOfParagraphs As Integer, eol As String = "", enc As TextEncoding = nil) As String + // Returns paragraphs off of the top of a file. + + static utf8 as TextEncoding = Encodings.UTF8 + if enc is nil then enc = utf8 + + if eol.LenB = 0 then + eol = EndOfLineOf( f, "", enc ) + if eol.LenB = 0 then return f.TextContents_MTC( enc ) // No EOL so return the whole contents of the file + end if + eol = eol.ConvertEncoding( utf8 ) + eol = eol.DefineEncoding( nil ) + + dim r as string + + // Not using TextInputStream.ReadLine because we want to grab the last EOL, if any. + + dim filled as boolean + dim blockSize as integer = numOfParagraphs * kBytesPerParagraph + dim bs as BinaryStream = BinaryStream.Open( f ) + if bs isA BinaryStream then + bs.Position = 0 + while not bs.EOF + dim block as string = bs.Read( blockSize ) + r = r + block + if r.CountFieldsB( eol ) > numOfParagraphs then + filled = true + exit + end if + wend + + bs = nil + end if + + if filled then + #if kMStringAvailable + r = r.Head_MTC( numOfParagraphs, eol ) + #else + dim pars() as string = r.SplitB( eol ) + dim lastParIndex as integer = numOfParagraphs - 1 + if pars.Ubound > lastParIndex then + redim pars( lastParIndex ) + pars.Append "" // Add back the EOL + end if + r = join( pars, eol ) + #endif + end if + + return r.DefineEncoding( enc ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function IsEmptyFile(f As FolderItem) As Boolean + return f.IsEmptyFile_MTC() + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsEmptyFile_MTC(Extends f As FolderItem) As Boolean + // Files that don't exist are considered empty + + dim r as boolean = true // Assume it is empty + if f.Exists then + if f.Directory then + r = false // Folders are not empty files + else + r = ( f.Length = 0 ) + end if + end if + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Items_MTC(Extends f As FolderItem, includeInvisibles As Boolean = True) As FolderItem() + dim r() as FolderItem + if f <> nil and f.Exists and f.Directory then + + dim thisItem as FolderItem + dim cnt as integer = f.Count + + // Do this as two separate loops to save some cycles + if includeInvisibles then + + redim r( cnt - 1 ) + for i as integer = 1 to cnt + thisItem = f.TrueItem( i ) + r( i - 1 ) = thisItem + next i + + else // Have to check for visibility + + for i as integer = 1 to cnt + thisItem = f.TrueItem( i ) + if thisItem.Visible then + r.Append thisItem + end if // isVisible + next i + + end if // includeInvisibles + + end if // f <> nil and f.Exists and f.Directory + + return r + End Function + #tag EndMethod + + #tag Method, Flags = &h1, CompatibilityFlags = TargetHasGUI + Protected Function MacBundleContentsFolder() As FolderItem + #if TargetDesktop + + return App.MacBundleContentsFolder_MTC + + #endif + End Function + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Function MacBundleContentsFolder_MTC(Extends theApp As Application) As FolderItem + #if TargetMacOS and TargetDesktop + + dim f as FolderItem = theApp.ExecutableFile.Parent.Parent + return f + + #else + + #pragma unused theApp + return nil + + #endif + + End Function + #tag EndMethod + + #tag Method, Flags = &h1, CompatibilityFlags = TargetHasGUI + Protected Function MacBundleFolder() As FolderItem + return App.MacBundleFolder_MTC + End Function + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Function MacBundleFolder_MTC(Extends theApp As Application) As FolderItem + #if TargetMacOS + + dim f as FolderItem = theApp.MacBundleContentsFolder_MTC.Parent + return f + + #else + + #pragma unused theApp + return nil + + #endif + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function MoveToTrash_MTC(Extends f As FolderItem) As Boolean + dim r as boolean = true + if f is nil or not f.Exists then return r // Sure, it doesn't exist, so it's trashed + + #if TargetMacOS + dim source as MemoryBlock = f.MacFSRef + dim dest as new MemoryBlock( 80 ) + + Declare Function FSMoveObjectToTrashSync lib kCarbonLib ( fsRef as Ptr, target as Ptr, options as UInt32 ) As Integer + + dim OSError as Integer = FSMoveObjectToTrashSync( source, dest, 0 ) + + if OSError <> 0 then + dim err as new RuntimeException + err.ErrorNumber = OSError + err.Message = "A MacOS error occurred while moving this item to the trash." + raise err + end if + #endif + + if f.Exists then + f.Delete() + end if + if f.Exists then + f.Delete() + end if + if f.Exists then + f.MoveFileTo( SpecialFolder.Trash ) + end if + if f.Exists then + f.MoveFileTo( SpecialFolder.Trash( f.Volume_MTC ) ) + end if + + if f.Exists then + r = false + end if + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub MoveTo_MTC(Extends source As FolderItem, destination As FolderItem, overwrite As Boolean = False) + // Works similarly to FolderItem.MoveFileTo, but allows you to force an overwrite. + + dim doStraightMove as boolean = true + + if overwrite and source.Exists and destination.Directory and destination.Exists and _ + not SameFolderItem( source.Parent, destination ) then + + dim srcName as string = source.Name + dim fileInDest as FolderItem = destination.Child( srcName ) + if fileInDest <> nil and fileInDest.Exists then + + // Rename the existing file + dim newName as string = destination.UniqueNameInFolder_MTC( srcName, "(old)" ) + fileInDest.Name = newName + fileInDest = destination.Child( newName ) + if fileInDest <> nil and fileInDest.Exists then + source.MoveFileTo( destination ) + if source.LastErrorCode = 0 and destination.Child( srcName ).Exists then + doStraightMove = false + + // Delete the original + dim err as integer = fileInDest.DeleteRecursive_MTC + #pragma unused err + + end if // source.LastErrorCode = 0 + + end if // fileInDest <> nil and ... + end if + + end if // not source.Exists or ... + + // Either there was an error or needed a straight move anyway + if doStraightMove then + source.MoveFileTo( destination ) + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function NameWithoutExtension_MTC(Extends f As FolderItem) As String + // Returns the portion of the name without the extension + + dim r as string = f.Name + dim ext as string = f.Extension_MTC + dim extLen as integer = ext.Len + if extLen <> 0 then + extLen = extLen + 1 // Include the dot + r = r.Left( r.Len - extLen ) + end if + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function NewTemporarySubFolder(baseName As String = "") As FolderItem + // Creates a new temporary subfolder using the given baseName, or the app name if not given. + + dim proposed as string = baseName.Trim + static myRandom as new Random + + if proposed = "" then proposed = App.ExecutableFile.NameWithoutExtension_MTC + proposed = proposed + "-" + format( microseconds, "#" ) + "-" + str( myRandom.InRange( 1, 999999 ) ) + + proposed = SpecialFolder.Temporary.UniqueNameInFolder_MTC( proposed ) + dim f as FolderItem = SpecialFolder.Temporary.Child( proposed ) + if f <> nil and not f.Exists then + f.CreateAsFolder + end if + + if f is nil or not f.Exists or not f.Directory then + f = nil + end if + + return f + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function pCurrentSystemVersion() As String + static r as string + + #if TargetMacOS + + if r = "" then + dim sh as new shell + sh.Execute "/usr/bin/sw_vers | grep 'ProductVersion'" + if sh.ErrorCode = 0 then + r = sh.Result.Trim + + dim rx as new RegEx + rx.SearchPattern = "\d.+$" + + dim match as RegExMatch = rx.Search( r ) + if match <> nil then + r = match.SubExpressionString( 0 ).Trim + r = r.DefineEncoding( Encodings.UTF8 ) + end if + end if + end if + + #endif + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function pFormatVersion(major As Integer, minor As Integer, bug As Integer) As String + const kFormatter = "0000" + + dim r as string = format( major, kFormatter ) + format( minor, kFormatter ) + format( bug, kFormatter ) + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function POSIXPathFromShellPath_MTC(path As String) As String + dim f as FolderItem = GetFolderItemFromShellPath_MTC( path ) + return f.POSIXPath_MTC + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function POSIXPath_MTC(Extends f As FolderItem) As String + #if TargetMacOS or TargetLinux + + static slashChar as integer = Asc( "\" ) + + dim path as string = f.ShellPath + dim chars() as string = path.Split( "" ) + dim newChars() as string + dim i as integer = 0 + do until i > chars.Ubound + dim thisChar as string = chars( i ) + dim nextChar as string + if i < chars.Ubound then nextChar = chars( i + 1 ) + if thisChar.Asc = slashChar then + newChars.Append nextChar + i = i + 2 // Skip next char + else + newChars.Append thisChar + i = i + 1 + end if + loop + + dim r as string = join( newChars, "" ) + return r + + #else + + return f.ShellPath + + #endif + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function pResolveAlias108(f As FolderItem) As FolderItem + dim r as FolderItem + + #if TargetMacOS + + dim releaseStack() as Ptr + + Soft Declare Function CFURLCreateWithString Lib kCarbonLib ( _ + allocator As Ptr, _ + URLString As CFStringRef, _ + baseURL As Ptr _ + ) As Ptr + Soft Declare Function CFURLCreateBookmarkDataFromFile Lib kCarbonLib ( _ + allocator As Ptr, _ + fileURL As Ptr, _ + ByRef errorRef As Ptr _ + ) As Ptr + Soft Declare Function CFURLCreateByResolvingBookmarkData Lib kCarbonLib ( _ + allocator As Ptr, _ + bookmark As Ptr, _ + options As UInt32, _ + relativeToURL As Ptr, _ + resourcePropertiesToInclude As Ptr, _ + ByRef isStale As Boolean, _ + ByRef errorRef As Ptr _ + ) As Ptr + Soft Declare Function CFURLGetString Lib kCarbonLib ( url As Ptr ) As CFStringRef + 'Soft Declare Function CFErrorGetCode Lib kCarbonLib ( err As Ptr ) As Int32 + Declare Sub CFRelease Lib kCarbonLib ( cf As Ptr ) + + dim isStale as Boolean + dim url, bookmarkData, newURL as Ptr + dim errorRef as Ptr + try + url = CFURLCreateWithString( nil, f.URLPath, nil ) + releaseStack.Append url + catch + url = nil + end + if url <> nil then + try + bookmarkData = CFURLCreateBookmarkDataFromFile( nil, url, errorRef ) + releaseStack.Append bookmarkData + releaseStack.Append errorRef + catch + bookmarkData = nil + end + end if + + if bookmarkData <> nil then + try + newURL = CFURLCreateByResolvingBookmarkData( _ + nil, bookmarkData, 0, nil, nil, isStale, errorRef _ + ) + releaseStack.Append newURL + releaseStack.Append errorRef + catch + newURL = nil + end + end if + + if newURL <> nil then + dim urlString as string = CFURLGetString( newURL ) + if urlString.LenB <> 0 then + r = new FolderItem( urlString, FolderItem.PathTypeURL ) + end if + end if + + Exception err As RuntimeException // Really shouldn't happen + r = nil + + Finally + + for each thisPtr as Ptr in releaseStack + if thisPtr <> nil then + CFRelease( thisPtr ) + end if + next + + #else + #pragma unused f + #endif + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ResolveAlias_MTC(Extends f As FolderItem) As FolderItem + if f <> nil and f.Alias then + + #if not TargetMacOS // Shouldn't ever happen + + f = f.Parent.Child( f.Name ) + + #else + + dim vers as string = pCurrentSystemVersion + if vers < pFormatVersion( 10, 8, 0 ) or not System.IsFunctionAvailable( "CFURLCreateBookmarkDataFromFile", kCarbonLib ) then + + Declare Function FSResolveAliasFileWithMountFlags Lib kCarbonLib ( _ + ref As Ptr, _ + resolveAliasChains As Boolean, _ + ByRef targetIsFolder As Boolean, _ + ByRef wasAliased As Boolean, _ + mountFlag As UInt32 _ + ) As Integer + + dim ref As MemoryBlock = f.MacFSRef + dim resolveAliasChains As Boolean = true + dim targetIsFolder, wasAliased As Boolean + dim mountFlag as UInt32 = 1 + dim err as integer + + err = FSResolveAliasFileWithMountFlags( ref, resolveAliasChains, targetIsFolder, wasAliased, mountFlag ) + + if err <> 0 then + f = nil + else + f = f.CreateFromMacFSRef( ref ) + end if + + else // It's 10.8 or later + + f = pResolveAlias108( f ) + + end if // It's 10.8 or greater + + #endif + + end if + + return f + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function SameFolderItem(f1 As FolderItem, f2 As FolderItem) As Boolean + dim r as boolean + + select case true + + case ( f1 is f2 ) + r = true + + case ( f1 is nil or f2 is nil ) + r = false + + case ( f1.Exists xor f2.Exists ) + r = false + + case ( f1.Directory xor f2.Directory ) + r = false + + else + dim a1, a2 as string + + a1 = f1.GetSaveInfo( nil ) + a2 = f2.GetSaveInfo( nil ) + + if StrComp( a1, a2, 0 ) = 0 then r = true + + end select + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function SameFolderItemAs_MTC(Extends f1 As FolderItem, f2 As FolderItem) As Boolean + return SameFolderItem( f1, f2 ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function Scan(start As UInt64 = 0, f As FolderItem, findBytes As String, ByRef pos As UInt64) As Boolean + return f.Scan_MTC( start, findBytes, pos ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Scan_MTC(Extends f As FolderItem, start As UInt64 = 0, findBytes As String, ByRef pos As UInt64) As Boolean + // Scans a files for the findBytes. + // Returns true if it was found, false if not or if there is an error. + + const kTargetReadLen = 262144 // 256 K -- can get up to twice this + + dim findLen as integer = findBytes.LenB + if findLen = 0 or findLen > f.Length then return false + dim findLenGreaterThan1 as boolean = ( findLen > 1 ) + + dim r as boolean + + dim enc as TextEncoding = findBytes.Encoding + dim bs as BinaryStream = BinaryStream.Open( f ) + if bs isA BinaryStream then + dim readLen as integer = kTargetReadLen + + bs.Position = start + dim block as string + while not bs.EOF + dim offset as integer = bs.Position + if block <> "" and findLenGreaterThan1 then + block = block.RightB( findLen - 1 ) + offset = offset - block.LenB + block = block + bs.Read( readLen, enc ) + else + block = bs.Read( readLen, enc ) + end if + dim findPos as UInt64 = block.InStrB( findBytes ) + if findPos > 0 then + pos = findPos + offset - 1 + r = true + exit + end if + wend + + bs = nil + end if + + return r + + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ShellPathFromPOSIXPath_MTC(path As String) As String + path = path.ReplaceAll( "\" , "\\" ) + path = path.ReplaceAll( " ", "\ " ) + path = path.ReplaceAll( ";", "\;" ) + path = path.ReplaceAll( """", "\""" ) + path = path.ReplaceAll( "'", "\'" ) + + return path + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function Tail(f As FolderItem, numOfParagraphs As Integer, eol As String = "", enc As TextEncoding = nil) As String + return f.Tail_MTC( numOfParagraphs, eol, enc ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Tail_MTC(Extends f As FolderItem, numOfParagraphs As Integer, eol As String = "", enc As TextEncoding = nil) As String + // Returns the last n paragraphs of a file. + // Has to read backwards to prevent loading the whole file. + + static utf8 as TextEncoding = Encodings.UTF8 + if enc is nil then enc = utf8 // We won't return a nil encoding + + if eol.LenB = 0 then + eol = EndOfLineOf( f, "", enc ) + if eol.LenB = 0 then return f.TextContents_MTC( enc ) // No EOL so return the whole contents of the file + end if + eol = eol.ConvertEncoding( utf8 ) + eol = eol.RightB( 1 ) // We are only interested in the last EOL character + eol = eol.DefineEncoding( nil ) + + dim trueNumOfParagraphs as integer = numOfParagraphs + + dim r as string + dim filled as boolean + dim bs as BinaryStream = BinaryStream.Open( f ) + if bs isA BinaryStream then + dim blockSize as UInt64 = kBytesPerParagraph * numOfParagraphs + dim readPos as UInt64 = bs.Length + + if readPos <> 0 then + + // Check for a trailing EOL + bs.Position = readPos - 1 + dim lastByte as string = bs.Read( 1 ) + if lastByte = eol then numOfParagraphs = numOfParagraphs + 1 + + while readPos <> 0 + if readPos < blockSize then + blockSize = readPos + readPos = 0 + else + readPos = readPos - blockSize + end if + + if blockSize < 1 then exit // Should never happen + + bs.Position = readPos + dim block as string = bs.Read( blockSize ) + r = block + r + if CountFieldsB( r, eol ) > numOfParagraphs then + filled = true + exit // We have enough + end if + wend + + end if // readPos <> 0 + + bs = nil + end if + + if filled then // We have more than enough + + #if kMStringAvailable // If M_String is available + r = r.Tail_MTC( trueNumOfParagraphs, eol ) + #else + #pragma unused trueNumOfParagraphs + + dim pars() as string = r.SplitB( eol ) + dim lastPar as integer = pars.Ubound + dim firstPar as integer = lastPar - numOfParagraphs + 1 + dim newPars() as string + redim newPars( numOfParagraphs - 1 ) + dim newParIndex as integer = -1 + for i as integer = firstPar to lastPar + newParIndex = newParIndex + 1 + newPars( newParIndex ) = pars( i ) + next i + r = join( newPars, eol ) + #endif + + end if + + return r.DefineEncoding( enc ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Function TemporaryAppFolder_MTC(Extends myApp As Application) As FolderItem + // Creates a temporary folder for the application + + #pragma unused myApp + + dim temp as FolderItem + dim name as string = App.ExecutableFile.NameWithoutExtension_MTC.Trim + if name = "" then + + return nil + + else + + temp = SpecialFolder.Temporary.GetFolder_MTC( name ) + + if temp is nil or not temp.Directory or not temp.Exists then + temp = nil + end if + + end if // name = "" + + return temp + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub TextContents(f As FolderItem, Assigns contents As String) + f.TextContents_MTC = contents + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function TextContents(f As FolderItem, enc As TextEncoding = nil) As String + return f.TextContents_MTC( enc ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub TextContents_MTC(Extends f As FolderItem, Assigns contents As String) + // Sets the contents of the file as text + + dim out as TextOutputStream = TextOutputStream.Create( f ) + + if out isA TextOutputStream then + out.Write contents + end if + + if out.WriteError then + dim e as new IOException + e.ErrorNumber = out.LastErrorCode + raise e + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function TextContents_MTC(Extends f As FolderItem, enc As TextEncoding = nil) As String + // Returns the contents of the file as text + + dim r as string + dim t as TextInputStream = TextInputStream.Open( f ) + if t isA TextInputStream then + r = t.ReadAll( enc ) + t = nil + end if + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function TrueItems_MTC(Extends f As FolderItem) As FolderItem() + dim r() as FolderItem + if f <> nil and f.Exists and f.Directory then + + dim cnt as integer = f.Count + redim r( cnt - 1 ) + for i as integer = 1 to cnt + r( i - 1 ) = f.TrueItem( i ) + next + + end if + + return r + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function UniqueNameInFolder_MTC(Extends source As FolderItem, itemName As String, suffix As string = "copy") As String + // Returns a unique name within the given folder + // Appends the suffix if needed, then the suffix + " " + index + + suffix = suffix.Trim + if suffix = "" then + suffix = " copy" + else + suffix = " " + suffix + end if + + dim uniqueName as string = itemName + dim child as FolderItem = source.Child( uniqueName ) + if child.Exists then + dim ext as string = child.Extension_MTC + if ext <> "" then ext = "." + ext + + itemName = child.NameWithoutExtension_MTC + uniqueName = itemName + suffix + ext + + dim index as integer = 0 + + while source.Child( uniqueName ).Exists + index = index + 1 + uniqueName = itemName + suffix + " " + str( index ) + ext + wend + + end if + + return uniqueName + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Volume_MTC(Extends f As FolderItem) As FolderItem + // Returns the Volume of the FolderItem + + if f is nil then return nil + + dim r as FolderItem = f + dim p as FolderItem = r.Parent + while p <> nil + r = p + p = r.Parent + wend + + return r + + End Function + #tag EndMethod + + + #tag Constant, Name = kBytesPerParagraph, Type = Double, Dynamic = False, Default = \"512", Scope = Private + #tag EndConstant + + #tag Constant, Name = kCarbonLib, Type = String, Dynamic = False, Default = \"Carbon", Scope = Protected + #tag EndConstant + + #tag Constant, Name = kMStringAvailable, Type = Boolean, Dynamic = False, Default = \"False", Scope = Private + #tag EndConstant + + #tag Constant, Name = kUseMBSPlugins, Type = Boolean, Dynamic = False, Default = \"False", Scope = Private + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Helpers/M_XojoScript.xojo_code b/Helpers/M_XojoScript.xojo_code new file mode 100644 index 0000000..4fce0be --- /dev/null +++ b/Helpers/M_XojoScript.xojo_code @@ -0,0 +1,758 @@ +#tag Module +Protected Module M_XojoScript + #tag Method, Flags = &h1 + Protected Function ErrorToString(error As XojoScript.Errors, errorInfo As Dictionary) As String + dim r as string = "Unknown error." + + select case integer( error ) + case 1 + r = "Syntax does not make sense." + + case 2 + r = "Type mismatch." + + case 3 + r = "Select Case does not support that type of expression." + + case 4 + r = "Obsolete. The compiler no longer generates this error." + + case 5 + r = "The parser's internal stack has overflowed." + + case 6 + r = "Too many parameters for this function." + + case 7 + r = "Not enough parameters for this function call." + + case 8 + r = "Wrong number of parameters for this function call." + + case 9 + r = "Parameters are incompatible with this function." + + case 10 + r = "Assignment of an incompatible data type." + + case 11 + r = "Undefined identifier." + + case 12 + r = "Undefined operator." + + case 13 + r = "Logic operations require Boolean operands." + + case 14 + r = "Array bounds must be integers." + + case 15 + r = "Can't call a non-function." + + case 16 + r = "Can't get an element from something that isn't an array." + + case 17 + r = "Not enough subscripts for this array's dimensions." + + case 18 + r = "Too many subscripts for this array's dimensions." + + case 19 + r = "Can't assign an entire array." + + case 20 + r = "Can't use an entire array in an expression." + + case 21 + r = "Can't pass an expression as a ByRef parameter. " + + case 22 + r = "Duplicate identifier." + + case 23 + r = "The backend code generator failed." + + case 24 + r = "Ambiguous call to overloaded method." + + case 25 + r = "Multiple inheritance is not allowed." + + case 26 + r = "Cannot create an instance of an interface." + + case 27 + r = "Cannot implement a class as though it were an interface." + + case 28 + r = "Cannot inherit from something that is not a class." + + case 29 + r = "This class does not fully implement the specified interface." + + case 30 + r = "Event handlers cannot live outside of a class." + + case 31 + r = "It is not legal to ignore the result of a function call." + + case 32 + r = "Can't use ""Self"" keyword outside of a class." + + case 33 + r = "Can't use ""Me"" keyword outside of a class." + + case 34 + r = "Can't return a value from a Sub." + + case 35 + r = "An exception object required here." + + case 36 + r = "The function declares a different return type than the interface function it is implementing." + + case 37 + r = "The function declares a different return type than the function in its superclass that it's overriding." + + case 38 + r = "The method uses the ""Handles"" clause to specify that it is an event handler, but the method cannot handle events. For example, the method may not be in a class or may be a shared method." + + case 39 + r = "The method uses the ""Handles"" clause but specifies an event that does not exist." + + case 40 + r = "Destructors can't have parameters." + + case 41 + r = "Can't use ""Super"" keyword outside of a class." + + case 42 + r = "Can't use ""Super"" keyword in a class that has no parent." + + case 43 + r = "This #else does not have a matching #if preceding it." + + case 44 + r = "This #endif does not have a matching #if preceding it." + + case 45 + r = "Only Boolean constants can be used with #if." + + case 46 + r = "Only Boolean constants can be used with #if." + + case 47 + r = "The variable specified after the Next statement does not match the loop's counter variable. " + _ + "For example, if you declare the For loop as using ""i"" as its counter, the loop must end in ""Next"" or ""Next i""." + + case 48 + r = "The size of an array must be a constant or number." + + case 49 + r = "You can't pass an array to an external function." + + case 50 + r = "External functions cannot use objects as parameters." + + case 51 + r = "External functions cannot use ordinary strings as parameters. Use CString, PString, WString, or CFStringRef instead." + + case 52 + r = "This kind of array can not be sorted." + + case 53 + r = "This property is protected. It can only be used from within its class." + + case 54 + r = "This method is protected. It can only be called from within its class." + + case 55 + r = "This local variable or constant has the same name as a Declare in this method. You must resolve this conflict." + + case 56 + r = "This global variable has the same name as a global function. You must resolve this conflict." + + case 57 + r = "This property has the same name as a method. You must resolve this conflict." + + case 58 + r = "This property has the same name as an event. You must resolve this conflict." + + case 59 + r = "This global variable has the same name as a class. You must resolve this conflict." + + case 60 + r = "This global variable has the same name as a module. You must change one of them." + + case 61 + r = "This local variable or parameter has the same name as a constant. You must resolve this conflict." + + case 62 + r = "This identifier is reserved and can't be used as a variable or property name." + + case 63 + r = "There is no class with this name." + + case 64 + r = "The library name must be a string constant." + + case 65 + r = "This Declare Function statement is missing its return type." + + case 66 + r = "You can't use the New operator with this class." + + case 67 + r = "This method doesn't return a value." + + case 68 + r = "End quote missing." + + case 69 + r = "A class cannot be its own superclass." + + case 70 + r = "Cannot assign a value to this property." + + case 71 + r = "Cannot get this property's value." + + case 72 + r = "The If statement is missing its condition." + + case 73 + r = "The current function must return a value, but this Return statement does not specify any value." + + case 74 + r = "A method parameter has options that are mutually exclusive. " + _ + "For example, the ""ByRef"" and ""ByVal"" options cannot be used together." + + case 75 + r = "This parameter option has already been specified. For example, this occurs if you declare a parameter as ""ByRef ByRef i As Integer""." + + case 76 + r = "A parameter passed by reference cannot have a default value." + + case 77 + r = "A ParamArray cannot have a default value." + + case 78 + r = "An Assigns parameter cannot have a default value." + + case 79 + r = "An Extends parameter cannot have a default value." + + case 80 + r = "Only the first parameter may use the Extends option." + + case 81 + r = "Only the last parameter may use the Assigns option." + + case 82 + r = "An ordinary parameter cannot follow a ParamArray." + + case 83 + r = "Only one parameter may use the Assigns option." + + case 84 + r = "Only one parameter may use the ParamArray option." + + case 85 + r = "A ParamArray cannot have more than one dimension." + + case 86 + r = "The keyword ""Then"" is expected after this If statement's condition." + + case 87 + r = "Obsolete. The compiler no longer generates this error." + + case 88 + r = "Constants must be defined with constant values." + + case 89 + r = "Illegal use of the Call keyword." + + case 90 + r = "No cases may follow the Else block." + + case 91 + r = "A computed property can only contain ""Get"" and ""Set"" blocks" + + case 92 + r = "A computed property getter or setter block ends with the wrong ""end"" line. " + _ + "For example, if you start the block with ""Get"", it must end with ""End Get""." + + case 93 + r = "Duplicate method definition." + + case 94 + r = "The library name for this declaration is blank." + + case 95 + r = "This If statement is missing an End If statement." + + case 96 + r = "This Select Case statement is missing an End Select statement." + + case 97 + r = "This For loop is missing its Next statement." + + case 98 + r = "This While loop is missing its Wend statement." + + case 99 + r = "This Try statement is missing an End Try statement." + + case 100 + r = "This Do loop is missing its Loop statement." + + case 101 + r = "Too few parentheses." + + case 102 + r = "Too many parentheses." + + case 103 + r = "The Continue statement only works inside a loop." + + case 104 + r = "There is no (%1) block to (%2) here." + + case 105 + r = "Shared methods cannot access instance properties." + + case 106 + r = "Shared methods cannot access instance methods." + + case 107 + r = "Shared computed property accessors cannot access instance properties." + + case 108 + r = "Shared computed property accessors cannot access instance methods." + + case 109 + r = "The Constructor of this class is protected, and can only be called from within this class." + + case 110 + r = "This String field needs to specify its length." + + case 111 + r = "Structures cannot contain fields of this type." + + case 112 + r = "Structures cannot contain multidimensional arrays." + + case 113 + r = "Enumerated types can only contain integers." + + case 114 + r = "An enumerations cannot be defined in terms of another enumeration." + + case 115 + r = "This is a constant; its value can't be changed." + + case 116 + r = "A String field must be at least 1 byte long." + + case 117 + r = "The storage size specifier only applies to String fields." + + case 118 + r = "A Structure cannot contain itself." + + case 119 + r = "A structure is not a class, and cannot be instantiated with New." + + case 120 + r = "An enumeration is not a class, and cannot be instantiated with New." + + case 121 + r = "This type is private, and can only be used within its module." + + case 122 + r = "Class members cannot be global." + + case 123 + r = "Module members must be public or private; they cannot be protected." + + case 124 + r = "Members of inner modules cannot be global." + + case 125 + r = "A Dim statement creates only one new object at a time." + + case 126 + r = "A constant was expected here, but this is some other kind of expression." + + case 127 + r = "This module is private, and can only be used within its containing module." + + case 128 + r = "Duplicate property definition." + + case 129 + r = "This datatype cannot be used as an array element." + + case 130 + r = "Delegate parameters cannot be optional." + + case 131 + r = "Delegates cannot use Extends, Assigns, or ParamArray." + + case 132 + r = "The Declare statement is illegal in XojoScript." + + case 133 + r = "It is not legal to dereference a Ptr value in an XojoScript." + + case 134 + r = "Delegate creation from Ptr values is not allowed in XojoScript." + + case 135 + r = "Delegate constant definition." + + case 136 + r = "Ambiguous interface method implementation." + + case 137 + r = "Illegal explicit interface method implementation. The class does not claim to implement this interface." + + case 138 + r = "The interface does not declare this method." + + case 139 + r = "This method contains a #If without a closing #endif statement." + + case 140 + r = "This interface contains a cyclical interface aggregation." + + case 141 + r = "The Extends modifier cannot be used on a class method." + + case 142 + r = "You cannot assign a non-value type to a value." + + case 143 + r = "Duplicate attribute name." + + case 144 + r = "Delegates cannot return structures." + + case 145 + r = "You cannot use AddressOf on this method. Possible causes include using AddressOf on event declarations or soft declares." + + case 146 + r = "You cannot use an Operator_Convert method to perform a convert-to operation on an interface." + + case 147 + r = "The ElseIf statement is missing its condition." + + case 148 + r = "This type cannot be used as an explicit constant type." + + case 149 + r = "Recursive constant declaration error." + + case 150 + r = "Custom error created using ""Error"" pragma." + + case 151 + r = "The second operand to the ""Unused"" pragma must be a local variable or parameter." + + case 152 + r = "The maximum units in last position parameter must be a constant." + + case 153 + r = "The maximum units in last position parameter is out of range." + + case 154 + r = "The StructureAlignment attribute's value must be of the following: 1, 2, 4, 8, 16, 32, 64, or 128." + + case 155 + r = "Pair creation via the "":"" operator is not allowed in XojoScript." + + case 156 + r = "Introspection via the GetTypeInfo operator is not allowed in XojoScript." + + case 157 + r = "The For statement is missing its condition." + + case 158 + r = "The While statement is missing its condition." + + case 159 + r = "Unsigned integer used in negative step loops can cause an infinite loop." + + case 160 + r = "Only objects can be used with the Is operator" + + case 161 + r = "Only objects can be used with AddHandler and RemoveHandler." + + case 162 + r = "The object you are passing to AddHandler does not have the specified event" + + case 163 + r = "Converting Delegates to Ptrs is not allowed in XojoScript." + + case 164 + r = "WeakAddressOf can only be used on instance methods." + + case 165 + r = "Declares directly into the runtime via Lib """" are no longer allowed." + + case 166 + r = "Objective-C declares must have at least one parameter (the target of the message send)." + + case 167 + r = "The property being declared has a different type than the property it is shadowing." + + case 168 + r = "A GoTo statement is trying to jump to a non-existent label." + + case 169 + r = "'#pragma warning' requires a warning message." + + case 170 + r = "'#pragma error' requires an error message." + + case 171 + r = "Duplicate label." + + case 172 + r = "Object properties cannot have default values" + + case 173 + r = "Array properties cannot have default values" + + case 174 + r = "For Each loops over iterable objects cannot be used in XojoScript" + + case 175 + r = "This object does not implement the Iterable interface and cannot be used in a For Each loop" + + case 178 + r = "This is a type name, not a variable; values can't be assigned to it" + + case 179 + r = "This is a module, not a variable; values can't be assigned to it" + + case 180 + r = "Can't find a type with this name" + + case 181 + r = "Expected a type name but found %1 instead" + + case 182 + r = "Can't find a type or module with this name" + + case 183 + r = "Expected a type or module name here but found %1 instead" + + case 184 + r = "Extension method %1 requires a conversion from %2 to %3; use CType to explicitly convert first" + + case 185 + r = "Can't find a type with this name. Did you mean %1?" + + case 186 + r = "Undefined operator. Type %1 does not define ""%2"" with type %3" + + case 187 + r = "Undefined operator. Type %1 does not define ""%2""." + + case 188 + r = "Cannot import %1 from %2 because it is Global, not Public." + + case 189 + r = "Cannot import %1 from %2 because it is Private to its container, not Public." + + case 190 + r = "Expected a value of type %1, but found a static namespace reference to %2." + + case 191 + r = "Cannot create an instance of %1 with New because it is not a class." + + case 192 + r = "Cannot create an instance of %1 with New because it is not a class. Did you mean %2?" + + case 193 + r = " %1 is not available." + + case 194 + r = "Too many arguments: got %1, expected only %2." + + case 195 + r = "Too many arguments: got %1, expected no more than %2." + + case 196 + r = "Not enough arguments: missing %3 value for parameter ""%4""" + + case 197 + r = "Not enough arguments: got %1, expected %2." + + case 198 + r = "Not enough arguments: got %1, expected at least %2." + + case 199 + r = "Assignment accessor must be invoked by assigning a value." + + case 200 + r = "This method cannot accept an assigned value (it lacks an Assigns parameter)." + + case 201 + r = "Parameter ""%1"" expects %2, but this is %3." + + case 202 + r = "Expected (%1), but these arguments are (%2)." + + case 203 + r = "ParamArray ""%1"" expects values of %2, but this is %3." + + case 204 + r = "Instance methods need an object: call this on an instance of %1." + + case 205 + r = "Extension methods need a base expression: call this on a value of %1." + + case 206 + r = "Static reference to instance method: call this on an instance of %1." + + case 207 + r = "Static reference to extension method: call this on a value of %1." + + case 208 + r = "This method extends %1, but the base expression is %2." + + case 209 + r = "Cannot convert from %1 to %2 because %3 is protected." + + case 210 + r = "Fixed-length string fields are no longer supported; use an array of Byte instead." + + case 211 + r = "The GetTypeInfo operator is unavailable on iOS." + + case 212 + r = "The Pair operator is unavailable on iOS." + + case 213 + r = "Objective-C declares can only be called when building for iOS or OS X." + + end select + + r = ExpandMessage( r, errorInfo ) + + return r + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function ExpandMessage(msg As String, info As Dictionary) As String + if info IsA Dictionary then + dim lastIndex as integer = info.Count - 1 + for i as integer = 0 to lastIndex + dim param as string = "%" + str( i + 1 ) + dim key as variant = info.Key( i ) + dim value as variant = info.Value( key ) + msg = msg.Replace( param, value ) + next + end if + + return msg + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function WarningToString(warning As XojoScript.Warnings, warningInfo As Dictionary) As String + dim r as string = "Uknown warning" + + select case warning + case XojoScript.Warnings.ConversionPrecision + r = "Conversion precision" + + case XojoScript.Warnings.ConversionSign + r = "Conversion sign" + + case XojoScript.Warnings.Deprecated + r = "This has been deprecated" + + case XojoScript.Warnings.DeprecatedWithReplacement + r = "%1 has been deprecated, use %2 instead" + + case XojoScript.Warnings.FloatingPointComparison + r = "Floating point comparison" + + case XojoScript.Warnings.NameLookupChange + r = "Name lookup change" + + case XojoScript.Warnings.OldStyleConstructor + r = "Using old-style Constructor" + + case XojoScript.Warnings.ShadowedConstant + r = "Shadowed constant" + + case XojoScript.Warnings.ShadowedProperty + r = "Shadowed property" + + case XojoScript.Warnings.UnknownPragmaWarning + r = "Unknown pragma" + + case XojoScript.Warnings.UnusedEventParameter + r = "Unused event parameter" + + case XojoScript.Warnings.UnusedLocal + r = "Unused local variable" + + case XojoScript.Warnings.UnusedMethodParameter + r = "Unused method parameter" + + end select + + r = ExpandMessage( r, warningInfo ) + + return r + + End Function + #tag EndMethod + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Images/arrow-right-3-small.png b/Images/arrow-right-3-small.png new file mode 100644 index 0000000..d11e0c0 Binary files /dev/null and b/Images/arrow-right-3-small.png differ diff --git a/Images/arrow-right-3.png b/Images/arrow-right-3.png new file mode 100644 index 0000000..3775bf5 Binary files /dev/null and b/Images/arrow-right-3.png differ diff --git a/Images/arrow-right-double-3-small.png b/Images/arrow-right-double-3-small.png new file mode 100644 index 0000000..6a4ca64 Binary files /dev/null and b/Images/arrow-right-double-3-small.png differ diff --git a/Images/arrow-right-double-3.png b/Images/arrow-right-double-3.png new file mode 100644 index 0000000..4a2b55a Binary files /dev/null and b/Images/arrow-right-double-3.png differ diff --git a/Images/errordata.bmp b/Images/errordata.bmp new file mode 100644 index 0000000..3f2f691 Binary files /dev/null and b/Images/errordata.bmp differ diff --git a/Images/errormask.bmp b/Images/errormask.bmp new file mode 100644 index 0000000..2629622 Binary files /dev/null and b/Images/errormask.bmp differ diff --git a/Images/knobImage.bmp b/Images/knobImage.bmp new file mode 100644 index 0000000..de799e5 Binary files /dev/null and b/Images/knobImage.bmp differ diff --git a/Images/markerdata.bmp b/Images/markerdata.bmp new file mode 100644 index 0000000..4dde7cf Binary files /dev/null and b/Images/markerdata.bmp differ diff --git a/Images/markermask.bmp b/Images/markermask.bmp new file mode 100644 index 0000000..c22db9d Binary files /dev/null and b/Images/markermask.bmp differ diff --git a/Images/plasticBack.bmp b/Images/plasticBack.bmp new file mode 100644 index 0000000..be3cc6e Binary files /dev/null and b/Images/plasticBack.bmp differ diff --git a/Images/rowicondata.bmp b/Images/rowicondata.bmp new file mode 100644 index 0000000..5cd3e3c Binary files /dev/null and b/Images/rowicondata.bmp differ diff --git a/Images/rowiconmask.bmp b/Images/rowiconmask.bmp new file mode 100644 index 0000000..aa11a96 Binary files /dev/null and b/Images/rowiconmask.bmp differ diff --git a/Images/system-run-3-small.png b/Images/system-run-3-small.png new file mode 100644 index 0000000..ffe866a Binary files /dev/null and b/Images/system-run-3-small.png differ diff --git a/Images/system-run-3.png b/Images/system-run-3.png new file mode 100644 index 0000000..ae4940f Binary files /dev/null and b/Images/system-run-3.png differ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..b212b3f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Kem Tekinay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MainMenuBar.xojo_menu b/MainMenuBar.xojo_menu deleted file mode 100644 index 8a54188..0000000 --- a/MainMenuBar.xojo_menu +++ /dev/null @@ -1,98 +0,0 @@ -#tag Menu -Begin Menu MainMenuBar - Begin MenuItem FileMenu - SpecialMenu = 0 - Text = "&File" - Index = -2147483648 - AutoEnable = True - Visible = True - Begin QuitMenuItem FileQuit - SpecialMenu = 0 - Text = "#App.kFileQuit" - Index = -2147483648 - ShortcutKey = "#App.kFileQuitShortcut" - Shortcut = "#App.kFileQuitShortcut" - AutoEnable = True - Visible = True - End - End - Begin MenuItem EditMenu - SpecialMenu = 0 - Text = "&Edit" - Index = -2147483648 - AutoEnable = True - Visible = True - Begin MenuItem EditUndo - SpecialMenu = 0 - Text = "&Undo" - Index = -2147483648 - ShortcutKey = "Z" - Shortcut = "Cmd+Z" - MenuModifier = True - AutoEnable = True - Visible = True - End - Begin MenuItem EditSeparator1 - SpecialMenu = 0 - Text = "-" - Index = -2147483648 - AutoEnable = True - Visible = True - End - Begin MenuItem EditCut - SpecialMenu = 0 - Text = "Cu&t" - Index = -2147483648 - ShortcutKey = "X" - Shortcut = "Cmd+X" - MenuModifier = True - AutoEnable = True - Visible = True - End - Begin MenuItem EditCopy - SpecialMenu = 0 - Text = "&Copy" - Index = -2147483648 - ShortcutKey = "C" - Shortcut = "Cmd+C" - MenuModifier = True - AutoEnable = True - Visible = True - End - Begin MenuItem EditPaste - SpecialMenu = 0 - Text = "&Paste" - Index = -2147483648 - ShortcutKey = "V" - Shortcut = "Cmd+V" - MenuModifier = True - AutoEnable = True - Visible = True - End - Begin MenuItem EditClear - SpecialMenu = 0 - Text = "#App.kEditClear" - Index = -2147483648 - AutoEnable = True - Visible = True - End - Begin MenuItem EditSeparator2 - SpecialMenu = 0 - Text = "-" - Index = -2147483648 - AutoEnable = True - Visible = True - End - Begin MenuItem EditSelectAll - SpecialMenu = 0 - Text = "Select &All" - Index = -2147483648 - ShortcutKey = "A" - Shortcut = "Cmd+A" - MenuModifier = True - AutoEnable = True - Visible = True - End - End -End -#tag EndMenu diff --git a/Menu Stuff/MainMenuBar.xojo_menu b/Menu Stuff/MainMenuBar.xojo_menu new file mode 100644 index 0000000..855bd6c --- /dev/null +++ b/Menu Stuff/MainMenuBar.xojo_menu @@ -0,0 +1,418 @@ +#tag Menu +Begin Menu MainMenuBar + Begin MenuItem FileMenu + SpecialMenu = 0 + Text = "&File" + Index = -2147483648 + AutoEnable = True + Visible = True + Begin MenuItem FileNew + SpecialMenu = 0 + Text = "New Script" + Index = -2147483648 + ShortcutKey = "N" + Shortcut = "Cmd+N" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem FileOpen + SpecialMenu = 0 + Text = "Open..." + Index = -2147483648 + ShortcutKey = "O" + Shortcut = "Cmd+O" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem FileClose + SpecialMenu = 0 + Text = "Close" + Index = -2147483648 + ShortcutKey = "W" + Shortcut = "Cmd+W" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem FileSave + SpecialMenu = 0 + Text = "Save" + Index = -2147483648 + ShortcutKey = "S" + Shortcut = "Cmd+S" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem FileSaveAs + SpecialMenu = 0 + Text = "Save As..." + Index = -2147483648 + ShortcutKey = "S" + Shortcut = "Cmd+Shift+S" + MenuModifier = True + AltMenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator0 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin QuitMenuItem FileQuit + SpecialMenu = 0 + Text = "#App.kFileQuit" + Index = -2147483648 + ShortcutKey = "#App.kFileQuitShortcut" + Shortcut = "#App.kFileQuitShortcut" + AutoEnable = True + Visible = True + End + End + Begin MenuItem EditMenu + SpecialMenu = 0 + Text = "&Edit" + Index = -2147483648 + AutoEnable = True + Visible = True + Begin MenuItem EditUndo + SpecialMenu = 0 + Text = "&Undo" + Index = -2147483648 + ShortcutKey = "Z" + Shortcut = "Cmd+Z" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditRedo + SpecialMenu = 0 + Text = "Redo" + Index = -2147483648 + ShortcutKey = "Z" + Shortcut = "Cmd+Shift+Z" + MenuModifier = True + AltMenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditSeparator1 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem EditCut + SpecialMenu = 0 + Text = "Cu&t" + Index = -2147483648 + ShortcutKey = "X" + Shortcut = "Cmd+X" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditCopy + SpecialMenu = 0 + Text = "&Copy" + Index = -2147483648 + ShortcutKey = "C" + Shortcut = "Cmd+C" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditPaste + SpecialMenu = 0 + Text = "&Paste" + Index = -2147483648 + ShortcutKey = "V" + Shortcut = "Cmd+V" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditClear + SpecialMenu = 0 + Text = "#App.kEditClear" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem EditSeparator2 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem EditSelectAll + SpecialMenu = 0 + Text = "Select &All" + Index = -2147483648 + ShortcutKey = "A" + Shortcut = "Cmd+A" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator4 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem EditComment + SpecialMenu = 0 + Text = "Comment" + Index = -2147483648 + ShortcutKey = "'" + Shortcut = "Cmd+'" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditUncomment + SpecialMenu = 0 + Text = "Uncomment" + Index = -2147483648 + ShortcutKey = "'" + Shortcut = "Cmd+Shift+'" + MenuModifier = True + AltMenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator5 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem EditFind + SpecialMenu = 0 + Text = "Find" + Index = -2147483648 + ShortcutKey = "F" + Shortcut = "Cmd+F" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditFindNext + SpecialMenu = 0 + Text = "Find Next" + Index = -2147483648 + ShortcutKey = "G" + Shortcut = "Cmd+G" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem EditFindPrevious + SpecialMenu = 0 + Text = "Find Previous" + Index = -2147483648 + ShortcutKey = "G" + Shortcut = "Cmd+Shift+G" + MenuModifier = True + AltMenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator7 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin PrefsMenuItem EditPreferences + SpecialMenu = 0 + Text = "Preferences..." + Index = -2147483648 + AutoEnable = True + Visible = True + End + End + Begin MenuItem ViewMenu + SpecialMenu = 0 + Text = "View" + Index = -2147483648 + AutoEnable = True + Visible = True + Begin MenuItem ViewShowInvisibles + SpecialMenu = 0 + Text = "Show &Invisibles" + Index = -2147483648 + ShortcutKey = "I" + Shortcut = "Cmd+Option+Alt+I" + MenuModifier = True + MacOptionKey = True + PCAltKey = True + AutoEnable = True + Visible = True + End + Begin MenuItem ViewShowLineNumbers + SpecialMenu = 0 + Text = "Show &Line Numbers" + Index = -2147483648 + ShortcutKey = "L" + Shortcut = "Cmd+Option+Alt+L" + MenuModifier = True + MacOptionKey = True + PCAltKey = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator8 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem ViewToggleToolbar + SpecialMenu = 0 + Text = "#App.kViewHideToolbar" + Index = -2147483648 + ShortcutKey = "T" + Shortcut = "Cmd+Option+Alt+T" + MenuModifier = True + MacOptionKey = True + PCAltKey = True + AutoEnable = True + Visible = True + End + End + Begin MenuItem ScriptMenu + SpecialMenu = 0 + Text = "Script" + Index = -2147483648 + AutoEnable = True + Visible = True + Begin MenuItem ScriptCompile + SpecialMenu = 0 + Text = "#kTBEditorLabelCompile" + Index = -2147483648 + ShortcutKey = "K" + Shortcut = "Cmd+K" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem ScriptRunInIDE + SpecialMenu = 0 + Text = "#kTBEditorLabelRunInIDE" + Index = -2147483648 + ShortcutKey = "R" + Shortcut = "Cmd+R" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator1 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem ScriptRun + SpecialMenu = 0 + Text = "#kTBEditorLabelTestRun" + Index = -2147483648 + ShortcutKey = "R" + Shortcut = "Cmd+Shift+R" + MenuModifier = True + AltMenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator2 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem ScriptGoToLine + SpecialMenu = 0 + Text = "Go To &Line..." + Index = -2147483648 + ShortcutKey = "L" + Shortcut = "Cmd+L" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem ScriptGoToErrorLine + SpecialMenu = 0 + Text = "Go To &Error Line" + Index = -2147483648 + ShortcutKey = "E" + Shortcut = "Cmd+E" + MenuModifier = True + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator3 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem ScriptInsertInclude + SpecialMenu = 0 + Text = "Insert Include File..." + Index = -2147483648 + AutoEnable = True + Visible = True + End + End + Begin MenuItem HelpMenu + SpecialMenu = 0 + Text = "Help" + Index = -2147483648 + AutoEnable = True + Visible = True + Begin MenuItem HelpXojoScriptingLanguageDocs + SpecialMenu = 0 + Text = "Xojo Scripting Language Documentation" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin MenuItem UntitledSeparator6 + SpecialMenu = 0 + Text = "-" + Index = -2147483648 + AutoEnable = True + Visible = True + End + Begin ApplicationMenuItem HelpAbout + SpecialMenu = 0 + Text = "About" + Index = -2147483648 + AutoEnable = True + Visible = True + End + End +End +#tag EndMenu diff --git a/Other Files/Syntax Definition/XojoScript.xml b/Other Files/Syntax Definition/XojoScript.xml new file mode 100644 index 0000000..aa0e76f --- /dev/null +++ b/Other Files/Syntax Definition/XojoScript.xml @@ -0,0 +1,202 @@ + + + XojoScript + + + ^\s*(protected\b|private\b)?\s*module\b + ^\s*end\s+module\b + + + ^\s*(protected\b|private\b)?\s*class\b + ^\s*end\s+class\b + + + ^\s*(?:(if|elseif)\b.*\sthen\b\s*(('|//)|(?!.+?))|if\s(?!.*\sthen\b)|^\s*(public\b|protected\b|private\b)?\s*(shared\b)?\s*(?<!end\b)\s*(sub|function|property)\b|(do|for|while|select|case|try|catch|else|Get|Set|#if|#elseif|#else)\b) + ^\s*(?:end|wend|next|loop|else|case|catch|elseif|#else|#elseif|#endif)\b + + + ^\s*(protected\b|private\b)?\s*interface\b + ^\s*end\s+interface\b + + ^(|.*\W)_\s*(|//.*|'.*)$ + + + + ^\s*(?:public|protected|private)?\b\s*(shared\b)?\s*(?<!end\b)\s*(?:sub|function)\b.+?\([^\)]*\)\s*(?:as\b\s*\w+\s*)? + + + ^\s*(?:public|protected|private)?\b\s*(shared\b)?\s*(?<!end\b)\s*(property\b).+ + + + ^\s*(?:protected\b|private\b)?\s*class\s+\w+\s* + + + ^\s*(?:protected\b|private\b)?\s*module\s+\w+\s* + + + ^\s*(?:protected\b|private\b)?\s*interface\s+\w+\s* + + + + + + (?<=[^\w.-􏟿]|^)(?=\d+\.|\.\d+|\d+e(?:\d|\b))(\d*\.\d*(?:e[-+]?\d*)?|\d+e[-+]?\d*)(?=[^\w.-􏟿]|$) + + + + (?<=[^\w.-􏟿]|^)(?:\d+|&h[[:xdigit:]]*\b|&o[0-7]*\b|&b[01]*\b)(?=[^\w.-􏟿]|$) + + + + (#\w+) + + + + '|//|\brem\b + [\n\r] + + + + + + "[^"\r\n]*(?:"|$)|&u[[:xdigit:]]*\b + + + + + (?<=&c)[[:xdigit:]]{1,2} + + + + (?<=&c[[:xdigit:]]{2})[[:xdigit:]]{1,2} + + + + (?<=&c[[:xdigit:]]{4})[[:xdigit:]]{1,2} + + + + + Boolean + Byte + Color + Double + Integer + UInteger + Int8 + UInt8 + Int16 + UInt16 + Int32 + UInt32 + Int64 + UInt64 + Single + String + CFString + Text + Ptr + + + + + + #Else + #ElseIf + #EndIf + #If + #Pragma + AddHandler + AddressOf + And + Array + As + Assigns + Break + ByRef + ByVal + Call + Case + Catch + Class + Const + Continue + CType + Declare + Delegate + Dim + Do + DownTo + Each + Else + ElseIf + End + Enum + Event + Exception + Exit + Extends + False + Finally + For + Function + GoTo + Handles + If + Implements + In + Inherits + Interface + Is + IsA + Lib + Loop + Me + Mod + Module + Namespace + New + Next + Nil + Not + Object + Of + Optional + Or + ParamArray + Private + Property + Protected + Public + Raise + RaiseEvent + Redim + Rem + RemoveHandler + Return + Select + Self + Set + Shared + Soft + Static + Step + Structure + Sub + Super + Then + To + True + Try + Using + Until + Wend + While + With + + + + diff --git a/Plists/Retinize.plist b/Plists/Retinize.plist new file mode 100644 index 0000000..3e2cf89 --- /dev/null +++ b/Plists/Retinize.plist @@ -0,0 +1,8 @@ + + + + + NSHighResolutionCapable + + + diff --git a/Preference Stuff/ContextPreferences.xojo_code b/Preference Stuff/ContextPreferences.xojo_code new file mode 100644 index 0000000..63ddc3f --- /dev/null +++ b/Preference Stuff/ContextPreferences.xojo_code @@ -0,0 +1,167 @@ +#tag Class +Protected Class ContextPreferences + #tag Method, Flags = &h0 + Function Clone() As ContextPreferences + dim copy as new ContextPreferences + + copy.BackgroundColor = BackgroundColor + copy.Bold = Bold + copy.HasBackgroundColor = HasBackgroundColor + copy.HighlightColor = HighlightColor + copy.Italic = Italic + copy.Name = Name + copy.Underline = Underline + + return copy + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub Constructor() + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(syntaxFile As FolderItem, contextName As String) + dim contexts as Dictionary = ContextsForFile( syntaxFile ) + dim context as HighlightContext = contexts.Value( contextName ) + + // + // Assign the defaults + // + BackgroundColor = context.BackgroundColor + Bold = context.Bold + DefaultBackgroundColor = context.BackgroundColor + HasBackgroundColor = context.HasBackgroundColor + HighlightColor = context.HighlightColor + Italic = context.Italic + Name = contextName + Underline = context.Underline + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Shared Function ContextsDict() As Dictionary + if mContextsDict is nil then + mContextsDict = new Dictionary + end if + + return mContextsDict + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Shared Function ContextsForFile(syntaxFile As FolderItem) As Dictionary + dim r as Dictionary = ContextsDict.Lookup( syntaxFile.NativePath, nil ) + if r is nil then + r = new Dictionary + dim hd as new HighlightDefinition + if not hd.LoadFromXml( syntaxFile ) then + dim err as new XmlException + err.Message = "Could not read syntax file " + syntaxFile.NativePath + raise err + end if + + for each context as HighlightContext in hd.Contexts + r.Value( context.Name ) = context + next + + ContextsDict.Value( syntaxFile.NativePath ) = r + end if + + return r + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub CopyTo(context As HighlightContext) + context.HighlightColor = HighlightColor + context.BackgroundColor = if( HasBackgroundColor, BackgroundColor, DefaultBackgroundColor ) + context.Bold = Bold + context.Italic = Italic + context.Underline = Underline + End Sub + #tag EndMethod + + + #tag Property, Flags = &h0 + BackgroundColor As Color + #tag EndProperty + + #tag Property, Flags = &h0 + Bold As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private DefaultBackgroundColor As Color + #tag EndProperty + + #tag Property, Flags = &h0 + HasBackgroundColor As Boolean + #tag EndProperty + + #tag Property, Flags = &h0 + HighlightColor As Color + #tag EndProperty + + #tag Property, Flags = &h0 + Italic As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mContextsDict As Dictionary + #tag EndProperty + + #tag Property, Flags = &h0 + Name As String + #tag EndProperty + + #tag Property, Flags = &h0 + Underline As Boolean + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="HighlightColor" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Preference Stuff/PreferenceWatcher.xojo_code b/Preference Stuff/PreferenceWatcher.xojo_code new file mode 100644 index 0000000..203a7de --- /dev/null +++ b/Preference Stuff/PreferenceWatcher.xojo_code @@ -0,0 +1,46 @@ +#tag Interface +Protected Interface PreferenceWatcher + #tag Method, Flags = &h0 + Sub PreferencesHaveChanged(prefs As Preferences) + + End Sub + #tag EndMethod + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Interface +#tag EndInterface diff --git a/Preference Stuff/Preferences.xojo_code b/Preference Stuff/Preferences.xojo_code new file mode 100644 index 0000000..e152676 --- /dev/null +++ b/Preference Stuff/Preferences.xojo_code @@ -0,0 +1,692 @@ +#tag Class +Protected Class Preferences + #tag Method, Flags = &h0 + Function BooleanValue(name as String, default as Boolean = False) As Boolean + Return ChildAdHocValues.Lookup(name, default) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub BooleanValue(name as String, assigns value as Boolean) + ChildAdHocValues.Value(name) = value + InformWatchers + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function ColorToText(c As Color) As Text + dim textValue as Text = "&c" + c.Red.ToHex( 2 ) + c.Green.ToHex( 2 ) + c.Blue.ToHex( 2 ) + c.Alpha.ToHex( 2 ) + return textValue + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ColorValue(name As String, Assigns value As Color) + ChildAdHocValues.Value(name) = ColorToText(value) + InformWatchers + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function ColorValue(name As String, default As Color) As Color + if not ChildAdHocValues.HasKey(name) then + return default + end if + + dim textValue as Text = ChildAdHocValues.Value(name) + return TextToColor(textValue) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(applicationId as String) + Self.ApplicationId = applicationId + PreferenceFile = EnsurePreferencesFile + + Storage = new Xojo.Core.Dictionary + + Storage.Value("Version") = kVersion + Storage.Value(kKeyValues) = nil + + LoadPreferences + + WatcherTimer = new Timer + AddHandler WatcherTimer.Action, WeakAddressOf WatcherTimerAction + WatcherTimer.Mode = Timer.ModeOff + WatcherTimer.Period = 20 + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub DeletePreferencesFile() + if PreferenceFile <> nil and PreferenceFile.Exists then + PreferenceFile.Delete + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub DeserializeProperties(data As Xojo.Core.Dictionary, restoreTo As Object) + dim ti as Introspection.TypeInfo = Introspection.GetType(restoreTo) + dim props() as Introspection.PropertyInfo = ti.GetProperties + + // + // Make sure computed properties are done first + // + dim doComputed as boolean = true + + do + for each prop as Introspection.PropertyInfo in props + if prop.IsComputed = doComputed then + dim propName as string = prop.Name + if data.HasKey(propName) then + dim value as variant = data.Value(propName) + + dim propType as string = prop.PropertyType.Name + if propType = "Color" then + value = TextToColor(value) + end if + + prop.Value(restoreTo) = value + end if + end if + next + + doComputed = not doComputed + loop until doComputed + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub Destructor() + Save + + WatcherTimer.Mode = Timer.ModeOff + RemoveHandler WatcherTimer.Action, WeakAddressOf WatcherTimerAction + WatcherTimer = nil + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function DoubleValue(name as String, default as Double = 0) As Double + Return ChildAdHocValues.Lookup(name, default) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub DoubleValue(name as String, assigns value as Double) + ChildAdHocValues.Value(name) = value + InformWatchers + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function EnsurePreferencesFile() As FolderItem + dim root as FolderItem = SpecialFolder.ApplicationData.Child(ApplicationId) + if not root.Exists then + root.CreateAsFolder + end if + + return root.Child("prefs.json") + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function GetValuesChild(name As String) As Xojo.Core.Dictionary + if Storage is nil then + return nil + end if + + dim values as Xojo.Core.Dictionary = Storage.Lookup(kKeyValues, nil) + if values is nil then + values = new Xojo.Core.Dictionary + Storage.Value(kKeyValues) = values + end if + + dim result as Xojo.Core.Dictionary = values.Lookup(name, nil) + if result is nil then + result = new Xojo.Core.Dictionary + values.Value(name) = result + end if + + return result + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub InformWatchers() + WatcherTimer.Mode = Timer.ModeSingle + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function IntegerValue(name as String, default as Integer = 0) As Integer + Return ChildAdHocValues.Lookup(name, default) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub IntegerValue(name as String, assigns value as Integer) + ChildAdHocValues.Value(name) = value + InformWatchers + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function IsWindowVisible(left As Integer, top As Integer, width as Integer) As Boolean + dim result as boolean + + #if TargetDesktop then + const kBuffer = 10 + + + dim t as integer = top + dim l as integer = left + dim r as integer = l + width + + dim lastScreen as integer = ScreenCount - 1 + for i as integer = 0 to lastScreen + dim thisScreen as Screen = Screen(i) + + if t < thisScreen.AvailableTop or t > (thisScreen.AvailableTop + thisScreen.AvailableHeight - kBuffer) then + continue for i + end if + + if l >= thisScreen.AvailableLeft and l < (thisScreen.AvailableLeft + thisScreen.AvailableWidth - kBuffer) then + result = true + exit + end if + + if r <= (thisScreen.AvailableLeft + thisScreen.AvailableWidth) and r > (thisScreen.AvailableLeft + kBuffer) then + result = true + exit + end if + next + #endif + + return result + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub LoadPreferences() + dim pfile as FolderItem = EnsurePreferencesFile + + if not pfile.Exists then + return + end if + + dim tis as TextInputStream = TextInputStream.Open(pfile) + dim t as string = tis.ReadAll(Encodings.UTF8) + tis.Close + tis = nil + + dim parsed as Auto = Xojo.Data.ParseJSON(t.ToText) + Storage = Xojo.Core.Dictionary(parsed) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function MakeKey(prefix As String, o As Object) As String + dim key() as string + + if prefix <> "" then + key.Append prefix + end if + + dim hasWin as boolean + + #if TargetDesktop then + if o IsA RectControl then + dim rc as RectControl = RectControl(o) + dim win as Window = rc.Window + if win IsA Object then + key.Append Introspection.GetType(win).FullName + hasWin = true + end if + key.Append rc.Name + end if + #endif + + dim ti as Introspection.TypeInfo = Introspection.GetType(o) + if not hasWin then + key.Append ti.FullName + end if + + return join(key, ".") + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ObjectValue(name As String, Assigns value As Object) + ChildObjectValues.Value(name) = SerializeProperties(value) + InformWatchers() + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function ObjectValue(name As String, newInstance As Object) As Object + // Restores the name to given new instance or returns that instance if not found + + dim data as Xojo.Core.Dictionary = ChildObjectValues.Lookup(name, nil) + if data isa Xojo.Core.Dictionary then + DeserializeProperties(data, newInstance) + end if + + return newInstance + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub RegisterWatcher(watcher As PreferenceWatcher) + // + // Don't register the same watcher twice + // + UnregisterWatcher( watcher ) + Watchers.Append watcher + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Remove(key as String) + if ChildAdHocValues.HasKey(key) then + ChildAdHocValues.Remove(key) + InformWatchers + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Sub Restore(widget As Listbox, prefix As String = "") + dim fk as string = MakeKey(prefix, widget) + dim child as Xojo.Core.Dictionary = ChildListboxValues.Lookup(fk, nil) + if child is nil then + return + end if + + dim widths as Variant = child.Lookup("Widths", nil) + if widths.IsArray then + dim j() as Auto + if true then // Scope + dim a as auto = widths + j = a + end if + dim lastCol as integer = if(j.Ubound >= widget.ColumnCount, widget.ColumnCount - 1, j.Ubound) + for col as integer = 0 to lastCol + dim thisWidth as integer = j(col) + widget.Column(col).WidthActual = thisWidth + widget.Column(col).WidthExpression = str(thisWidth) + next + + elseif not widths.IsNull then + widget.ColumnWidths = widths.StringValue + + end if + + if child.HasKey("SortedColumn") then + widget.SortedColumn = child.Value("SortedColumn") + widget.HeadingIndex = child.Value("SortedColumn") + widget.ColumnSortDirection(widget.SortedColumn) = child.Value("SortDirection") + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Sub Restore(widget as TabPanel, prefix as String = "") + dim fk as string = MakeKey(prefix, widget) + dim child as Xojo.Core.Dictionary = ChildTabPanelValues.Lookup(fk, nil) + if child is nil then + return + end if + + widget.Value = child.Lookup("Value", widget.Value) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Sub Restore(win as Window, prefix as String = "", left as Integer = - 1, top as Integer = - 1, width as Integer = - 1, height as Integer = - 1) + dim bounds as REALBasic.Rect = win.Bounds + + dim proposedLeft as Integer = if(left = -1, bounds.Left, left) + dim proposedTop as Integer = if(top = -1, bounds.Top, top) + dim proposedWidth as Integer = if(width = -1, bounds.Width, width) + dim proposedHeight as Integer = if(height = -1, bounds.Height, height) + + dim fk as string = MakeKey(prefix, win) + dim child as Xojo.Core.Dictionary = ChildWindowValues.Lookup(fk, nil) + if child isa Xojo.Core.Dictionary then + proposedLeft = child.Lookup("Left", proposedLeft) + proposedTop = child.Lookup("Top", proposedTop) + + if win.Resizeable then + proposedWidth = child.Lookup("Width", proposedWidth) + proposedHeight = child.Lookup("Height", proposedHeight) + end if + end if + + if not win.Resizeable or proposedWidth < win.MinWidth or proposedWidth > win.MaxWidth then + proposedWidth = bounds.Width + end if + + if not win.Resizeable or proposedHeight < win.MinHeight or proposedHeight > win.MaxHeight then + proposedHeight = bounds.Height + end if + + if IsWindowVisible(proposedLeft, proposedTop, proposedWidth) then + bounds.Left = proposedLeft + bounds.Top = proposedTop + end if + + if win.Resizeable then + bounds.Width = proposedWidth + bounds.Height = proposedHeight + end if + + win.Bounds = bounds + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Save() + dim pfile as FolderItem = EnsurePreferencesFile + dim tos as TextOutputStream = TextOutputStream.Create(pfile) + + tos.Write Xojo.Data.GenerateJSON(Storage) + + tos.Close + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function SerializeProperties(o As Object) As Xojo.Core.Dictionary + // Does simple serialization of an object + // Will only accept simple types (no arrays or objects) + + static acceptableTypes() as string = split("boolean,color,double,single,string,text,int8,int16,int32,int64,uint8,uint16,uint32,uint64", ",") + + dim ti as Introspection.TypeInfo = Introspection.GetType(o) + dim props() as Introspection.PropertyInfo = ti.GetProperties + + dim root as new Xojo.Core.Dictionary + + for each prop as Introspection.PropertyInfo in props + if prop.IsShared or not prop.CanRead or not prop.CanWrite then + continue for prop + end if + + // + // Make sure the prop type is fine + // + dim propType as string = prop.PropertyType.Name + if acceptableTypes.IndexOf(propType) = -1 then + dim err as new TypeMismatchException + err.Message = "Can't serialize the type " + propType + raise err + end if + + dim value as variant = prop.Value(o) + + // + // Work around a current bug in Xojo where empty string will + // generate an error when converted to JSON + // + if propType = "String" and value.StringValue = "" then + dim t as text + value = t + elseif propType = "Color" then + // + // A bug in Xojo 2015r22 corrupts colors as they go through an Auto + // + value = ColorToText(value.ColorValue) + end if + + root.Value(prop.Name) = value + + next + + return root + End Function + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Sub Store(widget as Listbox, prefix as String = "") + dim child as new Xojo.Core.Dictionary + + if widget.ScrollBarHorizontal then + dim widths() as Auto + + for i as Integer = 0 to widget.ColumnCount - 1 + widths.Append widget.Column(i).WidthActual + next + + child.Value("Widths") = widths + + else + child.Value("Widths") = widget.ColumnWidths + end if + + child.Value("SortedColumn") = widget.SortedColumn + child.Value("SortDirection") = if(widget.SortedColumn = -1, 0, widget.ColumnSortDirection(widget.SortedColumn)) + + dim fk as string = MakeKey(prefix, widget) + ChildListboxValues.Value(fk) = child + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Sub Store(widget as TabPanel, prefix as String = "") + dim child as new Xojo.Core.Dictionary + + child.Value("Value") = widget.Value + + dim fk as string = MakeKey(prefix, widget) + ChildTabPanelValues.Value(fk) = child + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0, CompatibilityFlags = TargetHasGUI + Sub Store(win as Window, prefix as String = "") + dim child as new Xojo.Core.Dictionary + + dim bounds as REALbasic.Rect = win.Bounds + child.Value("Left") = bounds.Left + child.Value("Top") = bounds.Top + child.Value("Width") = bounds.Width + child.Value("Height") = bounds.Height + + dim fk as string = MakeKey(prefix, win) + ChildWindowValues.Value(fk) = child + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function StringValue(name as String, default as String = "") As String + Return ChildAdHocValues.Lookup(name, default) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub StringValue(name as String, assigns value as String) + ChildAdHocValues.Value(name) = value + InformWatchers + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function TextToColor(t As Text) As Color + dim c as Color = RGB( _ + Integer.FromHex( t.Mid( 2, 2 ) ), _ + Integer.FromHex( t.Mid( 4, 2 ) ), _ + Integer.FromHex( t.Mid( 6, 2 ) ), _ + Integer.FromHex( t.Mid( 8, 2 ) ) _ + ) + + return c + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub UnregisterWatcher(watcher As PreferenceWatcher) + dim pos as integer = Watchers.IndexOf( watcher ) + if pos <> -1 then + Watchers.Remove pos + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub WatcherTimerAction(sender As Timer) + #pragma unused sender + + for each watcher as PreferenceWatcher in Watchers + watcher.PreferencesHaveChanged( self ) + next + End Sub + #tag EndMethod + + + #tag Property, Flags = &h1 + Protected ApplicationId As String + #tag EndProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + return GetValuesChild("Ad Hoc Values") + + End Get + #tag EndGetter + Protected ChildAdHocValues As Xojo.Core.Dictionary + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + return GetValuesChild("Listboxes") + + End Get + #tag EndGetter + Protected ChildListboxValues As Xojo.Core.Dictionary + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + return GetValuesChild("Objects") + + End Get + #tag EndGetter + Protected ChildObjectValues As Xojo.Core.Dictionary + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + return GetValuesChild("TabPanels") + + End Get + #tag EndGetter + Protected ChildTabPanelValues As Xojo.Core.Dictionary + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + return GetValuesChild("Windows") + + End Get + #tag EndGetter + Protected ChildWindowValues As Xojo.Core.Dictionary + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected PreferenceFile As FolderItem + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if PreferenceFile <> nil then + return PreferenceFile.Parent + else + return nil + end if + End Get + #tag EndGetter + PreferenceFolder As FolderItem + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected Storage As Xojo.Core.Dictionary + #tag EndProperty + + #tag Property, Flags = &h21 + Private Watchers() As PreferenceWatcher + #tag EndProperty + + #tag Property, Flags = &h21 + Private WatcherTimer As Timer + #tag EndProperty + + + #tag Constant, Name = kKeyObjectType, Type = String, Dynamic = False, Default = \"Object Type", Scope = Protected + #tag EndConstant + + #tag Constant, Name = kKeyValues, Type = String, Dynamic = False, Default = \"Values", Scope = Protected + #tag EndConstant + + #tag Constant, Name = kVersion, Type = String, Dynamic = False, Default = \"1.1", Scope = Protected + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Preference Stuff/TextContextControl.xojo_window b/Preference Stuff/TextContextControl.xojo_window new file mode 100644 index 0000000..4a1b0ee --- /dev/null +++ b/Preference Stuff/TextContextControl.xojo_window @@ -0,0 +1,675 @@ +#tag Window +Begin ContainerControl TextContextControl + AcceptFocus = False + AcceptTabs = True + AutoDeactivate = True + BackColor = &cFFFFFF00 + Backdrop = 0 + Compatibility = "" + Enabled = True + EraseBackground = True + HasBackColor = False + Height = 32 + HelpTag = "" + InitialParent = "" + Left = 0 + LockBottom = False + LockLeft = False + LockRight = False + LockTop = False + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + Top = 0 + Transparent = True + UseFocusRing = False + Visible = True + Width = 431 + Begin Label lblContextName + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 2 + Selectable = False + TabIndex = 0 + TabPanelIndex = 0 + Text = "Context:" + TextAlign = 2 + TextColor = &c00000000 + TextFont = "SmallSystem" + TextSize = 0.0 + TextUnit = 0 + Top = 7 + Transparent = False + Underline = False + Visible = True + Width = 88 + End + Begin ColorPicker_MTC cpForeground + AcceptFocus = False + AcceptTabs = False + AutoDeactivate = True + Backdrop = 0 + DoubleBuffer = False + Enabled = True + EraseBackground = True + Height = 32 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = 100 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + PromptText = "Select a color:" + Scope = 2 + TabIndex = 1 + TabPanelIndex = 0 + TabStop = True + Top = 0 + Transparent = True + UseFocusRing = True + Value = &c00000000 + Visible = True + Width = 32 + End + Begin CheckBox cbBold + AutoDeactivate = True + Bold = False + Caption = "Bold" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 180 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 6 + Underline = False + Value = False + Visible = True + Width = 65 + End + Begin CheckBox cbItalic + AutoDeactivate = True + Bold = False + Caption = "Italic" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 257 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 3 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 6 + Underline = False + Value = False + Visible = True + Width = 65 + End + Begin ColorPicker_MTC cpBackground + AcceptFocus = False + AcceptTabs = False + AutoDeactivate = True + Backdrop = 0 + DoubleBuffer = False + Enabled = True + EraseBackground = True + Height = 32 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = 136 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + PromptText = "Select a color:" + Scope = 2 + TabIndex = 4 + TabPanelIndex = 0 + TabStop = True + Top = 0 + Transparent = True + UseFocusRing = True + Value = &c00000000 + Visible = True + Width = 32 + End + Begin CheckBox cbUnderline + AutoDeactivate = True + Bold = False + Caption = "Underline" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 334 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 5 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 6 + Underline = False + Value = False + Visible = True + Width = 97 + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Open() + + End Sub + #tag EndEvent + + + #tag Method, Flags = &h21 + Private Sub ColorPickerConstructContextMenu(base As MenuItem) + base.Append new MenuItem( kMenuRestoreDefault ) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function ColorPickerContextualMenuAction(sender as ColorPicker_MTC, hitItem As MenuItem) As Boolean + + select case hitItem.Text + case kMenuRestoreDefault + + dim prefs as ContextPreferences = DefaultPreferences + if sender is cpForeground then + sender.Value = prefs.HighlightColor + elseif sender is cpBackground then + HasBackgroundColor = prefs.HasBackgroundColor + if HasBackgroundColor then + sender.Value = prefs.BackgroundColor + else + sender.Value = kDefaultBackgroundColor + end if + end if + + case else + return false + + end select + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub RestoreDefaults() + dim pref as ContextPreferences = DefaultPreferences.Clone + SetFromPrefs( pref, DefaultPreferences ) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SetFromPrefs(prefs As ContextPreferences, defaultPrefs As ContextPreferences) + cbBold.Value = prefs.Bold + cbItalic.Value = prefs.Italic + cbUnderline.Value = prefs.Underline + + if prefs.HasBackgroundColor then + cpBackground.Value = prefs.BackgroundColor + else + cpBackground.Value = &cFFFFFF00 + end + + cpForeground.Value = prefs.HighlightColor + + DefaultPreferences = defaultPrefs + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function ToContextPreferences() As ContextPreferences + dim r as ContextPreferences = DefaultPreferences.Clone + + r.BackgroundColor = cpBackground.Value + r.Bold = cbBold.Value + r.HasBackgroundColor = HasBackgroundColor + r.HighlightColor = cpForeground.Value + r.Italic = cbItalic.Value + r.Underline = cbUnderline.Value + + return r + End Function + #tag EndMethod + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if HasBackgroundColor then + return cpBackground.Value + + else + return kDefaultBackgroundColor + + end if + + End Get + #tag EndGetter + #tag Setter + Set + cpBackground.Value = value + End Set + #tag EndSetter + Background As Color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return cbBold.Value + End Get + #tag EndGetter + #tag Setter + Set + cbBold.Value = value + End Set + #tag EndSetter + Bold As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mContextName + End Get + #tag EndGetter + #tag Setter + Set + mContextName = value + lblContextName.Text = value + ":" + End Set + #tag EndSetter + ContextName As String + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private DefaultPreferences As ContextPreferences + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return cpForeground.Value + End Get + #tag EndGetter + #tag Setter + Set + cpForeground.Value = value + End Set + #tag EndSetter + Foreground As Color + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private HasBackgroundColor As Boolean + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return cbItalic.Value + End Get + #tag EndGetter + #tag Setter + Set + cbItalic.Value = value + End Set + #tag EndSetter + Italic As Boolean + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mContextName As String + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return cbUnderline.Value + End Get + #tag EndGetter + #tag Setter + Set + cbUnderline.Value = value + End Set + #tag EndSetter + Underline As Boolean + #tag EndComputedProperty + + + #tag Constant, Name = kDefaultBackgroundColor, Type = Color, Dynamic = False, Default = \"&cFFFFFF00", Scope = Private + #tag EndConstant + + #tag Constant, Name = kMenuRestoreDefault, Type = String, Dynamic = False, Default = \"Restore Default", Scope = Private + #tag EndConstant + + +#tag EndWindowCode + +#tag Events cpForeground + #tag Event + Function ConstructContextualMenu(base as MenuItem, x as Integer, y as Integer) As Boolean + #pragma unused x + #pragma unused y + + ColorPickerConstructContextMenu( base ) + return true + + End Function + #tag EndEvent + #tag Event + Function ContextualMenuAction(hitItem as MenuItem) As Boolean + return ColorPickerContextualMenuAction( me, hitItem ) + End Function + #tag EndEvent +#tag EndEvents +#tag Events cpBackground + #tag Event + Function ConstructContextualMenu(base as MenuItem, x as Integer, y as Integer) As Boolean + #pragma unused x + #pragma unused y + + ColorPickerConstructContextMenu( base ) + return true + End Function + #tag EndEvent + #tag Event + Function ContextualMenuAction(hitItem as MenuItem) As Boolean + return ColorPickerContextualMenuAction( me, hitItem ) + End Function + #tag EndEvent + #tag Event + Sub ValueChanged() + if me.Value <> kDefaultBackgroundColor then + HasBackgroundColor = true + else + HasBackgroundColor = false + end if + + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="AcceptFocus" + Visible=true + Group="Behavior" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AcceptTabs" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoDeactivate" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="Background" + Group="Behavior" + InitialValue="&c000000" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Bold" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="ContextName" + Visible=true + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EraseBackground" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Foreground" + Group="Behavior" + InitialValue="&c000000" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="300" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HelpTag" + Visible=true + Group="Appearance" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="InitialParent" + Group="Position" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Italic" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LockBottom" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockLeft" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockRight" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockTop" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="TabIndex" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabPanelIndex" + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabStop" + Visible=true + Group="Position" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Transparent" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Underline" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="UseFocusRing" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="300" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Preference Stuff/WndPreferences.xojo_window b/Preference Stuff/WndPreferences.xojo_window new file mode 100644 index 0000000..7460791 --- /dev/null +++ b/Preference Stuff/WndPreferences.xojo_window @@ -0,0 +1,797 @@ +#tag Window +Begin XsEditWindowBase WndPreferences + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = False + Compatibility = "" + Composite = False + Frame = 1 + FullScreen = False + FullScreenButton= False + HasBackColor = False + Height = 220 + ImplicitInstance= True + LiveResize = True + MacProcID = 0 + MaxHeight = 32000 + MaximizeButton = False + MaxWidth = 32000 + MenuBar = 0 + MenuBarVisible = True + MinHeight = 64 + MinimizeButton = True + MinWidth = 64 + Placement = 0 + Resizeable = False + Title = "Preferences" + Visible = True + Width = 600 + Begin DialogButtonContainer DialogButtonContainer1 + AcceptFocus = False + AcceptTabs = True + AlternateCaption= "Defaults" + AutoDeactivate = True + BackColor = &cFFFFFF00 + Backdrop = 0 + CancelCaption = "Cancel" + Enabled = True + EraseBackground = True + HasBackColor = False + Height = 54 + HelpTag = "" + InitialParent = "" + Left = 0 + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = False + OkCaption = "Save" + Scope = 2 + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + Top = 166 + Transparent = True + UseFocusRing = False + Visible = True + Width = 600 + End + Begin Label Label1 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = 0 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 2 + Selectable = False + TabIndex = 1 + TabPanelIndex = 0 + Text = "Code Font:" + TextAlign = 0 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 20 + Transparent = False + Underline = False + Visible = True + Width = 100 + End + Begin ComboBox cboCodeFont + AutoComplete = True + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialValue = "" + Italic = False + Left = 125 + ListIndex = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + TextFont = "SmallSystem" + TextSize = 0.0 + TextUnit = 0 + Top = 19 + Underline = False + UseFocusRing = True + Visible = True + Width = 238 + End + Begin Label Label1 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = 1 + InitialParent = "" + Italic = False + Left = 409 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 2 + Selectable = False + TabIndex = 3 + TabPanelIndex = 0 + Text = "Size:" + TextAlign = 0 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 21 + Transparent = False + Underline = False + Visible = True + Width = 46 + End + Begin TextField fldCodeSize + AcceptTabs = False + Alignment = 0 + AutoDeactivate = True + AutomaticallyCheckSpelling= False + BackColor = &cFFFFFF00 + Bold = False + Border = True + CueText = "" + DataField = "" + DataSource = "" + Enabled = True + Format = "" + Height = 22 + HelpTag = "" + Index = -2147483648 + Italic = False + Left = 467 + LimitText = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Mask = "999" + Password = False + ReadOnly = False + Scope = 0 + TabIndex = 4 + TabPanelIndex = 0 + TabStop = True + Text = "" + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 20 + Underline = False + UseFocusRing = True + Visible = True + Width = 80 + End + Begin CheckBox cbAutocompleteAppliesStandardCase + AutoDeactivate = True + Bold = False + Caption = "Autocomplete Applies Standard Case" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 5 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 54 + Underline = False + Value = False + Visible = True + Width = 270 + End + Begin CheckBox cbAutoCloseBrackets + AutoDeactivate = True + Bold = False + Caption = "Auto-close Brackets" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 6 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 79 + Underline = False + Value = False + Visible = True + Width = 270 + End + Begin Label Label1 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = 7 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 2 + Selectable = False + TabIndex = 16 + TabPanelIndex = 0 + Text = "Contexts:" + TextAlign = 0 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 111 + Transparent = False + Underline = False + Visible = True + Width = 100 + End + Begin CheckBox cbUseActiveLineHighlight + AutoDeactivate = True + Bold = False + Caption = "Highlight Active Line" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 310 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 21 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 54 + Underline = False + Value = False + Visible = True + Width = 155 + End + Begin ColorPicker_MTC cpActiveLineHighlightColor + AcceptFocus = False + AcceptTabs = False + AutoDeactivate = True + Backdrop = 0 + DoubleBuffer = False + Enabled = True + EraseBackground = True + Height = 32 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = 492 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + PromptText = "Select a color:" + Scope = 0 + TabIndex = 22 + TabPanelIndex = 0 + TabStop = True + Top = 52 + Transparent = True + UseFocusRing = True + Value = &c00000000 + Visible = True + Width = 32 + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Open() + // + // Get the fonts + // + + dim fonts() as string + + dim lastFontIndex as integer = FontCount - 1 + for i as integer = 0 to lastFontIndex + fonts.Append Font( i ) + next + + fonts.Sort + + // + // Prefix the system fonts + // + fonts.Insert 0, "System" + fonts.Insert 1, "SmallSystem" + + AllFonts = fonts + + // + // Set the CodeFont field + // + cboCodeFont.AddRows AllFonts + cboCodeFont.Text = App.Prefs.CodeFont + + // + // Set the CodeFontSize field + // + fldCodeSize.Text = str( App.Prefs.CodeFontSize ) + + // + // Set the AutocompleteAppliesStandardCase checkbox + // + cbAutocompleteAppliesStandardCase.Value = App.Prefs.AutocompleteAppliesStandardCase + + // + // Set the AutoCloseBrackets checkbox + // + cbAutoCloseBrackets.Value = App.Prefs.AutoCloseBrackets + + // + // Set the UseActiveLineHighlight checkbox + // + cbUseActiveLineHighlight.Value = App.Prefs.UseActiveLineHighlight + cpActiveLineHighlightColor.Value = App.Prefs.ActiveLineHighlightColor + cpActiveLineHighlightColor.Visible = cbUseActiveLineHighlight.Value + + // + // Contexts + // + 'ccStringsContext.SetFromPrefs( App.Prefs.ContextString, new ContextPreferences( App.SyntaxDefinitionFile, "String" ) ) + 'ccKeywordsContext.SetFromPrefs( App.Prefs.ContextKeyword, new ContextPreferences( App.SyntaxDefinitionFile, "Keywords" ) ) + 'ccCommentsContext.SetFromPrefs( App.Prefs.ContextComment, new ContextPreferences( App.SyntaxDefinitionFile, "Comment" ) ) + 'ccBasicTypesContext.SetFromPrefs( App.Prefs.ContextBasicType, new ContextPreferences( App.SyntaxDefinitionFile, "BasicTypes" ) ) + + dim contextPrefs() as ContextPreferences = App.Prefs.InterestingContextPrefs + + const kUseLeft = 63 + const kStartTop = 143 + const kTopOffset = 44 + + for i as integer = 0 to contextPrefs.Ubound + dim contextPref as ContextPreferences = contextPrefs( i ) + + dim cc as new TextContextControl + cc.ContextName = contextPref.Name + cc.SetFromPrefs contextPref, new ContextPreferences( App.SyntaxDefinitionFile, contextPref.Name ) + + cc.EmbedWithin self, kUseLeft, kStartTop + ( kTopOffset * i ) + ContextControls.Append cc + + self.Height = self.Height + kTopOffset + next + + End Sub + #tag EndEvent + + + #tag Method, Flags = &h21 + Private Sub RestoreDefaults() + dim prefs as XsEditPreferences = App.Prefs + + cbAutoCloseBrackets.Value = prefs.kDefaultAutoCloseBrackets + cbAutocompleteAppliesStandardCase.Value = prefs.kDefaultAutocompleteAppliesStandardCase + cbUseActiveLineHighlight.Value = prefs.kDefaultUseActiveLineHighlight + cpActiveLineHighlightColor.Value = prefs.kDefaultActiveHighlightColor + cpActiveLineHighlightColor.Visible = cbUseActiveLineHighlight.Value + + cboCodeFont.Text = prefs.kDefaultCodeFont + fldCodeSize.Text = str( prefs.kDefaultCodeFontSize ) + + for each cc as TextContextControl in ContextControls + cc.RestoreDefaults + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function ValidateAndSave() As Boolean + // Will attempt to validate the preferences. If it can't, will beep and somehow indicate the + // faulty pref + + // + // CodeFont + // + dim chosenFont as string = cboCodeFont.Text + dim index as integer = AllFonts.IndexOf( chosenFont ) + if index = -1 then + beep + cboCodeFont.SelStart = 0 + cboCodeFont.SelLength = chosenFont.Len + return false + else + // + // Set it to the right case + // + chosenFont = AllFonts( index ) + end if + + // + // If we get here, we can save + // + App.Prefs.CodeFont = chosenFont + App.Prefs.CodeFontSize = fldCodeSize.Text.Val + App.Prefs.AutocompleteAppliesStandardCase = cbAutocompleteAppliesStandardCase.Value + App.Prefs.AutoCloseBrackets = cbAutoCloseBrackets.Value + App.Prefs.UseActiveLineHighlight = cbUseActiveLineHighlight.Value + App.Prefs.ActiveLineHighlightColor = cpActiveLineHighlightColor.Value + + for each cc as TextContextControl in ContextControls + App.Prefs.ContextPrefValue( cc.ContextName ) = cc.ToContextPreferences + next + + App.Prefs.Save + + return true + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private AllFonts() As String + #tag EndProperty + + #tag Property, Flags = &h21 + Private ContextControls() As TextContextControl + #tag EndProperty + + +#tag EndWindowCode + +#tag Events DialogButtonContainer1 + #tag Event + Sub OkAction() + if ValidateAndSave then + self.Close + end if + End Sub + #tag EndEvent + #tag Event + Sub CancelAction() + self.Close + + End Sub + #tag EndEvent + #tag Event + Sub AlternateAction() + RestoreDefaults + End Sub + #tag EndEvent +#tag EndEvents +#tag Events cbUseActiveLineHighlight + #tag Event + Sub Action() + cpActiveLineHighlightColor.Visible = me.Value + End Sub + #tag EndEvent +#tag EndEvents +#tag Events cpActiveLineHighlightColor + #tag Event + Function ConstructContextualMenu(base as MenuItem, x as Integer, y as Integer) As Boolean + #pragma unused x + #pragma unused y + + base.Append new MenuItem( "Restore Default" ) + return true + End Function + #tag EndEvent + #tag Event + Function ContextualMenuAction(hitItem as MenuItem) As Boolean + select case hitItem.Text + case "Restore Default" + me.Value = App.Prefs.kDefaultActiveHighlightColor + return true + + end select + + return false + End Function + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Preference Stuff/XsEditPreferences.xojo_code b/Preference Stuff/XsEditPreferences.xojo_code new file mode 100644 index 0000000..a0541da --- /dev/null +++ b/Preference Stuff/XsEditPreferences.xojo_code @@ -0,0 +1,324 @@ +#tag Class +Protected Class XsEditPreferences +Inherits Preferences + #tag Method, Flags = &h1000 + Sub Constructor(applicationId as String) + // Calling the overridden superclass constructor. + Super.Constructor(applicationId) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function ContextPrefValue(contextName As String) As ContextPreferences + return ContextPreferences( ObjectValue( contextName + " Context" , new ContextPreferences( App.SyntaxDefinitionFile, contextName ) ) ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ContextPrefValue(contextName As String, Assigns value As ContextPreferences) + ObjectValue( contextName + " Context" ) = value + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function InterestingContextPrefs() As ContextPreferences() + dim interestingNames() as string = array( _ + "Keywords", "BasicTypes", "Doubles", "Integers", "String", "PreProcessor", "Comment" _ + ) + + dim arr() as ContextPreferences + for i as integer = 0 to interestingNames.Ubound + dim contextName as string = interestingNames( i ) + arr.Append ContextPrefValue( contextName ) + next + + return arr + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function InterestingContextPrefsDictionary() As Dictionary + dim arr() as ContextPreferences = InterestingContextPrefs + dim d as new Dictionary + for each pref as ContextPreferences in arr + d.Value( pref.Name ) = pref + next + + return d + + End Function + #tag EndMethod + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return ColorValue( kPrefActiveLineHighlightColor, kDefaultActiveHighlightColor ) + End Get + #tag EndGetter + #tag Setter + Set + ColorValue( kPrefActiveLineHighlightColor ) = value + End Set + #tag EndSetter + ActiveLineHighlightColor As Color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return BooleanValue( kPrefAutoCloseBrackets, kDefaultAutoCloseBrackets ) + End Get + #tag EndGetter + #tag Setter + Set + BooleanValue( kPrefAutoCloseBrackets ) = value + End Set + #tag EndSetter + AutoCloseBrackets As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return BooleanValue( kPrefAutocompleteAppliesStandardCase, kDefaultAutocompleteAppliesStandardCase ) + End Get + #tag EndGetter + #tag Setter + Set + BooleanValue( kPrefAutocompleteAppliesStandardCase ) = value + End Set + #tag EndSetter + AutocompleteAppliesStandardCase As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return StringValue( kPrefCodeFont, kDefaultCodeFont ) + + End Get + #tag EndGetter + #tag Setter + Set + StringValue( kPrefCodeFont ) = value + + End Set + #tag EndSetter + CodeFont As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return IntegerValue( kPrefCodeFontSize, kDefaultCodeFontSize ) + End Get + #tag EndGetter + #tag Setter + Set + IntegerValue( kPrefCodeFontSize ) = value + End Set + #tag EndSetter + CodeFontSize As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return BooleanValue( kPrefShowInvisibles, kDefaultShowInvisibles ) + End Get + #tag EndGetter + #tag Setter + Set + BooleanValue( kPrefShowInvisibles ) = value + + End Set + #tag EndSetter + ShowInvisibles As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return BooleanValue( kPrefShowLineNumbers, kDefaultShowLineNumbers ) + End Get + #tag EndGetter + #tag Setter + Set + BooleanValue( kPrefShowLineNumbers ) = value + End Set + #tag EndSetter + ShowLineNumbers As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return BooleanValue( kPrefShowToolbar, kDefaultShowToolbar ) + End Get + #tag EndGetter + #tag Setter + Set + BooleanValue( kPrefShowToolbar ) = value + End Set + #tag EndSetter + ShowToolbar As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return BooleanValue( kPrefUseActiveLineHighlight, kDefaultUseActiveLineHighlight ) + End Get + #tag EndGetter + #tag Setter + Set + BooleanValue( kPrefUseActiveLineHighlight ) = value + + End Set + #tag EndSetter + UseActiveLineHighlight As Boolean + #tag EndComputedProperty + + + #tag Constant, Name = kDefaultActiveHighlightColor, Type = Color, Dynamic = False, Default = \"&cF4FF9C", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultAutoCloseBrackets, Type = Boolean, Dynamic = False, Default = \"False", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultAutocompleteAppliesStandardCase, Type = Boolean, Dynamic = False, Default = \"False", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultCodeFont, Type = String, Dynamic = False, Default = \"System", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultCodeFontSize, Type = Double, Dynamic = False, Default = \"0", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultShowInvisibles, Type = Boolean, Dynamic = False, Default = \"False", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultShowLineNumbers, Type = Boolean, Dynamic = False, Default = \"True", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultShowToolbar, Type = Boolean, Dynamic = False, Default = \"True", Scope = Public + #tag EndConstant + + #tag Constant, Name = kDefaultUseActiveLineHighlight, Type = Boolean, Dynamic = False, Default = \"True", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefActiveLineHighlightColor, Type = String, Dynamic = False, Default = \"ActiveLine Highlight Color", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefAutoCloseBrackets, Type = String, Dynamic = False, Default = \"AutoCloseBrackets", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefAutocompleteAppliesStandardCase, Type = String, Dynamic = False, Default = \"AutocompleteAppliesStandardCase", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefCodeFont, Type = String, Dynamic = False, Default = \"CodeFont", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefCodeFontSize, Type = String, Dynamic = False, Default = \"CodeFontSize", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefShowInvisibles, Type = String, Dynamic = False, Default = \"ShowInvisibles", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefShowLineNumbers, Type = String, Dynamic = False, Default = \"ShowLineNumbers", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefShowToolbar, Type = String, Dynamic = False, Default = \"ShowToolbar", Scope = Public + #tag EndConstant + + #tag Constant, Name = kPrefUseActiveLineHighlight, Type = String, Dynamic = False, Default = \"Use Active Line Highlight", Scope = Public + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="ActiveLineHighlightColor" + Group="Behavior" + InitialValue="&c000000" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="AutoCloseBrackets" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutocompleteAppliesStandardCase" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="CodeFont" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="CodeFontSize" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="ShowInvisibles" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="ShowLineNumbers" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="ShowToolbar" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="UseActiveLineHighlight" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/README.md b/README.md new file mode 100644 index 0000000..1256b0c --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# README + +__XsEdit__ +An advanced XojoScript editor for [Xojo®](http://www.xojo.com/). + +## General Information + +The Xojo IDE has a built-in script editor, but it's missing some handy features like auto-complete or even line numbers. XsEdit fills that gap with a full-featured editor that still runs the scripts through the IDE. + +## Benefits + +Some of the benefits of XsEdit over the native XojoScript editor: + +- Line numbers. +- Compiler error messages and highlighting. +- Auto-complete. +- Find and replace dialog. +- Comment and Uncomment sections of code. +- Include external files to emulate context. + +## How To Use It + +Download and build the project. Start up the XsEdit app and either create a new script or open an existing one. Use the Compile and Run In IDE buttons to compile or run. + +### About Test Run + +The Test Run button will force your script to run directly in XsEdit. This is good for testing small scripts but any IDE scripting methods or functions like `Location` will not actually do anything. + +## About Auto-complete + +Auto-complete works, but it is not scope or context aware. It merely auto-completes any known keyword or any variable, module, class, interface, etc., that you've defined earlier in the script whether you can access to it at that point or not, and whether it makes sense the way you are using it or not. + +## About Includes + +You can write, compile, and run an IDE script without doing anything special, but you might want to edit a script that's meant to run in your app that might have a context assigned. To address this, XsEdit will allow you to include one or more external files by starting a line like this: + +``` +'#include /path/to/file.xojo_script +``` + +or this: + +``` +//#include file.xojo_script +``` + +(You may also use either an absolute path, or relative path to your current script.) + +Or you can use the menu option to select the file as a convenience. + +At compile time, XsEdit will substitute the contents of the included file at the point of the `include` directive. Within the included file, you can recreate the functions, methods, and properties of your context. (They don't actually have to do anything, just keep the compiler from complaining.) + +## Who Did This? + +This project was designed and implemented by: + +* Kem Tekinay (ktekinay at mactechnologies.com) +* Jeremy Cowgar (jeremy at cowgar.com) + +With special thanks to Thomas Tempelmann and his [CustomEditfield](https://github.com/tempelmann/custom-editfield). + +## FAQ + +**How much does this cost?** + +One-TRILLION dollars!! Or nothing, your choice. + +You may freely use this in any project, but don't come back to us if it erases your hard drive or paints your house yellow or something. See the included LICENSE.txt file for details. + +**How do I contribute?** + +Fork the project to your GitHub account. Use the "develop" branch for general fixes. Make your changes, then submit a pull request. We'll look it over and merge what's appropriate or provide feedback. + +## Release Notes + +1.0 (July 7, 2015) + +- Initial release. + diff --git a/Search Stuff/SearchOptions.xojo_code b/Search Stuff/SearchOptions.xojo_code new file mode 100644 index 0000000..036dda5 --- /dev/null +++ b/Search Stuff/SearchOptions.xojo_code @@ -0,0 +1,88 @@ +#tag Class +Protected Class SearchOptions + #tag Property, Flags = &h0 + FindTerm As String + #tag EndProperty + + #tag Property, Flags = &h0 + IsCaseSensitive As Boolean + #tag EndProperty + + #tag Property, Flags = &h0 + IsWholeWord As Boolean + #tag EndProperty + + #tag Property, Flags = &h0 + IsWrapAround As Boolean = True + #tag EndProperty + + #tag Property, Flags = &h0 + ReplaceTerm As String + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="FindTerm" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="IsCaseSensitive" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="IsWholeWord" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="IsWrapAround" + Group="Behavior" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="ReplaceTerm" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Search Stuff/SearchReceiverWindowBase.xojo_code b/Search Stuff/SearchReceiverWindowBase.xojo_code new file mode 100644 index 0000000..1513ab8 --- /dev/null +++ b/Search Stuff/SearchReceiverWindowBase.xojo_code @@ -0,0 +1,344 @@ +#tag Class +Protected Class SearchReceiverWindowBase +Inherits XsEditWindowBase + #tag Event + Sub Activate() + mActiveWindow = self + RaiseEvent Activate + + End Sub + #tag EndEvent + + #tag Event + Sub Close() + RaiseEvent Close + if mActiveWindow is self then + mActiveWindow = nil + end if + + if WndSearch.IsOpen then + WndSearch.AdjustControls + end if + End Sub + #tag EndEvent + + + #tag MenuHandler + Function EditFind() As Boolean Handles EditFind.Action + WndSearch.Show + + Return True + + End Function + #tag EndMenuHandler + + + #tag Method, Flags = &h0 + Shared Function ActiveWindow() As SearchReceiverWindowBase + return mActiveWindow + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub RaiseSearchEvent(type As FindTypes, options As SearchOptions) + select case type + case FindTypes.FindNext + RaiseEvent FindNext( options ) + + case FindTypes.FindPrevious + RaiseEvent FindPrevious( options ) + + case FindTypes.FindAll + RaiseEvent FindAll( options ) + + case FindTypes.Replace + RaiseEvent ReplaceOne( options ) + + case FindTypes.ReplaceAndFind + RaiseEvent ReplaceAndFindNext( options ) + + case FindTypes.ReplaceAll + RaiseEvent ReplaceAll( options ) + + end + End Sub + #tag EndMethod + + + #tag Hook, Flags = &h0 + Event Activate() + #tag EndHook + + #tag Hook, Flags = &h0 + Event Close() + #tag EndHook + + #tag Hook, Flags = &h0 + Event FindAll(options As SearchOptions) + #tag EndHook + + #tag Hook, Flags = &h0 + Event FindNext(options As SearchOptions) + #tag EndHook + + #tag Hook, Flags = &h0 + Event FindPrevious(options As SearchOptions) + #tag EndHook + + #tag Hook, Flags = &h0 + Event ReplaceAll(options As SearchOptions) + #tag EndHook + + #tag Hook, Flags = &h0 + Event ReplaceAndFindNext(options As SearchOptions) + #tag EndHook + + #tag Hook, Flags = &h0 + Event ReplaceOne(options As SearchOptions) + #tag EndHook + + + #tag Property, Flags = &h21 + Private Shared mActiveWindow As SearchReceiverWindowBase + #tag EndProperty + + + #tag Enum, Name = FindTypes, Type = Integer, Flags = &h0 + FindNext + FindPrevious + FindAll + Replace + ReplaceAndFind + ReplaceAll + #tag EndEnum + + + #tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Search Stuff/WndSearch.xojo_window b/Search Stuff/WndSearch.xojo_window new file mode 100644 index 0000000..48e29c4 --- /dev/null +++ b/Search Stuff/WndSearch.xojo_window @@ -0,0 +1,941 @@ +#tag Window +Begin XsEditWindowBase WndSearch + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = True + Compatibility = "" + Composite = False + Frame = 0 + FullScreen = False + FullScreenButton= False + HasBackColor = False + Height = 268 + ImplicitInstance= True + LiveResize = True + MacProcID = 0 + MaxHeight = 32000 + MaximizeButton = False + MaxWidth = 32000 + MenuBar = 0 + MenuBarVisible = True + MinHeight = 64 + MinimizeButton = True + MinWidth = 64 + Placement = 2 + Resizeable = False + Title = "Search" + Visible = True + Width = 600 + Begin PlatformPushButton btnFindNext + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "Next" + Default = True + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 446 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 20 + Underline = False + Visible = True + Width = 125 + End + Begin PlatformPushButton btnReplaceOne + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "&Replace" + Default = False + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 446 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + TabIndex = 1 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 155 + Underline = False + Visible = True + Width = 125 + End + Begin PlatformPushButton btnReplaceAll + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "Replace &All" + Default = False + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 446 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 228 + Underline = False + Visible = True + Width = 125 + End + Begin PlatformPushButton btnFindPrevious + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "&Previous" + Default = False + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 446 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + TabIndex = 4 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 52 + Underline = False + Visible = True + Width = 125 + End + Begin PlatformPushButton btnFindAll + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "&Find All" + Default = False + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 446 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + TabIndex = 5 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 93 + Underline = False + Visible = True + Width = 125 + End + Begin PlatformPushButton btnReplaceAndFind + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "R&eplace && Find" + Default = False + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 446 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + TabIndex = 6 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 187 + Underline = False + Visible = True + Width = 125 + End + Begin Label Label1 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = 0 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 2 + Selectable = False + TabIndex = 7 + TabPanelIndex = 0 + TabStop = True + Text = "Find:" + TextAlign = 0 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 20 + Transparent = False + Underline = False + Visible = True + Width = 100 + End + Begin Label Label1 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = 1 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 2 + Selectable = False + TabIndex = 8 + TabPanelIndex = 0 + TabStop = True + Text = "Replace:" + TextAlign = 0 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 155 + Transparent = False + Underline = False + Visible = True + Width = 100 + End + Begin TextField fldFind + AcceptTabs = False + Alignment = 0 + AutoDeactivate = True + AutomaticallyCheckSpelling= False + BackColor = &cFFFFFF00 + Bold = False + Border = True + CueText = "" + DataField = "" + DataSource = "" + Enabled = True + Format = "" + Height = 62 + HelpTag = "" + Index = -2147483648 + Italic = False + Left = 20 + LimitText = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Mask = "" + Password = False + ReadOnly = False + Scope = 2 + TabIndex = 9 + TabPanelIndex = 0 + TabStop = True + Text = "" + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 51 + Underline = False + UseFocusRing = True + Visible = True + Width = 414 + End + Begin TextField fldReplace + AcceptTabs = False + Alignment = 0 + AutoDeactivate = True + AutomaticallyCheckSpelling= False + BackColor = &cFFFFFF00 + Bold = False + Border = True + CueText = "" + DataField = "" + DataSource = "" + Enabled = True + Format = "" + Height = 62 + HelpTag = "" + Index = -2147483648 + Italic = False + Left = 20 + LimitText = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Mask = "" + Password = False + ReadOnly = False + Scope = 2 + TabIndex = 10 + TabPanelIndex = 0 + TabStop = True + Text = "" + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 186 + Underline = False + UseFocusRing = True + Visible = True + Width = 414 + End + Begin CheckBox cbCaseSensitive + AutoDeactivate = True + Bold = False + Caption = "Case Sensitive" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 11 + TabPanelIndex = 0 + TabStop = True + TextFont = "SmallSystem" + TextSize = 0.0 + TextUnit = 0 + Top = 125 + Underline = False + Value = False + Visible = True + Width = 119 + End + Begin CheckBox cbWrapAround + AutoDeactivate = True + Bold = False + Caption = "Wrap Around" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 151 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 12 + TabPanelIndex = 0 + TabStop = True + TextFont = "SmallSystem" + TextSize = 0.0 + TextUnit = 0 + Top = 125 + Underline = False + Value = False + Visible = True + Width = 101 + End + Begin CheckBox cbWholeWord + AutoDeactivate = True + Bold = False + Caption = "Whole Word" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 292 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 2 + State = 0 + TabIndex = 13 + TabPanelIndex = 0 + TabStop = True + TextFont = "SmallSystem" + TextSize = 0.0 + TextUnit = 0 + Top = 125 + Underline = False + Value = False + Visible = True + Width = 101 + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Activate() + IsActive = true + AdjustControls + + End Sub + #tag EndEvent + + #tag Event + Sub Close() + mIsOpen = false + End Sub + #tag EndEvent + + #tag Event + Sub Deactivate() + IsActive = false + AdjustControls + End Sub + #tag EndEvent + + #tag Event + Sub EnableMenuItems() + EditFindNext.Enabled = Options.FindTerm <> "" + EditFindPrevious.Enabled = Options.FindTerm <> "" + End Sub + #tag EndEvent + + #tag Event + Sub Open() + mIsOpen = true + End Sub + #tag EndEvent + + + #tag MenuHandler + Function EditFindNext() As Boolean Handles EditFindNext.Action + btnFindNext.Push + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditFindPrevious() As Boolean Handles EditFindPrevious.Action + btnFindPrevious.Push + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function FileClose() As Boolean Handles FileClose.Action + self.Close + Return True + + End Function + #tag EndMenuHandler + + + #tag Method, Flags = &h0 + Sub AdjustControls() + dim isActive as boolean = SearchReceiverWindowBase.ActiveWindow IsA Window and IsActive + dim hasSearchTerm as boolean = Options.FindTerm <> "" + + btnFindNext.Enabled = isActive and hasSearchTerm + btnFindPrevious.Enabled = isActive and hasSearchTerm + btnFindAll.Enabled = isActive and hasSearchTerm + + btnReplaceOne.Enabled = isActive and hasSearchTerm + btnReplaceAndFind.Enabled = isActive and hasSearchTerm + btnReplaceAll.Enabled = isActive and hasSearchTerm + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Shared Function IsOpen() As Boolean + return mIsOpen + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private IsActive As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mIsOpen As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mOptions As SearchOptions + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if mOptions is nil then + mOptions = new SearchOptions + end if + + return mOptions + End Get + #tag EndGetter + #tag Setter + Set + mOptions = value + + End Set + #tag EndSetter + Shared Options As SearchOptions + #tag EndComputedProperty + + +#tag EndWindowCode + +#tag Events btnFindNext + #tag Event + Sub Action() + SearchReceiverWindowBase.ActiveWindow.Show + SearchReceiverWindowBase.ActiveWindow.RaiseSearchEvent(SearchReceiverWindowBase.FindTypes.FindNext, Options ) + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnReplaceOne + #tag Event + Sub Action() + SearchReceiverWindowBase.ActiveWindow.RaiseSearchEvent(SearchReceiverWindowBase.FindTypes.Replace, Options ) + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnReplaceAll + #tag Event + Sub Action() + SearchReceiverWindowBase.ActiveWindow.Show + SearchReceiverWindowBase.ActiveWindow.RaiseSearchEvent(SearchReceiverWindowBase.FindTypes.ReplaceAll, Options ) + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnFindPrevious + #tag Event + Sub Action() + SearchReceiverWindowBase.ActiveWindow.Show + SearchReceiverWindowBase.ActiveWindow.RaiseSearchEvent(SearchReceiverWindowBase.FindTypes.FindPrevious, Options ) + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnFindAll + #tag Event + Sub Action() + SearchReceiverWindowBase.ActiveWindow.Show + SearchReceiverWindowBase.ActiveWindow.RaiseSearchEvent(SearchReceiverWindowBase.FindTypes.FindAll, Options ) + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnReplaceAndFind + #tag Event + Sub Action() + SearchReceiverWindowBase.ActiveWindow.RaiseSearchEvent(SearchReceiverWindowBase.FindTypes.ReplaceAndFind, Options ) + End Sub + #tag EndEvent +#tag EndEvents +#tag Events fldFind + #tag Event + Sub TextChange() + Options.FindTerm = me.Text + AdjustControls + End Sub + #tag EndEvent + #tag Event + Sub Open() + me.Text = WndSearch.Options.FindTerm + End Sub + #tag EndEvent +#tag EndEvents +#tag Events fldReplace + #tag Event + Sub TextChange() + Options.ReplaceTerm = me.Text + AdjustControls + End Sub + #tag EndEvent + #tag Event + Sub Open() + me.Text = Options.ReplaceTerm + End Sub + #tag EndEvent +#tag EndEvents +#tag Events cbCaseSensitive + #tag Event + Sub Open() + me.Value = Options.IsCaseSensitive + End Sub + #tag EndEvent + #tag Event + Sub Action() + Options.IsCaseSensitive = me.value + End Sub + #tag EndEvent +#tag EndEvents +#tag Events cbWrapAround + #tag Event + Sub Open() + me.Value = WndSearch.Options.IsWrapAround + End Sub + #tag EndEvent + #tag Event + Sub Action() + Options.IsWrapAround = me.Value + End Sub + #tag EndEvent +#tag EndEvents +#tag Events cbWholeWord + #tag Event + Sub Open() + me.Value = WndSearch.Options.IsWholeWord + End Sub + #tag EndEvent + #tag Event + Sub Action() + Options.IsWholeWord = me.Value + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Third Party Classes/CustomEditField/Autocomplete/AutocompleteOptions.xojo_code b/Third Party Classes/CustomEditField/Autocomplete/AutocompleteOptions.xojo_code new file mode 100644 index 0000000..364d466 --- /dev/null +++ b/Third Party Classes/CustomEditField/Autocomplete/AutocompleteOptions.xojo_code @@ -0,0 +1,74 @@ +#tag Class +Protected Class AutocompleteOptions + #tag Property, Flags = &h0 + CurrentPathComponent As string + #tag EndProperty + + #tag Property, Flags = &h0 + LongestCommonPrefix As string + #tag EndProperty + + #tag Property, Flags = &h0 + Options() As string + #tag EndProperty + + #tag Property, Flags = &h0 + Prefix As string + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="CurrentPathComponent" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LongestCommonPrefix" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Prefix" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrie.xojo_code b/Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrie.xojo_code new file mode 100644 index 0000000..1bae97d --- /dev/null +++ b/Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrie.xojo_code @@ -0,0 +1,118 @@ +#tag Class +Protected Class PaTrie + #tag Method, Flags = &h0 + Function addKey(key as string, data as variant = nil) As PatrieNode + if key = "" then Return nil + + if key.Encoding = nil then + key = key.DefineEncoding(Encodings.UTF8) + end if + + dim tmpNode as PaTrieNode + dim index as Integer = key.InStr(".") + + //if there's no path component to the key... + if index = 0 then + Return root.addKey(Key, data) + end if + + //now, if there is, find the parent node + dim keyPath as String = key.Left(index - 1) + dim searchPath as String + + tmpNode = root.findNode(keyPath, searchPath) + if tmpNode = nil then + //if not found, add the node + tmpNode = root.addKey(keyPath) + end if + + //strip the path from the key, and add it. + if tmpNode.KeyMembers = nil then tmpNode.KeyMembers = new PaTrie + return tmpNode.KeyMembers.addKey(key.Right(key.Len - index), data) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor() + root = new PaTrieNode + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function wordsForPrefix(key as string, byref longestCommonPrefix as string) As string() + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + dim result() as String + + dim searchPath as String + dim node as PaTrieNode + dim index as Integer = key.InStr(".") + + //no path + if index = 0 then + node = root.findNode(key, searchPath) + if node = nil then Return result + + //node IS the longestCommonPrefix + longestCommonPrefix = searchPath + node.Key + node.subnodePaths(searchPath, result) + Return result + end if + + //extract path and continue search. + dim keyPath as String = key.Left(index - 1) + node = root.findNode(keyPath, searchPath) + if node = nil then Return result + + //if searchPath + node.key <> keyPath then keyPath is a valid prefix for the node, but it's not the node! + if searchPath + node.Key <> keyPath then Return result + + if node.KeyMembers = nil then Return result + Return node.KeyMembers.wordsForPrefix(key.Right(key.Len - index), longestCommonPrefix) + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private Root As patrienode + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrieNode.xojo_code b/Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrieNode.xojo_code new file mode 100644 index 0000000..4b6e33c --- /dev/null +++ b/Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrieNode.xojo_code @@ -0,0 +1,241 @@ +#tag Class +Protected Class PaTrieNode + #tag Method, Flags = &h0 + Function addKey(key as string, data as variant = nil) As PaTrieNode + #pragma DisableBackgroundTasks + + if key = "" then + //overwrite data + self.Data = data + + //if this node was marked as non-terminal, mark it as terminal + self.intermediateNode = False + + //but it's me! + Return self + end if + + //if no children, just add a new one with the key + if UBound(SubNodes) < 0 then + Return AppendNewNode(key, data) + end if + + //there's at least one subnode + //find the best match for the key + dim bestIndex as Integer = -1 + dim bestLength, currentLength as Integer + for i as Integer = 0 to UBound(SubNodes) + currentLength = SubNodes(i).Key.longestCommonPrefixIndex(key) + if currentLength > bestLength then + bestLength = currentLength + bestIndex = i + end if + next + + //if no suitable children found, just add the node + if bestIndex < 0 then + Return AppendNewNode(key, data) + + end if + + dim matchNode as PaTrieNode = SubNodes(bestIndex) + + if bestLength = matchNode.Key.Len then + //current node is a perfect prefix for the key + //remove prefix and recurse + key = key.Mid(bestLength + 1, key.Len - bestLength) + Return matchNode.AddKey(key, data) + end if + + //key and node share a common prefix + //split current node and add both prefixes + return matchNode.SplitNode(key, data, bestLength) + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function appendNewNode(withKey as string, andData as variant) As PaTrieNode + dim newNode as new PaTrieNode + newNode.Key = withKey + newNode.Data = andData + SubNodes.Append newNode + keys.Append withKey.Left(1) + sortedNodes = False + Return newNode + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function findNode(key as string, byref path as string) As PaTrieNode + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + if key = "" then Return self + if UBound(SubNodes) < 0 then Return Nil + + dim prefixLength as Integer + dim bestMatch as Integer = -1 + dim bestLength as Integer + + //find the best match + for i as Integer = 0 to UBound(SubNodes) + prefixLength = SubNodes(i).Key.longestCommonPrefixIndex(key) + if prefixLength > bestLength then + bestLength = prefixLength + bestMatch = i + end if + next + + //no match found + if bestMatch < 0 then Return nil + + //check if key is contained in SubNode's key, if not, it can't be a match! + dim compareLength as Integer = min(key.Len, SubNodes(bestMatch).key.len) + if key.left(compareLength) <> SubNodes(bestMatch).Key.left(compareLength) then Return nil + + //continue search among subnodes + key = key.Mid(bestLength + 1, key.len - bestLength) + path = path + self.Key + Return SubNodes(bestMatch).findNode(key, path) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function splitNode(key as string, data as variant, prefixLength as integer) As PaTrieNode + dim common as String = self.Key.Mid(1, prefixLength) + + '//copy trailing key from current node to a new one + dim SubNodesCopy() as PaTrieNode + dim keysCopy() as String + ReDim SubNodesCopy(UBound(self.SubNodes)) + ReDim keysCopy(UBound(self.SubNodes)) + for i as Integer = 0 to UBound(self.SubNodes) + SubNodesCopy(i) = self.SubNodes(i) + keysCopy(i) = keys(i) + next + ReDim SubNodes(-1) + ReDim keys(-1) + + dim node1 as PaTrieNode = AppendNewNode(self.Key.Mid(prefixLength + 1, self.Key.Len - prefixLength), self.Data) + node1.SubNodes = SubNodesCopy + node1.keys = keysCopy + node1.intermediateNode = self.intermediateNode + node1.KeyMembers = self.KeyMembers + self.KeyMembers = nil + + //trailing key from new key to new node + dim node2 as PaTrieNode = AppendNewNode(key.Mid(prefixLength + 1, key.Len - prefixLength), data) + + self.Key = common + self.Data = nil + self.intermediateNode = true + + Return node2 + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub subnodePaths(path as string, where() as string) + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + if not sortedNodes then + keys.SortWith(SubNodes) + sortedNodes = true + end if + + if not intermediateNode then + if path + key <> "" then _ + where.Append path+key + end if + + for i as Integer = 0 to UBound(SubNodes) + SubNodes(i).subnodePaths(path+key, where) + Next + End Sub + #tag EndMethod + + + #tag Note, Name = Code adapted from + Code adapted from: + http://www.codeproject.com/KB/cs/iptocountry.aspx + + I made some modifications (such as keypaths) and simplified the code. + #tag EndNote + + + #tag Property, Flags = &h0 + Data As Variant + #tag EndProperty + + #tag Property, Flags = &h21 + Private intermediateNode As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + Key As String + #tag EndProperty + + #tag Property, Flags = &h0 + KeyMembers As Patrie + #tag EndProperty + + #tag Property, Flags = &h21 + Private Keys() As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private sortedNodes As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + SubNodes() As PaTrieNode + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Key" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Autocomplete/SuggestionWindow.xojo_window b/Third Party Classes/CustomEditField/Autocomplete/SuggestionWindow.xojo_window new file mode 100644 index 0000000..41e6d60 --- /dev/null +++ b/Third Party Classes/CustomEditField/Autocomplete/SuggestionWindow.xojo_window @@ -0,0 +1,554 @@ +#tag Window +Begin Window SuggestionWindow + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = False + Compatibility = "" + Composite = False + Frame = 3 + FullScreen = False + FullScreenButton= False + HasBackColor = False + Height = 300 + ImplicitInstance= True + LiveResize = True + MacProcID = 1040 + MaxHeight = 32000 + MaximizeButton = False + MaxWidth = 32000 + MenuBar = 0 + MenuBarVisible = True + MinHeight = 64 + MinimizeButton = False + MinWidth = 64 + Placement = 0 + Resizeable = False + Title = "" + Visible = True + Width = 120 + Begin ListBox optionList + AutoDeactivate = True + AutoHideScrollbars= True + Bold = False + Border = True + ColumnCount = 1 + ColumnsResizable= False + ColumnWidths = "" + DataField = "" + DataSource = "" + DefaultRowHeight= 14 + Enabled = True + EnableDrag = False + EnableDragReorder= False + GridLinesHorizontal= 0 + GridLinesVertical= 0 + HasHeading = False + HeadingIndex = -1 + Height = 300 + HelpTag = "" + Hierarchical = False + Index = -2147483648 + InitialParent = "" + InitialValue = "" + Italic = False + Left = 0 + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = True + RequiresSelection= False + Scope = 0 + ScrollbarHorizontal= False + ScrollBarVertical= True + SelectionType = 0 + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + TextFont = "SmallSystem" + TextSize = 0.0 + TextUnit = 0 + Top = 0 + Underline = False + UseFocusRing = False + Visible = True + Width = 120 + _ScrollOffset = 0 + _ScrollWidth = -1 + End + Begin Timer Timer1 + Enabled = True + Height = "32" + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Mode = 0 + Period = 10 + Scope = 0 + TabIndex = "1" + TabPanelIndex = 0 + TabStop = True + Top = 305 + Visible = True + Width = "32" + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Open() + #if TargetWin32 + Const WS_BORDER = &H800000 + ChangeWindowStyle( self, WS_BORDER, false ) + + Const WS_CAPTION = &h00C00000 + ChangeWindowStyle( self, WS_CAPTION, false ) + #endif + End Sub + #tag EndEvent + + + #tag Method, Flags = &h21 + Private Sub cancel(requestFocus as boolean) + //cancel action + if optionSubmitted then Return + optionSubmitted = true + + dim msg as new Message(self, self) + msg.addInfo(1, AutocompleteCancelledMsg) + msg.addInfo(2, requestFocus) + MessageCenter.sendMessage(msg) + + StartTimer + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ChangeWindowStyle(w as Window, flag as Integer, set as Boolean) + #pragma unused set + #if TargetWin32 + Dim oldFlags as Integer + Dim newFlags as Integer + Dim styleFlags As Integer + + Const SWP_NOSIZE = &H1 + Const SWP_NOMOVE = &H2 + Const SWP_NOZORDER = &H4 + Const SWP_FRAMECHANGED = &H20 + + Const GWL_STYLE = -16 + + Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (hwnd As Integer, _ + nIndex As Integer) As Integer + Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (hwnd As Integer, _ + nIndex As Integer, dwNewLong As Integer) As Integer + Declare Function SetWindowPos Lib "user32" (hwnd as Integer, hWndInstertAfter as Integer, _ + x as Integer, y as Integer, cx as Integer, cy as Integer, flags as Integer) as Integer + + oldFlags = GetWindowLong(w.WinHWND, GWL_STYLE) + + if not set then + newFlags = BitwiseAnd( oldFlags, Bitwise.OnesComplement( flag ) ) + else + newFlags = BitwiseOr( oldFlags, flag ) + end + + + styleFlags = SetWindowLong( w.WinHWND, GWL_STYLE, newFlags ) + styleFlags = SetWindowPos( w.WinHWND, 0, 0, 0, 0, 0, SWP_NOMOVE +_ + SWP_NOSIZE + SWP_NOZORDER + SWP_FRAMECHANGED ) + #else + #pragma unused w + #pragma unused flag + #endif + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub loadSuggestions(options() as string) + //load options + optionList.DeleteAllRows + + dim option as String + for each option in options + optionList.AddRow option + static p as new Picture(1, 1, 32) + dim neededWidth as integer = p.Graphics.StringWidth(option) + if neededWidth > self.Width then //auto-expand to fit the options. Thanks to Dr Gerard Hammond + self.Width = neededWidth + 10 + end + next + if optionList.ListCount > 0 then _ + optionList.ListIndex = 0 + + me.Height = min(optionList.ListCount * optionList.DefaultRowHeight + 4, Screen(0).Height/2) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub show(left as integer, top as integer) + //get options + dim options as AutocompleteOptions + dim msg as new Message(self, self) + msg.addInfo(1, CurrentAutocompleteOptionsMsg) + MessageCenter.sendMessage(msg) + options = msg.Info(3) + + if options = nil then Return + + //load suggestions + loadSuggestions(options.Options) + + me.Left = Left + me.Top = top + + if me.top + me.Height > Screen(0).Height then + me.Top = Screen(0).Height - me.Height + end if + + Super.Show + super.SetFocus + optionList.SetFocus + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub StartTimer() + //this is a workaround to close the window without crashing, don't know why + timer1.Mode = timer.ModeSingle + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub submit(what as string = "") + //submit selected option + if optionSubmitted then Return + optionSubmitted = true + + dim option as String + if what ="" then + option = optionList.Text + else + option = what + end if + + dim msg as new Message(self, self) + msg.addInfo(1, OptionSelectedMsg) + msg.addInfo(2, Option) + MessageCenter.sendMessage(msg) + + StartTimer + End Sub + #tag EndMethod + + + #tag Property, Flags = &h21 + Private optionSubmitted As boolean + #tag EndProperty + + + #tag Constant, Name = AutocompleteCancelledMsg, Type = Double, Dynamic = False, Default = \"1", Scope = Public + #tag EndConstant + + #tag Constant, Name = CurrentAutocompleteOptionsMsg, Type = Double, Dynamic = False, Default = \"2", Scope = Public + #tag EndConstant + + #tag Constant, Name = KeyDownMsg, Type = Double, Dynamic = False, Default = \"3", Scope = Public + #tag EndConstant + + #tag Constant, Name = OptionSelectedMsg, Type = Double, Dynamic = False, Default = \"4", Scope = Public + #tag EndConstant + + +#tag EndWindowCode + +#tag Events optionList + #tag Event + Function CellClick(row as Integer, column as Integer, x as Integer, y as Integer) As Boolean + #pragma unused x + #pragma unused y + + submit(me.cell(row,column)) + End Function + #tag EndEvent + #tag Event + Function KeyDown(Key As String) As Boolean + 'MsgBox str(asc(key)) + select case asc(key) + case 27, 8, 127 + cancel(true) + + case 9, 13, 3, 32 + submit + + case 28, 29, 30, 31 + Return False + + else + dim options as AutocompleteOptions + + dim msg as new Message(self, self) + Msg.addInfo(1, KeyDownMsg) + msg.addInfo(2, key) + MessageCenter.sendMessage(Msg) + + //KeyDownMsg + msg = new Message(self, self) + msg.addInfo(1, CurrentAutocompleteOptionsMsg) + MessageCenter.sendMessage(msg) + //msg should have the options now + options = msg.Info(3) + + if options = nil then + cancel(true) + Return true + end if + + loadSuggestions(options.Options) + if optionList.ListCount = 0 then cancel(true) + end select + Return true + End Function + #tag EndEvent + #tag Event + Sub LostFocus() + cancel(False) + End Sub + #tag EndEvent +#tag EndEvents +#tag Events Timer1 + #tag Event + Sub Action() + self.Close + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Third Party Classes/CustomEditField/CustomEditField.xojo_code b/Third Party Classes/CustomEditField/CustomEditField.xojo_code new file mode 100644 index 0000000..9f0dad4 --- /dev/null +++ b/Third Party Classes/CustomEditField/CustomEditField.xojo_code @@ -0,0 +1,6815 @@ +#tag Class +Protected Class CustomEditField +Inherits Canvas +Implements MessageReceiver + #tag Event + Sub Close() + //remove this control from all the mesage lists + self.unregisterReceiver + + if mHighlightTimer <> nil then + mHighlightTimer.Mode = 0 + mHighlightTimer.Enabled = false + mHighlightTimer = nil + end if + + if mRedrawTimer <> nil then + mRedrawTimer.Mode = 0 + mRedrawTimer.Enabled = false + mRedrawTimer = nil + end if + + //kill highlighting Thread + StopHighlighter + mHighlighter = nil + + // avoid circular references + caretBlinker = nil + lines = nil + + if CurrentFocusedField = self then mCurrentFocusedField = nil + + mWindowIsClosing = true + Close + End Sub + #tag EndEvent + + #tag Event + Function ConstructContextualMenu(base as MenuItem, x as Integer, y as Integer) As Boolean + //Paul Rodman's Suggestion + if ConstructContextualMenu(base,x,y) then return true + if base.Count>0 then base.Append new MenuItem("-") + //-- + + base.Append EditCut.Clone + base.Append EditCopy.Clone + base.Append EditPaste.Clone + base.Append EditClear.Clone + base.Append new MenuItem("-") + base.Append EditSelectAll.Clone + Return true + + End Function + #tag EndEvent + + #tag Event + Function DragEnter(obj As DragItem, action As Integer) As Boolean + #pragma unused obj + #pragma unused action + me.SetFocus + End Function + #tag EndEvent + + #tag Event + Function DragOver(x As Integer, y As Integer, obj As DragItem, action As Integer) As Boolean + #pragma unused obj + #pragma unused action + + //save the drag position + DragTextPos = CharPosAtXY(x, y) + + //if there's no DragTextSelection, then the text must come from some external source. + if DragTextSelection = nil then + changeSelection(DragTextPos, 0) + end if + + caretState = true + Redraw + End Function + #tag EndEvent + + #tag Event + Sub DropObject(obj As DragItem, action As Integer) + #pragma unused action + if not obj.TextAvailable then Return + + CurrentEventID = Ticks + + dim moveWithin as Boolean + ignoreRepaint = true + //check if the text comes from this same field. + moveWithin = DragSource = self + + //moved inside the selected text, do nothing. + if moveWithin and DragTextPos >= DragTextSelection.offset and DragTextPos <= DragTextSelection.offset + DragTextSelection.length then + ignoreRepaint = False + Return + end if + + //since the text is being moved inside the field, remove the old selection. + if moveWithin then + //erase selected + private_remove(DragTextSelection.offset, DragTextSelection.length, False) + end if + + //fix offsets, and insert text + if DragTextSelection = nil or DragTextPos < DragTextSelection.offset then + Insert(DragTextPos, obj.text) + + elseif DragTextPos > DragTextSelection.offset + DragTextSelection.length then + Insert(DragTextPos - DragTextSelection.length, obj.text) + + end if + + //select the text + changeSelection(SelStart, -obj.Text.len) + ignoreRepaint = False + + //if drag comes from an external source, mouseUp isn't raised, so clean up if needed + if not moveWithin then + dragTextOnDrag = False + DragSource = nil + DragTextSelection = nil + InvalidateAllLines + end if + + me.setfocus + Redraw + End Sub + #tag EndEvent + + #tag Event + Sub EnableMenuItems() + EditCopy.Enabled = me.selLength > 0 + EditClear.Enabled = EditCopy.Enabled + EditCut.Enabled = EditCopy.Enabled + + if me.TextLength > 0 then + EditSelectAll.Enable + end if + + dim c as new Clipboard + EditPaste.Enabled = c.TextAvailable + EnableMenuItems + End Sub + #tag EndEvent + + #tag Event + Sub GotFocus() + hasFocus = true + RaiseEvent GotFocus + enableBlinker(SelLength = 0) + Redraw + + mCurrentfocusedfield = self + End Sub + #tag EndEvent + + #tag Event + Function KeyDown(Key As String) As Boolean + if keyDown(key) then + Redraw + Return true + end if + + dim result as Boolean + caretBlinker.Reset + result = HandleKeyDown(key) + + Return result + End Function + #tag EndEvent + + #tag Event + Sub LostFocus() + CurrentEventID = 0 + hasFocus = False + RaiseEvent LostFocus + enableBlinker(False) + Redraw + End Sub + #tag EndEvent + + #tag Event + Function MouseDown(X As Integer, Y As Integer) As Boolean + CurrentEventID = 0 + Dragging = False + + if not hasFocus then self.SetFocus + dragTextOnDrag = False + + if MouseDown(X,Y) then Return true + if IsContextualClick then Return False + + ignoreRepaint = true + dim selstart as Integer + + selStart = CharPosAtXY(x,y) + + if Keyboard.ShiftKey then + changeSelection(min(selStart, CaretPos), abs(selStart - CaretPos)) + + elseif x < LineNumOffset then + selectedLine = lines.getLineNumberForOffset(selStart) + + if EnableLineFoldings and x >= LineNumOffset - blockStartImage.Width - 2 then + //toggle foldings here! + ToggleLineFold(selectedLine) + CreateMouseOverBlockHighlight(selectedLine) + + else + //line header clicked + SelectLine(selectedLine, false) + GutterClicked(selectedLine, x, y) + end if + + elseif SelLength > 0 and SelStart >= self.SelStart and SelStart <= self.SelStart + SelLength then + //omg drag! + dragTextOnDrag = true + + else + selectedLine = -1 + changeSelection(selStart, 0) + + end if + + updateDesiredColumn + lastMouseDownX = x + lastMouseDownY = y + ignoreRepaint = False + Redraw + Return true + End Function + #tag EndEvent + + #tag Event + Sub MouseDrag(X As Integer, Y As Integer) + //if the mouse doesn't move, don't do anything until it does. + if abs(lastMouseDownX - X) < 4 and abs(lastMouseDownY - Y) < 4 then Return + Dragging = true + + //if drag selected text... + if dragTextOnDrag then + HandleTextDrag(x,y) + Return + end if + + ignoreRepaint = true + + HandleDragOnGutter(x,y) + HandleVerticalMouseDrag(x,y) + HandleHorizontalMouseDrag(x,y) + + ignoreRepaint = False + Redraw + End Sub + #tag EndEvent + + #tag Event + Sub MouseExit() + if MouseOverBlock <> nil then + MouseOverBlock = nil + Redraw + end if + RaiseEvent MouseExit() + End Sub + #tag EndEvent + + #tag Event + Sub MouseMove(X As Integer, Y As Integer) + MouseMove(X,Y) + + //change mouse cursors + if x > LineNumOffset then //enter field + if cursorIsIbeam then Return + me.MouseCursor = System.Cursors.IBeam + cursorIsIbeam = true + + if MouseOverBlock <> nil then + MouseOverBlock = nil + Redraw + end if + + Return + end if + + // enter gutter + me.MouseCursor = System.Cursors.StandardPointer + cursorIsIbeam = false + + // visual block feedback + if not EnableLineFoldings or not HighlightBlocksOnMouseOverGutter then + if MouseOverBlock <> nil then + MouseOverBlock = nil + Redraw + end if + + Return + end if + + if x < LineNumOffset - blockStartImage.Width - 2 then + if MouseOverBlock <> nil then + MouseOverBlock = nil + Redraw + end if + + Return + end if + + CreateMouseOverBlockHighlight(lines.getLineNumberForOffset(CharPosAtXY(x,y))) + End Sub + #tag EndEvent + + #tag Event + Sub MouseUp(X As Integer, Y As Integer) + Dragging = False + + // Koen Van Hulle: check for triple click + if x > LineNumOffset and not checkTripleClick(x,y) then + //check for double click + if x > LineNumOffset and not checkDoubleClick(x,y) then + mouseUp(x,y) + isDoubleClick = False + + //reset drag variables. + if dragTextOnDrag then + dragTextOnDrag = False + + //clicked on the selected text, but it never got dragged, clear selction. + if DragTextSelection = nil then + changeSelection(CharPosAtXY(x,y), 0) + end if + + DragSource = nil + DragTextSelection = nil + InvalidateAllLines + Redraw + end if + end if + end if + + End Sub + #tag EndEvent + + #tag Event + Function MouseWheel(X As Integer, Y As Integer, deltaX as Integer, deltaY as Integer) As Boolean + #pragma unused x + #pragma unused y + + if MouseOverBlock <> nil then MouseOverBlock = nil + + changeScrollValues(ScrollPositionX + (deltaX * 5), ScrollPosition + deltay) + End Function + #tag EndEvent + + #tag Event + Sub Open() + me.EraseBackground = false + me.DoubleBuffer = TargetWin32 // avoids flicker on Windows + blockBeginPosX = -1 + + ignoreRepaint = true + RaiseEvent Open + + if TextFont = "" then TextFont = DEFAULT_FONT + if TextSize = 0 then TextSize = DEFAULT_FONTSIZE + + me.MouseCursor = System.Cursors.IBeam + cursorIsIbeam = true + self.AcceptFocus = true + self.AcceptTabs = true + + CalculateMaxVerticalSB + CalculateMaxHorizontalSB + + me.AcceptTextDrop + me.AcceptRawDataDrop("objectID") + + enableBlinker(hasFocus and SelLength = 0) + ignoreRepaint = False + + if TextSelectionColor = &c000000 then + TextSelectionColor = HighlightColor + end if + End Sub + #tag EndEvent + + #tag Event + Sub Paint(g As Graphics, areas() As REALbasic.Rect) + #pragma unused areas + + drawContents(g) + End Sub + #tag EndEvent + + + #tag MenuHandler + Function EditClear() As Boolean Handles EditClear.Action + me.SelText = "" + Redraw + Return true + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditCopy() As Boolean Handles EditCopy.Action + copy + Return true + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditCut() As Boolean Handles EditCut.Action + dim c as new Clipboard + c.Text = me.SelText.ReplaceAll (Chr(1), Chr(0)) + me.SelText = "" + Redraw + Return true + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditPaste() As Boolean Handles EditPaste.Action + paste + Return true + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditSelectAll() As Boolean Handles EditSelectAll.Action + SelectAll + redraw + Return true + End Function + #tag EndMenuHandler + + + #tag Method, Flags = &h0 + Sub AddBookmark(lineIndex as integer) + BookmarkTable.Value(lineIndex) = nil + InvalidateLine(lineIndex) + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub AppendText(text as string) + insert(TextStorage.Length, text) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub AutocompleteCancelled(requestFocus as boolean) + if requestFocus then SetFocus + + //stop listening messages from SuggestionWindow + self.unregisterForMessage(currentSuggestionWindow) + currentSuggestionWindow = nil + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub AutocompleteEOL() + //get Autocomplete options from client window + call fetchAutocompleteOptions + if CurrentAutocompleteOptions = nil then Return //nothing to autocomplete. + + dim maxIndex as Integer = UBound(CurrentAutocompleteOptions.Options) + dim firstMatch, longestCommonPrefix, currentPathComponent as String + + longestCommonPrefix = CurrentAutocompleteOptions.LongestCommonPrefix + currentPathComponent = CurrentAutocompleteOptions.CurrentPathComponent + if maxIndex > -1 then firstMatch = CurrentAutocompleteOptions.Options(0) + + if maxIndex >0 then //more than 1 option + OptionForTrailingSuggestion = longestCommonPrefix + trailingSuggestion = longestCommonPrefix.Right(longestCommonPrefix.Len - currentPathComponent.Len) + "…" + + elseif maxIndex = 0 and text <> firstMatch then //just 1 + OptionForTrailingSuggestion = firstMatch + trailingSuggestion = firstMatch.Mid(firstMatch.longestCommonPrefixIndex(currentPathComponent) + 1) + + else //word already fully typed + + Return + end if + + dim y as Integer + XYAtCharPos(CaretPos, CaretLine, AutocompleteSuggestionInsertionX, y) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub AutocompleteManual() + //get word where caret is at + dim CurrentWordSegment as TextSegment = CurrentWord + if CurrentWordSegment.length = 0 then Return + + //suggestion to autocomplete? + if trailingSuggestion.Len> 0 and trailingSuggestion <> "…" then + + dim suggestionLength as Integer + if trailingSuggestion.Right(1) = "…" then + suggestionLength = trailingSuggestion.len - 1 + + else + suggestionLength = trailingSuggestion.len + + end if + + //insert it + //Insert(CaretPos, trailingSuggestion.Left(suggestionLength)) + //AutocompleteOptionSelected(trailingSuggestion.Left(suggestionLength)) + AutocompleteOptionSelected(OptionForTrailingSuggestion)//CurrentAutocompleteOptions.CurrentPathComponent + trailingSuggestion.Left(suggestionLength)) + Return + end if + + //get all Autocomplete options for word + + call fetchAutocompleteOptions + if CurrentAutocompleteOptions = nil then Return //nothing to autocomplete + if ubound(CurrentAutocompleteOptions.Options) < 0 then Return + + //find XY pos of caret + dim x,y, fx, fy as Integer + XYAtCharPos(CaretPos, CaretLine, x,y) + getFieldXY(fx, fy) + x = x + fx + y = y + fy + + dim cx, cy as Integer + cx = x + cy = y + + // give the user the option to offset the suggestion window if needed. + if ShouldDisplaySuggestionWindowAtPos(cx, cy) then + x = cx + y = cy + end if + + //show suggestion window + currentSuggestionWindow = new SuggestionWindow + //start listening for messages from the SuggestionWindow + self.registerForMessage(currentSuggestionWindow) + currentSuggestionWindow.Show(x, y) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub AutocompleteOptionSelected(option as string) + //the string "option" was selected in the suggestions window. + + if option <> "" then + if SelLength > 0 then + //replace highlighted text + SelText = option + + else + + //add option to current word removing common prefix + if AutocompleteAppliesStandardCase then + //we need to replace the whole word with the Autocomplete option... + private_replace(CaretPos - CurrentAutocompleteOptions.CurrentPathComponent.len, CurrentAutocompleteOptions.CurrentPathComponent.len, option) + else + SelText = option.Mid(option.longestCommonPrefixIndex(CurrentAutocompleteOptions.currentPathComponent) + 1) + end if + end if + end if + + //stop listening messages from SuggestionWindow + self.unregisterForMessage(currentSuggestionWindow) + currentSuggestionWindow = nil + + //check indentations + if AutoIndentNewLines and not mIndentVisually then + dim thisLine as TextLine = lines.getLine(CaretLine) + if thisLine <> nil and thisLine.isBlockEnd then + dim state as Variant + if private_indentLine (CaretLine, false, state) then + InvalidateLine (CaretLine) + end + end if + end if + + SetFocus + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function BookmarkList() As integer() + dim indexes(), index as Integer + + for each index in BookmarkTable.keys + indexes.Append index + next + + indexes.Sort + + Return indexes + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub CalculateMaxHorizontalSB() + //maximum horizontal scrollbar value + //Thanks to Thomas Tempelmann for his suggestions. + if horizontalSB <> nil Then + dim contentWidth as integer = lastLongestLinePixels + LineNumOffset + RightScrollMargin + + dim n as Integer = self.Width + dim max as Integer = contentWidth - n + if max <= 0 then + max = 0 + n = 0 + end + + horizontalSB.enabled = max > 0 // you may want to remove this one + horizontalSB.Maximum = max + horizontalSB.PageStep = n + horizontalSB.LineStep = 8 + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub CalculateMaxVerticalSB() + //maximum vertical scrollbar value + if verticalSB <> nil then + if EnableLineFoldings then + verticalSB.Maximum = lines.Count - lines.invisibleLines - MaxVisibleLines + else + verticalSB.Maximum = lines.Count - MaxVisibleLines + end if + + //update the pageStep so a page jump is always th number of visible lines... or a page. + verticalSB.PageStep = MaxVisibleLines - 1 + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function CanRedo() As boolean + Return UndoMgr.CanRedo + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CanUndo() As boolean + Return UndoMgr.CanUndo + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CaretColumn() As Integer + dim line as TextLine = lines.getLine(CaretLine) + if line <> nil then + return CaretPos - line.offset + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CaretSymbol() As documentSymbol + Return SymbolAtline(CaretLine) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub changeScrollValues(horizontal as integer, vertical as integer) + //changes view to the given scroll values + caretBlinker.Reset + + dim needsRedraw as Boolean = false + dim valuesChanged as Boolean + + if horizontal <> ScrollPositionX then + //Cap values + horizontal = max(min(horizontal, lastLongestLinePixels - self.Width + LineNumOffset + RightScrollMargin), 0) + + //force a full redraw + if horizontal <> ScrollPositionX then + InvalidateAllLines + end if + + //change scrollbars + mScrollPositionX = horizontal + if horizontalSB <> nil then + horizontalSB.Value = horizontal + else + needsRedraw = true + end if + + valuesChanged = true + HorizontalScrollValueChanged + end if + + if vertical <> ScrollPosition then + //Cap values + if EnableLineFoldings then + dim v2 as Integer = max(min(vertical, lines.Count - lines.invisibleLines - MaxVisibleLines), 0) + if vertical <> v2 then + if lines.invisibleLines > 0 then + break // bug! The scrollposition jumps too far when moving down with the cursor if there's a folding above the scrollposition + end + vertical = v2 + end if + else + vertical = max(min(vertical, lines.Count - MaxVisibleLines), 0) + end if + + if vertical <> ScrollPosition then + InvalidateAllLines + end if + + //change scrollbars + mScrollPosition = vertical + if verticalSB <> nil then + verticalSB.Value = vertical + else + needsRedraw = true + end if + + valuesChanged = true + VerticalScrollValueChanged + end if + + if valuesChanged then ScrollValuesChanged + if needsRedraw then Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub changeSelection(selStart as integer, selLength as integer, viaDoubleClick as Boolean = False) + //changes the current document selection. + + //nothing to change + if selStart = mSelStart and selLength = mSelLength then Return + + //backwards selections? + if selLength < 0 then + selLength = -selLength + selStart = selStart - selLength + end if + + //cap selstart + if selStart < 0 then + selStart = 0 + ElseIf selStart > TextStorage.Length then + selStart = TextStorage.Length + end if + + //cap SelLength + if selStart + selLength > TextStorage.Length then + selLength = selLength - TextStorage.Length + end if + + //find starting line for selection. + dim linenum as Integer = lines.getLineNumberForOffset(SelStart) + dim line as TextLine = lines.getLine(linenum) + + //deal with invisible lines + if EnableLineFoldings then + dim startLineIdx, endLineIdx as Integer + dim startLine, endLine as TextLine + dim update as Boolean + + //check if selstart is in a Visible line, if not, move it to the next or previous visible line. + if SelStart <> mSelStart then + if linenum <> CaretLine then //only if the new line is different from Previous. + startLineIdx = lines.getLineNumberForOffset(SelStart) + startLine = lines.getLine(startLineIdx) + if startLine <> nil and SelStart > mSelStart then //moving fwd + if not startLine.visible then + startLineIdx = lines.nextVisibleLine(startLineIdx) + update = true + end if + elseif startLine <> nil and selstart < mSelStart then //moving bck + if not startLine.visible then + startLineIdx = lines.previousVisibleLine(startLineIdx) + update = true + end if + end if + + if update then + startLine = lines.getLine(startLineIdx) + if startLine <> nil then + + SelStart = offsetForXPos(startLine, caretDesiredColumn) + LineNum = startLineIdx + line = startLine + + if SelLength > 0 then + SelLength = mSelStart + mSelLength - SelStart + end if + + end if + end if + end if + end if + + if not update and SelLength > 0 and SelLength <> mSelLength then + endLineIdx = lines.getLineNumberForOffset(SelStart + SelLength) + endLine = lines.getLine(endLineIdx) + + if endLine <> nil and SelLength > mSelLength then //making selection bigger. + if not endLine.visible then + endLineIdx = lines.nextVisibleLine(endLineIdx) + update = true + end if + elseif endLine <> nil and SelLength < mSelLength then //smaller + if not endLine.visible then + endlineIdx = lines.previousVisibleLine(endLineIdx) + update = true + end if + end if + + if update then + endLine = lines.getLine(endLineIdx) + if endLine <> nil then + SelLength = offsetForXPos(endLine, caretDesiredColumn) - SelStart + end if + end if + end if + end if + + //make sure selection isn't inside a EOL delimiter + if line <> nil then + if SelStart > line.offset + line.length - line.delimiterLength then + if SelStart > mSelStart then + SelStart = line.offset + line.length + else + SelStart = line.offset + line.length - line.delimiterLength + end if + linenum = lines.getLineNumberForOffset(SelStart) + line = lines.getLine(linenum) + end if + end if + + // update selection if inside a placeholder... + dim selectedPlaceholder as TextPlaceholder = nil + if mSelStart < selStart then //moving start to the Right + dim placeholder as TextPlaceholder = line.PlaceholderForOffset(selStart) + if placeholder <> nil then + if selLength = 0 then + selStart = placeholder.offset + line.offset //sel + selLength = placeholder.length + + selectedPlaceholder = placeholder + //PlaceholderSelected(LineNum, TextStorage.getText(line.offset + placeholder.textRange.offset, placeholder.textRange.length)) + + else + selStart = placeholder.offset + placeholder.length +line.offset //desel + selLength = selLength - placeholder.length + 1 + end if + end if + + ElseIf mSelStart > selStart then //moving start to the Left + dim placeholder as TextPlaceholder = line.PlaceholderForOffset(selStart) + if placeholder <> nil then + if selLength = 0 then + selStart = placeholder.offset + line.offset //sel + selLength = placeholder.length + + selectedPlaceholder = placeholder + //PlaceholderSelected(LineNum, TextStorage.getText(line.offset + placeholder.textRange.offset, placeholder.textRange.length)) + + else + selStart = placeholder.offset +line.offset //sel + selLength = selLength + placeholder.length - 1 + end if + end if + + ElseIf mSelLength > selLength then //shrinking selection + dim endline as TextLine = lines.getline(lines.getLineNumberForOffset(selStart + selLength)) + if endline <> nil then + dim placeholder as TextPlaceholder = endline.PlaceholderForOffset(selStart + selLength) + + if placeholder <> nil then + selLength = max(selLength - placeholder.length + 1, 0) //desel + end if + end if + + elseif mSelLength < selLength then //expanding selection + dim endline as TextLine = lines.getline(lines.getLineNumberForOffset(selStart + selLength)) + if endline <> nil then + dim placeholder as TextPlaceholder = endline.PlaceholderForOffset(selStart + selLength) + + if placeholder <> nil then + selLength = selLength + placeholder.length - 1 //sel + end if + end if + end if + + + //change internal values + mSelStart = selStart + mSelLength = selLength + + //if SelLength = 0 or outside selection then update caret + if selLength = 0 or CaretPos < SelStart or CaretPos > SelStart + SelLength then + //has the line changed? invalidate lines + if LineNum <> CaretLine then + InvalidateLine(CaretLine) + mCaretLine = lines.getLineNumberForOffset(SelStart) + end if + + mCaretPos = SelStart + + //check if caret is out of view + ViewToCharPos(CaretLine, mCaretPos) + end if + + //blink only if no selection + enableBlinker(selLength = 0) + + if not UndoMgr.isUndoing then + //raise selChanged event + SelChanged(linenum + 1, SelStart - line.offset, SelLength) + + if selectedPlaceholder <> nil then + dim label as String = TextStorage.getText(line.offset + selectedPlaceholder.textRange.offset, selectedPlaceholder.textRange.length) + PlaceholderSelected(label, LineNum, lines.getLine(linenum), selectedPlaceholder, viaDoubleClick) + end if + end if + + //fire Autocomplete events + if SelLength > 0 or not EnableAutocomplete then Return + trailingSuggestion = "" + + //are we at end of current line? + if caretPos = line.offset + line.length - line.delimiterLength then + //check if caret is out of view + AutocompleteEOL + end if + + InvalidateLine(CaretLine) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function CharPosAtLineNum(lineNumber as integer) As integer + dim line as TextLine = lines.getLine(lineNumber) + if line = nil then Return -1 + Return line.offset + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CharPosAtXY(X as integer, Y as integer) As integer + dim lineNum as Integer + + //find the line + if EnableLineFoldings then + lineNum = lines.getNumberOfLinesNeededToView(min(lines.Count - 1, max(0, floor((y + (ScrollPosition * TextHeight)) / TextHeight)))) + else + lineNum = min(lines.Count - 1, max(0, floor((y + (ScrollPosition * TextHeight)) / TextHeight))) + end if + + //find the char offset. + dim line as TextLine = lines.getLine(lineNum) + + //not found? + if line = nil then Return -1 + + dim offset as Integer = leftMarginOffset + LineNumOffset - ScrollPositionX + Return offsetForXPos(line, x - offset) + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function checkDoubleClick(X as integer, Y as integer) As boolean + //grabbed from RB examples + //if SelLength > 0 then Return False + + dim doubleClickTime, currentClickTicks as Integer + + #if targetMacOS then + #if targetCarbon or TargetCocoa then + Declare Function GetDblTime Lib "Carbon" () as Integer + #else + Declare Function GetDblTime Lib "InterfaceLib" () as Integer Inline68K("2EB802F0") + #endif + doubleClickTime = GetDblTime() + if doubleClickTime <= 0 then + doubleClickTime = 30 + end + #endif + + #if targetWin32 then + Declare Function GetDoubleClickTime Lib "User32.DLL" () as Integer + doubleClickTime = GetDoubleClickTime() + // DoubleClickTime now holds the number of milliseconds + doubleClickTime = doubleClickTime / 1000.0 * 60 ' converted to Ticks + #endif + + #if TargetLinux then + Declare Function gtk_settings_get_default lib "libgtk-x11-2.0.so" as Ptr + Declare Sub g_object_get lib "libgtk-x11-2.0.so" (Obj as Ptr, first_property_name as CString, byref doubleClicktime as Integer, Null as Integer) + dim gtkSettings as MemoryBlock + gtkSettings = gtk_settings_get_default() + g_object_get(gtkSettings,"gtk-double-click-time",doubleClickTime, 0) + // DoubleClickTime now holds the number of milliseconds + doubleClickTime = doubleClickTime / 1000.0 * 60 ' converted to Ticks + #endif + + dim result as Boolean = false + currentClickTicks = ticks + //if the two clicks happened close enough together in time + if (currentClickTicks - lastClickTicks) <= doubleClickTime then + //if the two clicks occured close enough together in space + if abs(X - lastMouseUpX) <= 4 and abs(Y - lastMouseUpY) <= 4 then + isDoubleClick = True + handleDoubleClick //a double click has occured so call the event + result = true + else + isDoubleClick = False + end if + end if + lastClickTicks = currentClickTicks + lastMouseUpX = X + lastMouseUpY = Y + + Return result + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function checkTripleClick(X as integer, Y as integer) As boolean + //grabbed from RB examples + //if SelLength > 0 then Return False + if isDoubleClick = True then + dim doubleClickTime, currentClickTicks as Integer + + #if targetMacOS then + #if targetCarbon or TargetCocoa then + Declare Function GetDblTime Lib "Carbon" () as Integer + #else + Declare Function GetDblTime Lib "InterfaceLib" () as Integer Inline68K("2EB802F0") + #endif + doubleClickTime = GetDblTime() + #endif + + #if targetWin32 then + Declare Function GetDoubleClickTime Lib "User32.DLL" () as Integer + doubleClickTime = GetDoubleClickTime() + // DoubleClickTime now holds the number of milliseconds + doubleClickTime = doubleClickTime / 1000.0 * 60 ' converted to Ticks + #endif + + #if TargetLinux then + Declare Function gtk_settings_get_default lib "libgtk-x11-2.0.so" as Ptr + Declare Sub g_object_get lib "libgtk-x11-2.0.so" (Obj as Ptr, first_property_name as CString, byref doubleClicktime as Integer, Null as Integer) + dim gtkSettings as MemoryBlock + gtkSettings = gtk_settings_get_default() + g_object_get(gtkSettings,"gtk-double-click-time",doubleClickTime, 0) + // DoubleClickTime now holds the number of milliseconds + doubleClickTime = doubleClickTime / 1000.0 * 60 ' converted to Ticks + #endif + + dim result as Boolean = false + currentClickTicks = ticks + //if the three clicks happened close enough together in time + if (currentClickTicks - lastTripleClickTicks) <= doubleClickTime then + //if the three clicks occured close enough together in space + if abs(X - lastMouseUpX) <= 4 and abs(Y - lastMouseUpY) <= 4 then + handleTripleClick //a triple click has occured so call the event + result = true + end if + end if + lastTripleClickTicks = currentClickTicks + lastMouseUpX = X + lastMouseUpY = Y + isDoubleClick = False + Return result + else + isDoubleClick = False + return false + end if + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearBookmark(lineIndex as integer) + if not BookmarkTable.HasKey(lineIndex) then Return + + BookmarkTable.Remove(lineIndex) + InvalidateLine(lineIndex) + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearBookmarks() + BookmarkTable.Clear + InvalidateAllLines + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearDirtyLines() + lines.clearDirtyLines + InvalidateAllLines + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearHighlightedCharacterRanges() + if HighlightedRanges.SelectionCount = 0 then Return + + HighlightedRanges.Clear + InvalidateAllLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearLineIcons() + dim line as TextLine + for i as Integer = 0 to lines.Count - 1 + line = lines.getLine(i) + if line <> nil then line.icon = nil + next + + InvalidateAllLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor() + // Calling the overridden superclass constructor. + Super.RectControl + ignoreRepaint = true + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Copy() + if SelLength = 0 then Return + dim c as new Clipboard + c.Text = me.SelText.ReplaceAll (Chr(1), Chr(0)) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub CreateMouseOverBlockHighlight(lineIndex as integer) + dim line as TextLine = lines.getLine(lineIndex) + if line = nil then Return + + dim openingLine as integer + if line.isBlockStart then + openingLine = lineIndex + else + openingLine = OpeningBlockLineForLine(lineIndex) + end if + if openingLine < 0 then Return + + line = lines.getLine(openingLine) + if line = nil then Return + + dim x, y1, y2 as Integer + XYAtCharPos(line.offset, openingLine, x, y1) + + dim closingLine as Integer = lines.nextBlockEndLine(openingLine) + if closingLine < 0 then Return + + line = lines.getLine(closingLine) + if line = nil then Return + + XYAtCharPos(line.offset, closingLine, x, y2) + + if MouseOverBlock <> nil then + if MouseOverBlock.Value("y") = y1 - TextHeight - 2 and MouseOverBlock.Value("h") = y2 - y1 + TextHeight + 4 then Return //avoid a redraw if we can... + end if + + MouseOverBlock = nil + MouseOverBlock = new Dictionary + MouseOverBlock.Value("startLine") = openingLine + MouseOverBlock.Value("x") = LeftMarginOffset - 3 + MouseOverBlock.Value("y") = y1 - TextHeight - 2 + MouseOverBlock.Value("w") = self.Width - LineNumOffset - LeftMarginOffset + MouseOverBlock.Value("h") = y2 - y1 + TextHeight + 4 + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function CurrentAutocompleteOptions() As Autocompleteoptions + Return mCurrentAutocompleteOptions + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CurrentUndoEventID() As Integer + return self.CurrentEventID + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function CurrentWord() As textsegment + //gets the current word, where the caret is at. + //a word is anything except whitespaces. + + dim startIndex, endIndex as Integer + startIndex = previousCharInSet(CaretPos + 1, CURRENT_CARET_WORD_DELIMITER_PATTERN) + endIndex = nextCharInSet(CaretPos - 1, CURRENT_CARET_WORD_DELIMITER_PATTERN) + Return new TextSegment(startIndex, endIndex - startIndex) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CustomEditFieldPrinter(printerGraphics as graphics) As CustomEditFieldPrinter + printerGraphics.TextFont = TextFont + printerGraphics.TextSize = TextSize + printerGraphics.Bold = False + printerGraphics.Italic = False + printerGraphics.Underline = False + + Return new CustomEditFieldPrinter(printerGraphics, TextStorage, lines, TextColor, DisplayInvisibleCharacters, self.IndentVisually) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub DeleteChars(forwardDelete as boolean) + // handles delete key + + // check if the key would delete a placeholder... + dim line as TextLine = lines.getLine(CaretLine) + if line <> nil and line.HasPlaceholders then + dim placeholder as TextPlaceholder + if forwardDelete then + Placeholder = line.PlaceholderForOffset(CaretPos + 1) + else + Placeholder = line.PlaceholderForOffset(CaretPos - 1) + end if + if placeholder <> nil then + changeSelection(placeholder.offset + line.offset, placeholder.length) + end if + end if + + // delete highlighted text + if me.SelLength > 0 then + private_replace(selStart, me.SelLength, "") + Return + end if + + dim length, offset as Integer + + if forwardDelete then + // forward delete + if CaretPos >= TextStorage.Length then return + if Keyboard.OptionKey then + // delete word + length = nextNonAlpha(CaretPos) - CaretPos + else + // delete single char + length = 1 + end if + else + // backspace + if Keyboard.OptionKey then + // delete word + length = CaretPos - previousNonAlpha(CaretPos) + else + // delete single char + length = 1 + end + end if + + dim updateCaret as Boolean = not forwardDelete + if not mIndentVisually and mKeepEntireTextIndented then + // + // Here we may have a special case: If IndentVisually=false, and the user backspaces when the + // cursor is at the start of the indented line, we'll need to delete not only the indentation + // but the line delimiter to the previous line as well, or the user would not be able to ever join + // the current line with the previous line because it would get re-indented right away again. + // + dim caretCol as Integer = self.CaretColumn + dim lineText as String = self.GetLine(caretLine) + dim textLeftOfCaret as String = lineText.Left(caretCol) + if textLeftOfCaret.Trim = "" then + // Cursor is at start of line or inside indentation space + if forwardDelete then + dim rightOfCaret as String = lineText.Mid(caretCol+1,1) + if rightOfCaret <> "" and rightOfCaret <> LineDelimiter and rightOfCaret.Trim = "" then + // forward delete in indentation whitespace doesn't work - skip it + beep + return + end if + else + // delete to previous line + length = Caretpos - previousLineDelimiter(CaretPos) + 1 + end + end if + end + + if forwardDelete then + offset = selStart + else + offset = selStart - length + end + private_remove(offset, length, updateCaret) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub DisableUndoHandling() + UndoMgr.Enabled = false + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function DocumentSymbols() As DocumentSymbol() + //no symbols + if CurrentDocumentSymbols = nil then Return nil + + dim tmp() as DocumentSymbol + //dim key as String + dim line as TextLine + dim symbol as DocumentSymbol + + //copy symbols to new Dictionary + for Each line in CurrentDocumentSymbols.keys + //line = CurrentDocumentSymbols.Value(key) + //if line = nil then Continue for + + for each Symbol in line.LineSymbols.Values + //Symbol = line.LineSymbols.Value(key) + //tmp.Value(key) = new DocumentSymbol(symbol.Name, line.offset + symbol.Offset, symbol.Type) //with the correct offset! + + tmp.append( new DocumentSymbol(symbol.Name, line.offset + symbol.Offset, symbol.Type) ) //with the correct offset! + next + next + + //no symbols... + //if tmp.Count = 0 then Return nil + + Return tmp + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub drawContents(gr as graphics) + #if not DebugBuild + #pragma DisableBackgroundTasks + + #endif + + #if DebugBuild and EditFieldGlobals.DebugIndentation + System.DebugLog "drawContents() start, firstLineToIndent: "+str(lines.FirstLineForIndentation) + #endif + + dim lock as new LinesLock(self) // makes sure we're not updating while LineHighlighter is busy + #pragma unused lock + + self.updateIndentation() + + if not mRedrawEverything and not mRedrawCaret then + // It's probably an externally triggered refresh + // + // However, this assumption might not be fool-proof. What if + // the caret blinker sets mRedrawCaret, i.e. mRedrawEverything + // remains false, but some other external event wants a redraw + // at the same time? Then we'll only update the caret, possibly + // leaving the rest of the Canvas not redrawn as necessary. + // On the other hand, the caret blinking only happens if the + // Canvas has focus, meaning there can only be a floating window + // overlaying it - but if that's the case and the user drags it over + // the Canvas, then the Canvas needs refreshes, and the blinking + // could interfere with that. + // + // Therefore, it would certainly help if we could detect if there + // are other pending redraws. Ideally, we'd learn how large the + // invalidated rect is, and base our redraw (and clipping) on that. + + mRedrawEverything = true + end + + // We're going to implement the paint event ourselves + // so that we can automatically do the double buffering + + if gr.Height <= 0 or gr.Width <= 0 then + // there is nothing to draw + return + end if + + // Check our back buffer to make sure we've got + // one that we can draw to + dim realign as Boolean + + // If we don't have a back buffer, then we need to create one + // If our size is different than our + // back buffer, then we need to create a new one + realign = (mBackBuffer = nil) or (gr.Width <> mBackBuffer.Width) or (gr.Height <> mBackBuffer.Height) + + dim createBackBuffers as Boolean = not TargetMacOS + + if realign then + if createBackBuffers then + // create double buffer + // + // However (3 Sep 2013): + // This is meant to prevent flickering on Windows (and probably Linux too), + // but on Mac OS it's not needed any more. + // In fact, it would prevent Retina / HiDPI rendering from working. Therefore, for + // Mac builds, we now draw directly into the Canvas by not creating this back buffer + mBackBuffer = new Picture(gr.Width, gr.Height, 32) + end if + CalculateMaxHorizontalSB + CalculateMaxVerticalSB + InvalidateAllLines + mRedrawEverything = true + end if + + //get a graphics context to draw onto. + dim g as Graphics + if mBackBuffer = nil then + // draw directly into the Canvas graphics (required for Retina support) + g = gr + else + // draw first into a separate graphics buffer which is then painted into the Canvas at the end + g = mBackBuffer.graphics + end + + if mRedrawCaret and not mRedrawEverything then + // let's reduce the redraw to the small area where the Caret appears + 'g = g.Clip(0, 0, 100, 100) - should clip to the region of the caret here + 'gr = gr.Clip(0, 0, 100, 100) - should clip to the region of the caret here + mRedrawEverything = true + end + + //set old renderer + #if EditFieldGlobals.UseOldRenderer + g.UseOldRenderer = true + #endif + + #if DebugBuild + redrawTime = Microseconds + #endif + + dim sx, sy as Double + + dim gg as Graphics // for Gutter (left frame showing line numbers) + dim gutterWidth as Integer = LineNumOffset + + //Line numbers + if self.displayLineNumbers then + + //create line numbers picture, if needed. + if not createBackBuffers then + // draw gutter directly into Canvas' graphics object (necessary for Retina support) + gg = gr + else + // use separate graphics buffer for gutter + if Gutter = nil or Gutter.Height <> g.Height or gutter.Width <> gutterWidth then + Gutter = New Picture(gutterWidth, g.Height, 32) + gg = gutter.Graphics + #if EditFieldGlobals.UseOldRenderer + gg.UseOldRenderer = true + #endif + gg.TextFont = LineNumbersTextFont + gg.TextSize = LineNumbersTextSize + else + gg = gutter.Graphics + end if + end if + + // repaint gutter background, if needed + if fullRefresh or lastDrawnTopLine <> ScrollPosition then + gg.ForeColor = GutterBackgroundColor.lighterColor(10) + gg.FillRect LineNumOffset - FoldingOffset, 0, FoldingOffset, g.Height + gg.ForeColor = GutterBackgroundColor + gg.FillRect 0, 0, gutterWidth - FoldingOffset, g.Height + gg.ForeColor = GutterSeparationLineColor + gg.DrawLine LineNumOffset - 1, 0, LineNumOffset - 1, g.Height + lastDrawnTopLine = ScrollPosition + end if + end if + + //paint selection, and get their range + dim selection as new CharSelection(-1, -1, -1, -1, TextSelectionColor) + dim tmpSelection as CharSelection + //get selection range + if SelLength > 0 then + Selection.offset = SelStart + Selection.length = SelLength + selection.StartLine = lines.getLineNumberForOffset(SelStart) + selection.EndLine = Lines.getLineNumberForOffset(SelStart + SelLength) + end if + + //set text properties + g.TextSize = TextSize + g.TextFont = TextFont + if g <> gr then + gr.TextSize = TextSize + gr.TextFont = TextFont + end if + + //Starting positions + sx = leftMarginOffset + LineNumOffset - ScrollPositionX + sy = g.TextHeight + + dim line as TextLine + dim linesDrawn as Integer + dim firstLine as Integer + dim lastLine as Integer + + //the lowest possible line to draw is ScrollPosition, so start there. + if EnableLineFoldings then + firstline = lines.getNumberOfLinesNeededToView(ScrollPosition) + lastLine = lines.count - 1 + else + firstLine = ScrollPosition + lastLine = min(lines.Count - 1, ScrollPosition + MaxVisibleLines) + end if + + dim linesOnScreen as Integer + + for lineIdx as Integer = firstLine to lastLine + + linesOnScreen = linesOnScreen + 1 + + //get current line + line = lines.getLine(lineIdx) + if not line.visible then Continue for + + if _ + invalidLines.HasKey(lineIdx) or _ + fullRefresh or _ + selection.IsLineIndexInRange(lineIdx) or _ + (previouslyDrawnSelection <> nil and previouslyDrawnSelection.IsLineIndexInRange(lineIdx)) then + + // This line needs to be repainted (invalid), or is a full refresh, or is part of the previous or current selection + + //clear the background for this line + dim lineBackColor as Color = BackColor + + if not UseBackgroundColorForLine(lineIdx, lineBackColor) then + lineBackColor = BackColor + end if + g.ForeColor = lineBackColor + g.fillRect LineNumOffset, sy - g.TextHeight, g.Width - LineNumOffset, TextHeight + + //draw highlighted ranges! + dim ranges() as CharSelection = HighlightedRanges.SelectionsForLine(lineIdx) //first, draw the Highlighted ranges + line.AppendHighlightedWords(ranges, lineIdx) //then draw words with a background + ranges.Append(selection) //and finally, draw the selection + + if matchingBlockHighlight <> nil then + ranges.Append(MatchingBlockHighlight) + end if + + dim x,y,w as Integer + for each tmpSelection in ranges + If tmpSelection.IsLineIndexInRange(lineIdx) then //if in selection, Highlight line + + if hasFocus or not tmpSelection.LosesFocus then + g.ForeColor = tmpSelection.SelectionColor + else + g.ForeColor = BackColor.darkerColor(30) + end if + + if lineIdx > tmpSelection.StartLine and lineIdx < tmpSelection.EndLine then //fully selected line + g.FillRect LineNumOffset, sy - g.TextHeight, g.Width - line.VisualIndent(self.IndentVisually), TextHeight + + elseif lineIdx = tmpSelection.StartLine and tmpSelection.EndLine <> tmpSelection.StartLine then //firstLine + XYAtCharPos(tmpSelection.offset, lineIdx, x, y) + + if tmpSelection.Rounded then + g.FillRoundRect x, sy - g.TextHeight, g.Width - x + 10, TextHeight, 8, 8 + else + g.FillRect x, sy - g.TextHeight, g.Width - x, TextHeight + end if + + ElseIf lineIdx = tmpSelection.EndLine and tmpSelection.EndLine <> tmpSelection.StartLine then //last line + XYAtCharPos(tmpSelection.offset + tmpSelection.length, lineIdx, x, y) + + if tmpSelection.Rounded then + g.FillRoundRect LineNumOffset - 10, sy - g.TextHeight, x - LineNumOffset + 10, TextHeight, 8, 8 + else + g.FillRect LineNumOffset, sy - g.TextHeight, x - LineNumOffset, TextHeight + end if + + else //small block in line. + XYAtCharPos(tmpSelection.offset, lineIdx, x, y) + XYAtCharPos(tmpSelection.offset + tmpSelection.length, lineIdx, w, y) + + if tmpSelection.Rounded then + g.FillRoundRect x, sy - g.TextHeight, w - x, TextHeight, 8, 8 + else + g.FillRect x, sy - g.TextHeight, w - x, TextHeight + end if + end if + + end if + next + + //paint a line background? + PaintBelowLine(lineIdx, g, LineNumOffset, sy - g.TextHeight, g.Width - LineNumOffset - 1, TextHeight) + + //paint line + line.Paint(TextStorage, g, sx, sy - (g.TextHeight - g.TextAscent), TextColor, DisplayInvisibleCharacters, SelStart, SelLength, true, self.IndentVisually) + + //a line overlay? + PaintAboveLine(lineIdx, g, LineNumOffset, sy - g.TextHeight, g.Width - LineNumOffset - 1, TextHeight) + + //contents after folded line... + if line.folded then + dim tmp as TextLine = lines.getLine(lines.nextBlockEndLine(lineIdx)) + if tmp <> nil then + //make italic and paint after current line. + tmp.italic = True + tmp.Paint(TextStorage, g, sx + line.TotalWidth + EditFieldGlobals.BlockFoldedTrailImage.Width + 6, sy - (g.TextHeight - g.TextAscent), TextColor, false, SelStart, SelLength, false, self.IndentVisually) + tmp.italic = False + end if + end if + + //autocomplete suggestion + if SelLength = 0 and lineIdx = CaretLine and trailingSuggestion <> "" then + g.ForeColor = &cAAAAAA + g.DrawString trailingSuggestion, AutocompleteSuggestionInsertionX, sy - (g.TextHeight - g.TextAscent) + end if + + //draw gutter last so that it overwrites text that was drawn into the gutter area when it's horizontally scrolled. + if displayLineNumbers then + + //caret line is slightly darker + if EnableLineFoldings then + gg.ForeColor = GutterBackgroundColor.lighterColor(10) + gg.FillRect LineNumOffset - FoldingOffset - 1, sy - g.TextHeight, FoldingOffset, TextHeight + end if + if CaretLine = lineIdx then + gg.ForeColor = GutterBackgroundColor.darkerColor(20) + gg.FillRect 0, sy - g.TextHeight, LineNumOffset - 1 - FoldingOffset, TextHeight + gg.Bold = true + gg.ForeColor = &c000000 + else + #if FlashRefreshRanges then + gg.ForeColor = rgb(rnd * 255, rnd * 255, rnd * 255) 'GutterBackgroundColor + #else + gg.ForeColor = GutterBackgroundColor + #endif + gg.FillRect 0,sy - g.TextHeight,LineNumOffset - 1 - FoldingOffset, TextHeight + end if + + if DisplayDirtyLines and line.isDirty then + gg.ForeColor = DirtyLinesColor + gg.fillRect LineNumOffset - 4, sy - g.TextHeight, 3, TextHeight + end if + + //bookmarks? + if BookmarkTable.HasKey(lineIdx) then + dim img as Picture = UseBookmarkIconForLine(lineIdx) + if img = nil then img = BookmarkImage + gg.DrawPicture img, 0, sy - g.TextHeight + (g.TextHeight - img.Height)/2 + end if + + //row icon available? + if line.icon <> nil then + dim icn as Picture = line.icon + gg.DrawPicture icn, gutterWidth - icn.Width - 2 - FoldingOffset, sy - g.TextHeight + (g.TextHeight - icn.Height)/2 + else + //line number + gg.ForeColor = LineNumbersColor + if gr = gg then + gg.TextFont = LineNumbersTextFont + gg.TextSize = LineNumbersTextSize + end if + gg.DrawString str(lineIdx + 1), lineNumOffset - 2 - gg.StringWidth(str(lineIdx + 1)) - FoldingOffset, sy - (TextHeight - gg.TextAscent)/2 + if gr = gg then + gg.TextSize = TextSize + gg.TextFont = TextFont + end if + end if + + if EnableLineFoldings and line.isBlockStart then + if line.folded then //draw line folded marker + gg.DrawPicture blockFoldedImage, LineNumOffset - blockFoldedImage.Width - 2, sy - TextHeight + (TextHeight - blockFoldedImage.Height) / 2 + 1 + else + gg.DrawPicture blockStartImage, LineNumOffset - blockStartImage.Width - 2, sy - TextHeight + (TextHeight - blockStartImage.Height) / 2 + 1 + end if + elseif EnableLineFoldings and line.isBlockEnd then + gg.DrawPicture blockEndImage, LineNumOffset - blockEndImage.Width - 2, sy - TextHeight + (TextHeight - blockEndImage.Height) / 2 + 1 + end if + + gg.Bold = false + end if + + linesDrawn = linesDrawn + 1 + end if + + //go to next line + if TextHeight = 0 then break + sy = sy + TextHeight + if sy - g.TextHeight > g.Height then + exit for //or bail out if we've reached the end of the canvas + end if + next + + VisibleLineRange.offset = firstLine + VisibleLineRange.length = linesOnScreen + + //clear the rest of the buffer, if necessary + sy = sy - g.TextHeight + if sy < g.Height then + g.ForeColor = BackColor + g.FillRect gutterWidth, sy, g.Width-gutterWidth, g.Height - sy + end if + + //invalid lines are no longer invalid + invalidLines.Clear + fullRefresh = False + previouslyDrawnSelection = selection + + if MatchingBlockHighlight <> nil then + InvalidateLine(MatchingBlockHighlight.StartLine) + MatchingBlockHighlight = nil + end if + + //draw line numbers + if DisplayLineNumbers and gg <> gr then + g.DrawPicture Gutter, 0, 0 + end if + + //and frame + if Border then + g.ForeColor = BorderColor + g.DrawRect 0, 0, g.Width, g.Height + end if + + // paint visual block feedback + #if not TargetMacOS + if MouseOverBlock <> nil then + //compose editfield + blockHighlight + + dim tmpPic as Picture + if mBackBuffer <> nil then + tmpPic = NewPicture(gr.Width, gr.Height, 32) + g = tmpPic.Graphics + g.DrawPicture mBackBuffer, 0, 0, Width, Height, 0, 0, Width, Height + end + + dim blockPicture as Picture = NewPicture(self.Width - LineNumOffset, self.Height, 32) + Dim gb As Graphics = blockPicture.Graphics + + gb.ForeColor = &c000000 + gb.FillRect 0, 0, blockPicture.Width, blockPicture.Height + + gb = blockPicture.Mask.Graphics + gb.ForeColor = &cAAAAAA + gb.FillRect 0, 0, blockPicture.Width, blockPicture.Height + + gb.ForeColor = &cffffff + gb.FillRoundRect MouseOverBlock.value("x") + 1, MouseOverBlock.Value("y") + 1, MouseOverBlock.Value("w") - 2, MouseOverBlock.Value("h") - 2, 10,10 + + gb.ForeColor = &cA0A0A0 + gb.DrawRoundRect MouseOverBlock.value("x"), MouseOverBlock.Value("y"), MouseOverBlock.Value("w"), MouseOverBlock.Value("h"), 6,6 + + gb.ForeColor = &C999999 + gb.DrawRoundRect MouseOverBlock.value("x"), MouseOverBlock.Value("y"), MouseOverBlock.Value("w"), MouseOverBlock.Value("h"), 8,8 + gb.DrawRoundRect MouseOverBlock.value("x")+1, MouseOverBlock.Value("y")+1, MouseOverBlock.Value("w")-2, MouseOverBlock.Value("h")-2, 10,10 + + gb.ForeColor = &C888888 + gb.DrawRoundRect MouseOverBlock.value("x"), MouseOverBlock.Value("y"), MouseOverBlock.Value("w"), MouseOverBlock.Value("h"), 10,10 + gb.DrawRoundRect MouseOverBlock.value("x")+1, MouseOverBlock.Value("y")+1, MouseOverBlock.Value("w")-2, MouseOverBlock.Value("h")-2, 8,8 + + g.DrawPicture blockPicture, LineNumOffset, 0 + + if tmpPic <> nil then + gr.DrawPicture( tmpPic, 0, 0, Width, Height, 0, 0, Width, Height) + end if + else + if mBackBuffer <> nil then + gr.DrawPicture( mBackBuffer, 0, 0, gr.Width, gr.Height, 0, 0, mBackBuffer.Width, mBackBuffer.Height) + end + end if + + #else + // Now we can draw the back buffer to the screen + if mBackBuffer <> nil then + gr.DrawPicture( mBackBuffer, 0, 0, gr.Width, gr.Height, 0, 0, mBackBuffer.Width, mBackBuffer.Height) + end if + #endif + + //-----------------------------------------------------------overlays + + //paint the location of the Previous/next block char + if blockBeginPosX >= 0 then + PaintHighlightedBlock(gr, line.VisualIndent(self.IndentVisually)) + blockBeginPosX = -1 + blockBeginPosY = -1 + end if + + //right margin + if DisplayRightMarginMarker and RightMarginAtPixel > 0 then + 'gr.DrawLine rightMarginMarkerOffset - ScrollPositionX + LeftMarginOffset + LineNumOffset, 0, rightMarginMarkerOffset - ScrollPositionX + LeftMarginOffset + LineNumOffset, g.Height + gr.DrawPicture RightMarginLineImage, RightMarginAtPixel - ScrollPositionX + LeftMarginOffset + LineNumOffset, 0, 1, gr.Height, 0,0,1,1 + end if + + //paint caret + if DragSource = nil then + PaintCaret(CaretPos, gr, gutterWidth) + else + PaintCaret(DragTextPos, gr, gutterWidth) + end if + + PaintOver(gr, gutterWidth) + + #if TargetMacOS + // paint visual block feedback + if MouseOverBlock <> nil then + dim blockPicture as Picture = New Picture(self.Width - LineNumOffset, self.Height, 32) + blockPicture.Graphics.ForeColor = &c000000 + blockPicture.Graphics.FillRect 0, 0, blockPicture.Width, blockPicture.Height + + blockPicture.mask.Graphics.ForeColor = &cAAAAAA + blockPicture.mask.Graphics.FillRect 0, 0, blockPicture.Width, blockPicture.Height + + blockPicture.mask.Graphics.ForeColor = &cffffff + blockPicture.Mask.Graphics.FillRoundRect MouseOverBlock.value("x") + 1, MouseOverBlock.Value("y") + 1, MouseOverBlock.Value("w") - 2, MouseOverBlock.Value("h") - 2, 10, 10 + + blockPicture.mask.Graphics.ForeColor = &C888888 + blockPicture.mask.Graphics.PenWidth = 2 + blockPicture.mask.Graphics.PenHeight = 2 + blockPicture.Mask.Graphics.DrawRoundRect MouseOverBlock.value("x"), MouseOverBlock.Value("y"), MouseOverBlock.Value("w"), MouseOverBlock.Value("h"), 10, 10 + + gr.DrawPicture blockPicture, LineNumOffset, 0 + end if + #endif + + #if DebugBuild + redrawTime = Microseconds - redrawTime + #endif + + mRedrawEverything = false + mRedrawCaret = false + + #if DebugBuild and EditFieldGlobals.DebugIndentation + System.DebugLog "drawContents() done, linesOnScreen: "+str(VisibleLineRange.length) + #endif + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub enableBlinker(value as boolean) + if caretBlinker = nil then Return + + if value and not ReadOnly then + if DebugBuild and EditFieldGlobals.DebugIndentation then + // prevent repeated drawContent() calls for debugging + caretBlinker.Mode = timer.ModeOff + else + caretBlinker.Mode = timer.ModeMultiple + end if + caretState = true + Else + caretBlinker.Mode = timer.ModeOff + caretState = false + End If + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function fetchAutocompleteOptions() As boolean + //ask the client window for Autocomplete options for the current word. + mCurrentAutocompleteOptions = nil + + //get word where caret is + dim CurrentWordSegment as TextSegment = CurrentWord + if CurrentWordSegment.length = 0 then Return False + if CaretPos = CurrentWordSegment.offset then Return False //return false if cursor at the beginning of word... requested by Scott Fortmann-Roe + + //get the actual word text + dim prefix as String = TextStorage.getText(CurrentWordSegment.offset, CurrentWordSegment.length).Trim + if prefix.len = 0 or prefix = "." then Return False + + //raise event + mCurrentAutocompleteOptions = AutocompleteOptionsForPrefix(prefix) + + if CurrentAutocompleteOptions = nil then Return False + + if UBound(CurrentAutocompleteOptions.Options) < 0 then + mCurrentAutocompleteOptions = nil + Return False + end if + + CurrentAutocompleteOptions.Prefix = prefix + + //get current path component, what this is is, the last word after the last period in the prefix + //if the prefix is one.two.three then the current component will be "three" + dim tmpPath() as String = prefix.Split(".") + CurrentAutocompleteOptions.currentPathComponent = tmpPath(UBound(tmpPath)) + + Return true + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Find(what as string, ignoreCase as boolean, wrap as boolean, redraw as boolean = true, startPos as integer = - 1) As integer + if what = "" then Return -1 + + dim tmpTxt as String = TextStorage.getText(0, TextStorage.Length) + if tmpTxt.Encoding <> nil then tmpTxt = tmpTxt.ConvertEncoding(EditFieldGlobals.InternalEncoding) + + if startPos < 0 then startPos = SelStart + SelLength + startPos = tmpTxt.left(startPos).LenB //in case there are multi-bytes... + + static scanner as new RegEx ' let's make this static to avoid hard crashes on OS X with RB 2012r2.1 + + scanner.SearchPattern = what.ReplaceAll("\","\\").ReplaceAll("(","\(").replaceAll(")","\)").ReplaceAll("[","\[").ReplaceAll("]","\]").ReplaceAll("{","\{").ReplaceAll("}","\}").ReplaceAll("?","\?").ReplaceAll("*","\*").ReplaceAll("+","\+").ReplaceAll("|","\|").ReplaceAll("^","\^").ReplaceAll("$","\$").ReplaceAll(".","\.") + scanner.Options.CaseSensitive = not ignoreCase + + dim match as RegExMatch = scanner.Search(tmpTxt, startPos) + + if match = nil then + if wrap then + beep + Return find(what, ignoreCase, false, redraw, 0) + else + Return -1 //not found + end if + end if + + //reveal line if invisible (folded) + dim offset as integer = tmpTxt.LeftB(match.SubExpressionStartB(0)).len + dim length as integer = match.SubExpressionString(0).Len + dim lineIdx as Integer = lines.getLineNumberForOffset(offset) + + dim line as TextLine = lines.getLine(lineIdx) + if line <> nil and not line.visible then lines.revealLine(lineIdx) + + changeSelection(offset, length) + if redraw then self.Redraw + + Return SelStart + + Exception RegExSearchPatternException + Return -1 //ignore these... + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub FoldAllLines() + //hmmm + if not EnableLineFoldings then Return + lines.foldAll + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub FoldBlockAtCaretPos() + //fold the block where the caret is. + if not EnableLineFoldings then Return + + dim idx as Integer = OpeningBlockLineForLine(CaretLine) + if idx < 0 then Return + + ToggleLineFold(idx) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function FoldingOffset() As integer + if not EnableLineFoldings then Return 0 + Return blockStartImage.Width + 2 + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub getFieldXY(byref locx as integer, byref locy as integer) + //find the window where this control is... + //since the control can be deeeeeeep whithin container controls... + locx=me.Left + locy=me.top + + dim container as Window + Container=me.Window + + while true + locx=locx+Container.Left + locy=locy+Container.top + + if container isa ContainerControl then + Container=ContainerControl(Container).Window + + elseif Container isa Window then + + // Account for toolbar + // Thanks to Roger Meier + // This no longer works... from 2008r5+ it reports -1???? + #if RBVersion >= 2007.00 and TargetWin32 then + for i as integer = 0 to container.ControlCount-1 + if container.Control(i) isa Toolbar then + dim tb as Toolbar = Toolbar(container.Control(i)) + locy = locy + tb.Height + end if + next + #endif + + Exit + end if + Wend + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function GetLine(index as Integer) As string + //returns the text contained in a line. + dim line as TextLine = lines.getLine(index) + if line = nil then Return "" + + Return TextStorage.getText(line.offset, line.length) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub handleDoubleClick() + //highlight word(s) after a double click + + dim word as new TextSegment + dim char as String = TextStorage.getCharAt(CaretPos) + if IsWhitespace(char) then char = TextStorage.getCharAt(max(CaretPos - 1, 0)) + + //check if in placeholder.. + dim line as TextLine = lines.getLine(CaretLine) + if line <> nil and line.PlaceholderForOffset(CaretPos + 1) <> nil then + Word.offset = CaretPos + 1 + Word.length = 0 + + elseif IsBlockChar(char) then + //if is a block char, find the start/end block + dim tmp as String + + if BLOCK_OPEN_CHARS.instr(char) > 0 then // + Word.offset = CaretPos + 1 + word.length = NextBlockChar(char, CaretPos, tmp) - CaretPos - 1 + + else + Word.offset = PreviousBlockChar(char, CaretPos, tmp) + 1 + word.length = CaretPos - Word.offset + + end if + + elseif IsWhitespace(char) then + Word.offset = previousNonWhitespace(CaretPos) + Word.length = nextNonWhitespace(CaretPos) - word.offset + + elseif IsAlpha(char) then + Word.offset = previousNonAlpha(CaretPos) + Word.length = nextNonAlpha(CaretPos) - word.offset + + else + Word.offset = previousAlpha(CaretPos) + Word.length = nextAlpha(CaretPos) - word.offset + + end if + + changeSelection(word.offset, word.length, true) + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub HandleDragOnGutter(X as integer, Y as integer) + //Handle a mouse drag on the gutter. + + dim currPos as Integer = CharPosAtXY(x,y) + if selectedLine >=0 and x < LineNumOffset then + //drag on the line numbers + dim onLine as Integer = lines.getLineNumberForOffset(currPos) + dim fromLine, toLine as TextLine + fromLine = lines.getLine(min(selectedLine, onLine)) + toLine = lines.getLine(max(selectedLine, onLine)) + + changeSelection(fromLine.offset, toLine.offset + toLine.length - fromLine.offset - toline.delimiterLength) + + else + changeSelection(min(currPos, CaretPos), abs(currPos - CaretPos)) + + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub HandleHorizontalMouseDrag(x as integer, y as integer) + #pragma unused y + if x < LineNumOffset then + changeScrollValues(ScrollPositionX + (x - LineNumOffset), ScrollPosition) + + ElseIf x > Width then + changeScrollValues(ScrollPositionX + (x - Width), ScrollPosition) + + End If + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function HandleKeyDown(key as string) As boolean + //handles the keys pressed + ignoreRepaint = true + + const DELETE_KEY = 8 + const FWD_DELETE_KEY = 127 + const UP_KEY = 30 + const DOWN_KEY = 31 + const LEFT_KEY = 28 + const RIGHT_KEY = 29 + const PAGE_UP_KEY = 11 + const PAGE_DOWN_KEY = 12 + const HOME_KEY = 1 + const END_KEY = 4 + const ESC_KEY = 27 + + dim keyAsc as Integer = asc(key) + + #if DebugBuild + keyDownTime = Microseconds + #endif + + dim byPage as Boolean = Keyboard.OptionKey + dim toBorder as Boolean + #if TargetMacOS + toBorder = Keyboard.CommandKey + #else + toBorder = Keyboard.ControlKey + #endif + + //delete + if keyAsc = DELETE_KEY or keyAsc = FWD_DELETE_KEY then + //add a new event ID if changed typing, or no event ID, or time elapsed between events is 3 secs + if typing or CurrentEventID = 0 or ticks > CurrentEventID + (60 * UNDO_EVT_BLOCK_SECS) then + CurrentEventID = Ticks + end if + typing = False + DeleteChars(keyAsc = FWD_DELETE_KEY) + updateDesiredColumn + + //arrow keys + elseif keyAsc = LEFT_KEY then + CurrentEventID = 0 + moveCaretLeft toBorder + + elseif keyAsc = RIGHT_KEY then + CurrentEventID = 0 + moveCaretRight toBorder + + elseif keyAsc = UP_KEY then + CurrentEventID = 0 + moveCaretUp byPage, toBorder + + elseif keyAsc = DOWN_KEY then + CurrentEventID = 0 + moveCaretDown byPage, toBorder + + ElseIf keyAsc = PAGE_UP_KEY then + #if TargetMacOS + PageUp + #else + moveCaretUp true, toBorder // move cursor by a page + #endif + + elseif keyAsc = PAGE_DOWN_KEY then + #if TargetMacOS + PageDown + #else + moveCaretDown true, toBorder // move cursor by a page + #endif + + elseif keyAsc = HOME_KEY then + #if TargetMacOS + ScrollHome + #else + if toBorder then + moveCaretUp false, true // top of doc + else + moveCaretLeft true // start of line + end if + #endif + + elseif keyAsc = END_KEY then + #if TargetMacOS + ScrollEnd + #else + if toBorder then + moveCaretDown false, true // end of doc + else + moveCaretRight true // end of line + end if + #endif + + //autocomplete + //forward the event to the user to figure out if this key should trigger the autocomplete + //it has a performance penalty, if you would like to set your own key, replace + //ShouldTriggerAutocomplete(key, fetchAutocompleteOptions) with your key combination. + elseif EnableAutocomplete and ShouldTriggerAutocomplete(key, fetchAutocompleteOptions) then + AutocompleteManual + + // ignore any control chars (includes Esc) + elseif keyAsc <= 31 and keyAsc <> 13 then + ignoreRepaint = False + Return False + + Else + // Any other key can be inserted as text + + // Note: On Windows (and maybe Linux as well) this will misinterpret the Function Keys (F-Keys) to insert + // french accented characters, because damn RB/Xojo does not let us tell both apart. + // See also: https://forum.xojo.com/11253-how-can-i-tell-f-keys-and-accented-chars-apart-in-keydown-event + + //add a new event ID if changed typing, or no event ID, or time elapsed between events is 5 secs + if not typing or CurrentEventID = 0 or ticks > CurrentEventID + (60 * UNDO_EVT_BLOCK_SECS) then CurrentEventID = Ticks + typing = true + + //if there's a selection, replace it + if me.SelLength > 0 then + private_replace(selStart , me.SelLength, key) + else + //see if we need to Autocomplete brackets + dim bracketInserted as Boolean + if AutoCloseBrackets then + //scan the possible opening block chars... + for i as Integer = 1 to BLOCK_OPEN_CHARS.len + if key = BLOCK_OPEN_CHARS.Mid(i, 1) then + //found, so the closing block MUST be at the same location, in the BLOCK_CLOSE_CHARS + key = key + BLOCK_CLOSE_CHARS.Mid(i, 1) + bracketInserted = true + exit for + end if + next + end if + insert(selStart, key) + + //if autocompleted, move caret one char to the left + if bracketInserted then CaretPos = CaretPos - 1 + end if + + //autoindent lines? + //check if current (new) entered line needs autoindenting... + If AutoIndentNewLines and not mIndentVisually then + dim thisLine as TextLine = lines.getLine(CaretLine) + if thisLine <> nil and (key = chr(13) or key = chr(3) or thisLine.isBlockEnd) then + //indent this new line + dim state as Variant + if private_indentline (CaretLine, false, state) then + InvalidateLine (CaretLine) + end + end if + End If + + //save the screen location of the caret, in case we need to move up/down + updateDesiredColumn + end if + + #if DebugBuild + keyDownTime = Microseconds - keyDownTime + #endif + + if MouseOverBlock <> nil then + CreateMouseOverBlockHighlight(CaretLine) + end if + + //and redraw. + ignoreRepaint = False + Redraw + + Return true + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub HandleTextChanged() + if clearHighlightedRangesOnTextChange then + ClearHighlightedCharacterRanges + end if + + RaiseEvent TextChanged + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub HandleTextDrag(x as integer, y as integer) + //save the selection... + DragTextSelection = new DataRange + DragTextSelection.offset = SelStart + DragTextSelection.length = SelLength + + dim drag as Picture = SelectedTextDragImage + dim di as DragItem = New DragItem(self.TrueWindow, x,y, drag.Width, drag.Height) + + //set the drag text + di.Text = me.SelText + di.SetImage(drag) + + //set the dragging source. + DragSource = self + di.Drag + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub handleTripleClick() + // Select current line + me.selectline(CaretLine, True) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub HandleVerticalMouseDrag(x as integer, y as integer) + #pragma unused x + //if dragging selection outside visible area... + if y < 0 or y > Height then + + dim linesToScroll as Integer + + if y < 0 then + linesToScroll = y / TextHeight + + else + linesToScroll = (y - Height) / TextHeight + + end if + + //cap the number of times this method gets called per second (here is max 12 calls per second) + if Ticks > lastDragTicks + 5 then + changeScrollValues(ScrollPositionX, ScrollPosition + linesToScroll) + lastDragTicks = Ticks + end if + + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub Highlight() + #if true + HighlightNow nil + #else + // This is an attempt to delay the highlight thread just a little to keep performance up + // but it causes a too-long latency, leading to delayed screen updates, unfortunately. + if mHighlightTimer = nil then + mHighlightTimer = new Timer + mHighlightTimer.Period = 0 + AddHandler mHighlightTimer.Action, AddressOf HighlightNow + end if + mHighlightTimer.Mode = Timer.ModeSingle + #endif + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub HighlightCharacterRange(offset as integer, length as integer, withColor as color, rounded as boolean = false) + if offset >= TextLength or length = 0 then Return + + if offset + length >= TextLength then + length = TextLength - offset + end if + + dim tmp as new CharSelection + + tmp.offset = offset + tmp.length = length + + tmp.StartLine = lines.getLineNumberForOffset(tmp.offset) + tmp.EndLine = lines.getLineNumberForOffset(tmp.offset + tmp.length) + + tmp.SelectionColor = withColor + tmp.Rounded = rounded + tmp.LosesFocus = False + + tmp = HighlightedRanges.addSelection(tmp) + for i as Integer = tmp.StartLine to tmp.EndLine + InvalidateLine(i) + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub HighlightClosingBlock(text as string, offset as integer) + //find the next closing block, starting at offset + dim pos as Integer + dim closeChar as String + pos = NextBlockChar(text, offset, closeChar) + + if pos >= 0 then + if HighlightMatchingBracketsMode = 0 then //circle + XYAtCharPos(pos, blockBeginPosX, blockBeginPosY) + else + dim line as Integer = LineNumAtCharPos(pos) + MatchingBlockHighlight = new CharSelection(pos, 1, line, line, BracketHighlightColor) + InvalidateLine(line) + end if + + BlockCharsMatched(text, offset, closeChar, pos) + end + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function highLighterTask(createIfMissing as Boolean) As LineHighlighter + if mHighlighter = nil and createIfMissing then + //create Highlighter thread + mHighlighter = new LineHighlighter(self, SyntaxDefinition, self.modifiedLines, TextStorage, lines) + self.registerForMessage(mHighlighter) + end if + + return mHighlighter + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function HighLighterTaskBusy() As Boolean + if mHighlighter = nil then + return false + end + + return mHighlighter.State <> mHighlighter.NotRunning + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub HighlightNow(caller as Timer) + #pragma unused caller + + if mHighlighter = nil or mHighlighter.State = Thread.NotRunning then + highlighterTask(true).Run + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub HighlightOpeningBlock(text as string, offset as integer) + //find the previous opening block, starting at offset + + dim pos as Integer + dim openingChar as string + pos = PreviousBlockChar(text, offset, openingChar) + + if pos >= 0 then + if HighlightMatchingBracketsMode = 0 then//circle + XYAtCharPos(pos, blockBeginPosX, blockBeginPosY) + else + dim line as Integer = LineNumAtCharPos(pos) + MatchingBlockHighlight = new CharSelection(pos, 1, line, line, BracketHighlightColor) + InvalidateLine(line) + end if + + BlockCharsMatched(openingChar, pos, text, offset) + else + //no open block found + break + beep + end + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function indentStr(indents as integer) As string + if mIndentString = "" then + mIndentString = Chr(9) + for i as Integer = 1 to 8 + mIndentString = mIndentString + mIndentString + next + end if + + Return mIndentString.Left(indents) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Insert(offset as integer, text as string) + private_replace(offset, 0, Text, true) + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub internalReplace(offset as integer, length as integer, text as string) + // This internal function performs no undo, no change notification, nor updating of the caret position + + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + #pragma unused lock + + if ReadOnly then + break + Return + end if + + //get the default line ending from the line manager, if the text is an enter. + if text = chr(13) or text = chr(3) then text = lines.lineEnding + + //modify buffer and rescan lines + TextStorage.replace(offset, length, Text) + lines.replace(offset, length, text, false) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub InvalidateAllLines() + //mark all lines for redraw + InvalidateLine(-1) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub InvalidateLine(index as integer) + //invalidates the given line, that is, mark it for redrawing + fullRefresh = index < 0 or fullRefresh + invalidLines.Value(index) = true + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function IsAlpha(inText as string) As boolean + //is "inText" alphanumeric? + Return matchesRegex("\w", inText) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function IsBlockChar(char as string) As boolean + //is "char" a block char? + if blockCharsPattern = "" then + //build search pattern + for i as Integer = 1 to BLOCK_OPEN_CHARS.len + blockCharsPattern = blockCharsPattern + "\" + BLOCK_OPEN_CHARS.Mid(i, 1) + "\"+BLOCK_CLOSE_CHARS.Mid(i, 1) + next + blockCharsPattern = "[" + blockCharsPattern + "]" + end if + Return matchesRegex(blockCharsPattern, char) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsDirty() As boolean + Return UndoMgr.isDirty + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsUndoing() As Boolean + return UndoMgr.isUndoing + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function IsWhitespace(what as string) As boolean + //is "what" whitespace? + Return matchesRegex("\s", what) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineCount() As integer + Return lines.Count + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub LineCountChanged(newLineCount as integer) + #pragma unused newLineCount + //called by line manager, when the line number changes. + + //force to recalculate the line number gutter + LineNumOffset = 0 + + //raise the linecountchanged event + RaiseEvent LineCountChanged lines.Count + + //and calculate the maximum scrollbar values. + CalculateMaxVerticalSB + + InvalidateAllLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineDelimiter() As String + // Can be one or two chars (CR, CR+LF) + return lines.lineEnding + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineHasBookmark(lineIndex as Integer) As Boolean + Return BookmarkTable.HasKey(lineIndex) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub LineHighlighted(lineIndex as integer) + //mark for repaint + InvalidateLine(lineIndex) + + dim line as TextLine = lines.getLine(lineIndex) + if line = nil then Return + + if line.LineSymbols = nil or line.LineSymbols.Count = 0 then Return + + //extract symbols and add them to local table. + if CurrentDocumentSymbols = nil then CurrentDocumentSymbols = new Dictionary + + for i as Integer = 0 to line.LineSymbols.Count - 1 + //CurrentDocumentSymbols.Value(line.LineSymbols.Key(i)) = line + CurrentDocumentSymbols.Value(line) = nil + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineIcon(index as integer) As picture + //returns the rowicon for the index line + dim line as TextLine = lines.getLine(index) + if line = nil then + Return nil + end if + + return line.icon + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub LineIcon(index as integer, assigns value as picture) + //sets a rowicon + dim line as TextLine = lines.getLine(index) + if line = nil then + Return + end if + + line.icon = value + InvalidateLine(index) + + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineNumAtCharPos(offset as integer) As integer + Return lines.getLineNumberForOffset(offset) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineOffset(index as Integer) As Integer + //returns the text offset for the given line (lines start at 0) + dim line as TextLine = lines.getLine(index) + if line = nil then return 0 + return line.offset + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub LineSymbolsRemoved(symbols as dictionary) + //either the symbols have changed, or the line containing them was removed, so remove them from local table. + dim key as TextLine + + for each key in symbols.keys + if CurrentDocumentSymbols.HasKey(key) then CurrentDocumentSymbols.Remove(key) + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub loseFocus() + CurrentEventID = 0 + hasFocus = False + LostFocus + enableBlinker(False) + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function LTrimLines(s as String) As String + dim lines() as String = ReplaceLineEndings(s,EndOfLine).Split(EndOfLine) + + for i as Integer = 0 to lines.Ubound + lines(i) = lines(i).LTrim + next + + return Join(lines,EndOfLine) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function matchesRegex(pattern as string, expression as string) As boolean + //returns whether expression matches the pattern. + + static rg as new RegEx ' making this static avoids a hard crash when hitting the Return key right after running the CEF Demo project on OS X with RB 2012r2.1 + + rg.SearchPattern = pattern + + expression = expression.ConvertEncoding(EditFieldGlobals.InternalEncoding) + + dim myMatch as RegExMatch = rg.search(expression) + + Return myMatch <> nil + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub MaxLineLengthChanged(longestLineIndex as integer) + //called by the line manager, when there's a new line that's the longest line. + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //oh yes... this can be a lot better, for starters we can get the screen width by reading all the word lengths in this line... I guess I'm just lazy. + if longestLineIndex < 0 then Return + dim maxLine as TextLine = lines.getLine(longestLineIndex) + if maxline = nil or abs(maxLine.length - lastLongestLineLength) < 2 then Return + + //cache length + lastLongestLineLength = maxLine.length + + //measure string in pixels + dim tmp as Picture = tmpPicture + + dim maxLength as single = maxLine.TextWidth(TextStorage, tmp.Graphics, DisplayInvisibleCharacters) + + if maxLength = lastLongestLinePixels then Return + lastLongestLinePixels = maxLength + + //raise event + RaiseEvent MaxLineLengthChanged lastLongestLinePixels + + //calculate scrollbar + CalculateMaxHorizontalSB + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub moveCaretDown(pageUp as Boolean, moveToEnd as Boolean) + //arrow down... + + //if selection, move Caret to end of selection. + if not Keyboard.ShiftKey and selLength > 0 then + changeSelection(selStart + selLength, 0) + Return + end if + + dim lineNum as Integer + + //find line number + if selStart < CaretPos then + lineNum = lines.getLineNumberForOffset(selStart) + else + lineNum = lines.getLineNumberForOffset(selStart + selLength) + end if + + //default the lines to move to 1 + dim linesToMove as Integer = 1 + + if moveToEnd then + // move to end of document + linesToMove = lines.Count - 1 - ScrollPosition + elseif pageUp then + // move down a full page + linesToMove = MaxVisibleLines - 1 + end if + + dim line as TextLine + dim offset as Integer + //get line to move to + LineNum =LineNum + linesToMove + if LineNum >= lines.Count then //moving down on the last line, jump to the end of that line + LineNum = lines.Count - 1 + line = lines.getLine(LineNum) + offset = line.offset + line.length - line.delimiterLength + else + line = lines.getLine(LineNum) + //find offset for screen desired position + offset = offsetForXPos(line, caretDesiredColumn) + end if + + //if shift key pressed, then we're extending the selection + if Keyboard.ShiftKey then + changeSelection(min(CaretPos, offset), abs(offset - CaretPos)) + else + changeSelection(offset, 0) + end if + + //scroll if necessary. + if lineNum > ScrollPosition + VisibleLineRange.length - 2 then + changeScrollValues(ScrollPositionX, LineNum - VisibleLineRange.length + 2) + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub moveCaretLeft(toStartOfLine as Boolean) + //left arrow pressed + + dim pos as Integer + + //default the places to move to 1 + dim charsToMove as Integer = 1 + dim LineNum as Integer + + //if shift pressed, we're changing the current selection. + if Keyboard.ShiftKey then + + //if the end of the selection is after the CaretPos then, shrink selection + if selStart + selLength > CaretPos then + + //move to start of line. + if toStartOfLine then + + //lineNum = lines.getLineNumberForOffset(selStart+selLength - 1) + lineNum = lines.getLineNumberForOffset(selStart+selLength) + + if selStart +selLength > lines.getLine(LineNum).offset then + changeSelection(lines.getLine(LineNum).offset, CaretPos - lines.getLine(LineNum).offset) + pos = selStart + + end if + ViewToCharPos(LineNum, SelStart + SelLength) + + //move to previous word + ElseIf Keyboard.OptionKey then + dim previous as Integer = previousNonAlpha(selStart + selLength) + + changeSelection(previous, CaretPos - previous) + pos = previous + + ViewToCharPos(pos) + + //move just by one place + else + changeSelection(selStart, selLength - charsToMove) + pos = selStart + selLength + + ViewToCharPos(pos) + end if + + //selection end is at caretpos, expand selection + else + + //move to start of line + if toStartOfLine then + //LineNum = lines.getLineNumberForOffset(max(selStart - 1, 0)) + LineNum = lines.getLineNumberForOffset(max(selStart, 0)) + + if selStart > lines.getLine(LineNum).offset then + changeSelection(lines.getLine(LineNum).offset, CaretPos - lines.getLine(LineNum).offset) + pos = selStart + + end if + ViewToCharPos(LineNum, SelStart) + + //move to previous word + ElseIf Keyboard.OptionKey then + dim previous as Integer = previousNonAlpha(selStart) + + changeSelection(previous, CaretPos - previous) + pos = SelStart + ViewToCharPos(pos) + + //move one place + else + changeSelection(selStart - charsToMove, selLength + charsToMove) + pos = selStart + ViewToCharPos(pos) + + end if + + end if + + //no shift, but there's active selction, move CaretPos to start of selection + elseif selLength > 0 then + changeSelection(selStart, 0) + pos = selStart + + //moving the caret + else + + //move it to the start of the line + if toStartOfLine then + //get the line + LineNum = lines.getLineNumberForOffset(selStart) + charsToMove = selStart - lines.getLine(LineNum).offset + + //move to previous word boundary + elseif Keyboard.OptionKey then + charsToMove = selStart - previousNonAlpha(selStart) + + end if + //simple move + changeSelection(selStart - charsToMove, 0) + pos = CaretPos + ViewToCharPos(CaretLine, pos) + + //did we just "crossed" a block character? + dim char as String = TextStorage.getCharAt(CaretPos) + if IsBlockChar(char) then + //then find the opening/closing char for this block. + if BLOCK_CLOSE_CHARS.instr(char) > 0 then + HighlightOpeningBlock(char, caretpos) + + Else + HighlightClosingBlock(char, caretpos) + + end if + end if + end if + + updateDesiredColumn(CaretPos) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub moveCaretRight(toEndOfLine as Boolean) + //right arrow pressed + + dim pos as Integer + + //default move to 1 char + dim charsToMove as Integer = 1 + dim LineNum as Integer + + //shift down, modify selection + if Keyboard.ShiftKey then + + //shrink + if selStart < CaretPos then + + //move to end of line + if toEndOfLine then + LineNum = lines.getLineNumberForOffset(selStart) + + if selStart < lines.getLine(LineNum).offset + lines.getLine(LineNum).length then + dim line as TextLine = lines.getLine(LineNum) + changeSelection(line.offset + line.length - line.delimiterLength, CaretPos - line.offset - line.length) + //oct 10 changed changeSelection(line.offset + line.length,.... to stop before EOL + pos = selStart + end if + ViewToCharPos(LineNum, SelStart) + + //move to next word boundary + ElseIf Keyboard.OptionKey then + dim nextT as Integer = nextNonAlpha(selStart) + + changeSelection(nextT, CaretPos - nextT) + pos = nextT + ViewToCharPos(pos) + + //move one place + else + changeSelection(selStart + charsToMove, selLength - charsToMove) + pos = selStart + ViewToCharPos(pos) + + end if + + //expand + else + + //to end of line + if toEndOfLine then + LineNum = lines.getLineNumberForOffset(selStart + selLength) + + if selStart + selLength < lines.getLine(LineNum).offset + lines.getLine(LineNum).length then + dim line as TextLine = lines.getLine(LineNum) + changeSelection(selStart, line.offset + line.length - selStart - line.delimiterLength) + //oct 10 changed ..., line.offset + line.length - selStart - line.delimiterLength) to stop before EOL + pos = selStart + selLength + + end if + ViewToCharPos(LineNum, SelStart + SelLength) + + //next word + ElseIf Keyboard.OptionKey then + dim nextT as Integer = nextNonAlpha(selStart + SelLength) + + changeSelection(nextT, CaretPos - nextT) + pos = nextT + ViewToCharPos(pos) + + //single char + else + changeSelection(selStart, selLength + charsToMove) + pos = selStart + selLength + ViewToCharPos(pos) + + end if + + end if + + //no shift, but active selection, move to end of selection + elseif selLength > 0 then + changeSelection(selStart + selLength, 0) + pos = selStart + else + + //EOL + if toEndOfLine then + LineNum = lines.getLineNumberForOffset(selStart) + + charsToMove = lines.getLine(LineNum).offset + lines.getLine(LineNum).length - selStart - lines.getLine(LineNum).delimiterLength + + //next word + elseif Keyboard.OptionKey then + charsToMove = nextNonAlpha(selStart) - selStart + + end if + //simple move + dim char as String = TextStorage.getCharAt(CaretPos) + + //check if next char is a block char + if IsBlockChar(char) then + //and mark it for Highlight + if BLOCK_CLOSE_CHARS.instr(char) > 0 then + HighlightOpeningBlock(char, caretpos) + + Else + HighlightClosingBlock(char, caretpos) + + end if + end if + + changeSelection(selStart + charsToMove, 0) + pos = CaretPos + ViewToCharPos(CaretLine, pos) + end if + + updateDesiredColumn(CaretPos) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub moveCaretUp(pageUp as Boolean, moveToStart as Boolean) + //arrow up + + //if not shift key and there's a selection, simply move to start of selection + if not Keyboard.ShiftKey and selLength > 0 then + changeSelection(selStart, 0) + Return + end if + + dim lineNum as Integer + + //get starting line number + if selStart < CaretPos then + lineNum = lines.getLineNumberForOffset(selStart) + else + lineNum = lines.getLineNumberForOffset(selStart + selLength) + end if + + //default move to one place + dim linesToMove as Integer = 1 + + if moveToStart then + // move to start of document + linesToMove = ScrollPosition + MaxVisibleLines + elseif pageUp then + // move up a full page + linesToMove = MaxVisibleLines - 1 + end if + + dim line as TextLine + dim offset as Integer + + LineNum = LineNum - linesToMove + if LineNum < 0 then //moving up on the first line, jump to the begining of line + LineNum = 0 + line = lines.getLine(lineNum) + offset = 0 + else + line = lines.getLine(LineNum) + offset = offsetForXPos(line, caretDesiredColumn) + end if + + if Keyboard.ShiftKey then + changeSelection(min(CaretPos, offset), abs(offset - CaretPos)) + else + changeSelection(offset, 0) + end if + + //scroll + if lineNum < ScrollPosition then + changeScrollValues(ScrollPositionX, lineNum) + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function nextAlpha(fromOffset as integer) As integer + //find the next alphanumeric char, starting at fromOffset + for i as Integer = fromOffset + 1 to TextStorage.Length - 1 + dim char as String = TextStorage.getCharAt(i) + if IsAlpha(char) then Return i + next + Return TextStorage.Length + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function NextBlockChar(forChar as string, offset as integer, byref charToFind as String) As integer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //find the next block char, for the given "forChar" char + //dim charToFind as String + charToFind = "" + + //find the char that closes forChar + for i as Integer = 1 to BLOCK_OPEN_CHARS.len + if forChar = BLOCK_OPEN_CHARS.Mid(i, 1) then + charToFind = BLOCK_CLOSE_CHARS.Mid(i, 1) + exit for + end if + next + + if charToFind = "" then Return -1 + + //to handle nested blocks + dim depth as integer + dim char as String + #if DebugBuild + dim maxOffset as Integer = TextStorage.Length + #pragma unused maxOffset + #endif + + dim textToSearch as String = TextStorage.getText(offset + 1, TextStorage.Length - (offset + 1)) + textToSearch = textToSearch.ConvertEncoding(EditFieldGlobals.InternalEncoding) + + static scanner as new RegEx ' let's make this static to avoid hard crashes on OS X with RB 2012r2.1 + + scanner.SearchPattern = "\"+forChar+"|\"+charToFind + + dim match as RegExMatch = scanner.Search(textToSearch) + + While match <> nil + 'for i as Integer = offset + 1 to maxOffset + char = match.SubExpressionString(0)'TextStorage.getCharAt(i) + + //if it's the char we're looking for, and not nested, then we found it + if char = charToFind and depth = 0 then + Return textToSearch.leftb(match.SubExpressionStartB(0)).len + offset + 1 + + //else, if it's the same char as input, then we're nesting. + ElseIf char = forChar then + depth = depth + 1 + + //if nested, and we found a closing char, decrease nesting count. + ElseIf char = charToFind then + depth = depth - 1 + End If + + match = scanner.Search + 'next + Wend + Return -1 + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function nextCharInSet(fromOffset as integer, pattern as string = "[^\w\.]") As integer + //find the next char not in the given set, starting at pos "fromOffset" + for i as Integer = fromOffset + 1 to TextStorage.Length - 1 + dim char as String = TextStorage.getCharAt(i) + if matchesRegex(pattern, char) then Return i + next + Return TextStorage.Length + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function nextNonAlpha(fromOffset as integer) As integer + //find next non alphanumeric char, starting at fromOffset + for i as Integer = fromOffset + 1 to TextStorage.Length - 1 + dim char as String = TextStorage.getCharAt(i) + if not IsAlpha(char) then Return i + next + Return TextStorage.Length + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function nextNonWhitespace(fromOffset as integer, maxOffset as integer = - 1) As integer + if maxOffset < 0 then maxOffset = TextStorage.Length + + //find next non whitespace char, starting at fromOffset + for i as Integer = fromOffset + 1 to maxOffset - 1 + dim char as String = TextStorage.getCharAt(i) + if not IsWhitespace(char) then Return i + next + Return maxOffset + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function offsetForXPos(line as TextLine, xPos as integer) As integer + //finds the offset (column) for a given line and desired screen position (xpos) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + xPos = xPos - line.VisualIndent(self.IndentVisually) + + dim offset as Integer = line.length + dim lineWidth as Integer + xPos = max(0, xPos) //negative numbers would be in the margin + + dim startPos as Integer + dim searchWord as TextSegment = line.LocalSegmentForXPos(xPos) + if searchWord <> nil then startPos = searchWord.offset + dim tmp as Picture = tmpPicture + + if searchWord isa TextPlaceholder then + lineWidth = line.TextWidth(TextStorage, tmp.Graphics, DisplayInvisibleCharacters, startPos) //width up to placeholder + dim placeholderWidth as Double = searchWord.width + + if Dragging then + if xPos >= lineWidth + placeholderWidth / 2 then + offset = searchWord.offset + searchWord.length + else + offset = searchWord.offset + end if + else + offset = searchWord.offset + searchWord.length / 2 //put the offset in the middle of the thing + end if + + else + + for i as Integer = startPos to line.length + //scan the chars in the line until we find the right column. + + lineWidth = line.TextWidth(TextStorage, tmp.Graphics, DisplayInvisibleCharacters, i) + if lineWidth >= xPos then + dim matchChar as String = line.CharToDisplayAt(TextStorage, max(i - 1, 0), DisplayInvisibleCharacters) + if searchWord <> nil then + tmp.Graphics.Bold = searchWord.bold + tmp.Graphics.Italic = searchWord.italic + tmp.Graphics.Underline = searchWord.underline + end if + dim charw as Integer = tmp.Graphics.StringWidth(matchChar) + + if lineWidth - charw / 2 > xPos then + offset = max(i - 1, 0) + else + offset = i + end if + exit for + end if + + next + end if + + //avoid delimiters + offset = line.offset + min(offset, line.length - line.delimiterLength) + Return offset + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function OpeningBlockLineForLine(lineIndex as integer) As integer + // RDS - Did not work if cursor was ON the start line + dim temp_value as integer + temp_value = lines.previousBlockStartLine(LineIndex, true) + + if temp_value <= 0 then + if lines.getline(LineIndex).isBlockStart then temp_value = LineIndex + end if + return temp_value + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub PageDown() + ScrollPosition = min(lines.Count, ScrollPosition + MaxVisibleLines) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub PageUp() + ScrollPosition = max(ScrollPosition - MaxVisibleLines, 0) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub PaintCaret(atPos as integer, g as graphics, gutterWidth as Integer) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if not hasFocus and DragSource = nil then Return + if selLength > 0 and DragTextSelection = nil then Return + + caretState = not caretState + if caretState then Return + + dim xpos, ypos as Integer + + if atPos = CaretPos then + XYAtCharPos(atPos, CaretLine, xpos, ypos) + else + XYAtCharPos(atPos, xpos, ypos) + end if + + if xpos < gutterWidth or ypos < 0 then Return + + g.ForeColor = CaretColor + + if ThickInsertionPoint then + g.PenWidth = 2 + end if + g.DrawLine xpos - 1, ypos - 1 , xpos - 1, ypos - TextHeight + 1 + g.PenWidth = 1 + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub PaintHighlightedBlock(g as graphics, indent as Integer) + if not HighlightMatchingBrackets then Return + + //paints a blue circle over the highlighted block char. + g.PenWidth = 2 + g.PenHeight = 2 + g.ForeColor = &c4444FF + g.DrawOval indent + blockBeginPosX - 2 - g.StringWidth("(")/2, blockBeginPosY - g.TextHeight - 1, g.TextHeight + 4, g.TextHeight + 4 + g.PenWidth = 1 + g.PenHeight = 1 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Paste() + dim c as new Clipboard + if not c.TextAvailable then Return + + dim t as String = c.Text + + if IndentVisually then + // trim lines + t = LTrimLines(t) + end + + t = t.ReplaceAll (Chr(0), Chr(1)) + + me.SelText = t + + InvalidateAllLines + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function previousAlpha(fromOffset as integer) As integer + //find Previous alphanumeric char starting at "fromOffset" + for i as Integer = fromOffset - 1 downto 1 + dim char as String = TextStorage.getCharAt(i - 1) + if IsAlpha(char) then Return i + next + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function PreviousBlockChar(forChar as string, offset as integer, byref charToFind as String) As integer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //find previous block char + //dim charToFind as String + charToFind = "" + + //select the appropriate one + for i as Integer = 1 to BLOCK_CLOSE_CHARS.len + if forChar = BLOCK_CLOSE_CHARS.Mid(i, 1) then + charToFind = BLOCK_OPEN_CHARS.Mid(i, 1) + exit for + end if + next + + if charToFind = "" then Return - 1 + + dim depth as integer + dim char as String + + dim textToSearch as String = TextStorage.getText(0, offset) + textToSearch = textToSearch.ConvertEncoding(EditFieldGlobals.InternalEncoding) + + textToSearch = Reverse(textToSearch) + + static scanner as new RegEx ' let's make this static to avoid hard crashes on OS X with RB 2012r2.1 + + scanner.SearchPattern = "\"+forChar+"|\"+charToFind + + dim match as RegExMatch = scanner.Search(textToSearch) + + //scan text + 'for i as Integer = offset - 1 downto 0 + while match <> nil + char = match.SubExpressionString(0) 'TextStorage.getCharAt(i) + + //found + if char = charToFind and depth = 0 then + Return offset - 1 - textToSearch.leftb(match.SubExpressionStartB(0)).len + + //nest + ElseIf char = forChar then + depth = depth + 1 + + //un-nest + ElseIf char = charToFind then + depth = depth - 1 + End If + 'next + match = scanner.Search + wend + Return -1 + + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function previousCharInSet(fromOffset as integer, pattern as string = "[^\w\.]") As integer + //find character not in the given set + for i as Integer = fromOffset - 1 downto 1 + dim char as String = TextStorage.getCharAt(i - 1) + if matchesRegex(pattern, char) then Return i + next + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function previousLineDelimiter(fromOffset as integer) As integer + //find Previous line delimiter char starting at "fromOffset" + for i as Integer = fromOffset - 1 downto 1 + dim char as String = TextStorage.getCharAt(i-1) + if char = LineDelimiter then Return i + next + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function previousNonAlpha(fromOffset as integer) As integer + //find Previous non alphanumeric char starting at "fromOffset" + for i as Integer = fromOffset - 1 downto 1 + dim char as String = TextStorage.getCharAt(i - 1) + if not IsAlpha(char) then Return i + next + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function previousNonWhitespace(fromOffset as integer) As integer + //find Previous non whitespace char starting at "fromOffset" + for i as Integer = fromOffset - 1 downto 1 + dim char as String = TextStorage.getCharAt(i - 1) + if not IsWhitespace(char) then Return i + next + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function private_indentline(lineIndex as Integer, ltrimLine as Boolean, ByRef indentationState as Variant) As Boolean + dim line as TextLine = lines.getLine (lineIndex) + + if line = nil then + return true // true because it may just be an empty line, and that doesn't mean we can stop the outer loop, or am I (TT) wrong? + end + + if SyntaxDefinition = nil then + line.indent = 0 + Return false + end + + #if DebugBuild and EditFieldGlobals.DebugIndentation + 'System.DebugLog "indent line "+str(lineIndex)+": "+TextStorage.getText(line.offset, line.length) + #endif + + dim modified as Boolean + dim previousLine as TextLine + + // Determine the indentation level + dim currTextUntrimmed as String = TextStorage.getText(line.offset, line.length) + // we need to remove the EOL char at the end of the line as it could fool our leading whitespace detection if the entire line is whitespace + dim eol as String = self.LineDelimiter + if currTextUntrimmed.Right(eol.Len) = eol then + currTextUntrimmed = currTextUntrimmed.Left(currTextUntrimmed.Len-eol.Len) + end if + dim currTextTrimmed as String = currTextUntrimmed.LTrim + dim currLeadingSpaces as Integer = currTextUntrimmed.Len - currTextTrimmed.Len + dim origLeadingSpaces as Integer = currLeadingSpaces + + if ltrimLine and currLeadingSpaces > 0 then + // Discard leading spaces from current line + if mIndentVisually then + // remove now because we'll not add new spaces + + if CurrentEventID <= 0 then + // This must not happen - the caller must ensure that the EventID is set to Ticks + // before he starts any modification that invokes this method + break + CurrentEventID = Ticks + end if + + // now remove the leading spaces + private_replace (line.offset, origLeadingSpaces, "", false, CurrentEventID, true, true) + line = lines.getLine(lineIndex) + currLeadingSpaces = 0 + currTextUntrimmed = currTextTrimmed + modified = true + end if + end + + dim indentState as String + if indentationState = nil then + indentState = line.IndentationStateIn + else + indentState = indentationState + end + line.UpdateIndentationState (SyntaxDefinition, indentState, currTextTrimmed) + if line.ChangedIndentStateAndReset then + modified = true + end + indentationState = line.IndentationStateOut + + if line.isBlockEnd then + // use indentation of previous block start + + dim blockStartIdx as Integer = lines.previousBlockStartLine(lineIndex) + if blockStartIdx < 0 then + // no previous block opening found - use previous line's indent + blockStartIdx = max (0, lineIndex-1) + else + end if + previousLine = lines.getLine(blockStartIdx) + + // reset the indentation + dim newIndent as Integer + if previousLine <> nil then + // take the block start's indent value + newIndent = previousLine.indent + else + // if there's no block start to find, let's zero the indent level + newIndent = 0 + end if + if newIndent <> line.indent or line.NeedsIndentation then + line.indent = newIndent + modified = true + end + + else + // check if the previous line is a block start -> then we increase the indentation in the current line + + dim indentationIncrease as Integer + + previousLine = lines.getLine(lineIndex - 1) + if previousLine <> nil then + dim prevTxt as String = TextStorage.getText(previousLine.offset, previousLine.length) + dim prevHasContinuation as Integer = SyntaxDefinition.IsLineContinuation(prevTxt) + if prevHasContinuation > 0 then + // we're inside a line continuation + if previousLine.isContinuedFromLine < 0 then + // start indentation + indentationIncrease = prevHasContinuation + line.isContinuedFromLine = lineIndex-1 + else + // continue indentation, same indent as previous line + indentationIncrease = 0 + line.isContinuedFromLine = previousLine.isContinuedFromLine + end + else + if previousLine.isContinuedFromLine >= 0 then + // unindent a line continuation + previousLine = lines.getLine(previousLine.isContinuedFromLine) + indentationIncrease = 0 + else + indentationIncrease = previousLine.blockIndentation // got set in UpdateIndentationState() + end if + line.isContinuedFromLine = -1 + end if + end + + // increase the indentation + dim newIndent as Integer + if previousLine <> nil then + dim n as Integer + if mIndentVisually then + n = IndentPixels + else + n = 1 + end + newIndent = previousLine.indent + indentationIncrease * n + else + newIndent = 0 + end + if newIndent <> line.indent or line.NeedsIndentation then + line.indent = newIndent + modified = true + end + + end + + if mIndentVisually then + // we're done + return modified + end if + + // Indent by inserting spaces or tabs at the start of the line + + dim currIndent as String = currTextUntrimmed.Left(currLeadingSpaces) + + dim newIndentation as String = indentStr (line.indent) + #if DebugBuild + dim newIndentLen as Integer = newIndentation.Len + #pragma unused newIndentLen + #endif + + dim theText as String + if ltrimLine then + // Discard leading spaces from current line + theText = newIndentation + else + // Keep leading spaces in current line + theText = newIndentation + currIndent + end if + + if theText <> currIndent then + + if CurrentEventID <= 0 then + // This must not happen - the caller must ensure that the EventID is set to Ticks + // before he starts any modification that invokes this method + break + CurrentEventID = Ticks + end if + + private_replace (line.offset, origLeadingSpaces, theText, false, CurrentEventID, true, true) + modified = true + end + + return modified + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function private_lines() As LineManager + return self.lines + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub private_redraw(x as Integer, y as Integer, width as Integer, height as Integer) + // Invokes Canvas Paint + + #if TargetMacOS + #pragma unused x + #pragma unused y + #pragma unused width + #pragma unused height + + super.Invalidate false ' x, y, width, height + + #elseif TargetWin32 + Declare Sub InvalidateRect Lib "User32" ( hwnd as Integer, lpRect as Ptr, erase as Boolean ) + Declare Sub UpdateWindow Lib "User32" ( hwnd as Integer ) + + dim r as new MemoryBlock( 16 ) + r.Long( 0 ) = x + r.Long( 4 ) = y + r.Long( 8 ) = width + r.Long( 12 ) = height + + InvalidateRect( me.Handle, r, false ) + UpdateWindow( me.Window.Handle ) + #else + #pragma unused x + #pragma unused y + #pragma unused width + #pragma unused height + + // Draw directly, without the Paint event + drawContents(Graphics) + #endif + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub private_remove(offset as integer, length as integer, updateCaret as boolean = true) + // This method is used internally by the control, and externally by the undo mechanism, you shouldn't use it directly, use instead selstart and seltext. + + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + #pragma unused lock + + if ReadOnly then + break + beep + Return + end if + + if length = 0 then Return //nothing to delete + + dim undoText as String = TextStorage.getText(max(offset,0), length) + dim undoAttrs() as TextLineAttributes = lines.getAttributesOfLinesInRange(max(offset,0), length) + + if TextStorage.remove(offset, length) then + RaiseEvent TextRemoved(offset, undoText) + UndoMgr.Push(new UndoableDelete(self, offset, length, undoText, undoAttrs, CaretPos, CurrentEventID)) + lines.remove(offset, length) + if updateCaret then changeSelection(selStart - length, 0) + Highlight + HandleTextChanged + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub private_replace(offset as integer, length as integer, text as string, alwaysMarkChanged as Boolean = true, eventID as integer = - 1, keepSelection as Boolean = false, beSilent as Boolean = false) + // This method is used internally by the control, and externally by the undo mechanism, you shouldn't use it directly, use instead selstart and seltext. + + // If keepSelection = false, it means that the selection+caret is reset to the end of the replaced text + // If keepSelection = true, then the previous selection+caret remains intact, but selection pointers get shifted accordingly (see note below) + + if ReadOnly then + break + beep + Return + end if + + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + + // Use the default line ending from the line manager if the text is just the Return or Enter character. + if text = chr(13) or text = chr(3) then text = lines.lineEnding + + dim removedText as String = TextStorage.getText(offset, length) + dim removedAttrs() as TextLineAttributes = lines.getAttributesOfLinesInRange(offset, length) + + if eventID < 0 then eventID = CurrentEventID + UndoMgr.Push(new UndoableReplace(self, offset, length, removedText, text, removedAttrs, CaretPos, eventID)) + + //modify buffer and rescan lines + TextStorage.replace(offset, length, Text) + lines.replace(offset, length, text, alwaysMarkChanged) + + if keepSelection then + // We need to adjust the caret and selection if they're inside or past the changed text + // + // Note: This code has only been tested to work with replacements in a single line + // (i.e. for use by the IndentLine method). It might not work with multi-line replacements! + + dim lengthDiff as Integer = text.Len - removedText.Len + if lengthDiff <> 0 then + dim minLength as Integer = Min (text.Len, removedText.Len) + dim selStart as Integer = mSelStart + dim selEnd as Integer = selStart + mSelLength + if selEnd >= offset + minLength then + selEnd = Max (offset, selEnd + lengthDiff) + end + if selStart >= offset + minLength then + selStart = Max (offset, selStart + lengthDiff) + end + changeSelection(selStart, Max(0,selEnd-selStart)) + end if + + else + // set the caret past the replaced text + changeSelection(offset + text.Len, 0) + end + + lock = nil + + Highlight + + if not UndoMgr.isUndoing then + //fire textchanged events + HandleTextChanged + if removedText <> "" then RaiseEvent TextRemoved(offset, removedText) + RaiseEvent TextInserted(offset, text) + + if beSilent then + // In this case we don't want the highlighting of closing elements + return + end if + + //is the text a closing element? ] } ) ? + if text.len > 1 or not IsBlockChar(text) or not HighlightMatchingBrackets then Return + + //if so, highlight it + if BLOCK_CLOSE_CHARS.InStr(text) > 0 then + HighlightOpeningBlock(text, offset) + Else + HighlightClosingBlock(text, offset) + end if + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub receiveMessage(theMessage as Message) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + // Part of the MessageReceiver interface. + + dim type as Integer = theMessage.Info(1) //1 is the msg type in this particular scheme + if theMessage.Sender = lines then + + select case type + case LineManager.LineCountChangedMsg + dim count as Integer = theMessage.Info(2) //2 holds the # of lines + dim invisible as Integer = theMessage.Info(3) //3 holds the # of invisible lines + self.LineCountChanged(count - invisible) + + case LineManager.LineChangedMsg + dim index as integer = theMessage.Info(2) + dim length as Integer = theMessage.Info(3) + + if index = CaretLine and mHighlighter <> nil and mHighlighter.State <> thread.NotRunning then + mHighlighter.HighlightLine(index) + end if + + if SyntaxDefinition = nil and loadingDocument then Return + call modifiedLines.AddRange(index, length) + + case LineManager.MaxLineLengthChangedMsg + dim index as integer = theMessage.Info(2) + self.MaxLineLengthChanged(index) + + case LineManager.LineSymbolsRemovedMsg + LineSymbolsRemoved(theMessage.Info(2)) + end select + + elseIf theMessage.Sender = currentSuggestionWindow then + + select case Type + case SuggestionWindow.AutocompleteCancelledMsg + dim requestFocus as Boolean = theMessage.Info(2) + AutocompleteCancelled(requestFocus) + + case SuggestionWindow.KeyDownMsg + dim key as String = theMessage.Info(2) + call HandleKeyDown(key) + + case SuggestionWindow.CurrentAutocompleteOptionsMsg + theMessage.addInfo(3, CurrentAutocompleteOptions) + + case SuggestionWindow.OptionSelectedMsg + dim option as String = theMessage.Info(2) + AutocompleteOptionSelected(option) + + end select + + Elseif theMessage.Sender = mHighlighter then + select case type + case LineHighlighter.HighlightDoneMsg + RaiseEvent HighlightingComplete + + case LineHighlighter.LineHighlightedMsg + LineHighlighted(theMessage.Info(2)) + + case LineHighlighter.ScreenLinesHighlightedMsg + Redraw + + end select + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redo() + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + #pragma unused lock + + ignoreRepaint = true + UndoMgr.Redo + + //RaiseEvents + dim line as TextLine = lines.getLine(CaretLine) + if line <> nil then + RaiseEvent SelChanged(CaretLine + 1, SelStart - line.offset, SelLength) + end if + HandleTextChanged + + if not UndoMgr.isDirty then ClearDirtyLines + ignoreRepaint = False + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redraw(forced as Boolean = false) + // always redraws entire canvas + + if ignoreRepaint and not forced then Return + + //see if caret is visible + dim ScrollPosition as Integer = self.ScrollPosition + if EnableLineFoldings then ScrollPosition = lines.getNumberOfLinesNeededToView(ScrollPosition) + + ignoreRepaint = false + mRedrawEverything = true + + redrawThreadSafe + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub RedrawCaret() + // called by CaretBlinker to update the text cursor beam + + if ignoreRepaint or mWindowIsClosing then + Return + end if + + 'if Graphics = nil then Return + '#pragma warning "What is the point of this check? Can it be done another way?" + + //see if caret is visible + dim ScrollPosition as Integer = self.ScrollPosition + if EnableLineFoldings then ScrollPosition = lines.getNumberOfLinesNeededToView(ScrollPosition) + if (CaretLine < ScrollPosition or CaretLine > ScrollPosition + VisibleLineRange.length) then Return + + // This is a work in progress - the purpose is to avoid updating the entire + // Canvas every time we update the blinking text cursor. + + mRedrawCaret = true + + redrawThreadSafe // ideally, only the caret's area should be redrawn, though + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub redrawNow(caller as Timer) + #pragma unused caller + + self.Invalidate + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub redrawThreadSafe() + if App.CurrentThread = nil then + self.Invalidate() + else + // we can't issue a repaint from this thread in Xojo, so we need to use a Timer for it + if mRedrawTimer = nil then + mRedrawTimer = new Timer + AddHandler mRedrawTimer.Action, AddressOf redrawNow + mRedrawTimer.Period = 0 + end if + mRedrawTimer.Mode = Timer.ModeSingle + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub Refresh(eraseBackground As Boolean = True) + #pragma unused eraseBackground + + // We force the user to call Redraw instead of Refresh because + // we don't want the user to be able to accidentally cause an + // entire screen erase. So we override Refresh by making it + // a private function. + break + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub RefreshRect(x As Integer, y As Integer, width As Integer, height As Integer, eraseBackground As Boolean = True) + #pragma unused x + #pragma unused y + #pragma unused width + #pragma unused height + #pragma unused eraseBackground + + // We force the user to call Redraw instead of Refresh because + // we don't want the user to be able to accidentally cause an + // entire screen erase. So we override Refresh by making it + // a private function. + break + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ReHighlight() + //mark all lines as changed and re-highlight + lines.MarkAllLinesAsChanged + Highlight + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ReindentText() + // Removes all leading white space, adding proper indentation (using Tab chars) instead + + self.ReindentText(0, lines.Count - 1) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ReindentText(fromLine as Integer, toLine as integer) + // Removes all leading white space, adding proper indentation (using Tab chars) instead + + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + #pragma unused lock + + #if DebugBuild and (EditFieldGlobals.DebugTiming or EditFieldGlobals.DebugIndentation) + dim runtimer as new Debugging.LifeTimer("ReindentText "+str(fromLine)+" to "+str(toLine)) + #endif + + if CurrentEventID <= 0 then + // ensure that this entire process becomes a single undoable action + CurrentEventID = Ticks + end if + + dim needsRedraw as Boolean + + self.IgnoreRepaint = true + dim state as Variant + for i as Integer = fromLine to toLine + if private_indentline (i, true, state) then + InvalidateLine (i) + needsRedraw = true + end if + lines.LineIsIndented i + next + self.IgnoreRepaint = False + + if needsRedraw then + Highlight + end + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Replace(offset as integer, length as Integer, text as string, alwaysMarkDirty as Boolean) + private_replace(offset, length, text, alwaysMarkDirty) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ResetUndo() + UndoMgr.Reset + ClearDirtyLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ResetUndoDirtyFlag() + UndoMgr.ResetDirtyFlag + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function Reverse(s As String) As String + //From Joe Strout's String Utils module. + // Return s with the characters in reverse order. + if Len(s) < 2 then return s + + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + Dim characters() as String = Split( s, "" ) + Dim leftIndex as Integer = 0 + Dim rightIndex as Integer = UBound(characters) + While leftIndex < rightIndex + Dim temp as String = characters(leftIndex) + characters(leftIndex) = characters(rightIndex) + characters(rightIndex) = temp + leftIndex = leftIndex + 1 + rightIndex = rightIndex - 1 + Wend + Return Join( characters, "" ) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Save(toFile as folderItem, fileType as string = "Text", encoding as textencoding = nil) As boolean + if toFile = nil then Return False + + dim stream as BinaryStream + + //Modified by Dr. Gerard Hammond to allow the file to be saved even if it's already open by another app. + if toFile.Exists = false then + stream = BinaryStream.Create(toFile) + stream.Close + #if RBVersion < 2014.01 and TargetMacOS + toFile.MacType = fileType + #else + #pragma unused fileType + #endif + end if + stream = BinaryStream.Open(toFile, true) + stream.Length = 0 ////truncate the file + + if stream = nil then Return False + + dim txt as String = me.Text + if Encoding <> nil and not Encoding.Equals(txt.Encoding) then txt = txt.ConvertEncoding(Encoding) + + stream.Write(txt) + stream.Close + + UndoMgr.ResetDirtyFlag + ClearDirtyLines + Return true + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Save(toFile as folderItem, encoding as textencoding) As boolean + Return save(toFile, "Text", encoding) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ScrollEnd() + ScrollPosition = lines.Count + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ScrollHome() + ScrollPosition = 0 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SelectAll() + changeSelection(0, TextStorage.Length) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function SelectedTextDragImage() As picture + dim text as String = SelText + dim selection as String = text.Left(200) + " " + if text.Len > 200 then selection = selection + "..." + + dim w, h as Integer + dim tmp as Picture = tmpPicture + w = min(tmp.Graphics.StringWidth(selection+" "), Width) + h = tmp.Graphics.StringHeight(selection, w) + + dim image as Picture = New Picture(w, h, 32) + image.Graphics.TextSize = TextSize + image.Graphics.TextFont = TextFont + Image.Graphics.DrawString selection, 0, Image.Graphics.TextHeight - (Image.Graphics.TextHeight - Image.Graphics.TextAscent), w + + Return image + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SelectLine(lineNumber as integer, refresh as boolean = true) + //selects the given line. + if lineNumber < 0 or lineNumber >= lines.Count then + break + beep + Return + end if + + dim line as TextLine = lines.getLine(lineNumber) + if not line.visible then lines.revealLine(lineNumber) + changeSelection(line.offset, line.length - line.delimiterLength) + + if refresh then Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SelectNextPlaceholder() + dim startLine as Integer = CaretLine + dim offset as Integer = CaretPos + + for i as Integer = CaretLine to lines.Count - 1 + dim line as TextLine = lines.getLine(i) + if line = nil or not line.visible then Continue for + + dim nextPlaceholder as TextPlaceholder = line.NextPlaceholderFromOffset(offset) + if nextPlaceholder = nil then + offset = line.offset + line.length + Continue for + end if + + changeSelection(line.offset + nextPlaceholder.offset + nextPlaceholder.length / 2, 0) + Redraw + Return + next + + //wrap around... todo make this prettier :P + for i as Integer = 0 to startLine + dim line as TextLine = lines.getLine(i) + if line = nil or not line.visible then Continue for + + dim nextPlaceholder as TextPlaceholder = line.NextPlaceholderFromOffset(offset) + if nextPlaceholder = nil then + offset = line.offset + line.length + Continue for + end if + + changeSelection(line.offset + nextPlaceholder.offset + nextPlaceholder.length / 2, 0) + Redraw + Return + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SetScrollbars(horizontal as scrollbar, vertical as scrollbar) + //sets the scrollbars + horizontalSB = horizontal + verticalSB = vertical + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub StopHighlighter() + if mHighlighter <> nil then + if mHighlighter.state <> Thread.NotRunning then + mHighlighter.Kill + end if + lines.linesLock = nil + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function SymbolAtLine(index as integer) As documentSymbol + if index < 0 then return nil + + //check line first + dim symbol as DocumentSymbol + dim line as TextLine = lines.getLine(index) + if line <> nil and line.LineSymbols <> nil and line.LineSymbols.Count > 0 then + Symbol = line.LineSymbols.value(line.LineSymbols.Key(0)) + Return new DocumentSymbol(Symbol.Name, symbol.Offset + line.offset, Symbol.Type) + + //else, if it's an opening block, check line above... + elseif line <> nil and line.isBlockStart then + line = lines.getLine(index - 1) + if line <> nil and line.LineSymbols <> nil and line.LineSymbols.Count > 0 then + Symbol = line.LineSymbols.value(line.LineSymbols.Key(0)) + Return new DocumentSymbol(Symbol.Name, symbol.Offset + line.offset, Symbol.Type) + end if + end if + + //else... start looking upwards + + //find opening block for this line + dim blockIdx as Integer = OpeningBlockLineForLine(index) + + //no opening block found... + if blockIdx < 0 then Return nil + + //get line + line = lines.getLine(blockIdx) + if line = nil then Return nil + + //if line doesn't contain any symbols... move one line up... + if line.LineSymbols = nil or line.LineSymbols.Count = 0 then + line = lines.getLine(blockIdx - 1) + else + Symbol = line.LineSymbols.value(line.LineSymbols.Key(0)) + Return new DocumentSymbol(Symbol.Name, symbol.Offset + line.offset, Symbol.Type) + end if + + //recurse, search next enclosing block + if line = nil or line.LineSymbols = nil or line.LineSymbols.Count = 0 then + Return SymbolAtLine(blockIdx - 1) + end if + + Symbol = line.LineSymbols.value(line.LineSymbols.Key(0)) + Return new DocumentSymbol(Symbol.Name, symbol.Offset + line.offset, Symbol.Type) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function SymbolCount() As integer + if CurrentDocumentSymbols = nil then Return 0 + Return CurrentDocumentSymbols.Count + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Text(offset as Integer, length as Integer) As String + if offset >= 0 and length > 0 then + Return TextStorage.getText(offset, length) + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function tmpPicture() As picture + //return a temporary picture. + if sharedTmpPicture = nil then + sharedTmpPicture = New Picture(2,2,32) + #if EditFieldGlobals.UseOldRenderer + sharedTmpPicture.Graphics.UseOldRenderer = true + #endif + end if + + sharedTmpPicture.Graphics.TextFont = TextFont + sharedTmpPicture.Graphics.TextSize = TextSize + + //v1.1 fix, these weren't being cleared. + sharedTmpPicture.Graphics.Bold = False + sharedTmpPicture.Graphics.Italic = False + sharedTmpPicture.Graphics.Underline = False + + Return sharedTmpPicture + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ToggleLineFold(lineIndex as integer) + if not EnableLineFoldings then Return + + dim topLine as Integer = lines.toggleLineFolding(lineIndex) + if topLine > -1 then + //check if caret is in an invisible line + dim line as TextLine = lines.getLine(lines.getLineNumberForOffset(CaretPos)) + //if it's invisible, move caret to folded line + if not line.visible then + line = lines.getLine(topLine) + if line <> nil then changeSelection(line.offset, 0) + end if + InvalidateAllLines + else + InvalidateLine(lineIndex) + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Undo() + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + #pragma unused lock + + ignoreRepaint = true + UndoMgr.Undo + + //RaiseEvents + dim line as TextLine = lines.getLine(CaretLine) + if line <> nil then + RaiseEvent SelChanged(CaretLine + 1, SelStart - line.offset, SelLength) + end if + HandleTextChanged + + if not UndoMgr.isDirty then ClearDirtyLines + ignoreRepaint = False + Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub UnfoldAllLines() + //hmmm + if not EnableLineFoldings then Return + lines.unfoldAll + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub updateDesiredColumn(charPos as integer = - 1) + //saves the screenposition of the given offset + if lines.Count = 0 then Return + + if CharPos < 0 then charPos = CaretPos + desiredColumnCharPos = CharPos + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub updateIndentation() + if mKeepEntireTextIndented then + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + #pragma unused lock + + dim trimLines as Boolean = not mIndentVisually + dim indentationState as Variant + + dim lineIdx as Integer = lines.FirstLineForIndentation + + while lineIdx < self.LineCount + dim modified as Boolean = private_indentline (lineIdx, trimLines, indentationState) + if not modified then + if lineIdx > lines.LastLineForIndentation then + // we're done + exit + end if + end if + if trimLines then + InvalidateLine(lineIdx) + end if + lineIdx = lineIdx + 1 + wend + + lines.IndentationFinished + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ViewToCharPos(charPos as integer) + ViewToCharPos(lines.getLineNumberForOffset(CharPos), CharPos) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ViewToCharPos(charLine as integer, charPos as integer) + //move the view to the given char position. + + dim horizontal, vertical as Integer + dim ScrollPosition as Integer = self.ScrollPosition + if EnableLineFoldings then ScrollPosition = lines.getNumberOfLinesNeededToView(ScrollPosition) + + horizontal = ScrollPositionX + vertical = self.ScrollPosition + + //vertical check + if charLine < ScrollPosition then + vertical = charLine + elseif charLine > ScrollPosition + VisibleLineRange.length - 2 then + vertical = charLine - VisibleLineRange.length + 2 + end if + + //horizontal check + dim x, y as Integer + XYAtCharPos(charPos, charLine, x, y) + + if x< LineNumOffset or x >= self.Width then + horizontal = ScrollPositionX + x - (me.Width - RightScrollMargin) + end if + + changeScrollValues(horizontal, vertical) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function XposForOffset(line as TextLine, charPos as integer) As single + //screen position for the given CharPos + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + dim tmp as Picture = tmpPicture + + dim indent as Integer = line.VisualIndent(self.IndentVisually) + + Return line.TextWidth(TextStorage, tmp.Graphics, DisplayInvisibleCharacters, CharPos - line.offset) + indent + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub XYAtCharPos(charPos as integer, byref X as integer, byref Y as integer) + dim lineNumber as Integer + lineNumber = lines.getLineNumberForOffset(charPos) + XYAtCharPos(CharPos, LineNumber, x, y) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub XYAtCharPos(charPos as integer, lineNumber as integer, byref X as integer, byref Y as integer) + //find the screenx and screeny for the given CharPos + + //y + dim ypos as Integer + if EnableLineFoldings then + ypos = (lines.getNumberOfVisibleLinesUpToLine(lineNumber) - ScrollPosition) * TextHeight + else + ypos = (lineNumber - ScrollPosition) * TextHeight + end if + + //find the char offset. + dim line as TextLine = lines.getLine(lineNumber) + if line = nil then Return + + dim sx as Integer = leftMarginOffset + LineNumOffset - ScrollPositionX + + dim xpos as integer = sx + XposForOffset(line, charPos) + + x = xpos + y = ypos + TextHeight + End Sub + #tag EndMethod + + + #tag Hook, Flags = &h0 + Event AutocompleteOptionsForPrefix(prefix as string) As AutocompleteOptions + #tag EndHook + + #tag Hook, Flags = &h0 + Event BlockCharsMatched(blockOpenChar as string, blockOpenOffset as Integer, blockCloseChar as string, blockCloseOffset as Integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event Close() + #tag EndHook + + #tag Hook, Flags = &h0 + Event ConstructContextualMenu(base as menuitem, x as integer, y as integer) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event EnableMenuItems() + #tag EndHook + + #tag Hook, Flags = &h0 + Event GotFocus() + #tag EndHook + + #tag Hook, Flags = &h0 + Event GutterClicked(onLine as integer, x as integer, y as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event HighlightingComplete() + #tag EndHook + + #tag Hook, Flags = &h0 + Event HorizontalScrollValueChanged() + #tag EndHook + + #tag Hook, Flags = &h0 + Event KeyDown(key as string) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event LineCountChanged(newLineCount as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event LostFocus() + #tag EndHook + + #tag Hook, Flags = &h0 + Event MaxLineLengthChanged(maxLineLengthInPixels as single) + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseDown(X as integer, Y as integer) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseExit() + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseMove(X as integer, Y as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseUp(x as integer, y as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event Open() + #tag EndHook + + #tag Hook, Flags = &h0 + Event PaintAboveLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event PaintBelowLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event PaintOver(g as Graphics, gutterWidth as Integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event PlaceholderSelected(placeholderLabel as String, lineIndex as integer, line as textLine, placeholder as textPlaceholder, doubleClick as Boolean) + #tag EndHook + + #tag Hook, Flags = &h0 + Event ScrollValuesChanged() + #tag EndHook + + #tag Hook, Flags = &h0 + Event SelChanged(line as integer, column as integer, length as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event ShouldDisplaySuggestionWindowAtPos(byref X as Integer, byref Y as Integer) As Boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event ShouldTriggerAutocomplete(Key as string, hasAutocompleteOptions as boolean) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event TextChanged() + #tag EndHook + + #tag Hook, Flags = &h0 + Event TextInserted(offset as integer, text as String) + #tag EndHook + + #tag Hook, Flags = &h0 + Event TextRemoved(offset as integer, text as String) + #tag EndHook + + #tag Hook, Flags = &h0 + Event UseBackgroundColorForLine(lineIndex as integer, byref lineBackgroundColor as color) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event UseBookmarkIconForLine(lineIndex as integer) As Picture + #tag EndHook + + #tag Hook, Flags = &h0 + Event VerticalScrollValueChanged() + #tag EndHook + + + #tag Note, Name = KeyCodes + Key: beetlejuice + taken from Aaron's blog + http://ramblings.aaronballman.com/2006/02/Since_I_can_never_remember_this_sort_of_stuff_myself...html + + AscB(Key) Keyboard.AsyncKeyDown Explanation + 0 N/A Null Byte + 1 115 Home + 4 119 End + 5 114 Insert + 8 51 Backspace + 9 48 Tab + 11 116 Page Up + 12 121 Page Down + 13 36 Return + 27 53 Escape + 28 123 Left Arrow + 29 124 Right Arrow + 30 125 Down Arrow + 31 126 Up Arrow + 32 49 Space + 33 N/A ! + 34 N/A " + 35 N/A # + 36 N/A $ + 37 N/A % + 38 N/A & + 39 39 ' + 40 N/A ( + 41 N/A ) + 42 N/A * + 43 N/A + + 44 43 , + 45 27 - + 46 47 . + 47 44 / + 48 29 0 + 49 18 1 + 50 19 2 + 51 20 3 + 52 21 4 + 53 23 5 + 54 22 6 + 55 26 7 + 56 28 8 + 57 25 9 + 58 N/A : + 59 41 ; + 60 N/A < + 61 N/A = + 62 N/A > + 63 N/A ? + 64 N/A @ + 65 0 A + 66 11 B + 67 8 C + 68 2 D + 69 14 E + 70 3 F + 71 5 G + 72 4 H + 73 34 I + 74 38 J + 75 40 K + 76 37 L + 77 46 M + 78 45 N + 79 31 O + 80 35 P + 81 12 Q + 82 15 R + 83 1 S + 84 17 T + 85 32 U + 86 9 V + 87 13 W + 88 7 X + 89 16 Y + 90 6 Z + 91 33 [ + 92 42 \ + 93 30 ] + 94 N/A ^ + 95 N/A _ + 96 50 ` + 97 0 a + 98 11 b + 99 8 c + 100 2 d + 101 14 e + 102 3 f + 103 5 g + 104 4 h + 105 34 i + 106 38 j + 107 40 k + 108 37 l + 109 46 m + 110 45 n + 111 31 o + 112 35 p + 113 12 q + 114 15 r + 115 1 s + 116 17 t + 117 32 u + 118 9 v + 119 13 w + 120 7 x + 121 16 y + 122 6 z + 123 N/A { + 124 N/A | + 125 N/A } + 126 N/A ~ + 127 117 Delete + 200 122 F1 + 201 120 F2 + 202 99 F3 + 203 118 F4 + 204 96 F5 + 205 97 F6 + 206 98 F7 + 207 100 F8 + 208 101 F9 + 209 109 F10 + 210 103 F11 + 211 111 F12 + N/A 105 Print Screen + N/A 107 Scroll Lock + N/A N/A Pause + #tag EndNote + + + #tag Property, Flags = &h0 + AutoCloseBrackets As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + AutocompleteAppliesStandardCase As boolean = true + #tag EndProperty + + #tag Property, Flags = &h1 + Protected AutocompleteSuggestionInsertionX As Integer + #tag EndProperty + + #tag Property, Flags = &h0 + AutoIndentNewLines As boolean = true + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mBackColor + End Get + #tag EndGetter + #tag Setter + Set + mBackColor = value + InvalidateAllLines + redraw + End Set + #tag EndSetter + BackColor As color + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected blockBeginPosX As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected blockBeginPosY As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private blockCharsPattern As string + #tag EndProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mBlockendimage = nil then + mBlockendimage = EditFieldGlobals.LoadMaskedPicture(blockEndMarker) + end if + return mBlockendimage + End Get + #tag EndGetter + Protected Shared BlockEndImage As picture + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mBlockfoldedimage = nil then + mBlockfoldedimage = EditFieldGlobals.LoadMaskedPicture(blockFoldedMarker) + end if + return mBlockfoldedimage + End Get + #tag EndGetter + Protected Shared BlockFoldedImage As picture + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mBlockstartimage = nil then + mBlockstartimage = EditFieldGlobals.LoadMaskedPicture(blockStartMarker) + end if + return mBlockstartimage + End Get + #tag EndGetter + Protected Shared BlockStartImage As picture + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mBookmarkimage = nil then + mBookmarkimage = EditFieldGlobals.LoadMaskedPicture(bookmarksimg) + end if + return mBookmarkimage + End Get + #tag EndGetter + Protected Shared BookmarkImage As Picture + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mBookmarktable = nil then + //bookmarks + mBookmarktable = new Dictionary + end if + return mBookmarktable + End Get + #tag EndGetter + #tag Setter + Set + mBookmarktable = value + End Set + #tag EndSetter + Protected BookmarkTable As Dictionary + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mDrawFrame + End Get + #tag EndGetter + #tag Setter + Set + mDrawFrame = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + Border As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mFrameColor + End Get + #tag EndGetter + #tag Setter + Set + mFrameColor = value + Redraw + End Set + #tag EndSetter + BorderColor As color + #tag EndComputedProperty + + #tag Property, Flags = &h0 + BracketHighlightColor As color = &cFFFF00 + #tag EndProperty + + #tag Property, Flags = &h1 + Protected caretBlinker As CaretBlinker + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mCaretColor + End Get + #tag EndGetter + #tag Setter + Set + mCaretColor = value + redraw + End Set + #tag EndSetter + CaretColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + dim x, y as Integer + dim calcPos as Integer = desiredColumnCharPos + + //or the caretpos + if desiredColumnCharPos < 0 then calcPos = CaretPos + + //find screenpos + XYatCharPos(calcPos, x, y) + + return x - LineNumOffset - LeftMarginOffset + ScrollPositionX + End Get + #tag EndGetter + Protected caretDesiredColumn As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mCaretline + End Get + #tag EndGetter + CaretLine As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mCaretPos + End Get + #tag EndGetter + #tag Setter + Set + if value = mCaretPos then Return + changeSelection(value, 0) + End Set + #tag EndSetter + CaretPos As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private CaretState As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + ClearHighlightedRangesOnTextChange As boolean = true + #tag EndProperty + + #tag Property, Flags = &h1 + Protected CurrentDocumentSymbols As dictionary + #tag EndProperty + + #tag Property, Flags = &h1 + Protected CurrentEventID As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mCurrentfocusedfield + End Get + #tag EndGetter + Shared CurrentFocusedField As CustomEditField + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected currentSuggestionWindow As suggestionWindow + #tag EndProperty + + #tag Property, Flags = &h1 + Protected cursorIsIbeam As boolean = true + #tag EndProperty + + #tag Property, Flags = &h1 + Protected desiredColumnCharPos As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mDirtylinescolor + End Get + #tag EndGetter + #tag Setter + Set + mDirtylinescolor = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + DirtyLinesColor As color + #tag EndComputedProperty + + #tag Property, Flags = &h0 + disableReset As Boolean = False + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mDisplaydirtylines + End Get + #tag EndGetter + #tag Setter + Set + mDisplaydirtylines = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + DisplayDirtyLines As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mDisplayInvisibleCharacters + End Get + #tag EndGetter + #tag Setter + Set + mDisplayInvisibleCharacters = value + updateDesiredColumn + InvalidateAllLines + Redraw + End Set + #tag EndSetter + DisplayInvisibleCharacters As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mDisplayLineNumbers + End Get + #tag EndGetter + #tag Setter + Set + mDisplayLineNumbers = value + updateDesiredColumn + InvalidateAllLines + redraw + End Set + #tag EndSetter + DisplayLineNumbers As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mDisplayrightmarginmarker + End Get + #tag EndGetter + #tag Setter + Set + mDisplayrightmarginmarker = value + Redraw + End Set + #tag EndSetter + DisplayRightMarginMarker As boolean + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private Dragging As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared DragSource As CustomEditField + #tag EndProperty + + #tag Property, Flags = &h1 + Protected DragTextOnDrag As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected DragTextPos As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected DragTextSelection As datarange + #tag EndProperty + + #tag Property, Flags = &h0 + EnableAutocomplete As boolean = false + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mEnablelinefoldings + End Get + #tag EndGetter + #tag Setter + Set + if mEnablelinefoldings and not value then lines.unfoldAll + mEnablelinefoldings = value + LineNumOffset = 0 + updateDesiredColumn + InvalidateAllLines + redraw + End Set + #tag EndSetter + EnableLineFoldings As boolean + #tag EndComputedProperty + + #tag Property, Flags = &h0 + enableLineFoldingSetting As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected fullRefresh As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Gutter As picture + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mGutterBackgroundColor + End Get + #tag EndGetter + #tag Setter + Set + mGutterBackgroundColor = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + GutterBackgroundColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mGutterSeparationLineColor + End Get + #tag EndGetter + #tag Setter + Set + mGutterSeparationLineColor = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + GutterSeparationLineColor As Color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return LineNumOffset + End Get + #tag EndGetter + GutterWidth As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected hasFocus As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + HighlightBlocksOnMouseOverGutter As Boolean + #tag EndProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mHighlightedranges = nil then + mHighlightedranges = new CharSelectionManager + end if + return mHighlightedranges + End Get + #tag EndGetter + #tag Setter + Set + mHighlightedranges = value + End Set + #tag EndSetter + Protected HighlightedRanges As CharSelectionManager + #tag EndComputedProperty + + #tag Property, Flags = &h0 + HighlightMatchingBrackets As boolean = true + #tag EndProperty + + #tag Property, Flags = &h0 + HighlightMatchingBracketsMode As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected horizontalSB As scrollbar + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mIgnorerepaint + End Get + #tag EndGetter + #tag Setter + Set + mIgnorerepaint = value + End Set + #tag EndSetter + IgnoreRepaint As boolean + #tag EndComputedProperty + + #tag Property, Flags = &h0 + IndentPixels As Integer = 16 + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mIndentVisually + End Get + #tag EndGetter + #tag Setter + Set + if mIndentVisually <> value then + mIndentVisually = value + self.ReindentText + end if + End Set + #tag EndSetter + IndentVisually As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mInvalidlines = nil then + //dictionaries to keep track of modified and updated lines. + mInvalidlines = new Dictionary + end if + + return mInvalidlines + End Get + #tag EndGetter + #tag Setter + Set + mInvalidlines = value + End Set + #tag EndSetter + Protected invalidLines As dictionary + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private isDoubleClick As Boolean = False + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mKeepEntireTextIndented + End Get + #tag EndGetter + #tag Setter + Set + if mKeepEntireTextIndented <> value then + mKeepEntireTextIndented = value + self.ReindentText + end if + End Set + #tag EndSetter + KeepEntireTextIndented As Boolean + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected keyDownTime As double + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastClickTicks As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastDragTicks As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastDrawnTopLine As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastLongestLineLength As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastLongestLinePixels As single + #tag EndProperty + + #tag Property, Flags = &h21 + Private lastMouseDownX As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private lastMouseDownY As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private lastMouseUpX As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private lastMouseUpY As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private lastRedrawTicks As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastTripleClickTicks As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mLeftMarginOffset + End Get + #tag EndGetter + #tag Setter + Set + mLeftMarginOffset = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + LeftMarginOffset As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mLineNumbersColor + End Get + #tag EndGetter + #tag Setter + Set + mLineNumbersColor = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + LineNumbersColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mLinenumberstextfont + End Get + #tag EndGetter + #tag Setter + Set + mLinenumberstextfont = value + LineNumOffset = 0 + InvalidateAllLines + Redraw + End Set + #tag EndSetter + LineNumbersTextFont As string + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mLinenumberstextsize + End Get + #tag EndGetter + #tag Setter + Set + mLinenumberstextsize = min(value, max(TextSize, value)) + LineNumOffset = 0 + InvalidateAllLines + Redraw + End Set + #tag EndSetter + LineNumbersTextSize As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if not displayLineNumbers then Return 0 + + if mlineNumOffset = 0 then + dim tmp as Picture = tmpPicture + tmp.graphics.TextFont = LineNumbersTextFont + tmp.graphics.TextSize = LineNumbersTextSize + tmp.Graphics.Bold = true + mlineNumOffset = tmp.graphics.StringWidth(str(lines.Count)) + 10 + + if EnableLineFoldings then + mlineNumOffset = LineNumOffset + blockStartImage.Width + 2 + end if + end if + + return mlineNumOffset + End Get + #tag EndGetter + #tag Setter + Set + #pragma unused value + mlineNumOffset = 0 + End Set + #tag EndSetter + Protected LineNumOffset As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mLines = nil then + //and line manager + mLines = new LineManager(TextStorage, TabWidth) + + //register to receive messages from this line manager only + self.registerForMessage(mLines) + end if + + return mLines + End Get + #tag EndGetter + #tag Setter + Set + mLines = value + End Set + #tag EndSetter + Protected lines As LineManager + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private loadingDocument As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected MatchingBlockHighlight As CharSelection + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Note + Careful: + This returns just the number of rows that fit into the Canvas. + This is not the same as the number of text lines that may be appearing in + the Canvas if line folding is used! (That value is in VisibleLineRange.length) + #tag EndNote + #tag Getter + Get + if App.CurrentThread = nil then // this check prevents "ThreadAccessingUIException" when called from a thread + mMaxVisibleLines = Min (me.Height / TextHeight, lines.Count) + end + + return mMaxVisibleLines + End Get + #tag EndGetter + MaxVisibleLines As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mBackBuffer As Picture + #tag EndProperty + + #tag Property, Flags = &h21 + Private mBackColor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mBlockendimage As picture + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mBlockfoldedimage As picture + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mBlockstartimage As picture + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mBookmarkimage As Picture + #tag EndProperty + + #tag Property, Flags = &h21 + Private mBookmarktable As Dictionary + #tag EndProperty + + #tag Property, Flags = &h21 + Private mCaretColor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mCaretline As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mCaretPos As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mCurrentAutocompleteOptions As AutocompleteOptions + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mCurrentfocusedfield As CustomEditField + #tag EndProperty + + #tag Property, Flags = &h21 + Private mDirtylinescolor As color = &cFF9999 + #tag EndProperty + + #tag Property, Flags = &h21 + Private mDisplaydirtylines As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mDisplayInvisibleCharacters As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mDisplayLineNumbers As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mDisplayrightmarginmarker As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mDrawFrame As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mEnablelinefoldings As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mFrameColor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mGutterBackgroundColor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mGutterSeparationLineColor As Color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mHighlightedranges As CharSelectionManager + #tag EndProperty + + #tag Property, Flags = &h21 + Private mHighlighter As LineHighlighter + #tag EndProperty + + #tag Property, Flags = &h21 + Private mHighlightTimer As Timer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIgnorerepaint As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIndentString As String + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIndentVisually As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mInvalidlines As dictionary + #tag EndProperty + + #tag Property, Flags = &h21 + Private mKeepEntireTextIndented As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mLeftMarginOffset As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mLineNumbersColor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mLinenumberstextfont As string = "System" + #tag EndProperty + + #tag Property, Flags = &h21 + Private mLinenumberstextsize As Integer = 9 + #tag EndProperty + + #tag Property, Flags = &h21 + Private mlineNumOffset As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mLines As LineManager + #tag EndProperty + + #tag Property, Flags = &h21 + Private mMaxVisibleLines As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mModifiedlines As ModifiedLineRangeManager + #tag EndProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mModifiedlines = nil then + //dictionaries to keep track of modified and updated lines. + mModifiedlines = new ModifiedLineRangeManager + end if + + return mModifiedlines + End Get + #tag EndGetter + #tag Setter + Set + mModifiedlines = value + End Set + #tag EndSetter + Protected modifiedLines As ModifiedLineRangeManager + #tag EndComputedProperty + + #tag Property, Flags = &h0 + MouseOverBlock As Dictionary + #tag EndProperty + + #tag Property, Flags = &h21 + Private mReadOnly As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mRedrawCaret As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mRedrawEverything As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mRedrawTimer As Timer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mRightmargin As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private Shared mRightmarginlineimage As picture + #tag EndProperty + + #tag Property, Flags = &h21 + Private mscrollPosition As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mScrollPositionX As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mSelLength As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mSelStart As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mSyntaxDefinition As HighlightDefinition + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTabwidth As Integer = 4 + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTextColor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTextFont As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTextHeight As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTextselectioncolor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTextSize As Integer = 0 + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTextstorage As gapBuffer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mThickInsertionPoint As boolean = true + #tag EndProperty + + #tag Property, Flags = &h21 + Private mUndomgr As undomanager + #tag EndProperty + + #tag Property, Flags = &h21 + Private mVisibleLineRange As DataRange + #tag EndProperty + + #tag Property, Flags = &h21 + Private mWindowIsClosing As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private OptionForTrailingSuggestion As string + #tag EndProperty + + #tag Property, Flags = &h1 + Protected previouslyDrawnSelection As charSelection + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mReadOnly + End Get + #tag EndGetter + #tag Setter + Set + mReadOnly = value + + if not value then enableBlinker(False) + End Set + #tag EndSetter + ReadOnly As Boolean + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private redrawTime As double + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if mRightmargin = 0 then + //get default printer area from printer. + dim tmpPrinter as new PrinterSetup + mRightmargin = tmpPrinter.Width + end if + return mRightmargin + End Get + #tag EndGetter + #tag Setter + Set + mRightmargin = value + Redraw + End Set + #tag EndSetter + RightMarginAtPixel As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mRightmarginlineimage = nil then + mRightmarginlineimage = New Picture(1,1,32) + mRightmarginlineimage.Graphics.Pixel(0,0) = &cff + mRightmarginlineimage.Mask.Graphics.Pixel(0,0) = &cAAAAAA + end if + + return mRightmarginlineimage + End Get + #tag EndGetter + Protected Shared RightMarginLineImage As picture + #tag EndComputedProperty + + #tag Property, Flags = &h0 + RightScrollMargin As Integer = 150 + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mScrollPosition + End Get + #tag EndGetter + #tag Setter + Set + changeScrollValues(ScrollPositionX, value) + Redraw + End Set + #tag EndSetter + ScrollPosition As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mScrollPositionX + End Get + #tag EndGetter + #tag Setter + Set + changeScrollValues(value, ScrollPosition) + Redraw + End Set + #tag EndSetter + ScrollPositionX As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private selectedLine As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mSelLength + End Get + #tag EndGetter + #tag Setter + Set + changeSelection(SelStart, value) + Redraw + End Set + #tag EndSetter + SelLength As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mSelStart + End Get + #tag EndGetter + #tag Setter + Set + changeSelection(value, 0) + Redraw + End Set + #tag EndSetter + SelStart As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return TextStorage.getText(selStart, selLength) + End Get + #tag EndGetter + #tag Setter + Set + CurrentEventID = Ticks + private_replace(selStart, selLength, value, true) + End Set + #tag EndSetter + SelText As string + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private sharedTmpPicture As picture + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mSyntaxDefinition + End Get + #tag EndGetter + #tag Setter + Set + if mSyntaxDefinition <> value then + mSyntaxDefinition = value + lines.unfoldAll + InvalidateAllLines + + if CurrentDocumentSymbols <> nil then CurrentDocumentSymbols.Clear + ModifiedLines.Clear + + if mHighlighter <> nil then + //stop highligher + StopHighlighter + + mHighlighter.definition = value + lines.MarkAllLinesAsChanged + VisibleLineRange.length = -1 + + Highlight + end if + + if value = nil then HighlightingComplete + end if + End Set + #tag EndSetter + SyntaxDefinition As HighlightDefinition + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mTabwidth + End Get + #tag EndGetter + #tag Setter + Set + if value <= 0 then value = 1 + + mIndentString = "" + mTabwidth = value + lines.TabWidth = value + updateDesiredColumn + InvalidateAllLines + Redraw + End Set + #tag EndSetter + TabWidth As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return textStorage.getText(0, textStorage.Length) + End Get + #tag EndGetter + #tag Setter + Set + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.LifeTimer (CurrentMethodName) + #endif + + StopHighlighter + + dim lock as new LinesLock(self) // prevents LineHighlighter from interfering while we're modifying the lines + #pragma unused lock + + loadingDocument = true + ignoreRepaint = true + mCaretLine = 0 + changeSelection(0,0) + + 'Koen Van Hulle - Flag so the undo is not reset, needed for a custom "replace all" + ' where it is faster to do a replace in the text and replace the whole text by the replaced text + if not disableReset then + UndoMgr.Reset + else + dim lineAttrs() as TextLineAttributes + UndoMgr.Push(new UndoableReplace(self, 0, self.text.Len, self.text, value, lineAttrs, CaretPos, CurrentEventID)) + ' Reset the 'disableReset' property + disableReset = False + end if + + modifiedLines.Clear + + textStorage.setText(value) + lines.setText(value.Len) + + ' Koen Van Hulle - Add disable linefoldings for text bigger than 15000 lines + ' Otherwise it is becoming very slow + If lines.Count>15000 then + if me.EnableLineFoldings then + enableLineFoldingSetting = true + end if + me.EnableLineFoldings = False + elseif enableLineFoldingSetting then + me.EnableLineFoldings = True + end if + + TextChanged + + Highlight + InvalidateAllLines + ignoreRepaint = False + loadingDocument = False + Redraw + End Set + #tag EndSetter + Text As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mTextColor + End Get + #tag EndGetter + #tag Setter + Set + mTextColor = value + InvalidateAllLines + Redraw + End Set + #tag EndSetter + TextColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mTextFont + End Get + #tag EndGetter + #tag Setter + Set + if Value = mTextFont then Return + mTextFont = value + TextHeight = 0 + InvalidateAllLines + CalculateMaxHorizontalSB + CalculateMaxVerticalSB + Redraw + End Set + #tag EndSetter + TextFont As string + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if mTextHeight = 0 then + dim tmp as Picture = tmpPicture + mTextHeight = tmp.Graphics.TextHeight + end if + return mTextHeight + 1 + End Get + #tag EndGetter + #tag Setter + Set + mTextHeight = value + End Set + #tag EndSetter + TextHeight As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return TextStorage.Length + End Get + #tag EndGetter + TextLength As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mTextselectioncolor + End Get + #tag EndGetter + #tag Setter + Set + mTextselectioncolor = value + + //set default selection color to system default, if none specified. + if mTextselectioncolor = &c000000 then + mTextselectioncolor = HighlightColor + end if + End Set + #tag EndSetter + TextSelectionColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mTextSize + End Get + #tag EndGetter + #tag Setter + Set + if Value = mTextSize then Return + mTextSize = value + TextHeight = 0 + + lastLongestLineLength = 0 + lastLongestLinePixels = 0 + MaxLineLengthChanged(lines.LongestLineIdx) + + CalculateMaxHorizontalSB + CalculateMaxVerticalSB + + InvalidateAllLines + Redraw + End Set + #tag EndSetter + TextSize As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mTextstorage = nil then + //create a new text buffer + mTextstorage = new GapBuffer + + //and blinker timer. + caretBlinker = new CaretBlinker(self) + end if + + return mTextstorage + End Get + #tag EndGetter + #tag Setter + Set + mTextstorage = value + End Set + #tag EndSetter + Protected TextStorage As gapBuffer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mThickInsertionPoint + End Get + #tag EndGetter + #tag Setter + Set + mThickInsertionPoint = value + End Set + #tag EndSetter + ThickInsertionPoint As boolean + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected trailingSuggestion As string + #tag EndProperty + + #tag Property, Flags = &h1 + Protected typing As boolean + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if mUndomgr = nil then + mUndomgr = new UndoManager + end if + return mUndomgr + End Get + #tag EndGetter + #tag Setter + Set + mUndomgr = value + End Set + #tag EndSetter + UndoMgr As undomanager + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected verticalSB As scrollbar + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if mVisibleLineRange = nil then + mVisibleLineRange = new DataRange + end if + return mVisibleLineRange + End Get + #tag EndGetter + #tag Setter + Set + mVisibleLineRange = value + End Set + #tag EndSetter + VisibleLineRange As DataRange + #tag EndComputedProperty + + + #tag Constant, Name = BLOCK_CLOSE_CHARS, Type = String, Dynamic = False, Default = \")]}", Scope = Protected + #tag EndConstant + + #tag Constant, Name = BLOCK_OPEN_CHARS, Type = String, Dynamic = False, Default = \"([{", Scope = Protected + #tag EndConstant + + #tag Constant, Name = CURRENT_CARET_WORD_DELIMITER_PATTERN, Type = String, Dynamic = False, Default = \"[^\\w\\.]", Scope = Protected + #tag EndConstant + + #tag Constant, Name = DEFAULT_FONT, Type = String, Dynamic = False, Default = \"System", Scope = Public + #tag EndConstant + + #tag Constant, Name = DEFAULT_FONTSIZE, Type = Double, Dynamic = False, Default = \"12", Scope = Public + #tag EndConstant + + #tag Constant, Name = FlashRefreshRanges, Type = Boolean, Dynamic = False, Default = \"false", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_BACKSPACE, Type = Double, Dynamic = False, Default = \"51", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_DELETE, Type = Double, Dynamic = False, Default = \"117", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_DOWN, Type = Double, Dynamic = False, Default = \"125", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_END, Type = Double, Dynamic = False, Default = \"119", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_ESC, Type = Double, Dynamic = False, Default = \"53", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_HOME, Type = Double, Dynamic = False, Default = \"115", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_LEFT, Type = Double, Dynamic = False, Default = \"123", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_PGDWN, Type = Double, Dynamic = False, Default = \"121", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_PGUP, Type = Double, Dynamic = False, Default = \"116", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_RIGHT, Type = Double, Dynamic = False, Default = \"124", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_TAB, Type = Double, Dynamic = False, Default = \"48", Scope = Private + #tag EndConstant + + #tag Constant, Name = KEY_UP, Type = Double, Dynamic = False, Default = \"126", Scope = Private + #tag EndConstant + + #tag Constant, Name = UNDO_EVT_BLOCK_SECS, Type = Double, Dynamic = False, Default = \"3", Scope = Protected + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="AcceptFocus" + Visible=true + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AcceptTabs" + Visible=true + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoCloseBrackets" + Visible=true + Group="Behavior" + InitialValue="false" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutocompleteAppliesStandardCase" + Visible=true + Group="Behavior" + InitialValue="true" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoDeactivate" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoIndentNewLines" + Visible=true + Group="Behavior" + InitialValue="true" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&cffffff" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="Border" + Visible=true + Group="Appearance" + InitialValue="true" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="BorderColor" + Visible=true + Group="Appearance" + InitialValue="&h888888" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="BracketHighlightColor" + Visible=true + Group="Behavior" + InitialValue="&cFFFF00" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="CaretColor" + Visible=true + Group="Appearance" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="CaretLine" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="CaretPos" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ClearHighlightedRangesOnTextChange" + Visible=true + Group="Behavior" + InitialValue="true" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DirtyLinesColor" + Visible=true + Group="Appearance" + InitialValue="&cFF9999" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="disableReset" + Group="Behavior" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayDirtyLines" + Visible=true + Group="Appearance" + InitialValue="false" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayInvisibleCharacters" + Visible=true + Group="Behavior" + InitialValue="false" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayLineNumbers" + Visible=true + Group="Appearance" + InitialValue="true" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayRightMarginMarker" + Visible=true + Group="Appearance" + InitialValue="false" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DoubleBuffer" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EnableAutocomplete" + Visible=true + Group="Behavior" + InitialValue="false" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EnableLineFoldings" + Visible=true + Group="Behavior" + InitialValue="false" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="enableLineFoldingSetting" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EraseBackground" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="GutterBackgroundColor" + Visible=true + Group="Appearance" + InitialValue="&cEEEEEE" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="GutterSeparationLineColor" + Visible=true + Group="Appearance" + InitialValue="&c888888" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="GutterWidth" + Visible=true + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="100" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HelpTag" + Visible=true + Group="Appearance" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="HighlightBlocksOnMouseOverGutter" + Visible=true + Group="Behavior" + InitialValue="true" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HighlightMatchingBrackets" + Visible=true + Group="Behavior" + InitialValue="true" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HighlightMatchingBracketsMode" + Visible=true + Group="Behavior" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Circle" + "1 - Highlight" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="ignoreRepaint" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="IndentPixels" + Visible=true + Group="Behavior" + InitialValue="16" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="IndentVisually" + Visible=true + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + Type="Integer" + EditorType="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="InitialParent" + Group="Initial State" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="KeepEntireTextIndented" + Visible=true + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="leftMarginOffset" + Visible=true + Group="Appearance" + InitialValue="4" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LineNumbersColor" + Visible=true + Group="Appearance" + InitialValue="&c888888" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="LineNumbersTextFont" + Visible=true + Group="Appearance" + InitialValue="System" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="LineNumbersTextSize" + Visible=true + Group="Appearance" + InitialValue="9" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LockBottom" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockLeft" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockRight" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockTop" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxVisibleLines" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="ReadOnly" + Visible=true + Group="Behavior" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="RightMarginAtPixel" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="RightScrollMargin" + Visible=true + Group="Appearance" + InitialValue="150" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ScrollPosition" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ScrollPositionX" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="selLength" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="selStart" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="SelText" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="TabIndex" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabPanelIndex" + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabStop" + Visible=true + Group="Position" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="TabWidth" + Visible=true + Group="Behavior" + InitialValue="4" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Text" + Visible=true + Group="Appearance" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="TextColor" + Visible=true + Group="Appearance" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="TextFont" + Visible=true + Group="Appearance" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="TextHeight" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TextLength" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TextSelectionColor" + Visible=true + Group="Appearance" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="TextSize" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ThickInsertionPoint" + Visible=true + Group="Appearance" + InitialValue="true" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Transparent" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="UseFocusRing" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="100" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/CustomEditFieldPrinter.xojo_code b/Third Party Classes/CustomEditField/CustomEditFieldPrinter.xojo_code new file mode 100644 index 0000000..5e33add --- /dev/null +++ b/Third Party Classes/CustomEditField/CustomEditFieldPrinter.xojo_code @@ -0,0 +1,124 @@ +#tag Class +Protected Class CustomEditFieldPrinter + #tag Method, Flags = &h0 + Sub Constructor(g as graphics, textStorage as gapBuffer, lines as linemanager, defaultColor as color, displayInvisible as boolean, indentVisually as Boolean) + self.g = g + self.TextStorage = TextStorage + self.lines = lines + self.defaultColor = defaultColor + self.displayInvisible = displayInvisible + self.indentVisually = indentVisually + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function DrawBlock(x as integer, y as integer, width as integer, height as integer, lineRange as dataRange, wrap as boolean = false, lineNumbers as boolean = false) As integer + if g = nil then Return -1 + + dim minLine, maxLine as Integer + if lineRange = nil then //paint all + minLine = 0 + maxLine = lines.Count - 1 + else + minLine = lineRange.offset + maxLine = lineRange.offset + lineRange.length + end if + + dim currentLine as TextLine + dim sx, sy, lineNumOffset as Integer + + sx = x + sy = y + g.TextHeight - (g.TextHeight - g.TextAscent) + + if lineNumbers then + lineNumOffset = g.StringWidth(str(maxLine)+".") + 4 + end if + + dim linesUsed as Integer + + for i as Integer = minLine to maxLine + currentLine = lines.getLine(i) + if currentLine = nil then Continue for + + linesUsed = currentLine.PrinterPaint(TextStorage, g, sx + lineNumOffset, sy, width - lineNumOffset, defaultColor, displayInvisible, wrap, self.indentVisually) + + if lineNumbers then + g.ForeColor = &c888888 + g.DrawString str(i+1), sx + lineNumOffset - g.StringWidth(str(i+1)+".") - 2, sy + + g.ForeColor = &cCCCCCC + g.DrawLine sx + lineNumOffset - 2, sy - g.TextAscent, sx + lineNumOffset - 2, sy - g.TextAscent + (g.TextHeight * linesUsed) + end if + + sy = sy + g.TextHeight * linesUsed + + if sy > y + height or sy > g.Height then Return i + next + + Return maxLine + End Function + #tag EndMethod + + + #tag Property, Flags = &h1 + Protected defaultColor As color + #tag EndProperty + + #tag Property, Flags = &h1 + Protected displayInvisible As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected g As graphics + #tag EndProperty + + #tag Property, Flags = &h1 + Protected indentVisually As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lines As linemanager + #tag EndProperty + + #tag Property, Flags = &h1 + Protected TextStorage As gapBuffer + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/CustomScrollableEditField.xojo_window b/Third Party Classes/CustomEditField/CustomScrollableEditField.xojo_window new file mode 100644 index 0000000..ef33bf7 --- /dev/null +++ b/Third Party Classes/CustomEditField/CustomScrollableEditField.xojo_window @@ -0,0 +1,2226 @@ +#tag Window +Begin ContainerControl CustomScrollableEditField + AcceptFocus = True + AcceptTabs = False + AutoDeactivate = True + BackColor = &cFFFFFF00 + Backdrop = 0 + Compatibility = "" + Enabled = True + EraseBackground = True + HasBackColor = False + Height = 118 + HelpTag = "" + InitialParent = "" + Left = 32 + LockBottom = False + LockLeft = False + LockRight = False + LockTop = False + TabIndex = 0 + TabPanelIndex = 0 + TabStop = False + Top = 32 + Transparent = True + UseFocusRing = True + Visible = True + Width = 208 + Begin CustomEditField contentField + AcceptFocus = False + AcceptTabs = False + AutoCloseBrackets= False + AutocompleteAppliesStandardCase= True + AutoDeactivate = True + AutoIndentNewLines= True + BackColor = &cFFFFFF00 + Backdrop = 0 + Border = True + BorderColor = &c88888800 + BracketHighlightColor= &cFFFF0000 + CaretColor = &c00000000 + CaretLine = 0 + CaretPos = 0 + ClearHighlightedRangesOnTextChange= True + DirtyLinesColor = &cFF999900 + disableReset = False + DisplayDirtyLines= True + DisplayInvisibleCharacters= False + DisplayLineNumbers= True + DisplayRightMarginMarker= False + DoubleBuffer = False + EnableAutocomplete= True + Enabled = True + EnableLineFoldings= False + enableLineFoldingSetting= False + EraseBackground = False + GutterBackgroundColor= &cEEEEEE00 + GutterSeparationLineColor= &c88888800 + GutterWidth = 0 + Height = 102 + HelpTag = "" + HighlightBlocksOnMouseOverGutter= True + HighlightMatchingBrackets= True + HighlightMatchingBracketsMode= 0 + ignoreRepaint = False + IndentPixels = 16 + IndentVisually = False + Index = -2147483648 + InitialParent = "" + KeepEntireTextIndented= False + Left = 0 + leftMarginOffset= 4 + LineNumbersColor= &c88888800 + LineNumbersTextFont= "System" + LineNumbersTextSize= 9 + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = True + MaxVisibleLines = 0 + ReadOnly = False + RightMarginAtPixel= 0 + RightScrollMargin= 150 + Scope = 0 + ScrollPosition = 0 + ScrollPositionX = 0 + selLength = 0 + selStart = 0 + SelText = "" + TabIndex = 4 + TabPanelIndex = 0 + TabStop = True + TabWidth = 0 + Text = "" + TextColor = &c00000000 + TextFont = "System" + TextHeight = 0 + TextLength = 0 + TextSelectionColor= &c00000000 + TextSize = 0 + ThickInsertionPoint= True + Top = 0 + Transparent = True + UseFocusRing = False + Visible = True + Width = 192 + End + Begin ScrollBar vScrollBar + AcceptFocus = True + AutoDeactivate = True + Enabled = True + Height = 102 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = 192 + LineStep = 1 + LiveScroll = True + LockBottom = True + LockedInPosition= False + LockLeft = False + LockRight = True + LockTop = True + Maximum = 100 + Minimum = 0 + PageStep = 20 + Scope = 2 + TabIndex = 5 + TabPanelIndex = 0 + TabStop = True + Top = 0 + Value = 0 + Visible = True + Width = 16 + End + Begin ScrollBar hScrollBar + AcceptFocus = True + AutoDeactivate = True + Enabled = True + Height = 16 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = 0 + LineStep = 1 + LiveScroll = True + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = False + Maximum = 100 + Minimum = 0 + PageStep = 20 + Scope = 2 + TabIndex = 6 + TabPanelIndex = 0 + TabStop = True + Top = 102 + Value = 0 + Visible = True + Width = 192 + End + Begin Timer SelChangeDeferrer + Enabled = True + Height = "32" + Index = -2147483648 + InitialParent = "" + Left = -44 + LockedInPosition= False + Mode = 0 + Period = 0 + Scope = 0 + TabPanelIndex = 0 + Top = 0 + Visible = True + Width = "32" + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Activate() + ' + End Sub + #tag EndEvent + + #tag Event + Sub Close() + ' + End Sub + #tag EndEvent + + #tag Event + Function ConstructContextualMenu(base as MenuItem, x as Integer, y as Integer) As Boolean + #pragma unused base + #pragma unused x + #pragma unused y + ' + End Function + #tag EndEvent + + #tag Event + Sub ContentsChanged() + ' + End Sub + #tag EndEvent + + #tag Event + Function ContextualMenuAction(hitItem as MenuItem) As Boolean + #pragma unused hititem + ' + End Function + #tag EndEvent + + #tag Event + Sub Deactivate() + ' + End Sub + #tag EndEvent + + #tag Event + Function DragEnter(obj As DragItem, action As Integer) As Boolean + #pragma unused obj + #pragma unused action + ' + End Function + #tag EndEvent + + #tag Event + Sub DragExit(obj As DragItem, action As Integer) + #pragma unused obj + #pragma unused action + + ' + End Sub + #tag EndEvent + + #tag Event + Function DragOver(x As Integer, y As Integer, obj As DragItem, action As Integer) As Boolean + #pragma unused x + #pragma unused y + #pragma unused obj + #pragma unused action + ' + End Function + #tag EndEvent + + #tag Event + Sub DropObject(obj As DragItem, action As Integer) + #pragma unused obj + #pragma unused action + ' + End Sub + #tag EndEvent + + #tag Event + Sub EnableMenuItems() + ' + End Sub + #tag EndEvent + + #tag Event + Sub GotFocus() + break + End Sub + #tag EndEvent + + #tag Event + Function KeyDown(Key As String) As Boolean + #pragma unused key + ' + End Function + #tag EndEvent + + #tag Event + Sub KeyUp(Key As String) + #pragma unused key + ' + End Sub + #tag EndEvent + + #tag Event + Sub LostFocus() + break + End Sub + #tag EndEvent + + #tag Event + Function MouseDown(X As Integer, Y As Integer) As Boolean + #pragma unused x + #pragma unused y + ' + End Function + #tag EndEvent + + #tag Event + Sub MouseDrag(X As Integer, Y As Integer) + #pragma unused x + #pragma unused y + ' + End Sub + #tag EndEvent + + #tag Event + Sub MouseEnter() + ' + End Sub + #tag EndEvent + + #tag Event + Sub MouseExit() + ' + End Sub + #tag EndEvent + + #tag Event + Sub MouseMove(X As Integer, Y As Integer) + #pragma unused x + #pragma unused y + ' + End Sub + #tag EndEvent + + #tag Event + Sub MouseUp(X As Integer, Y As Integer) + #pragma unused x + #pragma unused y + ' + End Sub + #tag EndEvent + + #tag Event + Function MouseWheel(X As Integer, Y As Integer, DeltaX as Integer, DeltaY as Integer) As Boolean + #pragma unused x + #pragma unused y + #pragma unused DeltaX + #pragma unused DeltaY + ' + End Function + #tag EndEvent + + #tag Event + Sub Moved() + ' + End Sub + #tag EndEvent + + #tag Event + Sub Open() + ' + End Sub + #tag EndEvent + + #tag Event + Sub Paint(g As Graphics, areas() As REALbasic.Rect) + #pragma unused g + #pragma unused areas + + End Sub + #tag EndEvent + + #tag Event + Sub Resized() + ' + End Sub + #tag EndEvent + + #tag Event + Sub Resizing() + ' + End Sub + #tag EndEvent + + + #tag Method, Flags = &h0 + Sub AddBookmark(lineIndex as integer) + contentField.AddBookmark(lineIndex) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub AppendText(text as string) + contentField.AppendText(text) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function CanRedo() As boolean + Return contentField.CanRedo + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CanUndo() As boolean + Return contentField.CanUndo + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CaretSymbol() As documentSymbol + Return contentField.CaretSymbol + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CharPosAtLineNum(lineNumber as integer) As integer + Return contentField.CharPosAtLineNum(lineNumber) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CharPosAtXY(X as integer, Y as integer) As integer + Return contentField.CharPosAtXY(x,y) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearBookmark(lineIndex as integer) + contentField.ClearBookmark(lineIndex) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearBookmarks() + contentField.ClearBookmarks + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearDirtyLines() + contentField.ClearDirtyLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearHighlightedCharacterRanges() + contentField.ClearHighlightedCharacterRanges + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ClearLineIcons() + contentField.ClearLineIcons + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Copy() + contentField.Copy + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function CustomEditFieldPrinter(printerGraphics as graphics) As CustomEditFieldPrinter + Return contentField.CustomEditFieldPrinter(printerGraphics) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function DocumentSymbols() As DocumentSymbol() + Return contentField.DocumentSymbols + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function drawFocusRing(ringVisible as Boolean = true, windowGraphics As Graphics) As Boolean + #if TargetMacOS + + declare function QDBeginCGContext lib "Carbon" (port as Int32, ByRef contextPtr as Int32) as Integer + declare sub CGContextSynchronize lib "Carbon" (context as Int32) + declare function QDEndCGContext lib "Carbon" (port as Int32, ByRef context as Int32) as Integer + + dim context, grafPort as Int32 + dim res as Integer + + dim meTop, meLeft as Integer + meTop = me.Top + meLeft = me.Left + dim container as RectControl = me.Parent + while container <> nil + meTop = meTop + container.Top + meLeft = meLeft + container.Left + container = container.Parent + wend + + // We have to open a new drawing context because otherwise we might get our drawings clipped + // or we might draw into the wrong window + grafPort = windowGraphics.Handle(windowGraphics.HandleTypeCGrafPtr) + res = QDBeginCGContext (grafPort, context) + if res = 0 then + // Now draw the ring + declare function DrawThemeFocusRect lib "Carbon" (inRect as Ptr, DrawRing as Boolean) as Integer + dim mb as new MemoryBlock(8) + mb.Short(0) = meTop + mb.Short(2) = meLeft + mb.Short(4) = meTop+me.Height + mb.Short(6) = meLeft+me.Width + call DrawThemeFocusRect (mb, ringVisible) + // Close the drawing context again + if grafPort <> 0 then + CGContextSynchronize context + call QDEndCGContext (grafPort, context) + end if + end + if res <> 0 then break + return res = 0 + + #else + + // Draw something with RB's own functions + // ... + + #endif + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Find(what as string, ignoreCase as boolean, wrap as boolean, redraw as boolean = true, startPos as integer = - 1) As integer + Return contentField.Find(what, ignoreCase, wrap, redraw, startPos) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub FoldAllLines() + contentField.FoldAllLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub FoldBlockAtCaretPos() + contentField.FoldBlockAtCaretPos + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function GetLine(index as integer) As string + Return contentField.GetLine(index) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function HasFocus() As Boolean + return mHasFocus and CustomEditField.CurrentFocusedField = contentField + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub HighlightCharacterRange(offset as integer, length as integer, withColor as color, rounded as boolean = false) + contentField.HighlightCharacterRange(offset, length, withColor, rounded) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Insert(offset as integer, text as string) + contentField.Insert(offset, text) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub InvalidateAllLines() + contentField.InvalidateAllLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub InvalidateLine(index as integer) + contentField.InvalidateLine(index) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsDirty() As boolean + Return contentField.IsDirty + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineCount() As integer + Return contentField.LineCount + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineIcon(index as integer) As picture + Return contentField.LineIcon(index) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub LineIcon(index as integer, assigns value as picture) + contentField.LineIcon(index) = value + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineNumAtCharPos(offset as integer) As integer + Return contentField.LineNumAtCharPos(offset) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Paste() + contentField.Paste + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redo() + contentField.Redo + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redraw() + contentField.Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ReHighlight() + contentField.ReHighlight + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ReindentText() + contentField.ReindentText + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ReindentText(fromLine as Integer, toLine as integer) + contentField.ReindentText(fromLine, toLine) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ResetUndo() + contentField.ResetUndo + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ResetUndoDirtyFlag() + contentField.ResetUndoDirtyFlag + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Save(toFile as folderItem, fileType as string = "Text", encoding as textencoding = nil) As boolean + return contentField.Save(toFile, FileType, Encoding) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Save(toFile as folderItem, encoding as textencoding) As boolean + Return contentField.save(toFile, "Text", encoding) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SelectAll() + contentField.SelectAll + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SelectLine(lineNumber as integer, refresh as boolean = true) + contentField.SelectLine(lineNumber, refresh) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub SetDefaults() + contentField.BackColor = &cffffff + contentField.Border = true + contentField.BorderColor = &c888888 + contentField.CaretColor = &c000000 + contentField.DirtyLinesColor = &cFF9999 + contentField.DisplayDirtyLines = true + contentField.DisplayLineNumbers = true + contentField.DisplayRightMarginMarker = false + contentField.GutterBackgroundColor = &cEEEEEE + contentField.GutterSeparationLineColor = &C888888 + contentField.LeftMarginOffset = 4 + contentField.LineNumbersColor = &c888888 + contentField.RightMarginAtPixel = 0 + contentField.TextColor = &c000000 + contentField.TextFont = "" + contentField.TextSize = 0 + contentField.TextSelectionColor = &c000000 + contentField.ThickInsertionPoint = true + contentField.AutoCloseBrackets = False + contentField.AutocompleteAppliesStandardCase = true + contentField.AutoIndentNewLines = true + contentField.ClearHighlightedRangesOnTextChange = true + contentField.DisplayInvisibleCharacters = False + contentField.EnableAutocomplete = true + contentField.EnableLineFoldings = False + contentField.HighlightMatchingBrackets = true + contentField.HighlightMatchingBracketsMode = 0 + contentField.BracketHighlightColor = &cFFFF00 + contentField.LineNumbersTextFont = "System" + contentField.LineNumbersTextSize = 9 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SetFocus() + contentField.SetFocus + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function SymbolAtLine(index as integer) As documentSymbol + Return contentField.SymbolAtLine(index) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function SymbolCount() As integer + Return contentField.SymbolCount + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function TheEditField() As CustomEditField + return contentField + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ToggleLineFold(lineIndex as integer) + contentField.ToggleLineFold(lineIndex) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function TopInWindow() As Integer + // Helper function in place of "Top". Useful when this is embedded inside a ContainerControl + + dim n as Integer = self.Top + dim w as Window + w = me.Window + while w isA ContainerControl + n = n + w.Top + w = ContainerControl(w).Window + wend + return n + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Undo() + contentField.Undo + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub UnfoldAllLines() + contentField.UnfoldAllLines + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub UpdateFocusRing(windowGraphics As Graphics) + if me.UseFocusRing and mHasFocus then + mHadFocusRing = me.drawFocusRing(windowGraphics) + elseif mHadFocusRing then + // We need to make sure the focus ring gets erased. + 'Window.RefreshRect me.Left-8, me.Top-8, me.Width+16, me.Height+16 + mHadFocusRing = false + end + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function VerticalOffset(line_0 as Integer, findTop as Boolean) As Integer + dim x, y, charPos as Integer + if line_0 >= 0 then + charPos = contentField.CharPosAtLineNum (line_0) + if charPos < 0 then + // we are looking for the point past the last line + charPos = contentField.CharPosAtLineNum (contentField.LineCount-1) + if charPos < 0 then return 0 + contentField.XYAtCharPos (charPos, x, y) + return y + else + contentField.XYAtCharPos (charPos, x, y) + if findTop then + return y - contentField.TextHeight + else + return y - 1 + end + end + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub XYAtCharPos(charPos as integer, byref X as integer, byref Y as integer) + contentField.XYAtCharPos(CharPos, x, y) + End Sub + #tag EndMethod + + + #tag Hook, Flags = &h0 + Event AutocompleteOptionsForPrefix(prefix as string) As AutocompleteOptions + #tag EndHook + + #tag Hook, Flags = &h0 + Event BlockCharsMatched(blockOpenChar as string, blockOpenOffset as Integer, blockCloseChar as string, blockCloseOffset as Integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event Close() + #tag EndHook + + #tag Hook, Flags = &h0 + Event ConstructContextualMenu(base as menuitem, x as integer, y as integer) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event ContextualMenuAction(hititem as MenuItem) + #tag EndHook + + #tag Hook, Flags = &h0 + Event DragExit(obj as DragItem, action as Integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event EnableMenuItems() + #tag EndHook + + #tag Hook, Flags = &h0 + Event GotFocus() + #tag EndHook + + #tag Hook, Flags = &h0 + Event GutterClicked(onLine as integer, x as integer, y as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event HighlightingComplete() + #tag EndHook + + #tag Hook, Flags = &h0 + Event HorizontalScrollValueChanged() + #tag EndHook + + #tag Hook, Flags = &h0 + Event KeyDown(key as String) As Boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event KeyUp(key as String) + #tag EndHook + + #tag Hook, Flags = &h0 + Event LineCountChanged(newLineCount as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event LostFocus() + #tag EndHook + + #tag Hook, Flags = &h0 + Event MaxLineLengthChanged(maxLineLengthInPixels as single) + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseDown(X as integer, Y as integer) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseEnter() + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseExit() + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseMove(X as integer, Y as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event MouseUp(x as integer, y as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event Open() + #tag EndHook + + #tag Hook, Flags = &h0 + Event PaintAboveLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event PaintBelowLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event PaintOver(g as Graphics, gutterWidth as Integer) + #tag EndHook + + #tag Hook, Flags = &h0 + Event PlaceholderSelected(lineIndex as integer, placeholderLabel as String) + #tag EndHook + + #tag Hook, Flags = &h0 + Event ScrollValuesChanged() + #tag EndHook + + #tag Hook, Flags = &h0 + Event SelChanged() + #tag EndHook + + #tag Hook, Flags = &h0 + Event ShouldDisplaySuggestionWindowAtPos(ByRef x as Integer, ByRef y as Integer) As Boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event ShouldTriggerAutocomplete(Key as string, hasAutocompleteOptions as boolean) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event TextChanged() + #tag EndHook + + #tag Hook, Flags = &h0 + Event TextInserted(offset as integer, text as String) + #tag EndHook + + #tag Hook, Flags = &h0 + Event TextRemoved(offset as integer, text as String) + #tag EndHook + + #tag Hook, Flags = &h0 + Event UseBackgroundColorForLine(lineIndex as integer, byref lineBackgroundColor as color) As boolean + #tag EndHook + + #tag Hook, Flags = &h0 + Event UseBookmarkIconForLine(lineIndex as integer) As Picture + #tag EndHook + + #tag Hook, Flags = &h0 + Event VerticalScrollValueChanged() + #tag EndHook + + + #tag Note, Name = About + This is a drop-in class for a scrollable CustomEditField. + + It includes the Scrollbars so that you do not have to add them yourself, + making it easier to replace an existing edit control (EditField, TextArea) + with a CustomEditField. + + It may be that some methods that CustomEditField are missing here. This + might happen if CustomEditField gets new functions and we forget to add + them here, too. Simply add them yourself, and possibly notify Alex of this + so he can fix this for everyone. + + If you want to access the edit field of this container from the outside, use + the "TheEditField" function. + + Note: Due to limitations in RB or the IDE, you will need to set all the default + property values in code. See the "SetDefaults" method for that. + + Authors: Thomas Tempelmann and Alex Restrepo + #tag EndNote + + #tag Note, Name = Showing a Focus Ring + + As of v.1.8, to show a Focus Ring around the CustomScrollableEditField, you must set the + UseFocusRing property to True, and you must called UpdateFocusRiing from the + parent Window's Paint event with the parent Window's Graphics property. + + The Window's Paint event should look something like this: + + Sub Paint (g As Graphics, areas() As REALbasic.Rect) + // + // Whatever other painting the Window should do. + // + MyCustomScrollableEditField1.UpdateFocusRing(g) + MyCustomScrollableEditField2.UpdateFocusRing(g) + // ... and so on + End Sub + #tag EndNote + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.AutoCloseBrackets + End Get + #tag EndGetter + #tag Setter + Set + contentField.AutoCloseBrackets = value + End Set + #tag EndSetter + AutoCloseBrackets As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.AutocompleteAppliesStandardCase + End Get + #tag EndGetter + #tag Setter + Set + contentField.AutocompleteAppliesStandardCase = value + End Set + #tag EndSetter + AutocompleteAppliesStandardCase As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.AutoIndentNewLines + End Get + #tag EndGetter + #tag Setter + Set + contentField.AutoIndentNewLines = value + End Set + #tag EndSetter + AutoIndentNewLines As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.Border + End Get + #tag EndGetter + #tag Setter + Set + contentField.Border = value + End Set + #tag EndSetter + Border As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.BorderColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.BorderColor = value + End Set + #tag EndSetter + BorderColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.CaretColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.CaretColor = value + End Set + #tag EndSetter + CaretColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.CaretPos + End Get + #tag EndGetter + #tag Setter + Set + contentField.CaretPos = value + End Set + #tag EndSetter + CaretPos As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.ClearHighlightedRangesOnTextChange + End Get + #tag EndGetter + #tag Setter + Set + contentField.ClearHighlightedRangesOnTextChange = value + End Set + #tag EndSetter + ClearHighlightedRangesOnTextChange As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.DirtyLinesColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.DirtyLinesColor = value + End Set + #tag EndSetter + DirtyLinesColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.DisplayDirtyLines + End Get + #tag EndGetter + #tag Setter + Set + contentField.DisplayDirtyLines = value + End Set + #tag EndSetter + DisplayDirtyLines As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.DisplayInvisibleCharacters + End Get + #tag EndGetter + #tag Setter + Set + contentField.DisplayInvisibleCharacters = value + End Set + #tag EndSetter + DisplayInvisibleCharacters As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.DisplayLineNumbers + End Get + #tag EndGetter + #tag Setter + Set + contentField.DisplayLineNumbers = value + End Set + #tag EndSetter + DisplayLineNumbers As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.DisplayRightMarginMarker + End Get + #tag EndGetter + #tag Setter + Set + contentField.DisplayRightMarginMarker = value + End Set + #tag EndSetter + DisplayRightMarginMarker As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.EnableAutocomplete + End Get + #tag EndGetter + #tag Setter + Set + contentField.EnableAutocomplete = value + End Set + #tag EndSetter + EnableAutocomplete As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.EnableLineFoldings + End Get + #tag EndGetter + #tag Setter + Set + contentField.EnableLineFoldings = value + End Set + #tag EndSetter + EnableLineFoldings As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.GutterBackgroundColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.GutterBackgroundColor = value + End Set + #tag EndSetter + GutterBackgroundColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.GutterSeparationLineColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.GutterSeparationLineColor = value + End Set + #tag EndSetter + GutterSeparationLineColor As Color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return contentField.GutterWidth + End Get + #tag EndGetter + GutterWidth As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.HighlightMatchingBrackets + End Get + #tag EndGetter + #tag Setter + Set + contentField.HighlightMatchingBrackets = value + End Set + #tag EndSetter + HighlightMatchingBrackets As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.HighlightMatchingBracketsMode + End Get + #tag EndGetter + #tag Setter + Set + contentField.HighlightMatchingBracketsMode = value + End Set + #tag EndSetter + HighlightMatchingBracketsMode As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.LeftMarginOffset + End Get + #tag EndGetter + #tag Setter + Set + contentField.LeftMarginOffset = value + End Set + #tag EndSetter + LeftMarginOffset As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.LineNumbersColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.LineNumbersColor = value + End Set + #tag EndSetter + LineNumbersColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.LineNumbersTextFont + End Get + #tag EndGetter + #tag Setter + Set + contentField.LineNumbersTextFont = value + End Set + #tag EndSetter + LineNumbersTextFont As string + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.LineNumbersTextSize + End Get + #tag EndGetter + #tag Setter + Set + contentField.LineNumbersTextSize = value + End Set + #tag EndSetter + LineNumbersTextSize As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.MaxVisibleLines + End Get + #tag EndGetter + MaxVisibleLines As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mHadFocusRing As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mHasFocus As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mSettingSelection As Boolean + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.RightScrollMargin + End Get + #tag EndGetter + #tag Setter + Set + contentField.RightScrollMargin = value + End Set + #tag EndSetter + RighScrollMargin As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return contentField.RightMarginAtPixel + End Get + #tag EndGetter + #tag Setter + Set + contentField.RightMarginAtPixel = value + End Set + #tag EndSetter + RightMarginAtPixel As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.ScrollPosition + End Get + #tag EndGetter + #tag Setter + Set + contentField.ScrollPosition = value + End Set + #tag EndSetter + ScrollPosition As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.ScrollPositionX + End Get + #tag EndGetter + #tag Setter + Set + contentField.ScrollPositionX = value + End Set + #tag EndSetter + ScrollPositionX As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.SelLength + End Get + #tag EndGetter + #tag Setter + Set + mSettingSelection = true // necessary to avoid + contentField.SelLength = value + mSettingSelection = false + End Set + #tag EndSetter + SelLength As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.SelStart + End Get + #tag EndGetter + #tag Setter + Set + mSettingSelection = true + contentField.SelStart = value + mSettingSelection = false + End Set + #tag EndSetter + SelStart As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return contentField.SelText + End Get + #tag EndGetter + #tag Setter + Set + contentField.SelText = value + End Set + #tag EndSetter + SelText As string + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.SyntaxDefinition + End Get + #tag EndGetter + #tag Setter + Set + contentField.SyntaxDefinition = value + End Set + #tag EndSetter + SyntaxDefinition As HighlightDefinition + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.TabWidth + End Get + #tag EndGetter + #tag Setter + Set + contentField.TabWidth = value + End Set + #tag EndSetter + TabWidth As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.Text + End Get + #tag EndGetter + #tag Setter + Set + contentField.Text = value + End Set + #tag EndSetter + Text As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.TextColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.TextColor = value + End Set + #tag EndSetter + TextColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.TextFont + End Get + #tag EndGetter + #tag Setter + Set + contentField.TextFont = value + End Set + #tag EndSetter + TextFont As string + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return contentField.TextHeight + End Get + #tag EndGetter + #tag Setter + Set + contentField.TextHeight = value + End Set + #tag EndSetter + TextHeight As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return contentField.TextLength + End Get + #tag EndGetter + TextLength As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.TextSelectionColor + End Get + #tag EndGetter + #tag Setter + Set + contentField.TextSelectionColor = value + End Set + #tag EndSetter + TextSelectionColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.TextSize + End Get + #tag EndGetter + #tag Setter + Set + contentField.TextSize = value + End Set + #tag EndSetter + TextSize As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return contentField.ThickInsertionPoint + End Get + #tag EndGetter + #tag Setter + Set + contentField.ThickInsertionPoint = value + End Set + #tag EndSetter + ThickInsertionPoint As boolean + #tag EndComputedProperty + + +#tag EndWindowCode + +#tag Events contentField + #tag Event + Sub TextChanged() + TextChanged() + End Sub + #tag EndEvent + #tag Event + Sub SelChanged(line as integer, column as integer, length as integer) + #pragma unused line + #pragma unused column + #pragma unused length + + if mSettingSelection then + // not sure if this needs to be called at all when we're setting the selection explicitly, but at least + // we must not defer it then because it'll cause trouble in SourceCodeView.handleSourceChange + // because that method couldn't prevent repeated (usually recursive) calls to itself then. + RaiseEvent SelChanged() + else + // We postpone the SelChange event so that, if there's also a TextChange, + // we pass on the SelChange after the TextChange + SelChangeDeferrer.Mode = Timer.ModeSingle + end if + End Sub + #tag EndEvent + #tag Event + Function UseBackgroundColorForLine(lineIndex as integer, byref lineBackgroundColor as color) As boolean + Return UseBackgroundColorForLine(lineIndex, lineBackgroundColor) + End Function + #tag EndEvent + #tag Event + Sub GotFocus() + self.mHasFocus = true + GotFocus() + #if RBVersion < 2014.01 + // + // I don't really know when Invalidate was introduced + // + self.TrueWindow.Refresh + #else + self.TrueWindow.Invalidate + #endif + + End Sub + #tag EndEvent + #tag Event + Sub LostFocus() + self.mHasFocus = false + #if RBVersion < 2014.01 + // + // I don't really know when Invalidate was introduced + // + self.TrueWindow.Refresh + #else + self.TrueWindow.Invalidate + #endif + LostFocus() + End Sub + #tag EndEvent + #tag Event + Sub Open() + contentField.SetScrollbars hScrollBar, vScrollBar + self.SetDefaults + Open() + End Sub + #tag EndEvent + #tag Event + Sub EnableMenuItems() + EnableMenuItems() + End Sub + #tag EndEvent + #tag Event + Function KeyDown(key as string) As boolean + return KeyDown (key) + End Function + #tag EndEvent + #tag Event + Function AutocompleteOptionsForPrefix(prefix as string) As AutocompleteOptions + Return AutocompleteOptionsForPrefix(prefix) + End Function + #tag EndEvent + #tag Event + Sub Close() + Close + End Sub + #tag EndEvent + #tag Event + Function ConstructContextualMenu(base as menuitem, x as integer, y as integer) As boolean + Return ConstructContextualMenu(base, x, y) + End Function + #tag EndEvent + #tag Event + Sub HighlightingComplete() + HighlightingComplete + End Sub + #tag EndEvent + #tag Event + Sub LineCountChanged(newLineCount as integer) + LineCountChanged(newLineCount) + End Sub + #tag EndEvent + #tag Event + Sub MaxLineLengthChanged(maxLineLengthInPixels as single) + MaxLineLengthChanged(maxLineLengthInPixels) + End Sub + #tag EndEvent + #tag Event + Function MouseDown(X as integer, Y as integer) As boolean + Return MouseDown(x,y) + End Function + #tag EndEvent + #tag Event + Sub MouseMove(X as integer, Y as integer) + MouseMove(x,y) + End Sub + #tag EndEvent + #tag Event + Sub MouseUp(x as integer, y as integer) + MouseUp(x,y) + End Sub + #tag EndEvent + #tag Event + Sub ScrollValuesChanged() + ScrollValuesChanged + End Sub + #tag EndEvent + #tag Event + Function ShouldTriggerAutocomplete(Key as string, hasAutocompleteOptions as boolean) As boolean + Return ShouldTriggerAutocomplete(key, hasAutocompleteOptions) + End Function + #tag EndEvent + #tag Event + Sub TextInserted(offset as integer, text as String) + TextInserted(offset, text) + End Sub + #tag EndEvent + #tag Event + Sub TextRemoved(offset as integer, text as String) + TextRemoved(offset, text) + End Sub + #tag EndEvent + #tag Event + Function ContextualMenuAction(hitItem as MenuItem) As Boolean + ContextualMenuAction(hitItem) + End Function + #tag EndEvent + #tag Event + Sub PaintAboveLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + PaintAboveLine(lineIndex, g, x, y, w, h) + End Sub + #tag EndEvent + #tag Event + Sub PaintBelowLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + PaintBelowLine(lineIndex, g, x, y, w, h) + End Sub + #tag EndEvent + #tag Event + Sub PaintOver(g as Graphics, gutterWidth as Integer) + PaintOver(g, gutterWidth) + End Sub + #tag EndEvent + #tag Event + Sub VerticalScrollValueChanged() + VerticalScrollValueChanged + End Sub + #tag EndEvent + #tag Event + Sub HorizontalScrollValueChanged() + HorizontalScrollValueChanged + End Sub + #tag EndEvent + #tag Event + Function UseBookmarkIconForLine(lineIndex as integer) As Picture + Return UseBookmarkIconForLine(lineIndex) + End Function + #tag EndEvent + #tag Event + Sub GutterClicked(onLine as integer, x as integer, y as integer) + GutterClicked(onLine, x, y) + End Sub + #tag EndEvent + #tag Event + Sub BlockCharsMatched(blockOpenChar as string, blockOpenOffset as Integer, blockCloseChar as string, blockCloseOffset as Integer) + BlockCharsMatched(blockOpenChar, blockOpenOffset, blockCloseChar, blockCloseOffset) + End Sub + #tag EndEvent + #tag Event + Sub PlaceholderSelected(placeholderLabel as String, lineIndex as integer, line as textLine, placeholder as textPlaceholder, doubleClick as Boolean) + #pragma unused line + #pragma unused placeholder + #pragma unused doubleClick + + PlaceholderSelected(lineIndex, placeholderLabel) + End Sub + #tag EndEvent + #tag Event + Sub DragExit(obj As DragItem, action As Integer) + DragExit obj, action + End Sub + #tag EndEvent + #tag Event + Sub KeyUp(Key As String) + KeyUp (key) + End Sub + #tag EndEvent + #tag Event + Sub MouseEnter() + MouseEnter + End Sub + #tag EndEvent + #tag Event + Sub MouseExit() + MouseExit + End Sub + #tag EndEvent + #tag Event + Function ShouldDisplaySuggestionWindowAtPos(byref X as Integer, byref Y as Integer) As Boolean + return ShouldDisplaySuggestionWindowAtPos (x, y) + End Function + #tag EndEvent +#tag EndEvents +#tag Events vScrollBar + #tag Event + Sub ValueChanged() + contentField.ScrollPosition = me.Value + End Sub + #tag EndEvent +#tag EndEvents +#tag Events hScrollBar + #tag Event + Sub ValueChanged() + contentField.ScrollPositionX = me.Value + End Sub + #tag EndEvent +#tag EndEvents +#tag Events SelChangeDeferrer + #tag Event + Sub Action() + RaiseEvent SelChanged + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="AcceptFocus" + Visible=true + Group="Behavior" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AcceptTabs" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoCloseBrackets" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutocompleteAppliesStandardCase" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoDeactivate" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoIndentNewLines" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="Border" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="BorderColor" + Group="Behavior" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="CaretColor" + Group="Behavior" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="CaretPos" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ClearHighlightedRangesOnTextChange" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DirtyLinesColor" + Group="Behavior" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayDirtyLines" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayInvisibleCharacters" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayLineNumbers" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DisplayRightMarginMarker" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EnableAutocomplete" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EnableLineFoldings" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EraseBackground" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="GutterBackgroundColor" + Group="Behavior" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="GutterSeparationLineColor" + Group="Behavior" + InitialValue="&c000000" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="GutterWidth" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="300" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HelpTag" + Visible=true + Group="Appearance" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="HighlightMatchingBrackets" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HighlightMatchingBracketsMode" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="InitialParent" + Group="Position" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LeftMarginOffset" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LineNumbersColor" + Group="Behavior" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="LineNumbersTextFont" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="LineNumbersTextSize" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LockBottom" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockLeft" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockRight" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockTop" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxVisibleLines" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="RighScrollMargin" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="RightMarginAtPixel" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ScrollPosition" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ScrollPositionX" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="SelLength" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="SelStart" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="SelText" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="TabIndex" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabPanelIndex" + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabStop" + Visible=true + Group="Position" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="TabWidth" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Text" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="TextColor" + Group="Behavior" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="TextFont" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="TextHeight" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TextLength" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TextSelectionColor" + Group="Behavior" + InitialValue="&c000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="TextSize" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ThickInsertionPoint" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Transparent" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="UseFocusRing" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="300" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Third Party Classes/CustomEditField/DragImage.xojo_code b/Third Party Classes/CustomEditField/DragImage.xojo_code new file mode 100644 index 0000000..a7a1be1 --- /dev/null +++ b/Third Party Classes/CustomEditField/DragImage.xojo_code @@ -0,0 +1,140 @@ +#tag Module +Protected Module DragImage + #tag Method, Flags = &h21 + Private Function NewCGImage(p as Picture) As Ptr + #if targetMacOS + if p is nil then + return nil + end if + dim g as Graphics = p.Graphics + + if g is nil then //copy into new picture + dim pCopy as new Picture(p.Width, p.Height, 32) + dim gCopy as Graphics = pCopy.Graphics + if gCopy is nil then + return nil + end if + gCopy.DrawPicture p, 0, 0 + p = pCopy + g = gCopy + end if + if g is nil then //I give up + return nil + end if + + dim gworldData as Ptr = Ptr(g.Handle(Graphics.HandleTypeCGrafPtr)) + if gworldData = nil then + return nil + end if + + declare function QDBeginCGContext lib CarbonFramework (port as Ptr, ByRef contextPtr as Ptr) as Integer + + dim c as Ptr + dim OSError as Integer = QDBeginCGContext(gworldData, c) + if OSError <> 0 or c = nil then + return nil + end if + + + declare function CGBitmapContextCreateImage lib CarbonFramework (c as Ptr) as Ptr + + dim image as Ptr = CGBitmapContextCreateImage(c) + + declare function QDEndCGContext lib CarbonFramework (port as Ptr, ByRef context as Ptr) as Integer + + OSError = QDEndCGContext(gworldData, c) + + return image + #endif + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub SetImage(extends d as Dragitem, p as Picture) + #if targetMacOS + dim theImage as Ptr = NewCGImage(p) + if theImage = nil then + return + end if + + declare function SetDragImageWithCGImage lib "Carbon.framework" (inDrag as Integer, inCGImage as Ptr, inImageOffsetPt as Ptr, inImageFlags as UInt32) as Integer + + declare function CGImageGetHeight lib "Carbon.framework" (image as Ptr) as UInt32 + declare function CGImageGetWidth lib "Carbon.framework" (image as Ptr) as UInt32 + + const sizeOfHIPoint = 8 + dim offset as new MemoryBlock(sizeOfHIPoint) + offset.SingleValue(0) = -CGImageGetWidth(theImage)/2 + offset.SingleValue(4) = -CGImageGetHeight(theImage)/2 + + const kDragStandardTranslucency = 0 + + 'kDragRegionAndImage = (1L << 4) + 'kDragStandardTranslucency = 0, 65% + 'kDragDarkTranslucency = 1, 50% + 'kDragDarkerTranslucency = 2, 25% + 'kDragOpaqueTranslucency = 3 0% + + dim OSError as Integer = SetDragImageWithCGImage(d.Handle, theImage, offset, kDragStandardTranslucency) + #pragma unused OSError + #endif + + finally // careful: any "return" above will not execute this finally block! + #if targetMacOS + declare sub CFRelease lib "Carbon.framework" (cf as Ptr) + CFRelease theImage + #endif + + + + End Sub + #tag EndMethod + + + #tag Note, Name = Info + Code by Charles Yeomans + http://www.declaresub.com/article/121/providing-a-better-drag-image + #tag EndNote + + + #tag Constant, Name = CarbonFramework, Type = String, Dynamic = False, Default = \"Carbon.framework", Scope = Private + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Third Party Classes/CustomEditField/EditFieldGlobals.xojo_code b/Third Party Classes/CustomEditField/EditFieldGlobals.xojo_code new file mode 100644 index 0000000..09720a5 --- /dev/null +++ b/Third Party Classes/CustomEditField/EditFieldGlobals.xojo_code @@ -0,0 +1,159 @@ +#tag Module +Protected Module EditFieldGlobals + #tag Method, Flags = &h0 + Function DarkerColor(extends forColor as Color, offset as integer) As Color + //get a darker color for the given color. + Return rgb( max(forColor.Red - offset, 0), max(forColor.green - offset, 0), max(forColor.blue - offset, 0)) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function InvertColor(extends input as Color) As color + Return rgb(255 - input.Red, 255 - input.Green, 255 - input.Blue) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LighterColor(extends forColor as color, offset as integer) As color + //get a darker color for the given color. + Return rgb( min(forColor.Red + offset, 255), min(forColor.green + offset, 255), min(forColor.blue + offset, 255)) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function loadMaskedPicture(image as picture) As picture + if Image = nil then Return nil + + dim newpic as Picture = New Picture(image.Width/2, Image.Height, 32) + NewPic.Graphics.DrawPicture Image, 0,0 + NewPic.Mask.Graphics.DrawPicture Image, 0, 0, NewPic.Width, NewPic.Height, Image.Width/2, 0 + + Return newpic + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LongestCommonPrefixIndex(extends source as string, target as string) As integer + //finds the longest common prefix between 2 strings + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + dim n as Integer = min(source.len, Target.Len) + dim i as Integer + for i = 1 to n + if source.Mid(i,1) <> Target.Mid(i,1) then Exit for + next + Return i - 1 + End Function + #tag EndMethod + + + #tag Note, Name = TextStorageType + STORAGE_ARRAY and STORAGE_WIDEMEMORYBLOCK are Unicode-savvy, i.e. they can handle any character. + STORAGE_MEMORYBLOCK can only handle 1-byte encodings and will fail with most non-ASCII chars, therefore. + + STORAGE_WIDEMEMORYBLOCK should be more efficient than STORAGE_ARRAY. + STORAGE_MEMORYBLOCK is the most compact but the least versatile. + + Also note: STORAGE_WIDEMEMORYBLOCK may not work work right on Linux, but STORAGE_ARRAY should. + #tag EndNote + + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if mBlocktrailimage = nil then + mBlocktrailimage = EditFieldGlobals.LoadMaskedPicture(blockFoldedTrailMarker) + end if + + return mBlocktrailimage + End Get + #tag EndGetter + Protected BlockFoldedTrailImage As picture + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + // We must use either UTF-32 or UTF-8 but never UTF-16 + // because some chars (e.g. "🔍") in UTF-16 return 2 for their + // Length, which is incorrect and causes errors with this code. + if false then + // This should work but doesn't yet + return Encodings.UTF32 + else + return Encodings.UTF8 + end if + End Get + #tag EndGetter + Protected InternalEncoding As TextEncoding + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mBlocktrailimage As picture + #tag EndProperty + + + #tag Constant, Name = CEF_VERSION, Type = String, Dynamic = False, Default = \"1.8.1", Scope = Protected + #tag EndConstant + + #tag Constant, Name = DebugIndentation, Type = Boolean, Dynamic = False, Default = \"false", Scope = Protected + #tag EndConstant + + #tag Constant, Name = DebugTiming, Type = Boolean, Dynamic = False, Default = \"false", Scope = Protected + #tag EndConstant + + #tag Constant, Name = STORAGE_ARRAY, Type = Double, Dynamic = False, Default = \"1", Scope = Protected + #tag EndConstant + + #tag Constant, Name = STORAGE_MEMORYBLOCK, Type = Double, Dynamic = False, Default = \"0", Scope = Protected + #tag EndConstant + + #tag Constant, Name = STORAGE_WIDEMEMORYBLOCK, Type = Double, Dynamic = False, Default = \"2", Scope = Protected + #tag EndConstant + + #tag Constant, Name = TextStorageType, Type = Double, Dynamic = False, Default = \"2", Scope = Protected + #Tag Instance, Platform = Linux, Language = Default, Definition = \"1" + #tag EndConstant + + #tag Constant, Name = UseOldRenderer, Type = Boolean, Dynamic = False, Default = \"false", Scope = Protected + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Third Party Classes/CustomEditField/Extras/CaretBlinker.xojo_code b/Third Party Classes/CustomEditField/Extras/CaretBlinker.xojo_code new file mode 100644 index 0000000..b9f35b6 --- /dev/null +++ b/Third Party Classes/CustomEditField/Extras/CaretBlinker.xojo_code @@ -0,0 +1,96 @@ +#tag Class +Protected Class CaretBlinker +Inherits timer + #tag Event + Sub Action() + if owner = nil then Return + owner.RedrawCaret + End Sub + #tag EndEvent + + + #tag Method, Flags = &h0 + Sub Constructor(owner as CustomEditField) + me.Reference = new WeakRef(owner) + me.Period = 500 + me.Mode = timer.ModeMultiple + me.Enabled = true + End Sub + #tag EndMethod + + + #tag ComputedProperty, Flags = &h21 + #tag Getter + Get + if me.Reference <> nil then + return CustomEditField(me.Reference.Value) + else + return nil + end if + End Get + #tag EndGetter + Private owner As CustomEditField + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private Reference As weakRef + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + Type="Integer" + EditorType="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Mode" + Visible=true + Group="Behavior" + InitialValue="2" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Off" + "1 - Single" + "2 - Multiple" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Period" + Visible=true + Group="Behavior" + InitialValue="1000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Extras/CharSelection.xojo_code b/Third Party Classes/CustomEditField/Extras/CharSelection.xojo_code new file mode 100644 index 0000000..2f48eaa --- /dev/null +++ b/Third Party Classes/CustomEditField/Extras/CharSelection.xojo_code @@ -0,0 +1,196 @@ +#tag Class +Protected Class CharSelection +Inherits DataRange + #tag Method, Flags = &h0 + Sub Constructor(offset as integer, length as integer, startLine as integer, endLine as integer, selectionColor as color) + // Calling the overridden superclass constructor. + // Note that this may need modifications if there are multiple constructor choices. + // Possible constructor calls: + // Constructor(offset as integer, length as integer) -- From DataRange + // Constructor() -- From DataRange + Super.Constructor(offset, length) + self.StartLine = StartLine + self.EndLine = EndLine + self.SelectionColor = SelectionColor + + LosesFocus = True + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsLineIndexInRange(lineIndex as integer) As boolean + Return lineIndex >= self.StartLine and lineIndex <= self.EndLine + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function OverlapsSelection(selection as charSelection) As integer + if self.offset = selection.offset and self.length = Selection.length then Return OVERLAP_SAME + + if selection.inRange(self.offset) and self.EndOffset = selection.EndOffset then + Return OVERLAP_WITHIN_AT_START + end if + + if self.offset = selection.offset and selection.inRange(self.EndOffset) then + Return OVERLAP_WITHIN_AT_END + end if + + if inRange(selection.offset) and not inRange(selection.EndOffset) then //head in self + return OVERLAP_END + end if + + if selection.inRange(self.offset) and not selection.inRange(self.EndOffset) then //tail of Selection is within self + Return OVERLAP_START + end if + + if selection.inRange(self.offset) and selection.inRange(self.EndOffset) then //self within selection + Return OVERLAP_WITHIN + end if + + if self.inRange(selection.offset) and self.inRange(selection.EndOffset) then + Return OVERLAP_CONTAINS + end if + + + Return OVERLAP_NONE + End Function + #tag EndMethod + + + #tag Property, Flags = &h0 + EndLine As Integer + #tag EndProperty + + #tag Property, Flags = &h0 + LosesFocus As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + Rounded As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + SelectionColor As color + #tag EndProperty + + #tag Property, Flags = &h0 + StartLine As Integer + #tag EndProperty + + + #tag Constant, Name = OVERLAP_CONTAINS, Type = Double, Dynamic = False, Default = \"5", Scope = Public + #tag EndConstant + + #tag Constant, Name = OVERLAP_END, Type = Double, Dynamic = False, Default = \"1", Scope = Public + #tag EndConstant + + #tag Constant, Name = OVERLAP_NONE, Type = Double, Dynamic = False, Default = \"0", Scope = Public + #tag EndConstant + + #tag Constant, Name = OVERLAP_SAME, Type = Double, Dynamic = False, Default = \"7", Scope = Public + #tag EndConstant + + #tag Constant, Name = OVERLAP_START, Type = Double, Dynamic = False, Default = \"4", Scope = Public + #tag EndConstant + + #tag Constant, Name = OVERLAP_WITHIN, Type = Double, Dynamic = False, Default = \"2", Scope = Public + #tag EndConstant + + #tag Constant, Name = OVERLAP_WITHIN_AT_END, Type = Double, Dynamic = False, Default = \"6", Scope = Public + #tag EndConstant + + #tag Constant, Name = OVERLAP_WITHIN_AT_START, Type = Double, Dynamic = False, Default = \"3", Scope = Public + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="DebugDescription" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="EndLine" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="EndOffset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="length" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LosesFocus" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="offset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Rounded" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="SelectionColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="StartLine" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Extras/CharSelectionManager.xojo_code b/Third Party Classes/CustomEditField/Extras/CharSelectionManager.xojo_code new file mode 100644 index 0000000..d4c38ed --- /dev/null +++ b/Third Party Classes/CustomEditField/Extras/CharSelectionManager.xojo_code @@ -0,0 +1,159 @@ +#tag Class +Protected Class CharSelectionManager + #tag Method, Flags = &h0 + Function AddSelection(selection as CharSelection) As charSelection + if Selection.length = 0 then Return nil + + dim tmpSelection as CharSelection + dim overlapResult as Integer + + for i as Integer = 0 to UBound(Selections) + tmpSelection = Selections(i) + overlapResult = tmpSelection.OverlapsSelection(Selection) + + if overlapResult <> CharSelection.OVERLAP_NONE then + dim newOffset as Integer + + if tmpSelection.SelectionColor = selection.SelectionColor then + + dim newLength as Integer + dim newStartLine as Integer + dim newEndLine as Integer + + //merge selections if colors are the same + newOffset = min(tmpSelection.offset, Selection.offset) + newLength = max(tmpSelection.offset + tmpSelection.length, selection.offset + selection.length) - newOffset + newStartLine = min(tmpSelection.StartLine, selection.StartLine) + newEndLine = max(tmpSelection.EndLine, Selection.EndLine) + + Selection.StartLine = newStartLine + Selection.EndLine = newEndLine + Selection.offset = newOffset + Selection.length = newLength + + selections.Remove(i) + i = i - 1 + else + + select case overlapResult + case CharSelection.OVERLAP_END, CharSelection.OVERLAP_CONTAINS + //add the remaining range to the range arrays for further sub-splitting or merging down the line... + dim tmp as new CharSelection(Selection.offset, tmpSelection.offset + tmpSelection.length - Selection.offset, Selection.StartLine, tmpSelection.EndLine, tmpSelection.SelectionColor) + tmp.LosesFocus = tmpSelection.LosesFocus + tmp.Rounded = tmpSelection.Rounded + selections.Append(tmp) + + //split tmpSelection at end and merge the end with Selection + tmpSelection.length = Selection.offset - tmpSelection.offset + tmpSelection.EndLine = Selection.StartLine + + + case CharSelection.OVERLAP_START + newOffset = Selection.offset + selection.length + tmpSelection.length = tmpSelection.offset + tmpSelection.length - newOffset + tmpSelection.offset = newOffset + tmpSelection.StartLine = Selection.EndLine + + else + //new selection totally swallows the old + selections.Remove(i) + i = i - 1 + + end select + + + end if + + end if + next + + selections.Append(Selection) + mSelectioncount = UBound(Selections) + 1 + Return selection + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Clear() + ReDim Selections(-1) + mSelectioncount = 0 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function SelectionsForLine(lineIndex as integer) As charSelection() + dim result() as CharSelection + + dim Selection as CharSelection + for each Selection in Selections + if Selection.IsLineIndexInRange(lineIndex) then + result.Append Selection + end if + next + + Return result + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private mSelectioncount As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mSelectioncount + End Get + #tag EndGetter + SelectionCount As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected Selections() As charSelection + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="SelectionCount" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Extras/DataRange.xojo_code b/Third Party Classes/CustomEditField/Extras/DataRange.xojo_code new file mode 100644 index 0000000..688dbe1 --- /dev/null +++ b/Third Party Classes/CustomEditField/Extras/DataRange.xojo_code @@ -0,0 +1,158 @@ +#tag Class +Protected Class DataRange + #tag Method, Flags = &h0 + Sub Constructor() + Constructor(0,0) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(offset as integer, length as integer) + moffset = offset + mLength = length + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function InRange(offset as integer) As Boolean + Return offset >= moffset and offset < moffset + mLength + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function OverlapsOrTouchesRange(other as DataRange) As Boolean + return self.offset <= other.endOffset and other.offset <= self.endOffset + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function OverlapsRange(other as DataRange) As Boolean + return self.offset < other.endOffset and other.offset < self.endOffset + End Function + #tag EndMethod + + + #tag Hook, Flags = &h0 + Event LengthChanged() + #tag EndHook + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return "["+Str(moffset)+","+Str(endOffset)+"[" + End Get + #tag EndGetter + DebugDescription As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return offset + length + End Get + #tag EndGetter + EndOffset As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mLength + End Get + #tag EndGetter + #tag Setter + Set + if mLength = value then Return + mLength = value + LengthChanged + End Set + #tag EndSetter + Length As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mLength As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private moffset As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return moffset + End Get + #tag EndGetter + #tag Setter + Set + moffset = value + + End Set + #tag EndSetter + Offset As Integer + #tag EndComputedProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="DebugDescription" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="EndOffset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="length" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="offset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Extras/ModifiedLineRangeManager.xojo_code b/Third Party Classes/CustomEditField/Extras/ModifiedLineRangeManager.xojo_code new file mode 100644 index 0000000..3b5b052 --- /dev/null +++ b/Third Party Classes/CustomEditField/Extras/ModifiedLineRangeManager.xojo_code @@ -0,0 +1,235 @@ +#tag Class +Protected Class ModifiedLineRangeManager + #tag Method, Flags = &h0 + Sub AddRange(newStart as Integer, newLength as Integer) + if newLength = 0 then + return + end if + + mLock.Enter + + dim newEnd as Integer = newStart + newLength + + // cut the new range down to the space that's not already occupied by the existing ranges + dim i as Integer + for i = 0 to mRanges.Ubound + dim thisRange as DataRange = mRanges(i) + + if thisRange.offset > newEnd then + // there are no ranges before the new one -> add new range to end of array + exit + end if + + if thisRange.endOffset < newStart then + continue + end if + + // there is an overlap + + // newStart <= this range's end + + // find more ranges that touch the new range + dim nextRange as DataRange + dim k as Integer = i + while k < mRanges.Ubound + nextRange = mRanges(k+1) + if nextRange.endOffset <= newEnd then + // remove this range entirely + mRanges.Remove k+1 + nextRange = nil + elseif nextRange.offset <= newStart then + // this range can be merged with the new range + exit while + else + k = k + 1 + end if + wend + + // cut the new range + + if thisRange.offset <= newStart then + if thisRange.endOffset >= newEnd then + // all covered - we're done + else + // extend thisRange to end of new range + thisRange.length = newEnd - thisRange.offset + if nextRange <> nil and thisRange.endOffset >= nextRange.offset then + // thisRange reaches into next range -> merge them + thisRange.length = nextRange.endOffset - thisRange.offset + mRanges.Remove k + else + // we're done + end + end if + else // thisRange.offset > newStart + // adjust the start of thisRange + dim added as Integer = thisRange.offset - newStart + thisRange.offset = thisRange.offset - added + thisRange.length = thisRange.length + added + // adjust the end of thisRange + if thisRange.endOffset < newEnd then + thisRange.length = newEnd - thisRange.offset + if nextRange <> nil and thisRange.endOffset >= nextRange.offset then + // thisRange reaches into next range -> merge them + thisRange.length = nextRange.endOffset - thisRange.offset + mRanges.Remove k + end if + end if + + end if + + mLock.Leave + return + + next + + mRanges.Insert i, new DataRange (newStart, newEnd-newStart) + + mLock.Leave + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Clear() + ReDim mRanges(-1) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor() + mLock = new CriticalSection + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function RemoveLine(offset as Integer) As Boolean + mLock.Enter + + for i as Integer = 0 to mRanges.Ubound + dim thisRange as DataRange = mRanges(i) + + if thisRange.offset > offset then + // not found + exit + end if + + if thisRange.endOffset <= offset then + continue + end if + + // found - now cut it out + + if offset = thisRange.offset then + // head cut + thisRange.length = thisRange.length - 1 + thisRange.offset = offset + 1 + if thisRange.length = 0 then + mRanges.Remove i + end if + elseif offset = thisRange.endOffset -1 then + // tail cut + thisRange.length = thisRange.length - 1 + if thisRange.length = 0 then + mRanges.Remove i + end if + else + // center cut + mRanges.Insert i+1, new DataRange (offset+1, thisRange.EndOffset-offset-1) + thisRange.length = offset - thisRange.offset + end if + + mLock.Leave + return true + + next + + mLock.Leave + return false + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function RemoveNextLine(ByRef lineIdx as Integer) As Boolean + if mRanges.Ubound < 0 then + return false + end if + + mLock.Enter + + dim firstRange as DataRange = mRanges(0) + lineIdx = firstRange.offset + if not RemoveLine (lineIdx) then + break // internal error! + end + + mLock.Leave + + return true + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private mLock As CriticalSection + #tag EndProperty + + #tag Property, Flags = &h21 + #tag Note + This array is sorted by DataRange.offset, no overlaps allowed + #tag EndNote + Private mRanges() As DataRange + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return mRanges.Ubound + 1 + End Get + #tag EndGetter + RangeCount As Integer + #tag EndComputedProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="RangeCount" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/FindWindow.xojo_window b/Third Party Classes/CustomEditField/FindWindow.xojo_window new file mode 100644 index 0000000..9300c92 --- /dev/null +++ b/Third Party Classes/CustomEditField/FindWindow.xojo_window @@ -0,0 +1,898 @@ +#tag Window +Begin Window FindWindow + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = True + Compatibility = "" + Composite = True + Frame = 0 + FullScreen = False + FullScreenButton= False + HasBackColor = False + Height = 154 + ImplicitInstance= True + LiveResize = True + MacProcID = 0 + MaxHeight = 32000 + MaximizeButton = False + MaxWidth = 32000 + MenuBar = 0 + MenuBarVisible = True + MinHeight = 64 + MinimizeButton = False + MinWidth = 64 + Placement = 0 + Resizeable = False + Title = "Find" + Visible = True + Width = 527 + Begin Label StaticText1 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 18 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 0 + Selectable = False + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + Text = "Find:" + TextAlign = 2 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 14 + Transparent = False + Underline = False + Visible = True + Width = 100 + End + Begin Label StaticText2 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 18 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 0 + Selectable = False + TabIndex = 1 + TabPanelIndex = 0 + TabStop = True + Text = "Replace With:" + TextAlign = 2 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 46 + Transparent = False + Underline = False + Visible = True + Width = 100 + End + Begin ComboBox txtToFind + AutoComplete = False + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialValue = "" + Italic = False + Left = 123 + ListIndex = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 15 + Underline = False + UseFocusRing = True + Visible = True + Width = 384 + End + Begin ComboBox txtToReplace + AutoComplete = False + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialValue = "" + Italic = False + Left = 123 + ListIndex = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + TabIndex = 3 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 47 + Underline = False + UseFocusRing = True + Visible = True + Width = 384 + End + Begin CheckBox ignoreCase + AutoDeactivate = True + Bold = False + Caption = "Ignore Case" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 123 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + State = 1 + TabIndex = 4 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 79 + Underline = False + Value = True + Visible = True + Width = 100 + End + Begin CheckBox wrapAround + AutoDeactivate = True + Bold = False + Caption = "Wrap Around" + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 235 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + State = 1 + TabIndex = 5 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 79 + Underline = False + Value = True + Visible = True + Width = 110 + End + Begin PushButton btnReplaceAll + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "Replace All" + Default = False + Enabled = True + Height = 20 + HelpTag = "Replace all occurrences of the find text" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 100 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + TabIndex = 6 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 114 + Underline = False + Visible = True + Width = 93 + End + Begin PushButton btnReplace + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "Replace" + Default = False + Enabled = True + Height = 20 + HelpTag = "Replace selected text with the replacement text" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 205 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + TabIndex = 7 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 114 + Underline = False + Visible = True + Width = 80 + End + Begin PushButton btnReplaceAndFind + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "Replace && Find" + Default = False + Enabled = True + Height = 20 + HelpTag = "Replace selected text and find next occurrence of the find text" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 297 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + TabIndex = 8 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 114 + Underline = False + Visible = True + Width = 118 + End + Begin PushButton btnNext + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "Next" + Default = True + Enabled = True + Height = 20 + HelpTag = "find next occurrence of the find text" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 427 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + TabIndex = 9 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 114 + Underline = False + Visible = True + Width = 80 + End + Begin Label results + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 357 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 0 + Selectable = False + TabIndex = 10 + TabPanelIndex = 0 + TabStop = True + Text = "" + TextAlign = 2 + TextColor = &c88888800 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 79 + Transparent = False + Underline = False + Visible = True + Width = 150 + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Close() + lastLeft = me.Left + lastTop = me.top + CurrentFindWindow = nil + End Sub + #tag EndEvent + + #tag Event + Function KeyDown(Key As String) As Boolean + if Key = Chr(3) or Key = Chr(13) then + btnNext.Push + return true + end if + End Function + #tag EndEvent + + #tag Event + Sub Open() + if findTerms = nil then + findTerms = new Dictionary + end if + + if replaceTerms = nil then + replaceTerms = new Dictionary + end if + + CurrentFindWindow = self + txtToFind.Text = lastSearchTerm + txtToReplace.text = lastReplaceTerm + wrapAround.Value = lastWrapAroundValue + ignoreCase.Value = lastIgnoreCaseValue + if lastLeft >= 0 then me.Left = lastLeft + if lastTop >=0 then me.Top = lastTop + + for i as Integer = 0 to findTerms.Count - 1 + txtToFind.AddRow findTerms.Key(i) + next + + for i as Integer = 0 to replaceTerms.Count - 1 + txtToReplace.AddRow replaceTerms.Key(i) + next + End Sub + #tag EndEvent + + + #tag MenuHandler + Function FileClose() As Boolean Handles FileClose.Action + self.Close + Return True + + End Function + #tag EndMenuHandler + + + #tag Method, Flags = &h1 + Protected Shared Sub addFindTerm() + if findTerms = nil then + findTerms = new Dictionary + end if + + if findTerms.HasKey(lastSearchTerm) then Return + + findTerms.Value(lastSearchTerm) = nil + if CurrentFindWindow <> nil then + CurrentFindWindow.txtToFind.AddRow lastSearchTerm + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Shared Sub addReplaceTerm() + if replaceTerms = nil then + replaceTerms = new Dictionary + end if + + if replaceTerms.HasKey(lastReplaceTerm) then Return + + replaceTerms.Value(lastReplaceTerm) = nil + + if CurrentFindWindow <> nil then + CurrentFindWindow.txtToReplace.AddRow lastReplaceTerm + end if + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Shared Function findNext() As boolean + if CurrentFindWindow <> nil then + CurrentFindWindow.results.Text = "" + lastIgnoreCaseValue = CurrentFindWindow.ignoreCase.Value + lastWrapAroundValue = CurrentFindWindow.wrapAround.Value + end if + + if CurrentFindWindow <> nil and CurrentFindWindow.txtToFind.Text = "" then + beep + Return false + end if + + Return findNext(lastIgnoreCaseValue, lastWrapAroundValue, true, -1) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Shared Function findNext(ignoreCase as boolean, wrapAround as boolean, redraw as boolean, startPos as integer) As boolean + dim Target as CustomEditField = CustomEditField.CurrentFocusedField + if Target = nil then Return false + + addFindTerm + if Target.Find(lastSearchTerm, ignoreCase, wrapAround, redraw, startPos) > -1 then Return true + + beep + if CurrentFindWindow <> nil then + CurrentFindWindow.results.Text = "Not Found" + end if + Return False + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Shared Sub replace() + if CurrentFindWindow <> nil then + CurrentFindWindow.results.Text = "" + lastIgnoreCaseValue = CurrentFindWindow.ignoreCase.Value + lastWrapAroundValue = CurrentFindWindow.wrapAround.Value + end if + + dim Target as CustomEditField = CustomEditField.CurrentFocusedField + if Target = nil then Return + + addReplaceTerm + Target.SelText = lastReplaceTerm + Target.Redraw + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Shared Sub replaceAll() + dim Target as CustomEditField = CustomEditField.CurrentFocusedField + if Target = nil then Return + + dim count as Integer + dim eventID as Integer = Ticks // -> grouped undo + + addFindTerm + addReplaceTerm + Target.ignoreRepaint = true + dim startPos as Integer = 0 //start at begining of file + while findNext(lastIgnoreCaseValue, false, false, startPos) //WITHOUT wrapping, since this could lead to an infinite loop if replacement contains find term. + Target.private_replace(Target.SelStart, Target.SelLength, lastReplaceTerm, true, eventID) + count = count + 1 + startPos = Target.CaretPos //update startPos + wend + Target.ignoreRepaint = false + Target.Redraw + + if Count > 1 and CurrentFindWindow <> nil then + CurrentFindWindow.results.Text = str(Count) + " Replaced" + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Shared Sub replaceAndFind() + replace + call findNext + End Sub + #tag EndMethod + + + #tag Property, Flags = &h1 + Protected Shared CurrentFindWindow As FindWindow + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared findTerms As dictionary + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared lastIgnoreCaseValue As Boolean = true + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared lastLeft As Integer = -1 + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared lastReplaceTerm As string + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared lastSearchTerm As String + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared lastTop As Integer = -1 + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared lastWrapAroundValue As Boolean = true + #tag EndProperty + + #tag Property, Flags = &h1 + Protected Shared replaceTerms As dictionary + #tag EndProperty + + +#tag EndWindowCode + +#tag Events txtToFind + #tag Event + Sub TextChanged() + lastSearchTerm = me.Text + End Sub + #tag EndEvent + #tag Event + Function KeyDown(Key As String) As Boolean + if Key = Chr(3) or Key = Chr(13) then + btnNext.Push + return true + end if + End Function + #tag EndEvent +#tag EndEvents +#tag Events txtToReplace + #tag Event + Sub TextChanged() + lastReplaceTerm = me.text + End Sub + #tag EndEvent + #tag Event + Function KeyDown(Key As String) As Boolean + if Key = Chr(3) or Key = Chr(13) then + btnNext.Push + return true + end if + End Function + #tag EndEvent +#tag EndEvents +#tag Events ignoreCase + #tag Event + Sub Action() + lastIgnoreCaseValue = me.Value + End Sub + #tag EndEvent +#tag EndEvents +#tag Events wrapAround + #tag Event + Sub Action() + lastWrapAroundValue = me.Value + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnReplaceAll + #tag Event + Sub Action() + replaceAll + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnReplace + #tag Event + Sub Action() + replace + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnReplaceAndFind + #tag Event + Sub Action() + replaceAndFind + End Sub + #tag EndEvent +#tag EndEvents +#tag Events btnNext + #tag Event + Sub Action() + call findNext + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Third Party Classes/CustomEditField/Info.xojo_code b/Third Party Classes/CustomEditField/Info.xojo_code new file mode 100644 index 0000000..68ac627 --- /dev/null +++ b/Third Party Classes/CustomEditField/Info.xojo_code @@ -0,0 +1,577 @@ +#tag Module +Protected Module Info + #tag Note, Name = 1. Info + Note: The code is now hosted and updated here: + + http://github.com/tempelmann/custom-editfield + + The following is outdated information. For latest list of issues, see the above website! + + ---------------------------------------------------- + + CustomEditField + Well, here it is, a canvas-based, custom editfield. + Enjoy. + + Known Issues: + • Linux has a problem positioning the caret, as I don't have a linux box, this issue is unlikely to be solved anytime soon. + • A bug in RB2008r3.1 raises the keydown event twice for special chars entered using option + [key] + • A bug? in 2008r5+ prevents the editfield to account for the vertical offset of the toolbar (if present), when the autocomplete suggestions window appera, will appear + shifted up by the height of the toolbar. This is a Windows-only issue. To work around it, use the ShouldDisplaySuggestionWindowAtPos event and account for the vertical offset. + + Special Thanks to: + Ryan Dary, Scott Fortmann-Roe, Dr Gerard Hammond, Roger Meier, Paul Rodman, Dave Sisemore, Bobby Skinner, Thomas Tempelmann, Seth Verrinder, Dave Wooldridge + And to all of you out there who bought the source, reported bugs and made suggestions! + + ©2009 Alex Restrepo + send comments, suggestions, fixes to alexrestrepo@mac.com + Use in whatever way you like... at your own risk, no warranties! :P + let me know if you find it useful. + + License: + This control is licensed under the Creative Commons Attribution License. That is to say, anybody can use my work for any reason, + I only ask that you give me credit where credit is due (About window or documentation). + Also, if you make any changes/improvements please send them to me so I can make them available to the public. + + Thank you. + + Alex Restrepo + #tag EndNote + + #tag Note, Name = 2. Release Notes + See here: + + http://github.com/tempelmann/custom-editfield + #tag EndNote + + #tag Note, Name = 3. Docs + ********************************************************************************************************************************************** Global Settings: + CEF_VERSION: the current version of the editfield, in case you need it... + + TextStorageType: determines the kind of structure used internally by the editfield to store text, the possible values are: + + STORAGE_MEMORYBLOCK = 0 + This is the fastest storage Method, uses a MemoryBlock to store the text... however, does NOT support multi-byte characters. + Encodings: this method will re-encode the text to either macroman, or windowsansi. + + STORAGE_ARRAY = 1 + This is the slowest method, uses an array to store the characters in the text. This method supports utf8 and utf16 characters. + Encodings: same as input, utf8 if nil. + + STORAGE_WIDEMEMORYBLOCK = 2 + This method is slower than STORAGE_MEMORYBLOCK but faster than STORAGE_ARRAY, it uses a *wide* memory block to store the text, + which means that it will use 4bytes per character, that is, it will use 4 times the memory that STORAGE_MEMORYBLOCK uses (2bytes per char in windows). It is however + noticeably faster than STORAGE_ARRAY, and supports utf characters. + Encodings: UTF16... all text is stored as UTF16, and is returned as UTF16. + + UseOldRenderer: + This Boolean value forces the field to draw its contents using QuickDraw instead of Quartz (this setting is only meaningful under OSX). + setting this flag to true yields better performance, if you don't mind uglier results :) + + + + + ********************************************************************************************************************************************** CustomEditFieldPrinter + This class allows you to easily print the contents of the editfield. + To obtain a CustomEditFieldPrinter object, simply call CustomEditField.CustomEditFieldPrinter(g as Graphics) and pass the graphics context where you want to draw onto, + you'll receive a fully configured CustomEditFieldPrinter object. + + This class has a single method: + DrawBlock(x as integer, y as integer, width as integer, height as integer, lineRange as dataRange, wrap as boolean = false, lineNumbers as boolean = false) As integer + + it allows you to draw a "block" of text, to the specified rect (X,Y, Width, Height). You'll need to specify the range of lines to print, using a DataRange Object. Finally, 2 optional + parameters, wrap, which defaults to false, sets word wrap on or off, and lineNumbers allows you to print the linenumber to the left of the printed line. + + It returns the last painted text line that was drawn into the specified rect, with this value, you can set a new print range, and call DrawBlock again. + + For more information, please look at the Demo project in DemoWindow.Print. + + + + + ********************************************************************************************************************************************** CustomEditField + ********************************************************************************************************************************************** Undo: + The undo system has a global setting: CustomEditField.UNDO_EVT_BLOCK_SECS, you can set this to any value, and what it does is records + all actions of the same type up to this value in secods, all these actions will be undo/redo in a single step. If the action changes (from typing to deleting or the other way around) + all the actions up to that point (under UNDO_EVT_BLOCK_SECS) are recorded in a single block. + Setting it to 0 uses the original behavior (1 char at the time) + + + + + ********************************************************************************************************************************************** Block chars: + There are 2 constants that define the characters that define a "block" + BLOCK_OPEN_CHARS = "([{" + BLOCK_CLOSE_CHARS = ")]}" + + you can add more chars to these constants to define your own blocks, as long as their positions in the constant string match, and as long as they're a single char. + for example if you wanted '+' and '-' to define a block, modify the constants like so: + BLOCK_OPEN_CHARS = "([{+" + BLOCK_CLOSE_CHARS = ")]}-" + + + + + ********************************************************************************************************************************************** Events: + AutocompleteOptionsForPrefix(prefix as string) as AutocompleteOptions + Basically, you can use your own autocomplete engine here, prefix is the word for which you need to provide the autocomplete options. + You need to set the following properties in the AutocompleteOptions object you wish to return: + prefix as string: the word for which you will be providing the options. + options() as string: the Autocomplete options for the prefix + longestCommonPrefix as string: the longest common prefix of all the available options. + The project includes an autocomplete engine based on a patricia tree, check the included demo project to see how it works. + + HighlightingComplete + Raised when the highlighting Thread has finished Highlighting + + LineCountChanged(newLineCount as integer) + The number of lines has changed. Useful if you're handling your own scrollbars. + + MaxLineLengthChanged(maxLineLengthInPixels as integer) + There's a new longest line in the field. Useful when handling your own Scrollbars. + + SelChanged(line as integer, column as integer, length as integer) + Same as SelChanged for a regular editfield, however, this event gives you the curret line number, column number and selection length. + + ShouldTriggerAutocomplete(Key as string, hasAutocompleteOptions as boolean) as Boolean + key is the current pressed key, hasAutocompleteOptions is true if there any autocompleteOptions for the word where the caret is. + Return true if you want to trigger the autoComplete mechanism + + UseBackgroundColorForLine(lineIndex as integer, byref lineBackgroundColor as color) As boolean + Return true to use a custom background color for the specified line, set the lineBackgroundColor parameter to the desired color. + + Event UseBookmarkIconForLine(lineIndex as integer) As Picture + Raised when a bookmarked line is about to be drawn, return a Picture to use that picture as the bookmark indicator, or return nil to use the default. + + Event GutterClicked(onLine as integer, x as integer, y as integer) + Raised when the user clicks on the gutter, x and y are the local coordinates of the click + + Event PaintBelowLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + Raised before painting the textline with index "lineIndex", g is the graphics context for the textline, x, y, w, h the rect in the editfield where the textline will be drawn. + + Event PaintAboveLine(lineIndex as integer, g as graphics, x as integer, y as integer, w as integer, h as integer) + Raised after painting the textline with index "lineIndex", g is the graphics context for the textline, x, y, w, h the rect in the editfield where the textline will be drawn. + + Event PaintOver(g as Graphics) + Raised after the editfield is done drawing itself, in case you need to do Custom drawing on top of the editfield. + + Event PlaceholderSelected(placeholderLabel as String, lineIndex as integer, line as textLine, placeholder as textPlaceholder, doubleClick as Boolean) + Rasied when a placeholder is selected, either clicked on, or using the keyboard. + + Event TextInserted(offset as integer, text as String) + Raised after text is inserted, offset is the character position where "text" was inserted + + Event TextRemoved(offset as integer, text as String) + Raised after text is removed, offset is the character position from where "text" was removed + + Event HorizontalScrollValueChanged() + Raised when the editfield changes its horizontal scroll value + + Event VerticalScrollValueChanged() + Raised when the editfield changes its vertical scroll value + + Event ScrollValuesChanged() + Raised when the editfield changes any of its scroll values. + + Event ShouldDisplaySuggestionWindowAtPos(byref X as Integer, byref Y as Integer) As Boolean + Raised when the autocomplete suggestion window is about to appear, you can modify the x,y values of where you want the window to appear + Return true to use the new values. + + + ********************************************************************************************************************************************** Methods: + CanUndo as Boolean + Returns true if there are undo steps available. + + CaretSymbol as DocumentSymbol + Returns the document symbol for the current line. + + CanRedo as Boolean + Returns true if there are redo steps available. + + CharPosAtLineNum(lineNumber as integer) as integer + Returns the index of the first character of the given line number, the first character is 0. + Returns -1 for nonexisting lines + + CharPosAtXY(X as integer, Y as integer) as integer + Returns the index of the character at the given X, Y coordinates. + + ClearLineIcons + Clears all the line icons. + + Copy + Copies the selected text to the clipboard. + + CustomEditFieldPrinter(printerGraphics as graphics) as CustomEditFieldPrinter + Returns a CustomEditFieldPrinter object, ready to be used for printing or drawing. + + DocumentSymbols as DocumentSymbol() + Returns an array of all the Document Symbols in the current document. + You'll need to sort them by offset if you need them in order. + + Find(what as string, ignoreCase as boolean, wrap as boolean, redraw as boolean = true, startPos as integer = -1) as integer + Finds the "what" string in the editfield, ignoreCase, wrap determines if the search should start again from the begining of the document, + redraw redraws the editfield, startPos is the estarting search position, if -1, the search starts at the caret position. + Returns the location of the found string, -1 if not found. + + FoldAllLines + Folds all foldable lines. + + FoldBlockAtCaretPos + Folds the enclosing block where the caret is at. + + GetLine(index as integer) as string + Returns the text contained in the given line. + + HighlightCharacterRange(offset as integer, length as integer, withColor as color, rounded as boolean = false) + Highlights the specified character range, with the given color. The optional rounded parameter allows for a rounded-rect selection. + If the ClearHighlightedRangesOnTextChange property is true, all highlighted characters will be cleared when the text changes, if not, you are responsible for keeping the highlighted characters in sync. + Adding a character range over a previous one will merge both ranges, unless they have different colors, in which case the old one is splitted where they overlap. + + Insert(offset as integer, text as string) + Inserts the given text at the given index (offset). This method is used internally by the control, and externally by the undo mechanism, you shouldn't use it direclty, use instead selstart and seltext. + + InvalidateAllLines + Marks all the visible lines to be re-drawn as soon as possible. + + InvalidateLine(index as integer) + Marks the line at index to be re-drawn as soon as possible. + + LineCount as integer + Returns the number of text lines in the field. + + LineIcon(index as integer) as picture / LineIcon(index as integer, assigns value as picture) + Allows you to get and set an icon for the line at index, the icon is displayed only if the "display line numbers" option is active, since icons are drown on top of the line number. + Setting the lineIcon to nil clears the icon. + + LineNumAtCharPos(offset as integer) as integer + Returns the line number where the character at index 'offset' is. The first line is line 0, line 0 is displayed as line 1 on screen. + Returns -1 for offsets past the last character + + Paste + Pastes whatever text is in the clipboard into the EditField. + + Redo + Redoes the last undone step. + + Redraw + Redraws the whole control. + + ReHighlight + Rehighlights the whole document. + + Remove(offset as integer, length as integer, updateCaret as boolean = true) + Removes 'length' characters from the editfield, starting at index 'offset'. updateCaret will move the the caret to the left by 'length' characters, if true. + This method is used internally by the control, and externally by the undo mechanism, you shouldn't use it direclty, use instead selstart and seltext. + + Replace(offset as integer, length as integer, text as string) + Replaces 'length' characters with 'text' starting at index 'offset'. + This method is used internally by the control, and externally by the undo mechanism, you shouldn't use it direclty, use instead selstart and seltext. + + ResetUndo + Clears the undo/redo stacks. + + Save(toFile as folderItem, encoding as textencoding = nil) + Save(toFile as folderItem, fileType as string = "Text", encoding as textencoding = nil) + Saves the contents of the editfield to the specified file, if encoding is nil, the encoding of the text will be used, if not, the provided encoding will be used instead. + + SelectAll + What can I say.... + + SelectLine(lineNumber as integer) + Selects the given line. + + SelectNextPlaceholder + Selects the next placeholder in the current document. + + SetScrollbars(horizontal as scrollbar, vertical as scrollbar) + Use this method to set the scrollbars for the control. + + SymbolAtLine(index as integer) as DocumentSymbol + Returns the DocumentSymbol for the given line, nil if none. + + SymbolCount as Integer + Returns the number of symbols in the document. + + ToggleLineFold(lineIndex as integer) + Toggles the folding of the given line. + + Undo + Undoes the latest modification to the text. + + UnfoldAllLines + Unfolds all folded lines. + + XYAtCharPos(charPos as integer, byref X as integer, byref Y as integer) + Returns the X,Y onscreen coordinates of the given character at index 'charPos' + + + + + ********************************************************************************************************************************************** Properties: + AutoCloseBrackets as Boolean: + Automatically closes any (,[,{ whenever you type one. + + AutoIndentNewLines as Boolean: + Automatically indents new lines to match the indentation of the previous line. + + BackColor as Color: + Background color of the field. + + Border as Boolean: + Displays a border around the field. + + BorderColor as Color. + + CaretColor as Color. + + CaretPos as Integer: + Sets/gets the position of the caret. when selLength = 0, caretPos and selStart are the same. + + ClearHighlightedRangesOnTextChange as Boolean + If true all highlighted character ranges will be cleared when the text is changed, and before the TextChanged event is raised. You can re-calculate ranges in the textChanged event + and add them to the field, these will be redrawn on the next update which should be after the TextChanged event is handled. + + DirtyLinesColor As color + DisplayDirtyLines as Boolean + Shows modified/unsaved lines + + DisplayInvisibleCharacters as Boolean. + + DisplayLineNumbers as Boolean. + + DisplayRightMarginMarker as Boolean + + EnableAutocomplete as Boolean. + + GutterBackgroundColor as Color. + + GutterSeparationLineColor as Color. + + GutterWidth as integer. Returns the width size in pixels of the gutter. + + HighlightBlocksOnMouseOverGutter as Boolean + Turns on/off visual highlighting of code blocks when the mouse cursor is over the gutter + + HighlightMatchingBrackets as Boolean. + Highlights opening/closing brackets when you type them. + + LeftMarginOffset as Integer + Separation in pixels between the left edge of the control and the text + + LineNumbersColor as Color. + + MaxVisibleLines as Integer: + Returns the maximum number of visible lines. + + ReadOnly as Boolean + Makes the editfield editable or read-only + + RightMarginAtPixel as integer + Sets the location in pixels where the right margin marker is drawn. If set to 0, it will default to the defaul width for the default PrinterSetup. + + ScrollPosition as integer: + Sets/Gets the index of the first visible line in the field. (verticall scroll) + + ScrollPositionX as integer: + Sets/Gets the horizontal scroll position, in pixels. + + SelLength as integer: + length of the currently selected text + + SelStart as integer: + Starting index of the current selection. + + SelText as String: + Gets the currently selected text, replaces the currently selected text. + + SyntaxDefinition as HighlightDefinition + Syntax Highlight Definition used to highlight the contents of the editfield. If the definition is nil, the default text color is used to display the text. + + TabWidth as integer: + Gets/Sets the width (in spaces) of the Tab Character + + Text as String: + Gets/Sets the text for the field. + + TextColor as Color: + This is the default text color, used only when the syntaxDefinition is nil. + + TextFont as String: + The font used to display the text. + + TextHeight as Integer: + Returns the height of the text (line height) + + TextLength as Integer: + Returns the length of the text in the field. + + TextSelectionColor as Color + Gets/sets the color to use for text selection. if set to &c000000 the OS default color is used. + + TextSize as Integer: + The text size used to display text. + + ThickInsertionPoint as Boolean: + Determines wether the insertion point's width is 1 or 2 pixels + #tag EndNote + + #tag Note, Name = 4. Syntax Definitions + Quick and dirty guide on how to write a syntax definition. + If you want, send me your definitions and I'll make them available on my website. + + Basic structure: + + + + + + Definition Name + + + \{\s*(?:$|//|/\*)|\s*/\* + ^\s*\}|\s*\*/ + + + + + ^[ \t]*((?:public|private|protected)?[ \t]*class[^{]+) + + + ((?:public|private|protected)[ \t]+(?:(?:abstract|static|final|synchronized|native|strictfp)[ \t]+)*(?:[A-Za-z0-9_\-\.<>]+(?:\[\])?[ \t]+)?[A-Za-z0-9_\-]+[ \t]*\([^\)]*\)[ \t]*(?:\s*throws[ \t]*[A-Za-z0-9_\-, \t\.]+)?)\s*(?=\{?) + + + + + <#(.+?)#> + + + + + + + + (<[^>]*>) + + + + ("[^"><]*") + + + + ('[^'><]*') + + + + ([\w-]*)[ \t]*=(?=[ \t]*"[^"><]*") + + + + + + <!-- + --> + + + TODO + HACK + + + + + + #tag EndNote + + #tag Note, Name = 5. Issues and Bugs + To report issues go here: + + http://github.com/tempelmann/custom-editfield + #tag EndNote + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Third Party Classes/CustomEditField/LineManager/LineManager.xojo_code b/Third Party Classes/CustomEditField/LineManager/LineManager.xojo_code new file mode 100644 index 0000000..773b36f --- /dev/null +++ b/Third Party Classes/CustomEditField/LineManager/LineManager.xojo_code @@ -0,0 +1,1274 @@ +#tag Class +Class LineManager + #tag Method, Flags = &h1 + Protected Sub appendLine(segment as textline) + //append a line to the line array + lines.Append segment + + //mark it as changed + 'NotifyLineChangedRange(lines.Ubound, 1) + + //see if this one is longer than the previous longest + if segment.length > longestLineLength then + longestLineIndex = UBound(lines) + longestLineLength = segment.length + //and forward it to the delegate + NotifyMaxLineLengthChanged(longestLineIndex) + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function calcInvisibleLines() As integer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //clear the calculation caches + if neededCache <> nil then neededCache.Clear + if visibleCache <> nil then visibleCache.Clear + + //returns what is the logical pos of the given lineNumber + dim invisibleLines as Integer + + for i as Integer = 0 to Count - 1 + if not lines(i).visible then invisibleLines = invisibleLines + 1 + next + + Return invisibleLines + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub clear() + //clear lines and vars + RemoveLineSymbols(0, Count - 1) + ReDim lines(-1) + longestLineIndex = -1 + longestLineLength = 0 + lineEnding = chr(13) + currentInvisibleLines = -1 + mFirstLineForIndentation = 0 + mLastLineForIndentation = -1 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub clearDirtyLines() + dim line as TextLine + for each line in lines + line.isDirty = False + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(TextStorage as gapBuffer, TabWidth as integer) + //create generic eol segment + self.TextStorage = TextStorage + + //no need to find the longest line yet + needsLongestRescan = False + + //default the line endings to chr(13) + lineEnding = chr(13) + + self.TabWidth = TabWidth + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function createLines(lineInsertPoint as integer, offset as integer, length as integer, indent as Integer, lineContIdx as Integer, markDirty as Boolean = false) As integer + // This function creates TextLine objects from the textbuffer + + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + dim text as String = TextStorage.getText(offset, length) + dim insertedLines as integer + dim startSearchIndex as integer + dim ariLineLen() as integer + dim ariLineDelimeter() as integer + + FindLineLengths( text, ariLineLen, ariLineDelimeter ) + + dim lastDirtyLine as TextLine, wasLastDirtyBefore as Boolean + + for i as integer = 0 to ariLineLen.Ubound - 1 + dim segment as TextLine + segment = new TextLine(offset+startSearchIndex, ariLineLen(i), ariLineDelimeter(i), TabWidth, indent, lineContIdx) + startSearchIndex = startSearchIndex + ariLineLen(i) + + if markDirty then + lastDirtyLine = segment + wasLastDirtyBefore = lastDirtyLine.isDirty + lastDirtyLine.isDirty = true + end if + + //append/insert? + if lineInsertPoint + insertedLines > UBound(lines) then + AppendLine(segment) + else + insertLine(lineInsertPoint + insertedLines, segment) + end if + + insertedLines = insertedLines + 1 + next + + //trailing text + if startSearchIndex <= text.len then + if lineInsertPoint + insertedLines > UBound(lines) then + dim line as TextLine = new TextLine(offset + startSearchIndex, text.len - startSearchIndex, 0, TabWidth, indent, lineContIdx) + if markDirty then + lastDirtyLine = line + wasLastDirtyBefore = lastDirtyLine.isDirty + lastDirtyLine.isDirty = true + end if + appendLine(line) + insertedLines = insertedLines + 1 + end if + end if + + if lastDirtyLine <> nil then + // This deals with a special case: If we're called by CustomEditField.private_replace(), the first line gets deleted and then added here + // again. But if the insertion was a new line with a CR at the end, then this deleted/added line is only shifted down, but not actually + // changed. This code tries to deal with that by not marking it dirty. + lastDirtyLine.isDirty = wasLastDirtyBefore + end + + NotifyLineChangedRange(lineInsertPoint, insertedLines) + + Return insertedLines + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub FindLineLengths(sText as string, ariLineLen() as integer, ariDelimeterLen() as integer) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + dim prevLineEnding as String = lineEnding + lineEnding = "" // will be set below, by the first line delimiter that's encountered + + if sText <> "" then + + if sText.Encoding <> EditFieldGlobals.InternalEncoding then + // should not happen + break + sText = sText.ConvertEncoding(EditFieldGlobals.InternalEncoding) + end + + dim sTextCanonical as string = ReplaceLineEndings( sText, EndOfLine.UNIX ) + dim ars() as string = Split( sTextCanonical, EndOfLine.UNIX ) + dim iOffsetB as integer = 0 + + dim currentEncoding as TextEncoding = sText.Encoding + if currentEncoding = nil then currentEncoding = Encodings.ASCII + + //get the byte length for an EOL char... it could be > 1, e.g. in UTF-16 + dim EOLlen as Integer = currentEncoding.Chr(13).LenB + + for each sLine as string in ars + dim iLineLen as integer = sLine.Len + iOffsetB = iOffsetB + sLine.LenB + + dim c as integer = sText.MidB( iOffsetB + 1, EOLlen ).Asc + + if c = 13 then + iOffsetB = iOffsetB + EOLlen //move the offset by the already found marker + + if sText.MidB( iOffsetB + 1, EOLlen ).Asc = 10 then //windows + iLineLen = iLineLen + 2 + iOffsetB = iOffsetB + EOLlen + ariDelimeterLen.Append( 2 ) + if lineEnding = "" then lineEnding = Chr(13)+Chr(10) + + else //mac + iLineLen = iLineLen + 1 + ariDelimeterLen.Append( 1 ) + if lineEnding = "" then lineEnding = Chr(13) + + end if + + elseif c = 10 then //unix + iLineLen = iLineLen + 1 + iOffsetB = iOffsetB + EOLlen + ariDelimeterLen.Append( 1 ) + if lineEnding = "" then lineEnding = Chr(10) + + elseif c = 0 then + // end of text + ariDelimeterLen.Append( 0 ) + ariLineLen.Append( iLineLen ) + Exit + + else + Continue + end if + + ariLineLen.Append( iLineLen ) + next + + end if + + if lineEnding = "" then + // if text had no line delimiters we'll use the OS default + lineEnding = prevLineEnding + if lineEnding = "" then + #if TargetWin32 + lineEnding = EndOfLine.Windows + #else + // Mac nowadays uses Unix, not the old Chr(13), so does Linux + lineEnding = EndOfLine.Unix + #endif + end if + end if + + lineEnding = lineEnding.ConvertEncoding(EditFieldGlobals.InternalEncoding) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function findLineNumberForOffset(offset as integer) As integer + //binary search for the line that contains offset. + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if Count = 0 then Return -1 + + dim leftIndex as Integer + dim rightIndex as Integer = Count - 1 + + dim currLine as TextLine + + while leftIndex < rightIndex + dim pivot as Integer = (leftIndex + rightIndex) / 2 + + currLine = lines(pivot) + if offset < currLine.offset then + rightIndex = pivot - 1 + elseif offset > currLine.offset then + leftIndex = pivot + 1 + else + leftIndex = pivot + exit while + end if + wend + + if lines(leftIndex).offset > offset then Return leftIndex - 1 + Return leftIndex + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub fixOffsets(lineNumber as integer, startOffset as integer) + //fix all the offsets starting at the given line number, with the given offset + dim maxIndex as Integer = UBound(lines) + dim line as TextLine + + for i as Integer = lineNumber to maxIndex + line = lines(i) + line.offset = startOffset //set offset + startOffset = line.offset + line.length //add line length + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub foldAll() + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + for i as Integer = 0 to UBound(lines) + if lines(i).isBlockStart and not lines(i).folded then + call toggleLineFolding(i) + end if + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function getAttributesOfLinesInRange(offset as Integer, length as Integer) As TextLineAttributes() + dim attrs() as TextLineAttributes + for each line as TextLine in linesInRange (offset, length) + attrs.Append line.getAttributes + next + return attrs + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function getLine(index as integer) As TextLine + //get a given line + if index < 0 or index >= Count then Return nil + + Return lines(index) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function getLineNumberForOffset(offset as integer, length as integer = 0) As integer + //trivial case + if offset = 0 then Return 0 + + if offset = textLength + length then + //empty? + if Count = 0 then Return 0 + + //last line? + dim last as TextLine = lines(UBound(lines)) + if last.delimiterLength > 0 then Return Count + Return Count - 1 + end if + + //general case + Return findLineNumberForOffset(offset) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function getLongestLine() As textline + if longestLineIndex < 0 then Return nil + Return lines(longestLineIndex) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function getNumberOfAffectedLines(startLine as integer, offset as integer, length as integer) As integer + if length = 0 then Return 1 //same line + + dim target as Integer = offset + length + + dim line as TextLine = lines(startLine) + if line.delimiterLength = 0 then + //last line + Return 1 + end if + + if line.offset + line.length > Target then + Return 1 //within line + end if + + if line.offset + line.length = Target then + Return 2 //this and next + end if + + //general case + Return getLineNumberForOffset(Target) - startLine + 1 + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function getNumberOfLinesNeededToView(linesNeeded as integer) As integer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if invisibleLines = 0 then Return linesNeeded + if neededCache = nil then neededCache = new Dictionary + + //check cache + if neededCache.HasKey(linesNeeded) then Return neededCache.Value(linesNeeded) + + //returns the number of lines needed to display "lineNumber" visible lines. + dim visibleLines as Integer + + for i as Integer = 0 to Count - 1 + if lines(i).visible then visibleLines = visibleLines + 1 + + if visibleLines > linesNeeded then + neededCache.Value(linesNeeded) = i + Return i + end if + next + + neededCache.Value(linesNeeded) = Count - 1 + Return Count - 1 + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function getNumberOfVisibleLinesUpToLine(lineNumber as integer) As integer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if invisibleLines = 0 then Return lineNumber + if visibleCache = nil then visibleCache = new Dictionary + + //check cache + if visibleCache.HasKey(lineNumber) then Return visibleCache.Value(lineNumber) + + //returns the number of visible lines up to "lineNumber" + dim invisibleLines as Integer + + for i as Integer = 0 to lineNumber + if not lines(i).visible then invisibleLines = invisibleLines + 1 + + 'if i >= lineNumber then Return i - invisibleLines + next + 'Return Count - invisibleLines + visibleCache.Value(lineNumber) = lineNumber - invisibleLines + return lineNumber - invisibleLines + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub IndentationFinished() + mFirstLineForIndentation = lines.Ubound+1 + mLastLineForIndentation = -1 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub insert(offset as integer, text as string, alwaysMarkDirty as Boolean) + replace(offset, 0, text, alwaysMarkDirty) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub insertLine(index as integer, segment as textline) + //insert into line array + lines.Insert(index, segment) + + //mark it + 'NotifyLineChangedRange(index, 1) + + //see if new line is longer than previous longest + if segment.length > longestLineLength then + longestLineIndex = index + longestLineLength = segment.length + NotifyMaxLineLengthChanged(longestLineIndex) + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub LineIsIndented(lineIdx as Integer) + if lineIdx = mFirstLineForIndentation then + mFirstLineForIndentation = lineIdx + 1 + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub LineNeedsIndentation(lineIdx as Integer) + dim line as TextLine = getLine(lineIdx) + if line <> nil then + if lineIdx < mFirstLineForIndentation then + mFirstLineForIndentation = lineIdx + end if + if lineIdx > mLastLineForIndentation then + mLastLineForIndentation = lineIdx + end if + #if DebugBuild and EditFieldGlobals.DebugIndentation + System.DebugLog "InvalidateLine "+str(lineIdx)+", now: "+Str(mFirstLineForIndentation)+" to "+Str(mLastLineForIndentation) + #endif + line.NeedsIndentation = true + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function linesInRange(offset as Integer, length as Integer) As TextLine() + dim result() as TextLine + + dim idx as Integer = getLineNumberForOffset(offset, length) + if idx >= 0 and idx <= lines.Ubound then + dim line as TextLine = lines(idx) + length = length + (offset - line.Offset) // so that we cover the range from the start of the line + while length >= 0 + result.Append line + length = length - line.Length + idx = idx + 1 + if idx > lines.Ubound then + exit while + end if + line = lines(idx) + wend + end if + + return result + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LongestLineIdx() As Integer + Return longestLineIndex + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub MarkAllLinesAsChanged() + NotifyLineChangedRange(0, self.Count) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function nextBlockEndLine(forLine as integer, ignoreIfLineIsBlockStart as boolean = false) As integer + //Finds the next block end from this line, + //It accounts for nested blocks. + + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + #endif + + dim testLine as TextLine + testLine = getLine (forLine) + if testLine = nil then Return -1 + if not ignoreIfLineIsBlockStart and not testLine.isBlockStart then + Return -1 + end if + + dim forRule as Object = testLine.BlockStartRule + + dim depth as integer + dim idx, match, lastLine as Integer + + //search down + lastLine = UBound(lines) + match = -1 + for idx = forLine + 1 to lastLine + testLine = getLine(idx) + if testLine = nil then Continue for + + dim isBlkEnd as Boolean = testLine.isBlockEnd (forRule) + if depth = 0 and isBlkEnd then + //found it + match = idx + exit for + else + //nested + dim isBlkStart as Boolean = testLine.isBlockStart (forRule) + if isBlkStart and not isBlkEnd then + depth = depth + 1 + elseif isBlkEnd and not isBlkStart then //nested out + depth = depth - 1 + end if + end if + next + + Return match + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function nextVisibleLine(fromLine as integer) As integer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if invisibleLines = 0 then Return fromLine + + for i as Integer = fromLine to Count - 1 + if lines(i).visible then Return i + next + Return previousVisibleLine(fromLine) + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub NotifyLineChangedRange(startIndex as integer, length as Integer) + if length <= 0 then return + + #if DebugBuild + 'System.DebugLog "NotifyLineChangedRange("+Str(startIndex,"-#")+", "+Str(length,"-#")+")" + #endif + + dim msg as new Message(self, self) + msg.addInfo(1, LineChangedMsg) + msg.addInfo(2, startIndex) + msg.addInfo(3, length) + + MessageCenter.sendMessage(msg) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub NotifyLineCountChanged() + //notify changes + dim msg as new Message(self, self) + msg.addInfo(1, LineCountChangedMsg) + msg.addInfo(2, count) + MessageCenter.sendMessage(msg) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub NotifyMaxLineLengthChanged(longestLineIndex as integer) + //notify changes + dim msg as new Message(self, self) + msg.addInfo(1, MaxLineLengthChangedMsg) + msg.addInfo(2, longestLineIndex) + MessageCenter.sendMessage(msg) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function previousBlockStartLine(forLine as integer, ignoreIfLineIsBlockEnd as boolean = false) As integer + //Finds the previous block start from this line, + //It accounts for nested blocks. + + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + #endif + + dim testLine as TextLine + testLine = getLine (forLine) + if testLine = nil then Return - 1 + if not ignoreIfLineIsBlockEnd and not testLine.isBlockEnd then + Return -1 + end if + + dim forRule as Object = testLine.BlockEndRule + + dim depth as integer + dim idx, match as Integer + + match = -1 + for idx = forLine - 1 DownTo 0 + testLine = getLine(idx) + if testLine = nil then Continue for + + dim isBlkStart as Boolean = testLine.isBlockStart (forRule) + if depth = 0 and isBlkStart then + //found it + match = idx + exit for + else + //nested + dim isBlkEnd as Boolean = testLine.isBlockEnd (forRule) + if isBlkEnd and not isBlkStart then + depth = depth + 1 + elseif isBlkStart and not isBlkEnd then //out of inner block + depth = depth - 1 + end if + end if + next + + Return match + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function previousVisibleLine(fromLine as integer) As integer + //finds a the previous visible line from a given line. + + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if invisibleLines = 0 then Return fromLine + + for i as Integer = fromLine downto 0 + if lines(i).visible then Return i + next + Return -1 + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub remove(offset as integer, length as integer) + replace(offset, length, "", true) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub removeLine(index as integer) + RemoveLineSymbols(index, index) + lines.Remove(index) + //if removed line was the longest... we need to rescan + if index = longestLineIndex then needsLongestRescan = true + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub RemoveLineSlice(arr() As textline, fromIndex As Integer = 0, toIndex As Integer = 0) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + // Deletes a portion of the array. + // See "Slice Indexing" note. + // taken from Joe Strout's open source Array Utilities + // http://www.verex.com/opensource/ + + Dim ub As Integer = UBound( arr ) + if fromIndex < 0 then fromIndex = ub + 1 + fromIndex + if toIndex <= 0 then toIndex = ub + 1 + toIndex + + if fromIndex >= toIndex then return // empty (or invalid) range + RemoveLineSymbols(fromIndex, toIndex - 1) //used to be toIndex + if longestLineIndex >= fromIndex and longestLineIndex <= toIndex then needsLongestRescan = true + + // easy case: deleting the end of the array, we can just redim and be done + if toIndex - 1 = ub then + Redim arr( fromIndex - 1 ) + return + end if + + // another easy case: deleting just one element (equivalent to Array.Remove) + if fromIndex = toIndex - 1 then + arr.Remove fromIndex + return + end if + + // harder case: copy the data down, and THEN redim + Dim dest, src As Integer + dest = fromIndex + for src = toIndex to ub + arr(dest) = arr(src) + dest = dest + 1 + next + Redim arr( dest - 1 ) + return + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub RemoveLineSymbols(fromIndex as integer, toIndex as integer) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //look for symbols in the lines that are being removed. if found, signal the editfield to remove such symbols from its symbol table. + dim tmp as new Dictionary + + dim line as TextLine + for i as integer = fromIndex to toIndex + line = getLine(i) + if line = nil or line.LineSymbols = nil or line.LineSymbols.Count = 0 then Continue for + + 'for each key in line.LineSymbols.Keys + tmp.Value(line) = nil + 'next + next + + if tmp.Count = 0 then Return + + dim msg as new Message(self, self) + msg.addInfo(1, LineSymbolsRemovedMsg) + msg.addInfo(2, tmp) + + if toIndex - fromIndex <= 2 then + MessageCenter.sendMessage(msg) + else + MessageCenter.queueMessage(msg) + end if + + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub replace(offset as integer, length as integer, text as string, alwaysMarkDirty as Boolean) + // Replaces a chunk of text with "text" and length "length" at pos offset + // + // This method doesn't actually store the text (that's handled by TextStorage.replace), + // but only needs to read its length. + + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + dim lineNumber as Integer = getLineNumberForOffset(offset, length) + + if alwaysMarkDirty then + // mark line for needing indendation + LineNeedsIndentation lineNumber + end if + + //save original index as it may change + dim originalLine as Integer = lineNumber + + //old highlight context, in case the modified line has one + dim oldContext as HighlightContext + + dim lineCount as Integer = self.Count + + dim line as TextLine + if lineNumber <= lines.Ubound then + line = lines(lineNumber) + end if + + if length > 0 and line <> nil then //merge affected lines... + dim numberOfAffectedLines as Integer = getNumberOfAffectedLines(lineNumber, offset, length) + dim endLine as TextLine = lines(lineNumber + numberOfAffectedLines - 1) + + if numberOfAffectedLines > 1 and invisibleLines > 0 then + currentInvisibleLines = -1 + end if + + //save highlight context of last line + oldContext = endLine.Context + + line.length = endLine.offset + endLine.length - line.offset + line.delimiterLength = endLine.delimiterLength + removeLineSlice(lines, lineNumber + 1, lineNumber + numberOfAffectedLines) + line.InvalidateWords + end if + + if line = nil then + // ignore + elseif length = 0 and text.len = 1 and text <> lineEnding then //simple keystroke + RemoveLineSymbols(lineNumber, lineNumber) + line.length = line.length + 1 //make line longer by one char + fixOffsets(lineNumber + 1, line.offset + line.length) //fix the offsets + line.InvalidateWords + + //mark it + NotifyLineChangedRange(lineNumber, 1) + + //see if it's now the longestLine + if line.length > longestLineLength then + longestLineIndex = lineNumber + longestLineLength = line.length + NotifyMaxLineLengthChanged(longestLineIndex) + end if + + elseif length = 1 and Text.len = 0 then //simple delete + RemoveLineSymbols(lineNumber, lineNumber) + line.length = line.length - 1 //make smaller by one char + fixOffsets(lineNumber + 1, line.offset + line.length) //fix offsets + line.InvalidateWords + + //mark it + NotifyLineChangedRange(lineNumber, 1) + + //if this was the longest line, do a rescan + if lineNumber = longestLineIndex then needsLongestRescan = true + + else //every other case... long text, enters... + 'first, unfold line if folded, and we're adding to the end of the line... + if line.folded and offset = line.offset + line.length - line.delimiterLength then + call toggleLineFolding(originalLine) + end if + + //Line now contains a merged line composing all affected lines. + dim delta as Integer = line.length - length + text.len + + //remove merged line, and start parsing/inserting. + removeLine(lineNumber) + dim insertedLines as Integer = createLines(lineNumber, line.offset, delta, line.indent, line.isContinuedFromLine, alwaysMarkDirty) + lines(lineNumber).AdoptLine line + lineNumber = lineNumber + insertedLines + + //fix offsets starting from the last modified line. + line = lines(max(lineNumber - 1,0)) + if delta <> 0 then fixOffsets(lineNumber, line.offset + line.length) + + //reset context if any. + if oldContext <> nil then + lines(originalLine).Context = oldContext + NotifyLineChangedRange(originalLine, 1) + end + + end if + + //rescan lengths if necessary + if needsLongestRescan then rescanLengths + + //fire LineChangedDelegate if any + if lineCount <> count then + NotifyLineCountChanged + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub rescanLengths() + //find the longest line. + longestLineIndex = -1 + longestLineLength = -1 + + #pragma DisableBackgroundTasks + + dim line as TextLine + dim idx as Integer + + for Each line in lines + if line.length > longestLineLength then + longestLineLength = line.length + longestLineIndex = idx + end if + idx = idx + 1 + next + + needsLongestRescan = False + if longestLineIndex < 0 then Return + NotifyMaxLineLengthChanged(longestLineIndex) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub revealLine(lineNumber as integer) + //expand all invisible lines starting at linenumber, up + dim current as TextLine + for i as Integer = lineNumber DownTo 0 + current = lines(i) + if current.folded then call toggleLineFolding(i) + if current.visible then Return + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub setAttributesOfLinesInRange(offset as Integer, length as Integer, attrs() as TextLineAttributes) + dim lineCount as Integer + for each line as TextLine in linesInRange (offset, length) + line.setAttributes attrs(lineCount) + lineCount = lineCount + 1 + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub setText(length as Integer) + Clear + call createLines(0, 0, length, 0, -1) + NotifyLineCountChanged + NotifyMaxLineLengthChanged(longestLineIndex) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function toggleLineFolding(lineNumber as integer) As integer + dim line as TextLine = getLine(lineNumber) + if line = nil then Return -1 + + //check if line is a block end/start + if not line.isBlockStart and not line.isBlockEnd then + Return -1 + end if + + //now, find the range... + dim testLine as TextLine + dim idx, match as Integer + dim forRule as Object + + match = -1 + if line.isBlockStart then + //search down + match = nextBlockEndLine(lineNumber) + + if match < 0 then + Return -1 + end if + + forRule = line.BlockStartRule + + else + //search up + match = previousBlockStartLine(lineNumber) + + if match < 0 then + Return -1 + end if + + forRule = line.BlockEndRule + + //toggle lines' visibility + line = getLine(match) + dim tmp as Integer + tmp = match + match = lineNumber + lineNumber = tmp + end if + + line.folded = not line.folded + dim targetState as Boolean = not line.folded + + dim lineStack() as Boolean + lineStack.Append not line.folded + + for idx = lineNumber + 1 to match + testLine = lines(idx) + if targetState = false then //we're making everything invisible, we don't care + testLine.visible = false + else + //we have to check for parentStates! + testLine.visible = lineStack(UBound(lineStack)) + + dim isBlkStart as Boolean = testLine.isBlockStart (forRule) + dim isBlkEnd as Boolean = testLine.isBlockEnd (forRule) + + if isBlkStart and not isBlkEnd then + lineStack.Append(not testLine.folded and lineStack(UBound(lineStack))) + + ElseIf isBlkEnd and not isBlkStart then + call lineStack.Pop + + End if + end if + next + + //force recalculation of invisibleLines + currentInvisibleLines = -1 + NotifyLineCountChanged + Return lineNumber + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub unfoldAll() + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //expand all foldings + dim lines as Integer = Count - invisibleLines + + //make everything visible + for i as Integer = 0 to Count - 1 + if lines(i).folded then lines(i).folded = False + lines(i).visible = true + next + currentInvisibleLines = -1 + + if lines <> Count - invisibleLines then NotifyLineCountChanged + End Sub + #tag EndMethod + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return UBound(lines) + 1 + End Get + #tag EndGetter + Count As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected currentInvisibleLines As Integer = -1 + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mFirstLineForIndentation + End Get + #tag EndGetter + FirstLineForIndentation As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if currentInvisibleLines < 0 then + currentInvisibleLines = calcInvisibleLines + end if + Return currentInvisibleLines + End Get + #tag EndGetter + invisibleLines As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mLastLineForIndentation + End Get + #tag EndGetter + LastLineForIndentation As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h0 + lineEnding As string + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lines() As TextLine + #tag EndProperty + + #tag Property, Flags = &h0 + linesLock As CriticalSection + #tag EndProperty + + #tag Property, Flags = &h1 + Protected longestLineIndex As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected longestLineLength As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mFirstLineForIndentation As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mLastLineForIndentation As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mTabwidth As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected neededCache As dictionary + #tag EndProperty + + #tag Property, Flags = &h1 + Protected needsLongestRescan As boolean = false + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mTabwidth + End Get + #tag EndGetter + #tag Setter + Set + mTabwidth = value + + dim line as TextLine + for each line in lines + line.TabWidth = value + next + End Set + #tag EndSetter + TabWidth As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + Return TextStorage.Length + End Get + #tag EndGetter + Protected TextLength As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected TextStorage As gapBuffer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected tmpDefinition As highlightdefinition + #tag EndProperty + + #tag Property, Flags = &h1 + Protected visibleCache As dictionary + #tag EndProperty + + + #tag Constant, Name = LineChangedMsg, Type = Double, Dynamic = False, Default = \"2", Scope = Public + #tag EndConstant + + #tag Constant, Name = LineCountChangedMsg, Type = Double, Dynamic = False, Default = \"1", Scope = Public + #tag EndConstant + + #tag Constant, Name = LineSymbolsRemovedMsg, Type = Double, Dynamic = False, Default = \"4", Scope = Public + #tag EndConstant + + #tag Constant, Name = MaxLineLengthChangedMsg, Type = Double, Dynamic = False, Default = \"3", Scope = Public + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Count" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="FirstLineForIndentation" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="invisibleLines" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LastLineForIndentation" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="lineEnding" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="TabWidth" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/LineManager/LinesLock.xojo_code b/Third Party Classes/CustomEditField/LineManager/LinesLock.xojo_code new file mode 100644 index 0000000..bc3c05f --- /dev/null +++ b/Third Party Classes/CustomEditField/LineManager/LinesLock.xojo_code @@ -0,0 +1,70 @@ +#tag Class +Protected Class LinesLock + #tag Method, Flags = &h0 + Sub Constructor(cef as CustomEditField) + // acquire a lock on the LineManager + + dim lineMgr as LineManager = cef.private_lines + + if lineMgr.linesLock = nil then + lineMgr.linesLock = new CriticalSection + end if + + lineMgr.linesLock.Enter + + mLineMgr = lineMgr + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub Destructor() + // Release the lock + if mLineMgr <> nil then + mLineMgr.linesLock.Leave + end if + End Sub + #tag EndMethod + + + #tag Property, Flags = &h21 + Private mLineMgr As LineManager + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/LineManager/TextLine.xojo_code b/Third Party Classes/CustomEditField/LineManager/TextLine.xojo_code new file mode 100644 index 0000000..cfea3eb --- /dev/null +++ b/Third Party Classes/CustomEditField/LineManager/TextLine.xojo_code @@ -0,0 +1,1132 @@ +#tag Class +Class TextLine +Inherits TextSegment + #tag Event + Sub LengthChanged() + isDirty = true + End Sub + #tag EndEvent + + + #tag Method, Flags = &h21 + Private Sub addWord(word as textsegment) + if word.length = 0 then Return + + words.Append(word) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub AdoptLine(from as TextLine) + // If a line gets updated, a new TextLine object is often created. + // Then this method is called so that user-settable properties can + // be carried over. + + mLineAttributes = from.mLineAttributes + + mIndentationStateIn = from.mIndentationStateIn + mIndentationStateOut = from.mIndentationStateOut + mChangedIndentState = from.mChangedIndentState + mIsBlkStart = from.mIsBlkStart + mIsBlkEnd = from.mIsBlkEnd + mBlockIndent = from.mBlockIndent + mBlockStartRule = from.mBlockStartRule + mBlockEndRule = from.mBlockEndRule + isContinuedFromLine = from.isContinuedFromLine + + InvalidateWords() + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub AppendHighlightedWords(storage() as charSelection, lineIndex as integer) + dim word as TextSegment + + for each Word in words + if Word.hasBackgroundColor then + dim tmp as new CharSelection(self.offset + word.offset, word.length, lineIndex, lineIndex, word.backgroundColor) + storage.Append(tmp) + end if + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function BlockEndRule() As Object + return mBlockEndRule + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function BlockStartRule() As Object + return mBlockStartRule + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ChangedIndentStateAndReset() As Boolean + dim b as Boolean = mChangedIndentState + mChangedIndentState = false + return b + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function CharToDisplayAt(buffer as gapbuffer, index as integer, displayInvisible as boolean) As string + dim theChar as String = buffer.getCharAt(self.offset + index) + + select case theChar + case " " + return " " + + case chr(9) + return TABCHAR + + case chr(10), chr(13) + if displayInvisible then Return VISIBLEEOLCHAR + return "" + + else + Return theChar + + end select + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(offset as integer, length as integer, delimiterLength as integer, tabWidth as integer, indent as Integer, lineContIdx as Integer) + visible = true + folded = False + mLineAttributes = new TextLineAttributes + super.Constructor(offset, length) + self.delimiterLength = delimiterLength + self.TabWidth = tabWidth + self.indent = indent + self.isContinuedFromLine = lineContIdx + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function getAttributes() As TextLineAttributes + return mLineAttributes + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function HasPlaceholders() As Boolean + Return Placeholders.Ubound > -1 + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Highlight(definition as highlightdefinition, storage as gapBuffer, forcedContext as highlightcontext = nil, defaultColor as color = &c0) As highlightcontext + #pragma DisableBackgroundTasks + + + ReDim Words(-1) + ReDim placeholders(-1) + LineSymbols = nil + highlighted = False + + if me.length = 0 then + Return nil + end + + if definition = nil then + ParseLine(storage, DefaultColor) + Return nil + end if + + dim myText as String = storage.getText(offset, length) + + //run the highlighter, using this line as input and adding an extra EOL to make sure the definition matches EOLs + Context = definition.highlight(myText+chr(13), words, placeholders, forcedContext) + + //we added an extra eol, remove it. + words.Remove(UBound(words)) + + LineSymbols = definition.ScanSymbols(myText) + + //if the highlighter returned a non nil context, we need to inform the Highlight thread, so it Highlights the next line with the current context. + highlighted = true + Return Context + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub InvalidateWords() + redim words(-1) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function isBlockEnd(forRule as Object = nil) As Boolean + return mIsBlkEnd and (forRule = nil or forRule = mBlockEndRule) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function isBlockStart(forRule as Object = nil) As Boolean + return mIsBlkStart and mBlockIndent <> 0 and (forRule = nil or forRule = mBlockStartRule) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LocalSegmentForXPos(xpos as integer) As textsegment + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + dim word as TextSegment + dim sum as Single + + for Each Word in words + if xpos >= sum and xpos <= sum + Word.width then + exit for + end if + sum = sum + Word.width + next + + if Word = nil then Return nil //no words? + + //the desired xpos is within word. + Return Word + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function NextPlaceholderFromOffset(offset as Integer) As TextPlaceholder + if not self.HasPlaceholders then Return nil + + for each placeholder as TextPlaceholder in Placeholders + if Placeholder.offset + self.offset > offset then Return Placeholder + next + Return nil + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Paint(storage as gapBuffer, g as graphics, x as double, y as double, defaultColor as color, displayInvisible as boolean, selStart as integer, selLength as integer, showLeadingSpace as boolean, indentVisually as Boolean) + //draws this line + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //if there are no words in this line, we need to at least quick-parse it so we can display it. + if words.Ubound < 0 and length > 0 then + ParseLine(storage, defaultColor) + end if + + if indentVisually then + x = self.indent + x + end if + + width = 0 + dim text as String + dim word as TextSegment + dim wordFound as Boolean + dim darkerHilightColor as Color = HighlightColor.darkerColor(50) + + //paint tokens + for i as Integer = 0 to words.Ubound + Word = words(i) + g.ForeColor = darkerHilightColor + + dim selfWordOfs as Integer = word.Offset + self.Offset + + if word.TYPE = TYPE_SPACE then + text = " " + if not showLeadingSpace and not wordFound then Continue for + + ElseIf Word.TYPE = TYPE_TAB then + text = TABCHAR + if not showLeadingSpace and not wordFound then Continue for + + elseif Word.TYPE = TYPE_EOL then + if displayInvisible then + text = VISIBLEEOLCHAR + else + text = ""' + end if + if not showLeadingSpace and not wordFound then Continue for + + elseif word.TYPE = TYPE_PLACEHOLDER then + wordFound = true + //Highlight color + if highlighted then + g.ForeColor = word.textColor + else + g.ForeColor = defaultColor + end if + // DrawString can't handle UTF-32 (on OSX, at least) + text = storage.getText(TextPlaceholder(word).textRange.offset + offset, TextPlaceholder(word).textRange.length).ConvertEncoding(Encodings.UTF8) + text = PlaceholderPaddingString + text + PlaceholderPaddingString + + else + wordFound = true + //Highlight color + if highlighted then + g.ForeColor = word.textColor + else + g.ForeColor = defaultColor + end if + text = storage.getText(selfWordOfs, word.length) + + // convert Chr(1), which we use for original NUL chars, to a "NUL" character for display + text = text.ReplaceAll (Chr(1), "␀") + end if + + g.Bold = Word.bold or bold + g.Underline = Word.underline or underline + g.Italic = word.italic or italic + + //cache width + if word.lastFont <> g.TextFont or word.lastSize <> g.TextSize or showInvisible <> displayInvisible then + word.width = -1 + end if + if word.width < 0 then + word.width = g.StringWidth(text) + end if + + //draw txt + if (word.Type = TYPE_WORD or word.Type = TYPE_PLACEHOLDER or displayInvisible) and x + word.width >= 0 and x < g.Width and y >= 0 and y <= g.Height + g.TextHeight then + if Word.TYPE = TYPE_TAB then + Text = VISIBLETABCHAR //a small hack to make the visible char the same width as the tab + ElseIf Word.TYPE = TYPE_SPACE then + text = VISIBLESPACECHAR + end if + + if word.TYPE = TYPE_PLACEHOLDER then + dim oldc as color = g.ForeColor + dim colorOffset as Integer = 0 + + //make darker if placeholder is in selection... + if selfWordOfs >= selStart and selfWordOfs + Word.length <= selStart + selLength then + colorOffset = 50 + oldc = oldc.invertColor + end if + + g.ForeColor = TextPlaceholder(word).placeholderBackgroundColor.darkerColor(colorOffset) + g.FillRoundRect x, y - g.TextAscent, word.width, g.TextHeight + 1, g.TextHeight, g.TextHeight + + g.ForeColor = TextPlaceholder(word).placeholderBackgroundColor.darkerColor(30).darkerColor(colorOffset) + g.DrawRoundRect x, y - g.TextAscent, word.width, g.TextHeight + 1, g.TextHeight, g.TextHeight + + g.ForeColor = oldc + end if + + #if TargetMacOS + // draw the word normally + g.DrawString text, x, y + + #else + // On Windows and Linux, selection colors are usually rather dark, which + // in turn requires the text to be shown in white when it's selected. + + if _ + selLength > 0 and _ + (selStart < selfWordOfs + word.Length) and _ // does selection start before end of this word? + (selStart + selLength > selfWordOfs) then // does selection end after start of this word? + // if part of the text is selected, we need to split it up into the selected and unselected part, + // and draw them in different colors + + dim l1, l2, l3 as Integer, t1, t2, t3 as String, w as Double + dim normalColor as Color = g.ForeColor + + // part before selection + l1 = Max (0, selStart - selfWordOfs) + // part inside selection + l2 = selLength - Max (0, selfWordOfs - selStart) + // part past selection + l3 = word.Length - (l1 + l2) + + t1 = text.Left (l1) + t2 = text.Mid (l1+1, l2) + t3 = text.Mid (l1+l2+1) + + if t1 <> "" then + g.DrawString t1, x, y + w = g.StringWidth(t1) + end if + g.ForeColor = &cFFFFFF // white + g.DrawString t2, x+w, y + if t3 <> "" then + w = g.StringWidth(t1+t2) + g.ForeColor = normalColor + g.DrawString t3, x+w, y + end if + else + // draw the word normally + g.DrawString text, x, y + end if + #endif + + end if + + x = x + word.Width + width = width + Word.width + word.lastFont = g.TextFont + word.lastSize = g.TextSize + next + + //ellipsis image. + if folded then + dim img as Picture = EditFieldGlobals.BlockFoldedTrailImage + g.DrawPicture img, x + 3, y - g.TextAscent + (g.TextHeight - img.Height) / 2 + 1 + end if + showInvisible = displayInvisible + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ParseLine(buffer as gapBuffer, defaultColor as color) + //quick parses a line, splitting it using spaces and tabs + // http://support.realsoftware.com/listarchives/gettingstarted/2005-05/msg00157.html + + ReDim Words(-1) + dim text as String = buffer.getText(offset, length) + if text.Encoding <> nil then text = text.ConvertEncoding(EditFieldGlobals.InternalEncoding) + + static scanner as new RegEx ' let's make this static to avoid hard crashes on OS X with RB 2012r2.1 + + scanner.SearchPattern = "[ ]|\t|\x0A|(?:\x0D\x0A?)" + + dim match as RegExMatch + dim offset, length, subStart as Integer + dim char as String + + #if DebugBuild + dim tmp as String + #endif + + match = scanner.Search(text) + while match <> nil + subStart = text.Leftb(match.SubExpressionStartB(0)).len + + length = substart - offset + //everything from last position up to this whitespace + addWord new TextSegment(offset, length, TextSegment.TYPE_WORD, defaultColor) + #if DebugBuild + tmp = buffer.getText(offset + self.offset, length) + #endif + offset = substart + match.SubExpressionString(0).len + + //sort out the whitespace + char = match.SubExpressionString(0) + #if DebugBuild + tmp = char + tmp = buffer.getText(substart + self.offset, match.SubExpressionString(0).len) + #endif + + select case char + case chr(9) //tab + addWord new TextSegment(substart, match.SubExpressionString(0).len, TextSegment.TYPE_TAB, defaultColor) + case " "//space + addWord new TextSegment(substart, match.SubExpressionString(0).len, TextSegment.TYPE_SPACE, defaultColor) + case chr(10), chr(13), chr(13) + chr(10)//eol + addWord new TextSegment(substart, match.SubExpressionString(0).len, TextSegment.TYPE_EOL, defaultColor) + end select + + match = scanner.Search + wend + + //add any trailing text + if offset< text.len then + addWord new TextSegment(offset, text.len - offset, TextSegment.TYPE_WORD, defaultColor) + #if DebugBuild + tmp = text.Mid(offset + 1) + #endif + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function PlaceholderForOffset(offset as Integer) As TextPlaceholder + for each placeholder as TextPlaceholder in Placeholders + if Placeholder.inRange(offset - self.offset) then Return Placeholder + next + Return nil + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function PrinterPaint(storage as gapBuffer, g as graphics, x as double, y as double, w as integer, defaultColor as color, displayInvisible as boolean, wrap as boolean, indentVisually as Boolean) As integer + //draws this line + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + dim ox, lines as Integer + ox = x + lines = 1 + + //if there are no words in this line, we need to at least quick-parse it so we can display it. + if UBound(Words) < 0 and length > 0 then + ParseLine(storage, defaultColor) + end if + + dim words() as TextSegment + + width = 0 + dim text as String + dim word as TextSegment + + //make copies of tokens, in case we need to split + for each word in self.words + words.Append word.Clone + next + + //paint tokens + for i as Integer = 0 to UBound(Words) + Word = words(i) + g.ForeColor = HighlightColor.darkerColor(50) + + if word.TYPE = TYPE_SPACE then + text = " " + + ElseIf Word.TYPE = TYPE_TAB then + text = TABCHAR + + elseif Word.TYPE = TYPE_EOL then + if displayInvisible then + text = VISIBLEEOLCHAR + else + text = "" + end if + + elseif word.TYPE = TYPE_PLACEHOLDER then + //Highlight color + if highlighted then + g.ForeColor = word.textColor + else + g.ForeColor = defaultColor + end if + text = PlaceholderPaddingString + storage.getText(TextPlaceholder(word).textRange.offset + offset, TextPlaceholder(word).textRange.length) + PlaceholderPaddingString + + else + //Highlight color + if highlighted then + g.ForeColor = word.textColor + else + g.ForeColor = defaultColor + end if + text = storage.getText(word.offset + offset, word.length) + end if + + g.Bold = Word.bold or bold + g.Underline = Word.underline or underline + g.Italic = word.italic or italic + + //cache width + if word.lastFont <> g.TextFont or word.lastSize <> g.TextSize or showInvisible <> displayInvisible then + word.width = -1 + end if + if word.width < 0 then + word.width = g.StringWidth(text) + end if + + //text wrap + if x + word.width > ox + w then + //split word? + if Word.width > w then + dim idx as Integer + for idx = 1 to word.length + if g.StringWidth(storage.getText(word.offset + offset, idx)) >= w then exit for + next + idx = idx - 1 + dim tmp as TextSegment = word.SplitAtLength(idx) + if wrap then words.Insert i + 1, tmp + i = i - 1 + Continue for + elseif wrap then + y = y + g.TextHeight + x = ox + lines = lines + 1 + else //just clip + Return lines + end if + end if + + //draw background, if any + if word.hasBackgroundColor and word.Type <> word.TYPE_EOL then + dim oc as Color = g.ForeColor + g.ForeColor = word.backgroundColor + g.FillRect ceil(x), y - g.TextAscent, ceil(word.width), g.TextHeight + 1 + g.ForeColor = oc + end if + + //draw txt + if (word.Type = TYPE_WORD or word.Type = TYPE_PLACEHOLDER or displayInvisible) and x + word.width >= 0 and x < g.Width and y >= 0 and y <= g.Height + g.TextHeight then + if Word.TYPE = TYPE_TAB then + Text = VISIBLETABCHAR //a small hack to make the visible char the same width as the tab + ElseIf Word.TYPE = TYPE_SPACE then + text = VISIBLESPACECHAR + end if + + if word.TYPE = TYPE_PLACEHOLDER then + dim oldc as color = g.ForeColor + g.ForeColor = TextPlaceholder(word).placeholderBackgroundColor + g.fillRoundRect x, y - g.TextAscent, word.width, g.TextHeight + 1, g.TextHeight, g.TextHeight + + g.ForeColor = TextPlaceholder(word).placeholderBackgroundColor.darkerColor(30) + g.DrawRoundRect x, y - g.TextAscent, word.width, g.TextHeight + 1, g.TextHeight, g.TextHeight + g.ForeColor = oldc + end if + + dim x2 as Integer = x + if indentVisually then + x2 = self.indent + x2 + end if + g.DrawString text, x2, y + end if + + x = x + word.Width + width = width + Word.width + word.lastFont = g.TextFont + word.lastSize = g.TextSize + next + + showInvisible = displayInvisible + Return lines + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub setAttributes(attr as TextLineAttributes) + mLineAttributes = attr + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub TabWidth(assigns value as integer) + TABCHAR = "" + for i as Integer = 1 to value + TABCHAR = TABCHAR + " " + next + + dim word as TextSegment + for each Word in words + word.width = -1 + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function TextWidth(buffer as gapBuffer, g as graphics, displayInvisible as boolean, length as integer = - 1) As Single + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //gets the text to be displayed by this line. + if length = 0 then Return 0 + if length < 0 then + length = self.length + + if self.lastFont <> g.TextFont or self.lastSize <> g.TextSize then width = 0 + if TotalWidth > 0 then Return TotalWidth + end if + + self.lastFont = g.TextFont + self.lastSize = g.TextSize + + //unparsed? return the raw text + if UBound(words) < 0 then + ParseLine(buffer, &c00) + if UBound(words) < 0 then + Return g.StringWidth(buffer.getText(self.offset, self.length).ConvertEncoding(Encodings.UTF8)) ' StringWidth can't handle UTF-32 (on OSX, at least) + end if + end if + + dim ret as Single + dim word as TextSegment + dim charsToRead as Integer + dim text as String + dim u as Integer = UBound(words) + + for i as integer = 0 to u + Word = words(i) + + charsToRead = word.length + select case word.TYPE + case TextSegment.TYPE_SPACE + text = " " + + case TextSegment.TYPE_TAB + text = TABCHAR + + case TextSegment.TYPE_EOL + if displayInvisible then + text = " " + else + text = "" + end if + + case TextSegment.TYPE_PLACEHOLDER + text = PlaceholderPaddingString + buffer.getText(TextPlaceholder(word).textRange.offset + offset, TextPlaceholder(word).textRange.length) + PlaceholderPaddingString + + else + //find out the number of chars to read in the current token + if word.offset + word.length > length then + 'charsToRead = word.length + 'else + charsToRead = length - word.offset + end if + + text = buffer.getText(word.offset + self.offset, charsToRead) + end select + + if word.lastFont <> g.TextFont or word.lastSize <> g.TextSize then + word.width = -1 + end if + + if word.width < 0 or charsToRead < word.length then + //measure + g.Bold = word.bold + g.Italic = Word.italic + g.Underline = Word.underline + ret = ret + g.StringWidth(text.ConvertEncoding(Encodings.UTF8)) ' StringWidth can't handle UTF-32 (on OSX, at least) + else + ret = ret + word.width + end if + + word.lastFont = g.TextFont + word.lastSize = g.TextSize + if word.offset + Word.length >= length then exit for + next + + Return ret + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function TotalWidth() As integer + Return width + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub UpdateIndentationState(definition as highlightdefinition, prevIndentationState as String, myText as String) + // TODO: Maybe optimize this so that it doesn't need to perform the Regex checks + // every time. E.g, if neither the mIndentationState nor the text changed, then + // there's no need to update. + // However, how to I tell if the text has changed? I shouldn't buffer each line here, that might + // be wasteful. + + if mIndentationStateIn <> prevIndentationState then + mIndentationStateIn = prevIndentationState + mIndentationStateOut = prevIndentationState + mChangedIndentState = true + end + + if me.length = 0 then + mBlockIndent = 0 + mIsBlkStart = false + mIsBlkEnd = false + mBlockStartRule = nil + mBlockEndRule = nil + Return + end + + dim newState as String + mBlockIndent = definition.isBlockStart(myText, mIndentationStateIn, newState, mBlockStartRule) + if newState <> mIndentationStateOut then + mIndentationStateOut = newState + mChangedIndentState = true + end + + mIsBlkEnd = definition.isBlockEnd(myText, newState, newState, mBlockEndRule) + if newState <> mIndentationStateOut then + mIndentationStateOut = newState + mChangedIndentState = true + end + + mIsBlkStart = mBlockIndent <> 0 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function VisualIndent(isIndentedVisually as Boolean) As Integer + if isIndentedVisually then + return self.indent + end if + End Function + #tag EndMethod + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mBlockIndent + End Get + #tag EndGetter + blockIndentation As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h0 + Context As highlightContext + #tag EndProperty + + #tag Property, Flags = &h0 + delimiterLength As Integer + #tag EndProperty + + #tag Property, Flags = &h0 + folded As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected highlighted As boolean + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mLineAttributes.Icon + End Get + #tag EndGetter + #tag Setter + Set + mLineAttributes.Icon = value + End Set + #tag EndSetter + icon As Picture + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mIndent + End Get + #tag EndGetter + #tag Setter + Set + if value < 0 then break + mIndent = value + NeedsIndentation = false + End Set + #tag EndSetter + indent As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mIndentationStateIn + End Get + #tag EndGetter + IndentationStateIn As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mIndentationStateOut + End Get + #tag EndGetter + IndentationStateOut As String + #tag EndComputedProperty + + #tag Property, Flags = &h0 + isContinuedFromLine As Integer = -1 + #tag EndProperty + + #tag Property, Flags = &h0 + isDirty As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + LineSymbols As dictionary + #tag EndProperty + + #tag Property, Flags = &h0 + lock As CriticalSection + #tag EndProperty + + #tag Property, Flags = &h21 + Private mBlockEndRule As Object + #tag EndProperty + + #tag Property, Flags = &h21 + Private mBlockIndent As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mBlockStartRule As Object + #tag EndProperty + + #tag Property, Flags = &h21 + Private mChangedIndentState As Boolean = true + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIndent As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIndentationStateIn As String + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIndentationStateOut As String + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIsBlkEnd As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mIsBlkStart As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + #tag Note + Holds values that belong to a line as the user sees it, and + are controlled by the code using this Editfield rather than needed + for the Editfield's management. + + An example of this is the icon that can be assigned to a line. + #tag EndNote + Private mLineAttributes As TextLineAttributes + #tag EndProperty + + #tag Property, Flags = &h0 + NeedsIndentation As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private placeholders() As TextPlaceholder + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return width + End Get + #tag EndGetter + ScreenWidth As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected showInvisible As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected TABCHAR As String + #tag EndProperty + + #tag Property, Flags = &h0 + visible As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected words() As textsegment + #tag EndProperty + + + #tag Constant, Name = PlaceholderPaddingString, Type = String, Dynamic = False, Default = \" ", Scope = Private + #tag EndConstant + + #tag Constant, Name = VISIBLEEOLCHAR, Type = String, Dynamic = False, Default = \"\xC2\xB6", Scope = Protected + #tag EndConstant + + #tag Constant, Name = VISIBLESPACECHAR, Type = String, Dynamic = False, Default = \"\xC2\xB7", Scope = Protected + #tag EndConstant + + #tag Constant, Name = VISIBLETABCHAR, Type = String, Dynamic = False, Default = \"", Scope = Protected + #Tag Instance, Platform = Mac OS, Language = Default, Definition = \"\xE2\x9E\x9D" + #Tag Instance, Platform = Windows, Language = Default, Definition = \"\xC2\xBB" + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="BackgroundColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="blockIndentation" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="bold" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DebugDescription" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="delimiterLength" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="EndOffset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="folded" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackgroundColor" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="icon" + Group="Behavior" + InitialValue="0" + Type="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="ID" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="indent" + Group="Behavior" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="IndentationStateIn" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="IndentationStateOut" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="isContinuedFromLine" + Group="Behavior" + InitialValue="-1" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="isDirty" + Group="Behavior" + InitialValue="false" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="italic" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="length" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="NeedsIndentation" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="offset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ScreenWidth" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="TextColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Type" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="underline" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="visible" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="width" + Group="Behavior" + InitialValue="0" + Type="double" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/LineManager/TextLineAttributes.xojo_code b/Third Party Classes/CustomEditField/LineManager/TextLineAttributes.xojo_code new file mode 100644 index 0000000..aa25881 --- /dev/null +++ b/Third Party Classes/CustomEditField/LineManager/TextLineAttributes.xojo_code @@ -0,0 +1,55 @@ +#tag Class +Protected Class TextLineAttributes + #tag Note, Name = About + This class shall store the Textline properties that are to be kept when a line + gets edited or deleted and then restored by Undo. + #tag EndNote + + + #tag Property, Flags = &h0 + Icon As Picture + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Icon" + Group="Behavior" + Type="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/LineManager/TextPlaceholder.xojo_code b/Third Party Classes/CustomEditField/LineManager/TextPlaceholder.xojo_code new file mode 100644 index 0000000..c27ca12 --- /dev/null +++ b/Third Party Classes/CustomEditField/LineManager/TextPlaceholder.xojo_code @@ -0,0 +1,169 @@ +#tag Class +Protected Class TextPlaceholder +Inherits TextSegment + #tag Method, Flags = &h0 + Function Clone() As textsegment + dim tmp as new TextPlaceholder(offset, length, textRange.offset, textRange.length, self.textColor, backgroundColor, bold, italic, underline) + tmp.lastFont = lastFont + tmp.lastSize = lastSize + tmp.Type = TYPE + tmp.width = width + tmp.placeholderBackgroundColor = placeholderBackgroundColor + + Return tmp + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(offset as integer, length as integer, labelOffset as integer, labelLength as Integer, highlightColor as color, backgroundColor as color, bold as boolean = false, italic as boolean = false, underline as boolean = false) + // Calling the overridden superclass constructor. + // Note that this may need modifications if there are multiple constructor choices. + // Possible constructor calls: + // Constructor(offset as integer, length as integer, type as integer, highlightColor as color = &c0, backgroundColor as color = &c0, bold as boolean = false, italic as boolean = false, underline as boolean = false) -- From TextSegment + // Constructor() -- From TextSegment + // Constructor(offset as integer, length as integer) -- From DataRange + // Constructor() -- From DataRange + + Super.Constructor(offset, length, TextSegment.TYPE_PLACEHOLDER, HighlightColor, &c0, bold, italic, underline) + placeholderBackgroundColor = backgroundColor + if placeholderBackgroundColor = &c0 then placeholderBackgroundColor = &ce9effa + textRange = new DataRange(labelOffset, labelLength) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function inRange(offset as integer) As boolean + Return offset > self.offset and offset < self.offset + self.length //changed to < instead of <= per Thomas Tempelmann's suggestion. + End Function + #tag EndMethod + + + #tag Property, Flags = &h0 + placeholderBackgroundColor As color + #tag EndProperty + + #tag Property, Flags = &h0 + textRange As DataRange + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="BackgroundColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="bold" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DebugDescription" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="EndOffset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackgroundColor" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="ID" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="italic" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="length" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="offset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="placeholderBackgroundColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="TextColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Type" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="underline" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="width" + Group="Behavior" + InitialValue="0" + Type="double" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/LineManager/TextSegment.xojo_code b/Third Party Classes/CustomEditField/LineManager/TextSegment.xojo_code new file mode 100644 index 0000000..050cdaa --- /dev/null +++ b/Third Party Classes/CustomEditField/LineManager/TextSegment.xojo_code @@ -0,0 +1,232 @@ +#tag Class +Protected Class TextSegment +Inherits DataRange + #tag Method, Flags = &h0 + Function Clone() As textsegment + dim tmp as new TextSegment + tmp.backgroundColor = backgroundColor + tmp.bold = bold + tmp.hasBackgroundColor = hasBackgroundColor + tmp.italic = italic + tmp.lastFont = lastFont + tmp.lastSize = lastSize + tmp.textColor = textColor + tmp.Type = TYPE + tmp.underline = underline + tmp.width = width + tmp.offset = offset + tmp.length = length + + Return tmp + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor() + Constructor(0,0,0) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(offset as integer, length as integer, type as integer, highlightColor as color = &c0, backgroundColor as color = &c0, bold as boolean = false, italic as boolean = false, underline as boolean = false) + super.Constructor(offset, length) + self.Type = TYPE + self.textColor = highlightColor + self.backgroundColor = backgroundColor + hasBackgroundColor = backgroundColor <> &c0 + self.bold = bold + self.italic = italic + self.underline = underline + width = -1 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function SplitAtLength(length as integer) As textsegment + dim tmp as TextSegment = clone + tmp.offset = tmp.offset + length + tmp.length = self.length - length + tmp.width = -1 + self.length = length + self.width = -1 + + Return tmp + End Function + #tag EndMethod + + + #tag Property, Flags = &h0 + BackgroundColor As color + #tag EndProperty + + #tag Property, Flags = &h0 + Bold As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + HasBackgroundColor As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + ID As string + #tag EndProperty + + #tag Property, Flags = &h0 + Italic As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastFont As string + #tag EndProperty + + #tag Property, Flags = &h1 + Protected lastSize As integer + #tag EndProperty + + #tag Property, Flags = &h0 + TextColor As color + #tag EndProperty + + #tag Property, Flags = &h0 + Type As Integer + #tag EndProperty + + #tag Property, Flags = &h0 + Underline As boolean + #tag EndProperty + + #tag Property, Flags = &h0 + Width As double + #tag EndProperty + + + #tag Constant, Name = TYPE_EOL, Type = Double, Dynamic = False, Default = \"3", Scope = Public + #tag EndConstant + + #tag Constant, Name = TYPE_PLACEHOLDER, Type = Double, Dynamic = False, Default = \"4", Scope = Public + #tag EndConstant + + #tag Constant, Name = TYPE_SPACE, Type = Double, Dynamic = False, Default = \"1", Scope = Public + #tag EndConstant + + #tag Constant, Name = TYPE_TAB, Type = Double, Dynamic = False, Default = \"2", Scope = Public + #tag EndConstant + + #tag Constant, Name = TYPE_WORD, Type = Double, Dynamic = False, Default = \"0", Scope = Public + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="BackgroundColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="bold" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="DebugDescription" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="EndOffset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackgroundColor" + Group="Behavior" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="ID" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="italic" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="length" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="offset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="TextColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Type" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="underline" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="width" + Group="Behavior" + InitialValue="0" + Type="double" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/MessageCenter/Message.xojo_code b/Third Party Classes/CustomEditField/MessageCenter/Message.xojo_code new file mode 100644 index 0000000..3da09a3 --- /dev/null +++ b/Third Party Classes/CustomEditField/MessageCenter/Message.xojo_code @@ -0,0 +1,121 @@ +#tag Class +Protected Class Message +Inherits Dictionary + #tag Method, Flags = &h0 + Sub AddInfo(key as variant, info as variant) + //Add info to the message in a key/data fashion + value(key)=info + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(type as variant, sender as object) + self.type = Type + messageSender = sender + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Info(key as variant) As variant + //get the info for a given key + return Lookup(key,nil) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function InfoCount() As integer + return count + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function MessageType() As variant + return type + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function Sender() As object + return messageSender + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ToString() As string + dim tmp as String + dim key as String + + tmp="Type: "+messageType+EndOfLine + for Each key in Keys + tmp=tmp+key+": "+Value(key).StringValue+EndOfLine + Next + + Return tmp + End Function + #tag EndMethod + + + #tag Property, Flags = &h1 + Protected messageSender As object + #tag EndProperty + + #tag Property, Flags = &h1 + Protected type As variant + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="BinCount" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Count" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/MessageCenter/MessageCenter.xojo_code b/Third Party Classes/CustomEditField/MessageCenter/MessageCenter.xojo_code new file mode 100644 index 0000000..7cb9085 --- /dev/null +++ b/Third Party Classes/CustomEditField/MessageCenter/MessageCenter.xojo_code @@ -0,0 +1,181 @@ +#tag Module +Protected Module MessageCenter + #tag Method, Flags = &h1 + Protected Function isMessageInQueue(type as variant, matchInfoKey as Variant, matchInfoValue as Variant) As Boolean + if queue <> nil then + return queue.findMessageInQueue (type, matchInfoKey, matchInfoValue) <> nil + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function messageInQueue(type as variant, matchInfoKey as Variant, matchInfoValue as Variant) As Message + if queue <> nil then + return queue.findMessageInQueue (type, matchInfoKey, matchInfoValue) + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub queueMessage(theMessage as Message) + if messages = nil then return //no receivers + if theMessage = nil then Return //??? + + if queue = nil then + queue = new MessageQueue + end if + + queue.addMessage(theMessage) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub registerForMessage(extends theReceiver as MessageReceiver, messageType as variant) + //register a message receiver for a specific msg type + if messages = nil then + messages = new dictionary + end if + + dim tmp as dictionary + + if messages.hasKey(messageType) then + tmp = messages.value(messageType) + else + tmp = new Dictionary + messages.value(messageType) = tmp + end if + + tmp.value(theReceiver) = false + + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub sendMessage(theMessage as Message) + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + + if messages = nil then return //no receivers + + dim type as Variant + type = theMessage.messageType + + //no receivers for msg + if not messages.hasKey(type) then return + + dim receivers as dictionary + dim receiver as messageReceiver + + //send to all receivers + receivers=messages.value(type) + + for each receiver in receivers.Keys + receiver.receiveMessage(theMessage) + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub unregisterForMessage(extends theReceiver as MessageReceiver, messageType as variant) + if messages = nil then return //no receivers + + //no such message + if not messages.hasKey(messageType) then return + + //remove receiver + dim receivers as dictionary + receivers = messages.value(messageType) + + if not receivers.hasKey(theReceiver) then Return + + receivers.remove(thereceiver) + if receivers.Count = 0 then Messages.Remove(messageType) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub unregisterReceiver(extends theReceiver as messageReceiver) + if messages = nil then return //no receivers + + //find the object within our registered receivers + dim item as integer + dim type as Variant + dim receivers as dictionary + dim typesToRemove() as Variant + + for item=0 to messages.count-1 + //msg type + type = messages.key(item) + + //receivers + receivers = messages.value(type) + + if not receivers.hasKey(theReceiver) then Continue for + + receivers.remove(theReceiver) + if receivers.Count = 0 then typesToRemove.Append(type) + next + + for each type in typesToRemove + if Messages.HasKey(type) then Messages.Remove(Type) + next + End Sub + #tag EndMethod + + + #tag Note, Name = Info + Based on the article "Implementing a MessageCenter Module" + by Charles Yeomans + + Available in realbasic developer 3.6 + #tag EndNote + + + #tag Property, Flags = &h21 + Private messages As Dictionary + #tag EndProperty + + #tag Property, Flags = &h21 + Private queue As MessageQueue + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Module +#tag EndModule diff --git a/Third Party Classes/CustomEditField/MessageCenter/MessageQueue.xojo_code b/Third Party Classes/CustomEditField/MessageCenter/MessageQueue.xojo_code new file mode 100644 index 0000000..450dd79 --- /dev/null +++ b/Third Party Classes/CustomEditField/MessageCenter/MessageQueue.xojo_code @@ -0,0 +1,115 @@ +#tag Class +Protected Class MessageQueue +Inherits Timer + #tag Event + Sub Action() + // Timer tick... send the next queued msg + if ubound(queue)<0 then return + + dim msg as Message + msg = queue(0) + queue.remove(0) + + MessageCenter.sendMessage(msg) + + // if queue not empty, keep timer running + if UBound(queue) >= 0 then + me.Mode = Timer.ModeSingle + else + me.Mode = Timer.ModeOff ' necessary for RB 2012r2.1 in Cocoa - fixed in Xojo + end + End Sub + #tag EndEvent + + + #tag Method, Flags = &h0 + Sub addMessage(theMessage as Message) + queue.append theMessage + + if me.Mode = Timer.ModeOff then + me.Mode = Timer.ModeSingle + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h1000 + Sub Constructor() + me.Mode = Timer.ModeOff + me.Period = 0 // -> delivers as soon as possible, after the current event + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function findMessageInQueue(type as variant, matchInfoKey as Variant, matchInfoValue as Variant) As Message + for each m as Message in queue + if m.MessageType = type and m.Info(matchInfoKey) = matchInfoValue then + return m + end + next + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private queue(-1) As Message + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + Type="Integer" + EditorType="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Mode" + Visible=true + Group="Behavior" + InitialValue="2" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Off" + "1 - Single" + "2 - Multiple" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Period" + Visible=true + Group="Behavior" + InitialValue="1000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/MessageCenter/MessageReceiver.xojo_code b/Third Party Classes/CustomEditField/MessageCenter/MessageReceiver.xojo_code new file mode 100644 index 0000000..320d40f --- /dev/null +++ b/Third Party Classes/CustomEditField/MessageCenter/MessageReceiver.xojo_code @@ -0,0 +1,46 @@ +#tag Interface +Protected Interface MessageReceiver + #tag Method, Flags = &h0 + Sub receiveMessage(theMessage as Message) + + End Sub + #tag EndMethod + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Interface +#tag EndInterface diff --git a/Third Party Classes/CustomEditField/SyntaxHighlightEngine/DocumentSymbol.xojo_code b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/DocumentSymbol.xojo_code new file mode 100644 index 0000000..8fe6002 --- /dev/null +++ b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/DocumentSymbol.xojo_code @@ -0,0 +1,73 @@ +#tag Class +Protected Class DocumentSymbol + #tag Method, Flags = &h0 + Sub Constructor(name as string, offset as integer, type as string) + self.Name = name + self.Offset = Offset + self.Type = type + End Sub + #tag EndMethod + + + #tag Property, Flags = &h0 + Name As string + #tag EndProperty + + #tag Property, Flags = &h0 + Offset As Integer + #tag EndProperty + + #tag Property, Flags = &h0 + Type As string + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Offset" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Type" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightContext.xojo_code b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightContext.xojo_code new file mode 100644 index 0000000..eb7baff --- /dev/null +++ b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightContext.xojo_code @@ -0,0 +1,909 @@ +#tag Class +Protected Class HighlightContext + #tag Method, Flags = &h0 + Sub addKeyword(keyword as string) + if keyword="" then Return + keywords.Append(keyword) + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub addRegEx(newRegEx as string) + if newRegEx="" then Return + regexes.Append(newRegEx) + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub addSubContext(entry as HighlightContext) + if entry=nil then Return + subContexts.Append(entry) + + subExpressionCount = subExpressionCount + 1 + subExpressionIndex.Append subExpressionCount + + // clear caches, just in case + _subContextPattern = "" + _contextPattern = "" + + // add pattern to search string + if mSearchPattern <> "" then mSearchPattern = mSearchPattern + "|" + mSearchPattern = mSearchPattern + "(" + entry.ContextSearchPattern + ")" + fixSubExpressionCount(entry.ContextSearchPattern) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub appendToXMLNode(parent as xmlNode, depth as integer = 2) + // Appends this context to the parent xml node. + // This is done to export the syntax definition as an xml file. + + dim xdoc as XmlDocument + dim node, context as XmlNode + + xdoc = parent.OwnerDocument + context=parent.AppendChild(xdoc.CreateElement("highlightContext")) + + //name + context.SetAttribute("name",Name) + + //HighlightColor + context.SetAttribute("highlightColor","#"+HighlightDefinition.ColorToText(HighlightColor)) + + //BackgroundColor + if HasBackgroundColor then + context.SetAttribute("backgroundColor","#"+HighlightDefinition.ColorToText(BackgroundColor)) + end if + + //bold + if Bold then + context.SetAttribute("bold", "true") + end if + //italic + if Italic then + context.SetAttribute("italic", "true") + end if + //Underline + if Underline then + context.SetAttribute("underline", "true") + end if + + //Enabled + if not Enabled then + context.SetAttribute("enabled", "false") + end if + + //start regex? + if StartRegEx<>"" then + node=context.AppendChild(xdoc.CreateElement("startRegEx")) + node.AppendChild(xdoc.CreateTextNode(StartRegEx)) + IndentNode(node,depth+1) + end if + + //end regex? + if EndRegEx<>"" then + node=context.AppendChild(xdoc.CreateElement("endRegEx")) + node.AppendChild(xdoc.CreateTextNode(EndRegEx)) + IndentNode(node,depth+1) + end if + + //entry regex? + if EntryRegEx<>"" then + node=context.AppendChild(xdoc.CreateElement("entryRegEx")) + node.AppendChild(xdoc.CreateTextNode(EntryRegEx)) + IndentNode(node,depth+1) + end if + + //keywords + if UBound(keywords)>-1 then + node=context.AppendChild(xdoc.CreateElement("keywords")) + dim tmp as String + dim kw as XmlNode + for each tmp in keywords + kw=node.AppendChild(xdoc.CreateElement("string")) + kw.AppendChild(xdoc.CreateTextNode(tmp)) + IndentNode(kw,depth+2) + next + IndentNode(node,depth+1,true) + end if + + //regexes + if UBound(regexes)>-1 then + node=context.AppendChild(xdoc.CreateElement("regExes")) + dim tmp as String + dim kw as XmlNode + for each tmp in regexes + kw=node.AppendChild(xdoc.CreateElement("string")) + kw.AppendChild(xdoc.CreateTextNode(tmp)) + IndentNode(kw,depth+2) + next + IndentNode(node,depth+1,true) + end if + + //finally, subcontexs, if any + dim subContext as HighlightContext + for each subContext in subContexts + if subContext.Name = "fieldwhitespace" or subContext.isPlaceholder then Continue for + subContext.appendToXMLNode(context,depth+1) + next + + IndentNode(Context,depth, true) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor(caseSensitive as boolean, createBlank as boolean = true) + //init regex scanner + mScanner=new RegEx + mScanner.Options.DotMatchAll=true + mScanner.Options.CaseSensitive=caseSensitive + + mEnabled = true ' default + + //if add whitespace tokenizer + if createBlank then + dim blankSpaceContext as new HighlightContext(false, false) + blankSpaceContext.EntryRegEx = "([ ]|\t|\x0A|(?:\x0D\x0A?))"'"([\s])" + blankSpaceContext.Name = "fieldwhitespace" + addSubContext(blankSpaceContext) + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Contexts() As highlightcontext() + dim current, tmp() as HighlightContext + + for each current in subContexts + if current.Name <> "fieldwhitespace" and current.Enabled then tmp.Append current + next + + Return tmp + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ContextSearchPattern() As String + //return the regex pattern for this context + + //if there's a StartRegEx then the pattern is the StartRegEx, the same goes with the EntryRegEx + if StartRegEx<>"" then Return StartRegEx + if entryRegEx<>"" then Return entryRegEx + + //else, if there's a cached version of the regex, return it. + if _contextPattern<>"" then Return _contextPattern + + //finally, build the pattern using the keywords, regexes and subcontexts (these are exclusive) + //check for keywords + dim keyword as String + if UBound(keywords)>-1 then + _contextPattern="\b(" + for Each keyword in keywords + _contextPattern=_contextPattern+keyword+"|" + next + _contextPattern=Left(_contextPattern,_contextPattern.Len-1)+")\b" + Return _contextPattern + end if + + //else, check for regexes + dim aRegEx as String + if UBound(regexes)>-1 then + _contextPattern="(" + for Each aRegEx in regexes + _contextPattern=_contextPattern+aRegEx+"|" + next + _contextPattern=Left(_contextPattern,_contextPattern.Len-1)+")" + Return _contextPattern + end if + + // we seem never to get here + _contextPattern = subContextPattern + Return _contextPattern + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub fixSubExpressionCount(pattern as string) + // determine subexpression count + // determine subexpression count + // This method is original from Nick Lockwood: http://www.charcoaldesign.co.uk/oss#tokenizer + // It speeds up the matching of the matched regex. + + dim escaped, inCharClass, prevBracket as Boolean = false + escaped = false + for i as integer = 1 to pattern.Len + select case pattern.mid(i,1) + case "\" + escaped = true + prevBracket = false + case "(" + dim nextChar as String = pattern.Mid(i+1,1) + if not inCharClass and not escaped and nextChar <> "?" then subExpressionCount = subExpressionCount + 1 + prevBracket = false + escaped = false + case "[" + if inCharClass or escaped then + prevBracket = false + escaped = false + else + inCharClass = true + prevBracket = true + end + case "]" + if not prevBracket then inCharClass = false + prevBracket = false + else + prevBracket = false + escaped = false + end + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Highlight(text as string, subExpression as string, position as integer, positionB as integer, scanner as regex, tokens() as textsegment, placeholders() as TextPlaceholder) As boolean + #pragma DisableBackgroundTasks + + + //Highlight this context + dim match as RegExMatch + dim scanNextLine as Boolean = false + + //if there's a start and end regexes we need to find the EndRegEx + if StartRegEx.trim<>"" and mEndRegEx<>nil then + //find end... + #if true + dim oldPattern as String = scanner.SearchPattern + scanner.SearchPattern = EndRegEx + match = scanner.Search(text, positionB + subExpression.Lenb) //fix, added .lenb to support utf correctly + scanner.SearchPattern = oldPattern + #else + // this would be a slightly faster version, but it doesn't work right, e.g. with Postgresql syntax. No idea why + match = mEndRegex.Search(text, positionB + subExpression.Lenb) //fix, added .lenb to support utf correctly + scanner.SearchStartPosition = mEndRegex.SearchStartPosition + #endif + + //find the subExpression + if match<>nil then + subExpression=LeftB(text, match.SubExpressionStartB(0)+match.SubExpressionString(0).LenB) + subExpression=Right(subExpression, subExpression.Len-position) + else + //no match? then use the rest of the String... + subExpression=Right(text, text.Len-position) + scanner.SearchStartPosition = text.Lenb //fix, added .lenb to support utf correctly + scanNextLine = true + end if + end if + + dim entry as HighlightContext + dim substring as String + dim startPos, startPosB, charPos, charPosB as Integer + + //scan subcontexts + substring = mSearchPattern + if substring = "" then + //Highlight subExpression + select case subExpression + case " " + tokens.Append(new TextSegment(position, 1, TextSegment.TYPE_SPACE, HighlightColor, BackgroundColor)) + + case chr(9) + tokens.Append(new TextSegment(position, 1, TextSegment.TYPE_TAB, HighlightColor, BackgroundColor)) + + case chr(10), chr(13), chr(13) + chr(10) + tokens.Append(new TextSegment(position, subExpression.Len, TextSegment.TYPE_EOL, HighlightColor, BackgroundColor)) + + else + if subExpression.len > 0 then _ + tokens.Append(new TextSegment(position, subExpression.Len, TextSegment.TYPE_WORD, HighlightColor, BackgroundColor, bold, italic, underline)) + + end select + else + if mScanner.SearchPattern <> substring then + if mScanner.SearchPattern <> "" then break // should get set only once! + mScanner.SearchPattern = substring + end if + match=mScanner.Search(subExpression) + + while match<>nil + substring=match.SubExpressionString(0) + + // determine which token was matched + dim tknIndex as integer + for i as integer = 1 to match.SubExpressionCount - 1 + if match.SubExpressionString(i) = substring then + tknIndex = subExpressionIndex.IndexOf(i) + exit + end + next + + if tknIndex < 0 then //definition can't handle source! + exit while + end if + + #if DebugBuild + dim start as Integer = match.SubExpressionStartB(0) + dim wtf as String = subExpression.leftb(start) + #pragma unused wtf + #endif + charPos = subExpression.leftb(match.SubExpressionStartB(0)).len + charPosB = match.SubExpressionStartB(0) + + if charPos - startPos > 0 then _ + tokens.Append(new TextSegment(startPos + position, charPos - startPos, TextSegment.TYPE_WORD, HighlightColor, BackgroundColor, bold, italic, underline)) + + startPos = charPos + startPosB = charPosB + + entry = subContexts(tknIndex) 'findSubContextForMatch(substring, subExpression, start) + + //forward execution to subcontext... + if entry<>nil and not entry.isPlaceholder then + call entry.Highlight(subExpression, substring, position + startPos, positionB + startPosB, mScanner, tokens, placeholders) + #if DebugBuild + dim asub as String = subExpression.leftb(mScanner.SearchStartPosition) + #pragma unused asub + #endif + startPos = subExpression.leftb(mScanner.SearchStartPosition).len + startPosB = mScanner.SearchStartPosition + + elseIf entry <> nil and entry.isPlaceholder then + dim label as String = match.SubExpressionString(match.SubExpressionCount - 1) + dim tmp as Integer = text.leftb(match.SubExpressionStartB(match.SubExpressionCount - 1)).len + + dim placeholder as new TextPlaceholder(startPos + position, substring.Len, tmp + position, label.len, entry.HighlightColor, entry.BackgroundColor, entry.Bold, entry.Italic, entry.Underline) + tokens.Append(placeholder) + placeholders.Append(placeholder) + + startPos = subExpression.leftb(mScanner.SearchStartPosition).len + startPosB = mScanner.SearchStartPosition + end if + match=mScanner.Search + wend + + if subExpression.len - startPos > 0 then _ + tokens.Append(new TextSegment(startPos + position, subExpression.len - startPos, TextSegment.TYPE_WORD, HighlightColor, BackgroundColor, bold, italic, underline)) + end if + + Return scanNextLine + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub IndentNode(node As XmlNode, level As Integer, indentCloseTag As Boolean = False) + Dim i As Integer + Dim s As String + s = EndOfLine + For i = 1 To level + s = s + Chr(9) // Tab + Next + node.Parent.Insert(node.OwnerDocument.CreateTextNode(s), node) + If indentCloseTag Then + node.AppendChild(node.OwnerDocument.CreateTextNode(s)) + End If + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ListKeywords(storage() as string) + //add mine.. + dim keyword as String + for each keyword in keywords + storage.Append(keyword) + next + + //then subs... + for i as Integer = 0 to UBound(subContexts) + subContexts(i).ListKeywords(storage) + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub LoadFromXmlNode(node as xmlNode) + //load context out of an xml node + dim tmpObj as Variant + dim tmp as String + + //Highlight color + Name = node.GetAttribute("name") + + tmpObj = "&h" + node.GetAttribute("highlightColor").Mid(1) + HighlightColor = tmpObj.ColorValue + + //background color + tmp = node.GetAttribute("backgroundColor") + if tmp <> "" then + tmpObj = "&h" + tmp + BackgroundColor = tmpObj.ColorValue + end if + + //Bold + tmp = node.GetAttribute("bold") + if tmp <> "" then Bold = tmp = "true" + + //Italic + tmp = node.GetAttribute("italic") + if tmp <> "" then Italic = tmp = "true" + + //Underline + tmp = node.GetAttribute("underline") + if tmp <> "" then Underline = tmp = "true" + + //Enabled + tmp = node.GetAttribute("enabled") + Enabled = tmp <> "false" + + dim i, j as Integer + dim subNode as XmlNode + dim subContext as HighlightContext + + for i=0 to node.ChildCount-1 + subNode=node.Child(i) + select case subNode.Name + case "startRegEx" + StartRegEx=subNode.FirstChild.Value + case "endRegEx" + EndRegEx=subNode.FirstChild.Value + case "entryRegEx" + EntryRegEx=subNode.FirstChild.Value + case "keywords" + for j=0 to subNode.ChildCount-1 + if not subNode.Child(j) isa XmlComment then _ //add only if it's not a comment + addKeyword(subNode.Child(j).FirstChild.Value) + next + case "regExes" + for j=0 to subNode.ChildCount-1 + if not subNode.Child(j) isa XmlComment then _ + addRegEx(subNode.Child(j).FirstChild.Value) + next + case "highlightContext" + subContext=new HighlightContext(mScanner.Options.CaseSensitive) + subContext.loadFromXmlNode(subNode) + addSubContext(subContext) + end select + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function subContextPattern() As String + // this seems never to get called + + if _subContextPattern = "" then + + //get the regex for the subContexts + if UBound(subContexts)>= 0 then + dim s as String = "(" + for each current as HighlightContext in subContexts + s = s + current.ContextSearchPattern+"|" + next + s = Left(s,s.Len-1)+")" + _subContextPattern = s + end if + + end if + + Return _subContextPattern + End Function + #tag EndMethod + + + #tag Note, Name = About + Info + + HighlightContext + By Alex Restrepo + send comments, suggestions, fixes to alexrestrepo@mac.com + + A little experiment on SyntaxHighlighting + Contains the rules of how to Highlight a Context within a HighlightDefinition + A context is composed of ONE of the following: + - a start and end regexes, everything inside the start and end regexes is part of the context, this is a full match regex (subexpression 0) + - an EntryRegEx, specifies the regular expression to match the whole context (ie: an html tag), the first subexpression of the expression is used (subexpression 1) + - keywords, one or more keywords that need to be Highlighted, you can have multiple keyword contexts with different highlight colors. (array of strings) + - regexes, one or more regular expressions that define variations of the same context (ie: in java #include or #package), subexpression 1 is used for each entry. + + Methods: + addKeyword(keyword as String): adds the keyword to the keywords array + addRegEx(newRegEx as String): adds the newRegEx to the regexes array + addSubContext(context as HighlightContext): adds the context as a subcontext of this one, for example: properties within xml tags + appendToXMLNode(parent as xmlNode, depth as integer = 2): appends this context to the parent xml node as an xml node, this is done when exporting the parent definition as an xml. + Constructor(caseSensitive as Boolean): the constructor sets the case sensitiviness of the context. + contextRegEx as string: returns the composed regular expression with all the contents of the context, if the context has StartRegEx and EndRegEx, the contextRegEx is the StartRegEx + Highlight(text as string, style as styledText, subExpression as string, position as integer, scanner as regex): Highlights the subexpression, text is the text of the parent context, position is the position of the first character of the subexpression in the context. + loadFromXmlNode(node as XmlNode): loads the context from the xmlNode. + + Properties: + StartRegEx: the regular expression that defines the start of the context (ie: in java, /* for multiline comments) + EndRegEx: the regular expression that marks the end of the context (ie: */) + EntryRegEx: the regular expression that defines the context, ie: an xml tag: (<[^>]*>) + HighlightColor: the HighlightColor for the context + Name: the name of the Context, ie: "Tags" + + + Open source under the creative commons license. + Use in whatever way you like... at your own risk :P + let me know if you find it useful. + If you decide to use it in your projects, please give me credit in your about window or documentation, thanks. + #tag EndNote + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mBackgroundcolor + End Get + #tag EndGetter + #tag Setter + Set + mBackgroundcolor = value + + //this is to make whitespaces the same color as the "parent" context + //whitespace should always be subcontext(0)... if it's available at all. + if UBound(subContexts) > -1 and subContexts(0).Name = "fieldwhitespace" then + subContexts(0).BackgroundColor = value + end if + + mHasBackgroundColor = value <> &c0 + End Set + #tag EndSetter + BackgroundColor As color + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mBold + End Get + #tag EndGetter + #tag Setter + Set + mBold = value + End Set + #tag EndSetter + Bold As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mEnabled + End Get + #tag EndGetter + #tag Setter + Set + mEnabled = value + End Set + #tag EndSetter + Enabled As Boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if mEndRegex <> nil then + Return mEndRegex.SearchPattern + end if + End Get + #tag EndGetter + #tag Setter + Set + if value = "" then + mEndRegex = nil + else + mEndRegex = new RegEx + mEndRegex.Options.DotMatchAll = true + mEndRegex.SearchPattern = value + end if + End Set + #tag EndSetter + EndRegEx As string + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return mEntryRegex + End Get + #tag EndGetter + #tag Setter + Set + mEntryRegex = value + End Set + #tag EndSetter + EntryRegEx As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mHasbackgroundcolor + End Get + #tag EndGetter + HasBackgroundColor As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return mForeColor + End Get + #tag EndGetter + #tag Setter + Set + mForeColor = value + End Set + #tag EndSetter + HighlightColor As color + #tag EndComputedProperty + + #tag Property, Flags = &h0 + isPlaceholder As Boolean + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mItalic + End Get + #tag EndGetter + #tag Setter + Set + mItalic = value + End Set + #tag EndSetter + Italic As boolean + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private keywords() As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private mBackgroundcolor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private mBold As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mEnabled As Boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mEndRegex As RegEx + #tag EndProperty + + #tag Property, Flags = &h21 + Private mEntryRegex As String + #tag EndProperty + + #tag Property, Flags = &h21 + Private mForeColor As color = &c000000 + #tag EndProperty + + #tag Property, Flags = &h21 + Private mHasbackgroundcolor As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mItalic As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private mPlaceholderContextDef As HighlightContext + #tag EndProperty + + #tag Property, Flags = &h21 + Private mScanner As regex + #tag EndProperty + + #tag Property, Flags = &h21 + Private mSearchPattern As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private mStartRegex As String + #tag EndProperty + + #tag Property, Flags = &h21 + Private mUnderline As boolean + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return _name + End Get + #tag EndGetter + #tag Setter + Set + _name=value + End Set + #tag EndSetter + Name As string + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mPlaceholderContextDef + End Get + #tag EndGetter + #tag Setter + Set + if mPlaceholderContextDef <> nil then Return //if already set, bail out... + + mPlaceholderContextDef = value + self.addSubContext(value) + + for each subcontext as HighlightContext in subContexts + if subContext <> value then subContext.placeholderContextDef = value + next + End Set + #tag EndSetter + PlaceholderContextDef As HighlightContext + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private regexes() As string + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return mStartRegex + End Get + #tag EndGetter + #tag Setter + Set + mStartRegex = value + End Set + #tag EndSetter + StartRegEx As string + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private subContexts() As HighlightContext + #tag EndProperty + + #tag Property, Flags = &h21 + Private subExpressionCount As integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private subExpressionIndex() As integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mUnderline + End Get + #tag EndGetter + #tag Setter + Set + mUnderline = value + End Set + #tag EndSetter + Underline As boolean + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private _contextPattern As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private _name As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private _subContextPattern As String + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="BackgroundColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Bold" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EndRegEx" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="entryRegEx" + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackgroundColor" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HighlightColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="isPlaceholder" + Group="Behavior" + InitialValue="0" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Italic" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="StartRegEx" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Underline" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightDefinition.xojo_code b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightDefinition.xojo_code new file mode 100644 index 0000000..cac39cb --- /dev/null +++ b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightDefinition.xojo_code @@ -0,0 +1,997 @@ +#tag Class +Protected Class HighlightDefinition + #tag Method, Flags = &h21 + Private Sub addContext(context as HighlightContext) + if Context=nil then Return + subContexts.Append(Context) + + if Context <> PlaceholderContextDef then + context.PlaceholderContextDef = PlaceholderContextDef + end if + + subExpressionCount = subExpressionCount + 1 + subExpressionIndex.Append subExpressionCount + + fixSubExpressionCount (context.ContextSearchPattern) + + refreshSearchString + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub addSymbol(symbol as symbolsDefinition) + if Symbol = nil then Return + Symbols.Append Symbol + + symbolCount = symbolCount + 1 + symbolIndex.Append symbolCount + + // add pattern to search string + if symbolPattern <> "" then symbolPattern = symbolPattern + "|" + symbolPattern = symbolPattern + "(" + symbol.EntryRegex + ")" + fixSymbolCount(symbol.EntryRegex) + + // update prepared regex for symbolPattern + mSymbolRegex.SearchPattern = symbolPattern + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function bool2YN(value as boolean) As string + if value then Return "yes" + Return "no" + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Shared Function ColorToText(c as Color) As String + dim v as Variant = c + return Right("0000000"+Hex(v.IntegerValue),6) // aIntegerValue doesn't include a Color's transparency, so we're safe here + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor() + //init regex scanner + mContextRegex = new RegEx + mContextRegex.Options.DotMatchAll=true + + mSymbolRegex = new RegEx + + //add a blank space context, this will tokenize strings. + dim blankSpaceContext as new HighlightContext(false, false) + blankSpaceContext.EntryRegEx = "([ ]|\t|\x0A|(?:\x0D\x0A?))"'"([\s])" + blankSpaceContext.Name = "fieldwhitespace" + + addContext(blankSpaceContext) + blockEndDef = new Dictionary + blockStartDef = new Dictionary + lineContinuationDef = new Dictionary + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function ContextEnabled(name as String) As Boolean + for each current as HighlightContext in subContexts + if current.Name = name then + return current.Enabled + end if + next + ' not found? + break + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ContextEnabled(name as String, assigns ena as Boolean) + for each current as HighlightContext in subContexts + if current.Name = name then + if current.Enabled <> ena then + current.Enabled = ena + refreshSearchString + end if + return + end if + next + ' not found? + break + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Contexts() As highlightcontext() + dim tmp() as HighlightContext + + for each current as HighlightContext in subContexts + if current.Name <> "fieldwhitespace" and current.Enabled then tmp.Append current + next + + Return tmp + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub fixSubExpressionCount(pattern as string) + // determine subexpression count + // This method is original from Nick Lockwood: http://www.charcoaldesign.co.uk/oss#tokenizer + // It speeds up the matching of the matched regex. + dim escaped, inCharClass, prevBracket as Boolean = false + escaped = false + for i as integer = 1 to pattern.Len + select case pattern.mid(i,1) + case "\" + escaped = true + prevBracket = false + case "(" + dim nextChar as String = pattern.Mid(i+1,1) + if not inCharClass and not escaped and nextChar <> "?" then self.subExpressionCount = self.subExpressionCount + 1 + prevBracket = false + escaped = false + case "[" + if inCharClass or escaped then + prevBracket = false + escaped = false + else + inCharClass = true + prevBracket = true + end + case "]" + if not prevBracket then inCharClass = false + prevBracket = false + else + prevBracket = false + escaped = false + end + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub fixSymbolCount(pattern as string) + // determine subexpression count + // This method is original from Nick Lockwood: http://www.charcoaldesign.co.uk/oss#tokenizer + // It speeds up the matching of the matched regex. + dim escaped, inCharClass, prevBracket as Boolean = false + escaped = false + for i as integer = 1 to pattern.Len + select case pattern.mid(i,1) + case "\" + escaped = true + prevBracket = false + case "(" + dim nextChar as String = pattern.Mid(i+1,1) + if not inCharClass and not escaped and nextChar <> "?" then symbolCount = symbolCount + 1 + prevBracket = false + escaped = false + case "[" + if inCharClass or escaped then + prevBracket = false + escaped = false + else + inCharClass = true + prevBracket = true + end + case "]" + if not prevBracket then inCharClass = false + prevBracket = false + else + prevBracket = false + escaped = false + end + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Highlight(text as string, tokens() as textsegment, placeholders() as textplaceholder, forceMatch as highlightContext = nil) As highlightcontext + #pragma DisableBackgroundTasks + + + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.AccumulationTimer(CurrentMethodName) + #endif + + dim match as RegExMatch + dim subExpression as String + dim context as HighlightContext + dim startPos, startPosB as Integer + dim openContext as HighlightContext + + if text.Encoding <> nil then text = text.ConvertEncoding(EditFieldGlobals.InternalEncoding) + + if forceMatch = nil then + // perform the initial search + match = mContextRegex.Search(Text) + end if + + dim charPos, charPosB as Integer + while forceMatch <> nil or match<>nil + if match = nil Then + subExpression = "" + else + subExpression = match.SubExpressionString(0) + end if + + // determine which token was matched + if forceMatch <> nil then + context = forceMatch + forceMatch = nil + else + dim tknIndex as integer = -1 + for i as integer = 1 to match.SubExpressionCount - 1 + if match.SubExpressionString(i) = subExpression then + tknIndex = subExpressionIndex.IndexOf(i) + if tknIndex >= 0 then + context = subContexts(tknIndex) + if not context.Enabled then + break ' must not happen - the mContextRegex may not contain disabled contexts + end if + end if + exit + end + next + + if tknIndex < 0 then //definition can't handle source!? + exit while + end if + end if + + //find the actual character position within the string, since SubExpressionStartB returns the byte position, + //and if you have multi-byte strings you get an offsetted highlight. + if match = nil then + charPos = 0 + else + charPos = text.leftb(match.SubExpressionStartB(0)).len + charposB = match.SubExpressionStartB(0) + end if + + //Highlight everything up to this point with the default color. + if charPos - startPos > 0 then + tokens.Append(new TextSegment(startPos, charPos-startPos, TextSegment.TYPE_WORD, DefaultColor)) + end if + + startPos = charPos + startPosB = charPosB + + //forward execution to the context for any further processing. + if context <> nil and not context.isPlaceholder then + if context.Highlight(text, subExpression, startPos, startPosB, mContextRegex, tokens, placeholders) then + openContext = context + end if + startPos = text.leftb(mContextRegex.SearchStartPosition).len + startPosB = mContextRegex.SearchStartPosition + + ElseIf context <> nil and context.isPlaceholder then + dim label as String = match.SubExpressionString(match.SubExpressionCount - 1) + dim tmp as Integer = text.leftb(match.SubExpressionStartB(match.SubExpressionCount - 1)).len + dim placeholder as new TextPlaceholder(startPos, subExpression.Len, tmp, label.len, context.HighlightColor, context.BackgroundColor, context.Bold, context.Italic, context.Underline) + tokens.Append(placeholder) + placeholders.Append(placeholder) + + startPos = text.leftb(mContextRegex.SearchStartPosition).len + startPosB = mContextRegex.SearchStartPosition + end if + + //and search again + match = mContextRegex.Search + wend + + //Highlight the rest of the text with the default color. + if text.len - startPos > 0 then + tokens.Append(new TextSegment(startPos, text.len - startPos, TextSegment.TYPE_WORD, DefaultColor)) + end if + + Return openContext + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub IndentNode(node As XmlNode, level As Integer, indentCloseTag As Boolean = False) + static ss As String + if ss = "" then + ss = EndOfLine + For i as Integer = 1 To 20 + ss = ss + Chr(9) // Tab + Next + end if + dim s as String = ss.Left(level+1) + node.Parent.Insert(node.OwnerDocument.CreateTextNode(s), node) + If indentCloseTag Then + node.AppendChild(node.OwnerDocument.CreateTextNode(s)) + End If + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsBlockEnd(lineText as string, stateIn as String, ByRef stateOut as String, ByRef ruleOut as Object) As Boolean + // returns true if it's a block end, new state and the matched rule (opaque, only useful for matching with IsBlockStart's returned value) + + stateOut = stateIn + + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.AccumulationTimer(CurrentMethodName) + #endif + + dim v as Variant = blockEndDef.Lookup (stateIn, nil) + if v.IsArray then + dim ps() as Pair = v + for each p as Pair in ps + if p <> nil then + dim scanner as RegEx = p.Left + if scanner.Search(lineText) <> nil then + dim ruleAndState as Pair = p.Right + dim state as Pair = ruleAndState.Right + if state.Left.BooleanValue then + // change state + stateOut = state.Right + end + ruleOut = ruleAndState.Left + return true + end + end if + next + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsBlockStart(lineText as string, stateIn as String, ByRef stateOut as String, ByRef ruleOut as Object) As Integer + // returns indent value, new state and the matched rule (opaque, only useful for matching with IsBlockEnd's returned value) + + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.AccumulationTimer(CurrentMethodName) + #endif + + stateOut = stateIn + + dim v as Variant = blockStartDef.Lookup (stateIn, nil) + if v.IsArray then + dim ps() as Pair = v + for each p as Pair in ps + if p <> nil then + dim scanner as RegEx = p.Left + if scanner.Search(lineText) <> nil then + dim indentAndState as Pair = p.Right + dim state as Pair = indentAndState.Right + if state.Left.BooleanValue then + // change state + stateOut = state.Right + end + ruleOut = scanner + return indentAndState.Left + end + end if + next + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsLineContinuation(lineText as string) As Integer + // returns indent value + + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.AccumulationTimer(CurrentMethodName) + #endif + + if lineContinuationDef.Count = 0 then Return 0 + + dim scanner as RegEx = lineContinuationDef.Key(0) + + if scanner.Search(lineText) <> nil then + Return lineContinuationDef.Value(scanner) + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function Keywords() As string() + //get all the keyword strings in this definition. + dim tmp() as String + for i as Integer = 0 to UBound(subContexts) + subContexts(i).ListKeywords(tmp) + next + + tmp.Sort + Return tmp + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LineContinuationIndent() As Integer + // returns the block indentation of the first default state + + dim ps() as Pair = blockStartDef.Lookup ("", nil) + if not (ps is nil) then + dim p as Pair = ps(0) + dim indentAndState as Pair = p.Right + return indentAndState.Left + end + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LoadFromXml(data as folderItem) As boolean + if data=nil then Return False + + //read a file... + dim tis as TextInputStream=TextInputStream.Open(data) + if tis=nil then Return False + + dim xml as String=tis.ReadAll(Encodings.UTF8) + tis.Close + + Return loadFromXml(xml) + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function LoadFromXml(data as string) As boolean + dim xml as XmlDocument + Dim root, node as XMLNode + Dim context as HighlightContext + dim Symbol as SymbolsDefinition + Dim i, j as Integer + + //load a xml syntax definition. + try + xml=new XmlDocument + xml.LoadXml(data) + + root=xml.Child(0) + //doc check + if root.Name<>"highlightDefinition" then + break + Return False + end if + if val(root.GetAttribute("version")) > version then + break + Return False + end if + + dim lastStartRule as Object + + for i=0 to root.ChildCount-1 + node=root.Child(i) + select case node.Name + case "name" + //syntax name + Name=node.FirstChild.Value + + case "blockStartMarker" + if lastStartRule <> nil then + // Error: There's still an unfinished start rule open + break + return false + end + dim newstate as XmlAttribute = node.GetAttributeNode("newstate") + dim newstateValue as String + if newstate <> nil then newstateValue = newstate.Value + dim cond as String = node.GetAttribute("condition") + dim values() as Pair + dim v as Variant = blockStartDef.Lookup(cond, nil) + if not v.IsArray then + blockStartDef.Value(cond) = values + else + values = v + end if + dim re as new RegEx + re.SearchPattern = node.FirstChild.Value + values.Append re : (node.GetAttribute("indent").Val : (newstate <> nil : newstateValue)) + lastStartRule = re + + case "blockEndMarker" + if lastStartRule = nil then + // Error: End rule without start rule + break + return false + end + dim newstate as XmlAttribute = node.GetAttributeNode("newstate") + dim newstateValue as String + if newstate <> nil then newstateValue = newstate.Value + dim cond as String = node.GetAttribute("condition") + dim values() as Pair + dim v as Variant = blockEndDef.Lookup(cond, nil) + if not v.IsArray then + blockEndDef.Value(cond) = values + else + values = v + end if + dim re as new RegEx + re.SearchPattern = node.FirstChild.Value + values.Append re : (lastStartRule : (newstate <> nil : newstateValue)) + lastStartRule = nil + + case "lineContinuationMarker" + //indent is the number of indentations. + dim re as new RegEx + re.SearchPattern = node.FirstChild.Value + lineContinuationDef.Value(re) = val(node.GetAttribute("indent")) + + case "symbols" + for j = 0 to node.ChildCount - 1 + dim child as XmlNode = node.Child(j) + if child.Name = "symbol" then + Symbol = new SymbolsDefinition + Symbol.loadFromXmlNode(child) + addSymbol(Symbol) + end if + next + + case "placeholders" + placeholderContextDef = new HighlightContext(False, False) + placeholderContextDef.EntryRegEx = node.FirstChild.Value + placeholderContextDef.isPlaceholder = true + placeholderContextDef.Name = "Placeholders" + + dim tmpObj as Variant + if node.GetAttribute("highlightColor") <> "" then + tmpObj = "&h" + node.GetAttribute("highlightColor").Mid(1) + PlaceholderContextDef.HighlightColor = tmpObj.ColorValue + end if + + if node.GetAttribute("backgroundColor") <> "" then + tmpObj = "&h" + node.GetAttribute("backgroundColor").Mid(1) + PlaceholderContextDef.BackgroundColor = tmpObj.ColorValue + end if + + dim tmp as String + + //Bold + tmp = node.GetAttribute("bold") + if tmp <> "" then placeholderContextDef.Bold = tmp = "true" + + //Italic + tmp = node.GetAttribute("italic") + if tmp <> "" then placeholderContextDef.Italic = tmp = "true" + + //Underline + tmp = node.GetAttribute("underline") + if tmp <> "" then placeholderContextDef.Underline = tmp = "true" + + //Enabled + tmp = node.GetAttribute("enabled") + placeholderContextDef.Enabled = tmp <> "false" + + self.addContext placeholderContextDef + + case "contexts" + //contexts + dim tmpObj as Variant + tmpObj = "&h" + node.GetAttribute("defaultColor").Mid(1) + defaultColor=tmpObj.ColorValue + caseSensitive = YN2Bool(node.GetAttribute("caseSensitive")) + for j=0 to node.ChildCount-1 + Context=new HighlightContext(caseSensitive) + Context.loadFromXmlNode(node.Child(j)) + addContext(Context) + next + end select + Next + + Return true + Catch + break + Return False + end try + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub refreshSearchString() + // build search string from all contexts + + dim patterns() as String + for each c as HighlightContext in subContexts + if c.Enabled then + patterns.Append "(" + c.ContextSearchPattern + ")" + end if + next + + mContextRegex.SearchPattern = Join (patterns, "|") + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function SaveAsXml(file as folderItem) As boolean + if file=nil then Return False + + //save definition as an xml + try + dim tos as TextOutputStream = TextOutputStream.Create(file) + tos.Write(toXml) + tos.Close + + Return true + catch + Return False + end try + + + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ScanSymbols(forText as string) As dictionary + //simple symbol scan + + if mSymbolRegex = nil then Return nil + + if forText.Encoding <> nil then forText = forText.ConvertEncoding(Encodings.UTF8) + + dim match as RegExMatch + dim symbol as String + dim pos as integer + dim local as new Dictionary + dim tknIndex as integer + dim symbolDef as SymbolsDefinition + + match = mSymbolRegex.Search(forText) + while match <> nil + Symbol = match.SubExpressionString(0) + pos = forText.leftb(match.SubExpressionStartB(0)).len + + for i as integer = 1 to match.SubExpressionCount - 1 + if match.SubExpressionString(i) = symbol then + tknIndex = symbolIndex.IndexOf(i) + exit + end + next + + if tknIndex < 0 or tknIndex > Symbols.Ubound then //definition can't handle source!? + exit while + end if + + symbolDef = Symbols(tknIndex) + + Symbol = Symbol.Trim //strip spaces + if Symbol <> "" then + local.Value(Symbol) = new DocumentSymbol(Symbol, pos, symbolDef.Type) + end if + + match = mSymbolRegex.Search + wend + + if local.Count = 0 then Return nil + Return local + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function SupportsCodeBlocks() As boolean + Return blockStartDef.Count > 0 and blockEndDef.Count > 0 + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function ToXML() As string + Dim xml as XmlDocument + Dim root, node as XMLNode + Dim context as HighlightContext + dim Symbol as SymbolsDefinition + + xml = New XmlDocument + + //root + root = xml.AppendChild(xml.CreateElement("highlightDefinition")) + root.SetAttribute("version",Str(version,"#.0")) + + //name + node = root.AppendChild(xml.CreateElement("name")) + node.AppendChild(xml.CreateTextNode(name)) + IndentNode(node,1) + + //block markers + for each cond as String in blockStartDef.Keys + dim ps() as Pair = blockStartDef.Value(cond) + for each p as Pair in ps + // p.Left: RegEx with SearchPattern + // p.Right: Pair of indent and state + node = root.AppendChild(xml.CreateElement("blockStartMarker")) + node.AppendChild(xml.CreateTextNode(RegEx(p.Left.ObjectValue).SearchPattern)) + if cond <> "" then + node.SetAttribute("condition", cond) + end if + dim indentAndState as Pair = p.Right + node.SetAttribute("indent", Str(indentAndState.Left, "#")) + dim state as Pair = indentAndState.Right + if state.Left.BooleanValue then + node.SetAttribute("newstate", state.Right) + end if + IndentNode(node,1) + next + next + for each cond as String in blockEndDef.Keys + dim ps() as Pair = blockEndDef.Value(cond) + for each p as Pair in ps + // p.Left: RegEx with SearchPattern + // p.Right: Pair of (rule_ref, Pair of (indent, state)) + node = root.AppendChild(xml.CreateElement("blockEndMarker")) + node.AppendChild(xml.CreateTextNode(RegEx(p.Left.ObjectValue).SearchPattern)) + if cond <> "" then + node.SetAttribute("condition", cond) + end if + dim state as Pair = Pair(p.Right).Right + if state.Left.BooleanValue then + node.SetAttribute("newstate", state.Right) + end if + IndentNode(node,1) + next + next + + for each key as RegEx in lineContinuationDef.Keys + node = root.AppendChild(xml.CreateElement("lineContinuationMarker")) + node.AppendChild(xml.CreateTextNode(key.SearchPattern)) + node.SetAttribute("indent", Str(lineContinuationDef.Value(key), "#")) + IndentNode(node,1) + next + + node = root.AppendChild(xml.CreateElement("symbols")) + for each Symbol in Symbols + Symbol.appendToXMLNode(node) + next + IndentNode(node,1, true) + + if PlaceholderContextDef <> nil then + node = root.AppendChild(xml.CreateElement("placeholders")) + + //HighlightColor + node.SetAttribute("highlightColor","#"+ColorToText(HighlightColor)) + + //BackgroundColor + if PlaceholderContextDef.HasBackgroundColor then + node.SetAttribute("backgroundColor","#"+ColorToText(PlaceholderContextDef.BackgroundColor)) + end if + + //bold + if PlaceholderContextDef.Bold then + node.SetAttribute("bold", "true") + end if + //italic + if PlaceholderContextDef.Italic then + node.SetAttribute("italic", "true") + end if + //Underline + if PlaceholderContextDef.Underline then + node.SetAttribute("underline", "true") + end if + + //Enabled + if not PlaceholderContextDef.Enabled then + node.SetAttribute("enabled", "false") + end if + + node.AppendChild(xml.CreateTextNode(PlaceholderContextDef.EntryRegEx)) + + IndentNode(node,1, false) + end if + + node = root.AppendChild(xml.CreateElement("contexts")) + node.SetAttribute("defaultColor","#"+ColorToText(defaultColor)) + node.SetAttribute("caseSensitive",bool2YN(caseSensitive)) + + //process contexts + for Each Context in subContexts + if Context.Name = "fieldwhitespace" or context.isPlaceholder then Continue for + + Context.appendToXMLNode(node) + next + + IndentNode(node,1, true) + IndentNode(root,0, true) + + Return xml.ToString + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function YN2Bool(value as string) As boolean + if value="yes" then Return true + Return False + End Function + #tag EndMethod + + + #tag Note, Name = About + Info + + HighlightDefinition + By Alex Restrepo + send comments, suggestions, fixes to alexrestrepo@mac.com + + A little experiment on SyntaxHighlighting + Contains the rules of how to Highlight the contents of the EditField. + A definition is composed of one or more HighlightContexts + + Methods: + Highlight(text as string, style as styledText): highlights the provided text using the provided styledtext object. + LoadFromXml(data as string): loads a HighlightDefinition stored in a xml string + LoadFromXml(data as folderItem): loads a HighlightDefinition stored in a xml file + SaveAsXml(file as folderitem): saves the HighlightDefinition as an xml file. + + Properties: + CaseSensitive: gets or sets if the contained syntax is case-sensitive + DefaultColor: gets or sets the default color for the text + Name: the name of the definition (ie: Xml or REALbasic) + + + Open source under the creative commons license. + Use in whatever way you like... at your own risk :P + let me know if you find it useful. + If you decide to use it in your projects, please give me credit in your about window or documentation, thanks. + #tag EndNote + + + #tag Property, Flags = &h21 + #tag Note + Key: condition + Value: Array of Pair of (regex, Pair of (regex_of_blockStart, Pair of (changeState as Boolean : newState as String)) + #tag EndNote + Private blockEndDef As dictionary + #tag EndProperty + + #tag Property, Flags = &h21 + #tag Note + Key: condition + Value: Array of Pair of (regex, Pair of (indent as Integer, Pair of (changeState as Boolean : newState as String)) + #tag EndNote + Private blockStartDef As dictionary + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return _caseSensitive + End Get + #tag EndGetter + #tag Setter + Set + _caseSensitive = value + mContextRegex.Options.CaseSensitive = value + End Set + #tag EndSetter + CaseSensitive As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return _defaultColor + End Get + #tag EndGetter + #tag Setter + Set + _defaultColor=value + End Set + #tag EndSetter + DefaultColor As color + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private lineContinuationDef As Dictionary + #tag EndProperty + + #tag Property, Flags = &h21 + Private mContextRegex As RegEx + #tag EndProperty + + #tag Property, Flags = &h21 + Private mSymbolRegex As RegEx + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return _name + End Get + #tag EndGetter + #tag Setter + Set + _name=value + End Set + #tag EndSetter + Name As string + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private PlaceholderContextDef As HighlightContext + #tag EndProperty + + #tag Property, Flags = &h21 + Private subContexts() As HighlightContext + #tag EndProperty + + #tag Property, Flags = &h21 + Private subExpressionCount As integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private subExpressionIndex() As integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private symbolCount As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private symbolIndex() As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private symbolPattern As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private Symbols() As SymbolsDefinition + #tag EndProperty + + #tag Property, Flags = &h21 + Private _caseSensitive As boolean + #tag EndProperty + + #tag Property, Flags = &h21 + Private _defaultColor As color + #tag EndProperty + + #tag Property, Flags = &h21 + Private _name As string + #tag EndProperty + + + #tag Constant, Name = version, Type = Double, Dynamic = False, Default = \"1.3", Scope = Public + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="caseSensitive" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="defaultColor" + Group="Behavior" + InitialValue="&h000000" + Type="color" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/SyntaxHighlightEngine/LineHighlighter.xojo_code b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/LineHighlighter.xojo_code new file mode 100644 index 0000000..ea14dba --- /dev/null +++ b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/LineHighlighter.xojo_code @@ -0,0 +1,346 @@ +#tag Class +Class LineHighlighter +Inherits Thread + #tag Event + Sub Run() + dim owner as CustomEditField = self.owner + if owner = nil then Return + + #if DebugBuild and EditFieldGlobals.DebugIndentation + System.DebugLog "LineHighlighter.Run..." + #endif + + dim lock as LinesLock + if not owner.IndentVisually then + lock = new LinesLock(owner) + end if + + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.LifeTimer(CurrentMethodName) + #endif + + do + ProcessVisibleLines + ProcessDirtyLines + if changedLines.RangeCount = 0 then + exit + end + #if DebugBuild and EditFieldGlobals.DebugIndentation + System.DebugLog "LineHighlighter loop" + #endif + loop + + HighlightingDone + + #if DebugBuild and EditFieldGlobals.DebugIndentation + System.DebugLog "LineHighlighter.Run done." + #endif + + End Sub + #tag EndEvent + + + #tag Method, Flags = &h0 + Sub Constructor(owner as customEditField, definition as highlightdefinition, changedLines as modifiedLineRangeManager, buffer as gapBuffer, lines as linemanager) + self.definition = definition + self.changedLines = changedLines + self.buffer = buffer + self.mLines = new WeakRef(lines) + self.mOwner = new WeakRef(owner) + self.Priority = Thread.HighPriority + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub DoneWithScreenLines() + if not MessageCenter.isMessageInQueue (self, 1, ScreenLinesHighlightedMsg) then + dim msg as new Message(self, self) + msg.addInfo(1, ScreenLinesHighlightedMsg) + MessageCenter.sendMessage(msg) + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub HighlightingDone() + if not MessageCenter.isMessageInQueue (self, 1, HighlightDoneMsg) then + dim msg as new Message(self, self) + msg.addInfo(1, HighlightDoneMsg) + MessageCenter.queueMessage(msg) + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub HighlightLine(index as integer) + #pragma DisableBackgroundTasks + + dim lock as new LinesLock(owner) + #pragma unused lock + + dim line, previous, nextLine as TextLine + dim context, previousContext as HighlightContext + dim processed as Integer + + dim lineFoldingsEnabled as Boolean = owner.EnableLineFoldings + + dim lines as LineManager = LineManager(mLines.Value) + + //get line + line = lines.getLine(index) + if line = nil then return //line can be nil if no longer in document + + //get context of previous line + previous = lines.getLine(index - 1) + if previous <> nil then + context = previous.Context + end if + + previousContext = line.Context + context = line.Highlight(definition, buffer, context) + + //restore fold markers + //if the line is a blockStart, or if it was and it's folded, check the fold markers + if lineFoldingsEnabled and (line.isBlockStart or (line.folded and not line.isBlockStart)) then + nextLine = lines.getLine(lines.nextBlockEndLine(index, true)) + if nextLine <> nil and ((line.folded <> not nextLine.visible) or (line.folded and not line.isBlockStart)) then + //if we got to this point, is because it's a startblock in an invalid state. + call lines.toggleLineFolding(index) + end if + end if + + LineHighlighted(index) + processed = processed + 1 + + if context <> nil then + //scan next + do + index = index + 1 + line = lines.getLine(index) + if line = nil then exit do + if line.Context = context then Continue + context = line.Highlight(definition, buffer, context) + processed = processed + 1 + LineHighlighted(index) + Loop Until context = nil + + //add final line + line = lines.getLine(index + 1) + if line <> nil then + owner.InvalidateLine(index + 1) + call changedLines.AddRange(index + 1, 1) // it will be highlighted in a future pass + end if + + //if context changed + elseif previousContext <> context then + + index = index + 1 + line = lines.getLine(index) + while line <> nil and line.Context = previousContext + context = line.Highlight(definition, buffer, context) + processed = processed + 1 + LineHighlighted(index) + index = index + 1 + line = lines.getLine(index) + wend + + //final line + if line <> nil then + call changedLines.AddRange(index, 1) + owner.InvalidateLine(index) + end if + + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub LineHighlighted(index as integer) + dim msg as new Message(self, self) + msg.addInfo(1, LineHighlightedMsg) + msg.addInfo(2, index) + MessageCenter.sendMessage(msg) + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ProcessDirtyLines() + do + dim lock as LinesLock + + try + lock = new LinesLock(owner) + + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.AccumulationTimer(CurrentMethodName) + #endif + + dim lineIdx as Integer + if not changedLines.RemoveNextLine (lineIdx) then + exit do + end if + + HighlightLine (lineIdx) + + exception exc as ThreadEndException + lock = nil + return + end try + + loop + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ProcessVisibleLines() + dim lock as new LinesLock(owner) + + #if DebugBuild and EditFieldGlobals.DebugTiming + dim runtimer as new Debugging.AccumulationTimer(CurrentMethodName) + #endif + + try + dim needsRefresh as Boolean + + dim startLine as Integer = owner.VisibleLineRange.offset + dim endLine as Integer = Max(owner.MaxVisibleLines,owner.VisibleLineRange.length) - startLine + 1 + + for lineIdx as Integer = startLine to endLine + + if changedLines.RemoveLine(lineIdx) then + // this is a dirty line that needs processing + HighlightLine (lineIdx) + needsRefresh = true + end if + + next + + if needsRefresh then + DoneWithScreenLines + end if + + exception exc as ThreadEndException + lock = nil + return + end try + + End Sub + #tag EndMethod + + + #tag Note, Name = How this works + The algorithm was changed in 1.7.3 (10 Sep2013) + + Before, this class tried to maintain its own Dictionary to remember what lines were already highlighted + in the latest thread run. + + Now this is all managed by ModifiedLineRangeManager: Modified lines get added to it, and this + class fetches lines from it, thereby removing them from the ModifiedLineRangeManager. This avoids + duplicate handling unless a line gets changed again afterwards. + #tag EndNote + + + #tag Property, Flags = &h1 + Protected buffer As gapBuffer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected changedLines As ModifiedLineRangeManager + #tag EndProperty + + #tag Property, Flags = &h0 + definition As highlightdefinition + #tag EndProperty + + #tag Property, Flags = &h1 + #tag Note + LineManager + #tag EndNote + Protected mLines As WeakRef + #tag EndProperty + + #tag Property, Flags = &h21 + Private mOwner As weakRef + #tag EndProperty + + #tag ComputedProperty, Flags = &h21 + #tag Getter + Get + if mOwner <> nil then + return CustomEditField(mOwner.Value) + else + //stop thread, since the owner is no longer valid + self.Kill + return nil + end if + End Get + #tag EndGetter + Private owner As CustomEditField + #tag EndComputedProperty + + + #tag Constant, Name = HighlightDoneMsg, Type = Double, Dynamic = False, Default = \"0", Scope = Public + #tag EndConstant + + #tag Constant, Name = LineHighlightedMsg, Type = Double, Dynamic = False, Default = \"1", Scope = Public + #tag EndConstant + + #tag Constant, Name = ScreenLinesHighlightedMsg, Type = Double, Dynamic = False, Default = \"2", Scope = Public + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + EditorType="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Priority" + Visible=true + Group="Behavior" + InitialValue="5" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="StackSize" + Visible=true + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/SyntaxHighlightEngine/SymbolsDefinition.xojo_code b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/SymbolsDefinition.xojo_code new file mode 100644 index 0000000..8485112 --- /dev/null +++ b/Third Party Classes/CustomEditField/SyntaxHighlightEngine/SymbolsDefinition.xojo_code @@ -0,0 +1,149 @@ +#tag Class +Protected Class SymbolsDefinition + #tag Method, Flags = &h0 + Sub appendToXMLNode(parent as xmlNode, depth as integer = 2) + //appends this context to the parent xml node + //this is done to export the syntax definition as an xml file. + + dim xdoc as XmlDocument + dim node, context as XmlNode + + xdoc = parent.OwnerDocument + context=parent.AppendChild(xdoc.CreateElement("symbol")) + + //name + context.SetAttribute("type",type) + + //entry regex? + if EntryRegex<>"" then + node=context.AppendChild(xdoc.CreateElement("entryRegEx")) + node.AppendChild(xdoc.CreateTextNode(EntryRegEx)) + IndentNode(node,depth+1) + end if + + IndentNode(Context,depth, true) + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub IndentNode(node As XmlNode, level As Integer, indentCloseTag As Boolean = False) + Dim i As Integer + Dim s As String + s = EndOfLine + For i = 1 To level + s = s + Chr(9) // Tab + Next + node.Parent.Insert(node.OwnerDocument.CreateTextNode(s), node) + If indentCloseTag Then + node.AppendChild(node.OwnerDocument.CreateTextNode(s)) + End If + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub loadFromXmlNode(node as xmlNode) + //load context out of an xml node + + //type + type = node.GetAttribute("type") + + dim i as Integer + dim subNode as XmlNode + + for i=0 to node.ChildCount-1 + subNode=node.Child(i) + select case subNode.Name + case "entryRegEx" + EntryRegex = subNode.FirstChild.Value + end select + next + End Sub + #tag EndMethod + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mRegex + End Get + #tag EndGetter + #tag Setter + Set + mRegex = value + End Set + #tag EndSetter + EntryRegex As string + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mRegex As string + #tag EndProperty + + #tag Property, Flags = &h21 + Private mType As string + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mType + End Get + #tag EndGetter + #tag Setter + Set + mType = value + End Set + #tag EndSetter + Type As string + #tag EndComputedProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="EntryRegex" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Type" + Group="Behavior" + Type="string" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/TextStorage/ArrayStorage.xojo_code b/Third Party Classes/CustomEditField/TextStorage/ArrayStorage.xojo_code new file mode 100644 index 0000000..496a7a1 --- /dev/null +++ b/Third Party Classes/CustomEditField/TextStorage/ArrayStorage.xojo_code @@ -0,0 +1,155 @@ +#tag Class +Protected Class ArrayStorage +Implements IBufferStorage + #tag Method, Flags = &h0 + Sub Constructor(size as integer) + ReDim Storage(size - 1) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Copy(from as IbufferStorage, fromIndex as integer, localIndex as integer, length as integer) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if length = 0 then Return + dim src as ArrayStorage = ArrayStorage(from) + + dim maxLocal, maxSrc as Integer + maxLocal = UBound(Storage) + maxSrc = UBound(src.Storage) + + for i as Integer = 0 to length - 1 + if localIndex + i > maxLocal or fromIndex + i > maxSrc then exit for + Storage(localIndex + i) = src.Storage(fromIndex + i) + next + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Size() As integer + Return UBound(Storage) + 1 + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Size(assigns length as integer) + // Part of the IBufferStorage interface. + ReDim Storage(length - 1) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function StringValue(index as integer, length as integer) As string + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if length = 0 then Return "" + if index >= Size then Return "" + + //create a copy of the right size + dim tmp() as String + ReDim tmp(length - 1) + + for i as Integer = 0 to length - 1 + tmp(i) = Storage(i + index) + next + + dim ret as String = Join(tmp, "") + + if ret.Encoding = nil then + break ' must never happen + end if + + return ret.ConvertEncoding(EditFieldGlobals.InternalEncoding) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub StringValue(index as integer, length as integer, assigns value as string) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if length = 0 then Return + if value.Len = 0 then Return + + dim encoding as TextEncoding = value.Encoding + if encoding = nil then + // we should never get here - it would probably lead to incorreclty encoded text when it's not pure ASCII + break + encoding = Encodings.SystemDefault + value = value.DefineEncoding(encoding) + end + if encoding.Equals(encodings.UTF16) then + // UTF-16 causes problems with certain chars (e.g. "🔍"), so we must + // convert it into UTF-8 or UTF-32. + value = value.ConvertEncoding(EditFieldGlobals.InternalEncoding) + end if + + //split data into a tmp array + dim chars() as String = value.Split("") + + //and copy it + for i as Integer = 0 to length - 1 + Storage(i + index) = chars(i) + next + End Sub + #tag EndMethod + + + #tag Note, Name = Info + Text storage as an array of strings. + #tag EndNote + + + #tag Property, Flags = &h21 + Private Storage() As string + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/TextStorage/GapBuffer.xojo_code b/Third Party Classes/CustomEditField/TextStorage/GapBuffer.xojo_code new file mode 100644 index 0000000..9078937 --- /dev/null +++ b/Third Party Classes/CustomEditField/TextStorage/GapBuffer.xojo_code @@ -0,0 +1,314 @@ +#tag Class +Class GapBuffer + #tag Method, Flags = &h1 + Protected Sub checkBounds(index as integer) + //checks if index is within bounds... + + if index < 0 or index > Length then + dim ex as new OutOfBoundsException + ex.Message = "Tried to access the buffer at invalid index."+EndOfLine+"Logic length = "+str(Length)+", index = "+str(index) + Raise ex + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor() + //create a new gap buffer + buffer = getBufferStorage(0) + gapStart = 0 + gapEnd = 0 + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub ensureBufferSize(minRequiredLength as integer) + //makes sure there's at least minRequiredLength spaces available in the buffer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + dim newbuffer as IBufferStorage + + dim delta as Integer + + //gap too small + if GapLength < minRequiredLength or GapLength < minGapSize then + //resize buffer + delta = max(minRequiredLength, maxGapSize) - GapLength + newbuffer = getBufferStorage(buffer.Size + delta) + + //gap too big! + elseif GapLength > maxGapSize then + delta = max(minRequiredLength, minGapSize) - GapLength + newbuffer = getBufferStorage(buffer.size + delta) + + else //no need to resize! + Return + + end if + + //copy contents to new buffer + newbuffer.Copy(buffer, 0, 0, gapStart) + 'newbuffer.StringValue(0, gapStart) = buffer.StringValue(0, gapStart) //before gap + newbuffer.Copy(buffer, gapEnd, newbuffer.Size - (buffer.Size - gapEnd), buffer.Size - gapEnd) + 'newbuffer.StringValue(newbuffer.Size - (buffer.Size - gapEnd), buffer.Size - gapEnd) = buffer.StringValue(gapEnd, buffer.Size - gapEnd) //after gap + buffer = newbuffer + gapEnd = gapEnd + delta + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Function getBufferStorage(size as integer) As IBufferStorage + #if EditFieldGlobals.TextStorageType = EditFieldGlobals.STORAGE_MEMORYBLOCK then + Return new MemoryBlockStorage(size) + #elseif EditFieldGlobals.TextStorageType = EditFieldGlobals.STORAGE_ARRAY then + Return new ArrayStorage(size) + #else + Return new MemoryBlockStorageWide(size) + #endif + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function getCharAt(offset as integer) As string + //if offset before gap + if offset < gapStart then + Return buffer.StringValue(offset, 1) + end if + + //else, offset it by length + Return buffer.StringValue(offset + GapLength, 1) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function getText(index as integer, length as integer) As string + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //nothing to get + if Length <= 0 or self.Length = 0 then Return "" + + checkBounds(index) + + dim delta as Integer = index + Length + + //all text before gap? + if delta < gapStart then + Return buffer.StringValue(index, Length) + end if + + //all text after gap? + if index > gapStart then + return buffer.StringValue(index + GapLength, Length) + end if + + //text before and after gap + dim result as IBufferStorage = getBufferStorage(length) + result.Copy(buffer, index, 0, gapStart - index) + result.Copy(buffer, gapEnd, gapStart - index, delta - gapStart) + + Return result.StringValue(0, result.size) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub insert(index as integer, text as string) + replace(index, 0, text) + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub placeGap(index as integer) + //move the gap to a different place within the buffer + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if index = gapStart and GapLength>0 then Return + + dim newbuffer as IBufferStorage = buffer + + //empty? + if buffer.Size = 0 then Return + + //moving before current gap + if index < gapStart then + dim count as Integer = gapStart - index //items to move + newbuffer.StringValue(index + GapLength, Count) = buffer.StringValue(index, Count) //move items + + gapStart = gapStart - Count + gapEnd = gapEnd - Count + + //moving after current gap start + else + dim count as Integer = index - gapStart //items to move + if count > 0 then + newbuffer.StringValue(gapStart, Count) = buffer.StringValue(gapEnd, Count) //move items + + gapStart = gapStart + Count + gapEnd = gapEnd + Count + end if + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function remove(index as integer, length as integer) As boolean + //make sure we can remove + if index < 0 or index > self.Length or self.Length = 0 then Return false + replace(index, Length, "") + Return true + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub replace(index as integer, length as integer, text as string) + text = text.ConvertEncoding(EditFieldGlobals.InternalEncoding) ' make sure it's not in UTF-16 + + checkBounds(index) + + placeGap(index) + dim minLengthRequired as Integer = text.len + ensureBufferSize(minLengthRequired) + + //replace chars by moving them INTO the gap + gapEnd = gapEnd + Length + + //add the text + buffer.StringValue(index, text.len) = text + gapStart = gapStart + text.len + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub setText(text as string) + //set the whole thing at once + text = text.ConvertEncoding(EditFieldGlobals.InternalEncoding) ' make sure it's not in UTF-16 + buffer.Size = text.len + buffer.StringValue(0, text.len) = text + gapStart = text.Len/2 + gapEnd = gapStart + End Sub + #tag EndMethod + + + #tag Note, Name = Info + loosely adapted from: + http://www.codeproject.com/KB/recipes/GenericGapBuffer.aspx + + more info here: + http://en.wikipedia.org/wiki/Gap_buffer + #tag EndNote + + + #tag Property, Flags = &h1 + Protected buffer As IBufferStorage + #tag EndProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + return mgapEnd + End Get + #tag EndGetter + #tag Setter + Set + if value > buffer.Size then value = buffer.Size + mgapEnd = value + End Set + #tag EndSetter + Protected gapEnd As Integer + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return gapEnd - gapStart + End Get + #tag EndGetter + GapLength As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected gapStart As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return buffer.Size - GapLength + End Get + #tag EndGetter + Length As Integer + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mgapEnd As Integer + #tag EndProperty + + + #tag Constant, Name = maxGapSize, Type = Double, Dynamic = False, Default = \"256", Scope = Private + #tag EndConstant + + #tag Constant, Name = minGapSize, Type = Double, Dynamic = False, Default = \"32", Scope = Private + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="GapLength" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Length" + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/TextStorage/IBufferStorage.xojo_code b/Third Party Classes/CustomEditField/TextStorage/IBufferStorage.xojo_code new file mode 100644 index 0000000..cd1a340 --- /dev/null +++ b/Third Party Classes/CustomEditField/TextStorage/IBufferStorage.xojo_code @@ -0,0 +1,70 @@ +#tag Interface +Protected Interface IBufferStorage + #tag Method, Flags = &h0 + Sub Copy(from as IbufferStorage, fromIndex as integer, localIndex as integer, length as integer) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Size() As integer + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Size(assigns length as integer) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function StringValue(index as integer, length as integer) As string + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub StringValue(index as integer, length as integer, assigns value as string) + + End Sub + #tag EndMethod + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Interface +#tag EndInterface diff --git a/Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorage.xojo_code b/Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorage.xojo_code new file mode 100644 index 0000000..19c8fbe --- /dev/null +++ b/Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorage.xojo_code @@ -0,0 +1,129 @@ +#tag Class +Protected Class MemoryBlockStorage +Implements IBufferStorage + #tag Method, Flags = &h0 + Sub Constructor(size as integer) + Storage = new MemoryBlock(size) + + // since this storage class can only handle single-byte characters, multi-byte strings need to be converted + #if TargetWin32 + mSingleByteEncoding = encodings.WindowsANSI + #elseif TargetLinux + mSingleByteEncoding = encodings.ISOLatin1 + #else + mSingleByteEncoding = encodings.MacRoman + #endif + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Copy(from as IbufferStorage, fromIndex as integer, localIndex as integer, length as integer) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + if from.size = 0 or length = 0 then Return //nuthin' to copy + dim src as MemoryBlockStorage = MemoryBlockStorage(from) + + me.Storage.StringValue(localIndex, min(length, storage.Size - localIndex)) = src.Storage.StringValue(fromIndex, min(length, src.Size - fromIndex)) + me.mSingleByteEncoding = src.mSingleByteEncoding + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Size() As integer + Return Storage.Size + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Size(assigns length as integer) + // Part of the IBufferStorage interface. + Storage.Size = length + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function StringValue(index as integer, length as integer) As string + // Part of the IBufferStorage interface. + if length = 0 then Return "" + if index >= Size then Return "" + + dim res as String = me.Storage.StringValue(index, min(length, storage.Size - index)).DefineEncoding(mSingleByteEncoding) + + Return res.ConvertEncoding(EditFieldGlobals.InternalEncoding) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub StringValue(index as integer, length as integer, assigns value as string) + // Part of the IBufferStorage interface. + if length = 0 then Return + + //Couldn't make memory blocks work correctly with multi-byte chars, so I force the data to be single-byte by re-encoding it. + if value.encoding <> nil then + value = value.ConvertEncoding(mSingleByteEncoding) + else + // we should never get here - it would probably lead to incorreclty encoded text when it's not pure ASCII + break + value = value.DefineEncoding(mSingleByteEncoding) + end if + + Storage.StringValue(index, length) = value + End Sub + #tag EndMethod + + + #tag Note, Name = Info + Text storage as a MemoryBlock + #tag EndNote + + + #tag Property, Flags = &h21 + Private mSingleByteEncoding As TextEncoding + #tag EndProperty + + #tag Property, Flags = &h21 + Private Storage As memoryBlock + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorageWide.xojo_code b/Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorageWide.xojo_code new file mode 100644 index 0000000..4fbdf98 --- /dev/null +++ b/Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorageWide.xojo_code @@ -0,0 +1,152 @@ +#tag Class +Protected Class MemoryBlockStorageWide +Implements IBufferStorage + #tag Method, Flags = &h0 + Sub Constructor(size as integer) + Storage = new MemoryBlock(size * BytesPerChar) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Copy(from as IbufferStorage, fromIndex as integer, localIndex as integer, length as integer) + #if not DebugBuild + #pragma DisableBackgroundTasks + #pragma DisableBoundsChecking + + #endif + + //indexes and length all have to be multiplied by BytesPerChar + fromIndex = fromIndex * BytesPerChar + localIndex = localIndex * BytesPerChar + length = length * BytesPerChar + + if from.size = 0 or length = 0 then Return //nuthin' to copy + dim src as MemoryBlockStorageWide = MemoryBlockStorageWide(from) + + Storage.StringValue(localIndex, min(length, storage.Size - localIndex)) = src.Storage.StringValue(fromIndex, min(length, src.Size * BytesPerChar - fromIndex)) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function Size() As integer + Return Storage.Size / BytesPerChar + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Size(assigns length as integer) + // Part of the IBufferStorage interface. + Storage.Size = length * BytesPerChar + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function StringValue(index as integer, length as integer) As string + // Part of the IBufferStorage interface. + if length = 0 then Return "" + if index >= Size then Return "" + + index = index * BytesPerChar + length = length * BytesPerChar + + dim res as String = Storage.StringValue(index, min(length, storage.Size - index)).DefineEncoding(Encodings.UTF32) + + return res.ConvertEncoding(EditFieldGlobals.InternalEncoding) + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub StringValue(index as integer, length as integer, assigns value as string) + // Part of the IBufferStorage interface. + + if length = 0 then Return + + #if BytesPerChar <> 4 + error - this code requires that this value is always 4 + #endif + + // We need to store the data in UTF-32 format + dim newVal as String = value + if newVal.Encoding <> Encodings.UTF32 then + #if TargetCarbon + // Oddly, Carbon can't convert from UTF-8 directly to UTF-32, so we go with UTF-16 in between + newVal = value.ConvertEncoding(Encodings.UTF16).ConvertEncoding(Encodings.UTF32) + #else + newVal = value.ConvertEncoding(Encodings.UTF32) + #endif + end if + + index = index * BytesPerChar + length = length * BytesPerChar + + if newVal.LenB <> length then + dim d as MemoryBlock = newVal + if d.UInt32Value(0) = &h0000FEFF or d.UInt32Value(0) = &hFFFE0000 then + // remove BOM + newVal = d.StringValue(4, d.Size-4) + end if + if newVal.LenB <> length then + // This would happen with UTF-16 chars that occupy two 16 bit words. + // For example, "🔍" has Length=2 in UTF-16, but has Length=1 in UTF-32 and in UTF-8. + // Since we cannot handle this, this must never happen, i.e. the caller may never + // handle UTF-16 encoded Strings. If it does, it's an internal bug. + break + end if + end if + + Storage.StringValue(index, length) = newVal + End Sub + #tag EndMethod + + + #tag Note, Name = Info + Text storage as a MemoryBlock + #tag EndNote + + + #tag Property, Flags = &h21 + Private Storage As memoryBlock + #tag EndProperty + + + #tag Constant, Name = BytesPerChar, Type = Double, Dynamic = False, Default = \"4", Scope = Private + #tag EndConstant + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Undo/UndoManager.xojo_code b/Third Party Classes/CustomEditField/Undo/UndoManager.xojo_code new file mode 100644 index 0000000..06fbcdf --- /dev/null +++ b/Third Party Classes/CustomEditField/Undo/UndoManager.xojo_code @@ -0,0 +1,239 @@ +#tag Class +Protected Class UndoManager + #tag Method, Flags = &h1 + Protected Sub addActionToRedoStack(action as UndoableAction) + RedoStack.Append action + undoStackIndex = undoStackIndex - 1 + End Sub + #tag EndMethod + + #tag Method, Flags = &h1 + Protected Sub addActionToUndoStack(action as UndoableAction) + UndoStack.Append action + undoStackIndex = undoStackIndex + 1 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Constructor() + mEnabled = true + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsDirty() As boolean + Return undoStackIndex <> 0 + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Function IsUndoing() As boolean + Return undoing + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Push(action as UndoableAction) + if undoing or not mEnabled then Return + if action = nil then Return + addActionToUndoStack(action) + + redim RedoStack(-1) + EnableMenuItems + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redo() + if not CanRedo then Return + dim ID as Integer = RedoStack(UBound(RedoStack)).EventID + redo(ID) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redo(id as integer) + if not CanRedo then Return + + dim match as Boolean + do + match = false + undoing = true + + if id = RedoStack(UBound(RedoStack)).EventID then + dim action as UndoableAction = RedoStack.Pop + addActionToUndoStack(action) + + action.Redo + match = true + end if + + undoing = False + loop until id = 0 or not CanRedo or not match + + EnableMenuItems + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Reset() + ReDim UndoStack(-1) + ReDim RedoStack(-1) + undoStackIndex = 0 + undoing = False + EnableMenuItems + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub ResetDirtyFlag() + undoStackIndex = 0 + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Undo() + if not CanUndo then Return + dim ID as Integer = UndoStack(UBound(UndoStack)).EventID + Undo(ID) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Undo(ID as integer) + if not CanUndo then Return + + dim match as Boolean + do + match = false + undoing = true + + if id = UndoStack(UBound(UndoStack)).EventID then + dim action as UndoableAction = UndoStack.Pop + addActionToRedoStack(action) + + action.Undo + match = true + end if + + undoing = False + loop until id = 0 or not CanUndo or not match + + EnableMenuItems + + End Sub + #tag EndMethod + + + #tag Note, Name = About + Part of CustomEditField + #tag EndNote + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return mEnabled and UBound(RedoStack) > -1 + End Get + #tag EndGetter + CanRedo As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + Return mEnabled and UBound(UndoStack) > -1 + End Get + #tag EndGetter + CanUndo As boolean + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return mEnabled + End Get + #tag EndGetter + #tag Setter + Set + mEnabled = value + End Set + #tag EndSetter + Enabled As Boolean + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private mEnabled As Boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected RedoStack() As UndoableAction + #tag EndProperty + + #tag Property, Flags = &h1 + Protected undoing As boolean + #tag EndProperty + + #tag Property, Flags = &h1 + Protected UndoStack() As UndoableAction + #tag EndProperty + + #tag Property, Flags = &h1 + Protected undoStackIndex As Integer + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="CanRedo" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="CanUndo" + Group="Behavior" + InitialValue="0" + Type="boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Undo/UndoableAction.xojo_code b/Third Party Classes/CustomEditField/Undo/UndoableAction.xojo_code new file mode 100644 index 0000000..efcf892 --- /dev/null +++ b/Third Party Classes/CustomEditField/Undo/UndoableAction.xojo_code @@ -0,0 +1,64 @@ +#tag Interface +Protected Interface UndoableAction + #tag Method, Flags = &h0 + Function EventID() As integer + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub EventID(assigns value as integer) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redo() + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Undo() + + End Sub + #tag EndMethod + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Interface +#tag EndInterface diff --git a/Third Party Classes/CustomEditField/Undo/UndoableDelete.xojo_code b/Third Party Classes/CustomEditField/Undo/UndoableDelete.xojo_code new file mode 100644 index 0000000..94f264b --- /dev/null +++ b/Third Party Classes/CustomEditField/Undo/UndoableDelete.xojo_code @@ -0,0 +1,127 @@ +#tag Class +Protected Class UndoableDelete +Implements UndoableAction + #tag Method, Flags = &h0 + Sub Constructor(field as CustomEditField, offset as integer, length as integer, text as string, attrs() as TextLineAttributes, oldCaretPos as integer, ID as integer) + Reference = new WeakRef(field) + self.offset = offset + self.length = length + self.text = text + self.attrs = attrs + self.oldCaretPos = oldCaretPos + EventID = ID + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function EventID() As integer + // Part of the UndoableAction interface. + Return EventDesc + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub EventID(assigns value as integer) + // Part of the UndoableAction interface. + EventDesc = value + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redo() + if owner = nil then Return + owner.private_remove(offset, text.len) + owner.SelStart = min(owner.TextLength, max(0, owner.CaretPos)) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Undo() + if owner = nil then Return + owner.Insert(offset, text) + owner.private_lines.setAttributesOfLinesInRange(offset, length, attrs) + owner.SelStart = min(owner.TextLength, max(0, oldCaretPos)) + End Sub + #tag EndMethod + + + #tag Property, Flags = &h1 + Protected attrs() As TextLineAttributes + #tag EndProperty + + #tag Property, Flags = &h1 + Protected EventDesc As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected length As integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected offset As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected oldCaretPos As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if me.Reference <> nil then + return CustomEditField(me.Reference.Value) + else + return nil + end if + End Get + #tag EndGetter + Protected owner As CustomEditField + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected Reference As weakRef + #tag EndProperty + + #tag Property, Flags = &h1 + Protected text As string + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/Undo/UndoableReplace.xojo_code b/Third Party Classes/CustomEditField/Undo/UndoableReplace.xojo_code new file mode 100644 index 0000000..c551c2b --- /dev/null +++ b/Third Party Classes/CustomEditField/Undo/UndoableReplace.xojo_code @@ -0,0 +1,133 @@ +#tag Class +Protected Class UndoableReplace +Implements UndoableAction + #tag Method, Flags = &h0 + Sub Constructor(field as customEditField, offset as integer, length as Integer, originalText as string, text as string, attrs() as TextLineAttributes, oldCaretPos as integer, ID as integer) + Reference = new WeakRef(field) + self.offset = offset + self.length = length + self.originalText = originalText + self.text = text + self.attrs = attrs + self.oldCaretPos = oldCaretPos + EventID = id + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function EventID() As integer + // Part of the UndoableAction interface. + Return EventDesc + + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub EventID(assigns value as integer) + // Part of the UndoableAction interface. + EventDesc = value + + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Redo() + if owner = nil then Return + owner.private_replace(offset, originalText.len, text, true) + owner.SelStart = min(owner.TextLength, max(0, owner.CaretPos)) + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Sub Undo() + if owner = nil then Return + owner.private_replace(offset, text.len, originalText, true) + owner.private_lines.setAttributesOfLinesInRange(offset, length, attrs) + owner.SelStart = min(owner.TextLength, max(0, oldCaretPos)) + End Sub + #tag EndMethod + + + #tag Property, Flags = &h1 + Protected attrs() As TextLineAttributes + #tag EndProperty + + #tag Property, Flags = &h1 + Protected EventDesc As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected length As integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected offset As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected oldCaretPos As Integer + #tag EndProperty + + #tag Property, Flags = &h1 + Protected originalText As string + #tag EndProperty + + #tag ComputedProperty, Flags = &h1 + #tag Getter + Get + if me.Reference <> nil then + return CustomEditField(me.Reference.Value) + else + return nil + end if + End Get + #tag EndGetter + Protected owner As CustomEditField + #tag EndComputedProperty + + #tag Property, Flags = &h1 + Protected Reference As weakRef + #tag EndProperty + + #tag Property, Flags = &h1 + Protected text As string + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + InitialValue="-2147483648" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Third Party Classes/CustomEditField/imgs/blockEndMarker.bmp b/Third Party Classes/CustomEditField/imgs/blockEndMarker.bmp new file mode 100644 index 0000000..28390e9 Binary files /dev/null and b/Third Party Classes/CustomEditField/imgs/blockEndMarker.bmp differ diff --git a/Third Party Classes/CustomEditField/imgs/blockFoldedMarker.bmp b/Third Party Classes/CustomEditField/imgs/blockFoldedMarker.bmp new file mode 100644 index 0000000..67a6706 Binary files /dev/null and b/Third Party Classes/CustomEditField/imgs/blockFoldedMarker.bmp differ diff --git a/Third Party Classes/CustomEditField/imgs/blockFoldedTrailMarker.bmp b/Third Party Classes/CustomEditField/imgs/blockFoldedTrailMarker.bmp new file mode 100644 index 0000000..8b1d7d4 Binary files /dev/null and b/Third Party Classes/CustomEditField/imgs/blockFoldedTrailMarker.bmp differ diff --git a/Third Party Classes/CustomEditField/imgs/blockStartMarker.bmp b/Third Party Classes/CustomEditField/imgs/blockStartMarker.bmp new file mode 100644 index 0000000..f5bea62 Binary files /dev/null and b/Third Party Classes/CustomEditField/imgs/blockStartMarker.bmp differ diff --git a/Third Party Classes/CustomEditField/imgs/bookmarksimg.bmp b/Third Party Classes/CustomEditField/imgs/bookmarksimg.bmp new file mode 100644 index 0000000..8d83b40 Binary files /dev/null and b/Third Party Classes/CustomEditField/imgs/bookmarksimg.bmp differ diff --git a/Third Party Classes/CustomEditField/imgs/button.png b/Third Party Classes/CustomEditField/imgs/button.png new file mode 100644 index 0000000..df93385 Binary files /dev/null and b/Third Party Classes/CustomEditField/imgs/button.png differ diff --git a/Widgets/ColorPicker_MTC.xojo_code b/Widgets/ColorPicker_MTC.xojo_code new file mode 100644 index 0000000..8f32240 --- /dev/null +++ b/Widgets/ColorPicker_MTC.xojo_code @@ -0,0 +1,264 @@ +#tag Class +Protected Class ColorPicker_MTC +Inherits Canvas + #tag Event + Function MouseDown(X As Integer, Y As Integer) As Boolean + return not IsContextualClick + + #pragma unused X + #pragma unused Y + + End Function + #tag EndEvent + + #tag Event + Sub MouseUp(X As Integer, Y As Integer) + if X < 0 or X > me.Width or Y < 0 or Y > me.Height then return + + dim c as Color = zColor + if SelectColor( c, PromptText ) then + me.Value = c + end if + + End Sub + #tag EndEvent + + #tag Event + Sub Paint(g As Graphics, areas() As REALbasic.Rect) + #pragma unused areas + + const kMargin = 4 + const kArc = 4 + + dim w as integer = g.Width - kMargin - kMargin + dim h as integer = g.Height - kMargin - kMargin + g.ForeColor = zColor + g.FillRoundRect( kMargin, kMargin, w, h, kArc, kArc ) + + g.ForeColor = &c00000000 + g.DrawRoundRect( 0, 0, g.Width, g.Height, kArc, kArc ) + + End Sub + #tag EndEvent + + + #tag Hook, Flags = &h0 + Event ValueChanged() + #tag EndHook + + + #tag Property, Flags = &h0 + PromptText As String = "Select a color:" + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return zColor + End Get + #tag EndGetter + #tag Setter + Set + zColor = value + me.Refresh + + RaiseEvent ValueChanged() + + End Set + #tag EndSetter + Value As Color + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private zColor As Color = &c00000000 + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="AcceptFocus" + Visible=true + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AcceptTabs" + Visible=true + Group="Behavior" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AutoDeactivate" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="DoubleBuffer" + Visible=true + Group="Behavior" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EraseBackground" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="100" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HelpTag" + Visible=true + Group="Appearance" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + Type="Integer" + EditorType="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="InitialParent" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LockBottom" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockLeft" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockRight" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockTop" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="PromptText" + Group="Behavior" + InitialValue="Select a color:" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="TabIndex" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabPanelIndex" + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabStop" + Visible=true + Group="Position" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Transparent" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="UseFocusRing" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Value" + Group="Behavior" + InitialValue="&c000000" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="100" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Widgets/DialogButtonContainer.xojo_window b/Widgets/DialogButtonContainer.xojo_window new file mode 100644 index 0000000..3864528 --- /dev/null +++ b/Widgets/DialogButtonContainer.xojo_window @@ -0,0 +1,450 @@ +#tag Window +Begin ContainerControl DialogButtonContainer + AcceptFocus = False + AcceptTabs = True + AutoDeactivate = True + BackColor = &cFFFFFF00 + Backdrop = 0 + Compatibility = "" + Enabled = True + EraseBackground = True + HasBackColor = False + Height = 54 + HelpTag = "" + InitialParent = "" + Left = 32 + LockBottom = False + LockLeft = False + LockRight = False + LockTop = False + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + Top = 32 + Transparent = True + UseFocusRing = False + Visible = True + Width = 416 + Begin PlatformPushButton AlternateButton + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "Alternate" + Default = False + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Scope = 0 + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 14 + Underline = False + Visible = False + Width = 120 + End + Begin PlatformPushButton OkButton + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = False + Caption = "OK" + Default = True + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 184 + LockBottom = False + LockedInPosition= False + LockLeft = False + LockRight = True + LockTop = True + Scope = 0 + TabIndex = 1 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 14 + Underline = False + Visible = True + Width = 100 + End + Begin PlatformPushButton CancelButton + AutoDeactivate = True + Bold = False + ButtonStyle = "0" + Cancel = True + Caption = "Cancel" + Default = False + Enabled = True + Height = 20 + HelpTag = "" + IncreaseHeightLinux= 4 + IncreaseHeightMac= 0 + IncreaseHeightWindows= 4 + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 296 + LockBottom = False + LockedInPosition= False + LockLeft = False + LockRight = True + LockTop = True + Scope = 0 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 14 + Underline = False + Visible = True + Width = 100 + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Open() + // + // Organize the buttons + // (Already set up for Windows) + // + + #if TargetMacOS then + // + // Switch places + // + + dim okLeft as integer = CancelButton.Left + dim cancelLeft as integer = OkButton.Left + + OkButton.Left = okLeft + CancelButton.Left = cancelLeft + + #endif + + RaiseEvent Open() + End Sub + #tag EndEvent + + + #tag Hook, Flags = &h0 + Event AlternateAction() + #tag EndHook + + #tag Hook, Flags = &h0 + Event CancelAction() + #tag EndHook + + #tag Hook, Flags = &h0 + Event OkAction() + #tag EndHook + + #tag Hook, Flags = &h0 + Event Open() + #tag EndHook + + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return AlternateButton.Caption + End Get + #tag EndGetter + #tag Setter + Set + value = value.Trim + + AlternateButton.Caption = value + AlternateButton.Visible = value <> "" + End Set + #tag EndSetter + AlternateCaption As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return CancelButton.Caption + End Get + #tag EndGetter + #tag Setter + Set + CancelButton.Caption = value.Trim + End Set + #tag EndSetter + CancelCaption As String + #tag EndComputedProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + return OkButton.Caption + End Get + #tag EndGetter + #tag Setter + Set + OkButton.Caption = value.Trim + End Set + #tag EndSetter + OkCaption As String + #tag EndComputedProperty + + +#tag EndWindowCode + +#tag Events AlternateButton + #tag Event + Sub Action() + RaiseEvent AlternateAction + End Sub + #tag EndEvent +#tag EndEvents +#tag Events OkButton + #tag Event + Sub Action() + RaiseEvent OkAction + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events CancelButton + #tag Event + Sub Action() + RaiseEvent CancelAction + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="AcceptFocus" + Visible=true + Group="Behavior" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AcceptTabs" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="AlternateCaption" + Visible=true + Group="Behavior" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="AutoDeactivate" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CancelCaption" + Visible=true + Group="Behavior" + InitialValue="Cancel" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="EraseBackground" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="300" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HelpTag" + Visible=true + Group="Appearance" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="InitialParent" + Group="Position" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LockBottom" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockLeft" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockRight" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockTop" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="OkCaption" + Visible=true + Group="Behavior" + InitialValue="OK" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="TabIndex" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabPanelIndex" + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabStop" + Visible=true + Group="Position" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Transparent" + Visible=true + Group="Behavior" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="UseFocusRing" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="300" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Widgets/PlatformPushButton.xojo_code b/Widgets/PlatformPushButton.xojo_code new file mode 100644 index 0000000..26535f6 --- /dev/null +++ b/Widgets/PlatformPushButton.xojo_code @@ -0,0 +1,279 @@ +#tag Class +Protected Class PlatformPushButton +Inherits PushButton + #tag Event + Sub Open() + dim increase as integer + + #if TargetWin32 then + increase = IncreaseHeightWindows + #elseif TargetLinux then + increase = IncreaseHeightLinux + #elseif TargetMacOS then + increase = IncreaseHeightMac + #endif + + if increase <> 0 then + self.Top = self.Top - (increase \ 2) + self.Height = self.Height + increase + end if + + RaiseEvent Open() + + End Sub + #tag EndEvent + + + #tag Hook, Flags = &h0 + Event Open() + #tag EndHook + + + #tag Property, Flags = &h0 + IncreaseHeightLinux As Integer = 4 + #tag EndProperty + + #tag Property, Flags = &h0 + IncreaseHeightMac As Integer = 0 + #tag EndProperty + + #tag Property, Flags = &h0 + IncreaseHeightWindows As Integer = 4 + #tag EndProperty + + + #tag ViewBehavior + #tag ViewProperty + Name="AutoDeactivate" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Bold" + Visible=true + Group="Font" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="ButtonStyle" + Visible=true + Group="Appearance" + InitialValue="0" + Type="ButtonFrameStyle" + EditorType="Enum" + #tag EnumValues + "0 - Push" + "1 - Gradient" + "2 - Rounded" + "3 - Recessed" + "4 - Textured" + "5 - Rounded Textured" + "6 - Square" + "7 - Bevel" + "8 - Round" + "9 - Help" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Cancel" + Visible=true + Group="Appearance" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Caption" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Default" + Visible=true + Group="Appearance" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Enabled" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="20" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="HelpTag" + Visible=true + Group="Appearance" + Type="String" + EditorType="MultiLineEditor" + #tag EndViewProperty + #tag ViewProperty + Name="IncreaseHeightLinux" + Visible=true + Group="Behavior" + InitialValue="4" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="IncreaseHeightMac" + Visible=true + Group="Behavior" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="IncreaseHeightWindows" + Visible=true + Group="Behavior" + InitialValue="4" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Index" + Visible=true + Group="ID" + Type="Integer" + EditorType="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="InitialParent" + Group="Position" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Italic" + Visible=true + Group="Font" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Left" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="LockBottom" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockLeft" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockRight" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="LockTop" + Visible=true + Group="Position" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="TabIndex" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabPanelIndex" + Group="Position" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="TabStop" + Visible=true + Group="Position" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="TextFont" + Visible=true + Group="Font" + InitialValue="System" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="TextSize" + Visible=true + Group="Font" + InitialValue="0" + Type="Single" + #tag EndViewProperty + #tag ViewProperty + Name="TextUnit" + Visible=true + Group="Font" + InitialValue="0" + Type="FontUnits" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Pixel" + "2 - Point" + "3 - Inch" + "4 - Millimeter" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Top" + Visible=true + Group="Position" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Underline" + Visible=true + Group="Font" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="80" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/Windows/TBConstants.xojo_code b/Windows/TBConstants.xojo_code new file mode 100644 index 0000000..f682145 --- /dev/null +++ b/Windows/TBConstants.xojo_code @@ -0,0 +1,29 @@ +#tag Module +Protected Module TBConstants + #tag Constant, Name = kTBEditorButtonIndexCompile, Type = Double, Dynamic = False, Default = \"0", Scope = Public + #tag EndConstant + + #tag Constant, Name = kTBEditorButtonIndexRunInIDE, Type = Double, Dynamic = False, Default = \"2", Scope = Public + #tag EndConstant + + #tag Constant, Name = kTBEditorButtonIndexTestRun, Type = Double, Dynamic = False, Default = \"3", Scope = Public + #tag EndConstant + + #tag Constant, Name = kTBEditorHelpTagIDEUnavailableSuffix, Type = String, Dynamic = False, Default = \" (The IDE is not currently reachable. If it\xE2\x80\x99s running\x2C quit and relaunch it.)", Scope = Public + #tag EndConstant + + #tag Constant, Name = kTBEditorHelpTagRunInIDE, Type = String, Dynamic = False, Default = \"Run the current script in the Xojo IDE", Scope = Public + #tag EndConstant + + #tag Constant, Name = kTBEditorLabelCompile, Type = String, Dynamic = False, Default = \"Compile", Scope = Public + #tag EndConstant + + #tag Constant, Name = kTBEditorLabelRunInIDE, Type = String, Dynamic = False, Default = \"Run in IDE", Scope = Public + #tag EndConstant + + #tag Constant, Name = kTBEditorLabelTestRun, Type = String, Dynamic = False, Default = \"Test Run", Scope = Public + #tag EndConstant + + +End Module +#tag EndModule diff --git a/Windows/TBEditor.xojo_toolbar b/Windows/TBEditor.xojo_toolbar new file mode 100644 index 0000000..62b5132 --- /dev/null +++ b/Windows/TBEditor.xojo_toolbar @@ -0,0 +1,4 @@ +#tag Toolbar +Begin Toolbar TBEditor +End +#tag EndToolbar diff --git a/Windows/WndAbout.xojo_window b/Windows/WndAbout.xojo_window new file mode 100644 index 0000000..e8cf730 --- /dev/null +++ b/Windows/WndAbout.xojo_window @@ -0,0 +1,314 @@ +#tag Window +Begin Window WndAbout + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = True + Compatibility = "" + Composite = True + Frame = 0 + FullScreen = False + FullScreenButton= False + HasBackColor = False + Height = 262 + ImplicitInstance= True + LiveResize = True + MacProcID = 0 + MaxHeight = 262 + MaximizeButton = False + MaxWidth = 446 + MenuBar = 0 + MenuBarVisible = True + MinHeight = 262 + MinimizeButton = True + MinWidth = 446 + Placement = 2 + Resizeable = False + Title = "About" + Visible = True + Width = 446 + Begin HTMLViewer HtmlViewerAbout + AutoDeactivate = True + Enabled = True + Height = 262 + HelpTag = "" + Index = -2147483648 + Left = 0 + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = True + Renderer = 1 + Scope = 2 + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + Top = 0 + Visible = True + Width = 446 + End +End +#tag EndWindow + +#tag WindowCode + #tag Property, Flags = &h21 + Private IsInitialLoad As Boolean = True + #tag EndProperty + + + #tag Constant, Name = kAbout, Type = String, Dynamic = False, Default = \"\n\n\tAbout XsEdit\n\t\n\n\n\n
\n\t
XsEdit
\n\t
$(Version)
\n
\n\n
\n\t

\n\t\tAn advanced open-source XojoScript editor\n\t

\n\t

\n\t\tA special thanks goes out to Thomas Tempelmann for the open source\n\t\tCustom Edit Field.\n\t

\n\t

\n\t\tXsEdit is is distributed under the\n\t\tMIT license.\n\t

\n
\n\n
\n\tCopyright ©2015 by Kem Tekinay and Jeremy Cowgar. All rights reserved.\n
\n", Scope = Private + #tag EndConstant + + +#tag EndWindowCode + +#tag Events HtmlViewerAbout + #tag Event + Sub Open() + IsInitialLoad = true + + dim msg as String = kAbout + msg = msg.ReplaceAll( "$(Version)", App.ShortVersion ) + + me.LoadPage msg, nil + End Sub + #tag EndEvent + #tag Event + Function CancelLoad(URL as String) As Boolean + if IsInitialLoad then + IsInitialLoad = false + return false + else + ShowURL( URL ) + return true + end if + End Function + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Windows/WndEditor.xojo_window b/Windows/WndEditor.xojo_window new file mode 100644 index 0000000..6a2dd9c --- /dev/null +++ b/Windows/WndEditor.xojo_window @@ -0,0 +1,2207 @@ +#tag Window +Begin SearchReceiverWindowBase WndEditor Implements PreferenceWatcher + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = True + Compatibility = "" + Composite = False + Frame = 0 + FullScreen = False + FullScreenButton= True + HasBackColor = False + Height = 400 + ImplicitInstance= False + LiveResize = True + MacProcID = 0 + MaxHeight = 32000 + MaximizeButton = True + MaxWidth = 32000 + MenuBar = 503097343 + MenuBarVisible = True + MinHeight = 400 + MinimizeButton = True + MinWidth = 600 + Placement = 0 + Resizeable = True + Title = "Untitled" + Visible = True + Width = 600 + Begin Timer tmrReindent + Height = 32 + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Mode = 0 + Period = 50 + Scope = 2 + TabPanelIndex = 0 + Top = 0 + Width = 32 + End + Begin CustomEditField fldCode + AcceptFocus = False + AcceptTabs = False + AutoCloseBrackets= False + AutocompleteAppliesStandardCase= True + AutoDeactivate = True + AutoIndentNewLines= True + BackColor = &cFFFFFF00 + Backdrop = 0 + Border = True + BorderColor = &c88888800 + BracketHighlightColor= &cFFFF0000 + CaretColor = &c00000000 + CaretLine = 0 + CaretPos = 0 + ClearHighlightedRangesOnTextChange= True + DirtyLinesColor = &cFF999900 + disableReset = False + DisplayDirtyLines= False + DisplayInvisibleCharacters= False + DisplayLineNumbers= True + DisplayRightMarginMarker= False + DoubleBuffer = False + EnableAutocomplete= True + Enabled = True + EnableLineFoldings= False + enableLineFoldingSetting= False + EraseBackground = True + GutterBackgroundColor= &cEEEEEE00 + GutterSeparationLineColor= &c88888800 + GutterWidth = 0 + Height = 385 + HelpTag = "" + HighlightBlocksOnMouseOverGutter= True + HighlightMatchingBrackets= True + HighlightMatchingBracketsMode= 0 + ignoreRepaint = False + IndentPixels = 16 + IndentVisually = False + Index = -2147483648 + InitialParent = "" + KeepEntireTextIndented= False + Left = 0 + leftMarginOffset= 4 + LineNumbersColor= &c88888800 + LineNumbersTextFont= "System" + LineNumbersTextSize= 9 + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = True + MaxVisibleLines = 0 + ReadOnly = False + RightMarginAtPixel= 0 + RightScrollMargin= 150 + Scope = 2 + ScrollPosition = 0 + ScrollPositionX = 0 + selLength = 0 + selStart = 0 + SelText = "" + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + TabWidth = 4 + Text = "" + TextColor = &c00000000 + TextFont = "System" + TextHeight = 0 + TextLength = 0 + TextSelectionColor= &c00000000 + TextSize = 0 + ThickInsertionPoint= True + Top = 0 + Transparent = True + UseFocusRing = True + Visible = True + Width = 585 + End + Begin ScrollBar sbVertical + AcceptFocus = True + AutoDeactivate = True + Enabled = True + Height = 385 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = 585 + LineStep = 1 + LiveScroll = False + LockBottom = True + LockedInPosition= False + LockLeft = False + LockRight = True + LockTop = True + Maximum = 100 + Minimum = 0 + PageStep = 20 + Scope = 0 + TabIndex = 1 + TabPanelIndex = 0 + TabStop = True + Top = 0 + Value = 0 + Visible = True + Width = 15 + End + Begin ScrollBar sbHorizontal + AcceptFocus = True + AutoDeactivate = True + Enabled = True + Height = 15 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = 0 + LineStep = 1 + LiveScroll = False + LockBottom = True + LockedInPosition= False + LockLeft = True + LockRight = True + LockTop = False + Maximum = 100 + Minimum = 0 + PageStep = 20 + Scope = 2 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + Top = 385 + Value = 0 + Visible = True + Width = 585 + End + Begin TBEditor tbToolbar + Enabled = True + Height = 32 + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Scope = 2 + TabPanelIndex = 0 + Top = 0 + Visible = True + Width = 100 + End + Begin XojoScript XS + Height = 32 + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Scope = 2 + Source = "" + TabPanelIndex = 0 + Top = 0 + Width = 32 + End + Begin IPCSocket IDESocket + Height = 32 + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Path = "" + Scope = 2 + TabPanelIndex = 0 + Top = 0 + Width = 32 + End + Begin Timer tmrSetAutocompleteScript + Height = 32 + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Mode = 0 + Period = 10 + Scope = 0 + TabPanelIndex = 0 + Top = 0 + Width = 32 + End + Begin Timer tmrSetContentsChanged + Height = 32 + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Mode = 0 + Period = 250 + Scope = 0 + TabPanelIndex = 0 + Top = 0 + Width = 32 + End + Begin Timer tmrCheckForXojoIDE + Height = 32 + Index = -2147483648 + InitialParent = "" + Left = 0 + LockedInPosition= False + Mode = 2 + Period = 1000 + Scope = 0 + TabPanelIndex = 0 + Top = 0 + Width = 32 + End +End +#tag EndWindow + +#tag WindowCode + #tag Event + Sub Activate() + SetTitle() + AdjustRunInIDEButton + tmrCheckForXojoIDE.Mode = Timer.ModeMultiple + End Sub + #tag EndEvent + + #tag Event + Function CancelClose(appQuitting as Boolean) As Boolean + #pragma unused appQuitting + + SetContentsChanged() + if not ContentsChanged then + return false + end if + + // + // The contents have changed + // + + dim dlg as new MessageDialog + dlg.Message = "Save changes before closing?" + dlg.ActionButton.Caption = "&Save" + dlg.CancelButton.Caption = "&Cancel" + dlg.CancelButton.Visible = true + dlg.AlternateActionButton.Caption = "&Don't Save" + dlg.AlternateActionButton.Visible = true + + dim btn as MessageDialogButton = dlg.ShowModalWithin( self ) + if btn is dlg.CancelButton then + return true + elseif btn is dlg.AlternateActionButton then + return false + else // Save button + return ( not Save() ) + end if + End Function + #tag EndEvent + + #tag Event + Sub Close() + App.Prefs.UnregisterWatcher( self ) + End Sub + #tag EndEvent + + #tag Event + Sub Deactivate() + tmrCheckForXojoIDE.Mode = Timer.ModeOff + End Sub + #tag EndEvent + + #tag Event + Sub EnableMenuItems() + EditUndo.Enabled = fldCode.CanUndo + EditRedo.Enabled = fldCode.CanRedo + + ViewShowInvisibles.Checked = App.Prefs.ShowInvisibles + ViewShowLineNumbers.Checked = App.Prefs.ShowLineNumbers + + ScriptGoToErrorLine.Enabled = LastCompilerErrorLine > 0 + + dim find as string = WndSearch.Options.FindTerm + EditFindNext.Enabled = find <> "" + EditFindPrevious.Enabled = find <> "" + + dim toggleToolbarText as string = if( App.Prefs.ShowToolbar, App.kViewHideToolbar, App.kViewShowToolbar ) + if ViewToggleToolbar.Text <> toggleToolbarText then + ViewToggleToolbar.Text = toggleToolbarText + end if + + ScriptRunInIDE.Enabled = IsIDEAvailable + End Sub + #tag EndEvent + + #tag Event + Sub FindAll(options As SearchOptions) + dim find as string = options.FindTerm + + if find = "" then + beep + return + end if + + fldCode.ClearHighlightedCharacterRanges + fldCode.ClearLineIcons + + dim s as string = fldCode.Text + dim findLen as integer = find.Len + + dim pos as integer = InStrWithOptions( s, options ) + if pos = 0 then + beep + return + end if + + dim firstPos as integer = pos + while pos <> 0 + fldCode.HighlightCharacterRange pos - 1, findLen, kColorFindAll + pos = InStrWithOptions( pos + findLen, s, options ) + wend + + fldCode.ScrollPosition = fldCode.LineNumAtCharPos( firstPos - 1 ) - 1 + + End Sub + #tag EndEvent + + #tag Event + Sub FindNext(options As SearchOptions) + #pragma unused options + + DoFindNext + End Sub + #tag EndEvent + + #tag Event + Sub FindPrevious(options As SearchOptions) + #pragma unused options + + DoFindPrevious + End Sub + #tag EndEvent + + #tag Event + Sub Open() + // + // See if the window needs to be staggered + // + + const kMaxToConsider = 40 + + dim lastIndex as integer = WindowCount - 1 + if lastIndex > kMaxToConsider then + lastIndex = kMaxToConsider // After that, don't worry about it + end if + + dim doItAgain as boolean + do + doItAgain = false + + for i as integer = 0 to lastIndex + dim testWindow as Window = Window( i ) + if testWindow is self then + continue for i + end if + if testWindow.Left = self.Left then + self.Left = self.Left + 10 + doItAgain = true + end if + if testWindow.Top = self.Top then + self.Top = self.Top + 10 + doItAgain = true + end if + next + loop until not doItAgain + + dim hd as new HighlightDefinition + if not hd.LoadFromXml( App.SyntaxDefinitionFile ) then + MsgBox "Could not load syntax definition" + quit + end if + + fldCode.SyntaxDefinition = hd + + SetAutocompleteWords() + SetCEFPrefs() + + fldCode.SetScrollbars( sbHorizontal, sbVertical ) + + App.Prefs.RegisterWatcher( self ) + End Sub + #tag EndEvent + + #tag Event + Sub ReplaceAll(options As SearchOptions) + dim find as string = options.FindTerm + dim replacement as string = options.ReplaceTerm + + if find = "" then + return + end if + + fldCode.ClearHighlightedCharacterRanges + fldCode.ClearLineIcons + + dim replaceLen as integer = replacement.Len + + dim pos as integer = InStrWithOptions( fldCode.Text, options ) + if pos = 0 then + beep + return + end if + + dim count as integer + + fldCode.IgnoreRepaint = true + while pos <> 0 + count = count + 1 + + fldCode.SelStart = pos - 1 + fldCode.SelLength = replaceLen + fldCode.SelText = "" + fldCode.Insert pos - 1, replacement + + pos = InStrWithOptions( pos + replaceLen, fldCode.Text, options ) + wend + fldCode.IgnoreRepaint = false + fldCode.Invalidate + + MsgBox "Replaced " + format( count, "#," ) + " occurrences." + End Sub + #tag EndEvent + + #tag Event + Sub ReplaceAndFindNext(options As SearchOptions) + #pragma unused options + + DoReplaceOne() + DoFindNext() + End Sub + #tag EndEvent + + #tag Event + Sub ReplaceOne(options As SearchOptions) + #pragma unused options + + DoReplaceOne() + End Sub + #tag EndEvent + + + #tag MenuHandler + Function EditComment() As Boolean Handles EditComment.Action + dim lineIndexes() as integer = SelectedLineIndexes + if lineIndexes.Ubound = -1 then + return true + end if + + fldCode.IgnoreRepaint = true + + for each index as integer in lineIndexes + dim charPos as integer = fldCode.CharPosAtLineNum( index ) + fldCode.SelStart = charPos + fldCode.SelLength = 0 + fldCode.SelText = kCommentToken + next + + // + // Select after the last line + // + SelectAfterLineIndex( lineIndexes( lineIndexes.Ubound ) ) + + fldCode.IgnoreRepaint = false + fldCode.Invalidate + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditFindNext() As Boolean Handles EditFindNext.Action + DoFindNext + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditFindPrevious() As Boolean Handles EditFindPrevious.Action + DoFindPrevious + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditRedo() As Boolean Handles EditRedo.Action + fldCode.Redo + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditUncomment() As Boolean Handles EditUncomment.Action + dim lineIndexes() as integer = SelectedLineIndexes + if lineIndexes.Ubound = -1 then + return true + end if + + dim rx as new RegEx + rx.SearchPattern = "^([\x20\t]*)(" + kCommentToken + ")" + rx.ReplacementPattern = "$1" + + fldCode.IgnoreRepaint = true + + for each lineIndex as integer in lineIndexes + dim startPos as integer = fldCode.CharPosAtLineNum( lineIndex ) + dim endPos as integer + if lineIndex >= fldCode.LineCount then + endPos = fldCode.Text.Len + else + endPos = fldCode.CharPosAtLineNum( lineIndex + 1 ) + end if + fldCode.SelStart = startPos + fldCode.SelLength = endPos - startPos + dim thisLine as string = fldCode.SelText + dim origLine as string = thisLine + + thisLine = rx.Replace( thisLine ) + if thisLine <> origLine then + fldCode.SelText = thisLine + end if + next + + SelectAfterLineIndex( lineIndexes( lineIndexes.Ubound ) ) + + fldCode.IgnoreRepaint = false + fldCode.Invalidate + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function EditUndo() As Boolean Handles EditUndo.Action + fldCode.Undo + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function FileClose() As Boolean Handles FileClose.Action + if FileClose.Text = "Close All" then + App.CloseAllWindows + return false + else + self.Close + return true + end if + + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function FileSave() As Boolean Handles FileSave.Action + call Save() + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function FileSaveAs() As Boolean Handles FileSaveAs.Action + call SaveAs() + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ScriptCompile() As Boolean Handles ScriptCompile.Action + self.ScriptCompile + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ScriptGoToErrorLine() As Boolean Handles ScriptGoToErrorLine.Action + ScrollToErrorLine + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ScriptGoToLine() As Boolean Handles ScriptGoToLine.Action + dim l as integer = WndGoToLine.ShowModalWithin( self, fldCode.LineCount ) + if l > -1 then + // + // l is one-based but lines are zro-based + // + fldCode.ScrollPosition = l - 2 + fldCode.SelectLine l - 1 + end if + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ScriptInsertInclude() As Boolean Handles ScriptInsertInclude.Action + InsertIncludeDirective + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ScriptRun() As Boolean Handles ScriptRun.Action + self.ScriptTestRun + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ScriptRunInIDE() As Boolean Handles ScriptRunInIDE.Action + self.ScriptRunInIDE + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ViewShowInvisibles() As Boolean Handles ViewShowInvisibles.Action + App.Prefs.ShowInvisibles = not App.Prefs.ShowInvisibles + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ViewShowLineNumbers() As Boolean Handles ViewShowLineNumbers.Action + App.Prefs.ShowLineNumbers = not App.Prefs.ShowLineNumbers + + Return True + + End Function + #tag EndMenuHandler + + #tag MenuHandler + Function ViewToggleToolbar() As Boolean Handles ViewToggleToolbar.Action + App.Prefs.ShowToolbar = ViewToggleToolbar.Text = App.kViewShowToolbar + Return True + + End Function + #tag EndMenuHandler + + + #tag Method, Flags = &h21 + Private Sub AdjustRunInIDEButton() + dim shouldEnable as boolean = IsIDEAvailable + + dim btn as ToolItem = tbToolbar.Item( kTBEditorButtonIndexRunInIDE ) + btn.Enabled = shouldEnable + + dim tag as string + if shouldEnable then + tag = kTBEditorHelpTagRunInIDE + else + tag = kTBEditorHelpTagRunInIDE + kTBEditorHelpTagIDEUnavailableSuffix + end if + if btn.HelpTag <> tag then + btn.HelpTag = tag + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function CharPosOfNext(options As SearchOptions) As Integer + if options.FindTerm = "" then + return -1 + end if + + dim s as string = fldCode.Text + + dim pos as integer = InStrWithOptions( fldCode.SelStart + 1 + fldCode.SelLength, s, options ) + if pos = 0 and options.IsWrapAround then + pos = InStrWithOptions( s, options ) + end if + + return pos - 1 // Zero based + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function CharPosOfPrevious(options As SearchOptions) As Integer + dim find as string = options.FindTerm + + if find = "" then + return -1 + end if + + dim findLen as integer = find.Len + + dim curPos as integer = fldCode.SelStart + 1 + if curPos = 0 then + return -1 + end if + + dim s as string = fldCode.Text + + dim pos as integer = -1 // This is zero-based + dim nextPos as integer = InStrWithOptions( s, options ) + if nextPos = 0 then + return -1 + end if + + // + // See if we have to wrap around + // + if nextPos >= curPos then + if options.IsWrapAround then + curPos = s.Len + 1 + else + return -1 + end if + end if + + while nextPos > 0 and nextPos < curPos + pos = nextPos - 1 + nextPos = InStrWithOptions( nextPos + findLen, s, options ) + wend + + return pos + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub DoFindNext() + fldCode.ClearHighlightedCharacterRanges + fldCode.ClearLineIcons + + dim pos as integer = CharPosOfNext( WndSearch.Options ) + if pos > -1 then + fldCode.SelStart = pos + fldCode.SelLength = WndSearch.Options.FindTerm.Len + else + beep + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub DoFindPrevious() + fldCode.ClearHighlightedCharacterRanges + fldCode.ClearLineIcons + + dim pos as integer = CharPosOfPrevious( WndSearch.Options ) + if pos > -1 then + fldCode.SelStart = pos + fldCode.SelLength = WndSearch.Options.FindTerm.Len + else + beep + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub DoReplaceOne() + dim options as SearchOptions = WndSearch.Options + + if options.FindTerm = "" then + return + end if + + dim find as string = options.FindTerm + dim replacement as string = options.ReplaceTerm + dim s as string = fldCode.Text + + if find = "" then + return + end if + + if StrComp( find, replacement, 0 ) = 0 then + beep + return + end if + + // + // See where we have to start + // + dim currentSelection as string = fldCode.SelText + dim start as integer = fldCode.SelStart + 1 + if StrComp( currentSelection, replacement, 0 ) = 0 then + start = start + replacement.Len + end if + + dim pos as integer = InStrWithOptions( start, s, options ) + if pos = 0 then + if options.IsWrapAround then + pos = InStrWithOptions( s, options ) + else + beep + return + end if + end if + + if pos = 0 then + beep + return + end if + + fldCode.SelStart = pos - 1 + fldCode.SelLength = find.Len + fldCode.SelText = "" + fldCode.Insert pos - 1, replacement + + fldCode.SelStart = pos - 1 + replacement.Len + fldCode.SelLength = 0 + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub HighlightCode(location As XojoScriptLocation, msg As String, c As Color, lineIcon As Picture) + dim startPosition as integer = location.Character + dim endPosition as integer = location.EndCharacter + dim length as integer = endPosition - startPosition + + fldCode.HighlightCharacterRange( startPosition, length, c ) + + dim startLine as integer = fldCode.LineNumAtCharPos( startPosition ) + if lineIcon isa Picture then + dim endLine as integer = fldCode.LineNumAtCharPos( endPosition ) + + for l as integer = startLine to endLine + fldCode.LineIcon( l ) = lineIcon + next + end if + + // + // Scroll to that line + // + LastCompilerErrorLine = startLine + ScrollToErrorLine + + fldCode.HelpTag = msg + + #pragma warning "How to handle the msg?" + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub HighlightCode(location As XojoScriptLocation, error As XojoScript.Errors, errorInfo As Dictionary, c As Color) + dim errorString as string = M_XojoScript.ErrorToString( error, errorInfo ) + + + HighlightCode( location, errorString, c, errordata ) + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function InsertIncludedFiles(src As String) As String + // Locates the include directive and inserts the contents of the located files. + // Raise an exception if the file can't be located + + dim pattern as string = "^[\x20\t]*(?://|')"+ kIncludeDirective + "[\x20\t]+(.*)" + + dim rx as new RegEx + rx.SearchPattern = pattern + + dim match as RegExMatch = rx.Search( src ) + while match IsA RegExMatch + dim path as string = match.SubExpressionString( 1 ).Trim + + dim f as FolderItem + + #pragma BreakOnExceptions false + try + if MyDocument is nil then + f = new FolderItem( path, FolderItem.PathTypeNative ) + else + f = GetRelativeFolderItem_MTC( path, MyDocument.Parent ) + end if + catch err as UnsupportedFormatException + f = nil + end try + #pragma BreakOnExceptions default + + if f is nil then + RaiseBadIncludeException( "An error occurred while accessing the file at " + path, match.SubExpressionString( 0 ) ) + elseif f.Directory then + RaiseBadIncludeException( "The path " + path + " points to a folder", match.SubExpressionString( 0 ) ) + elseif not f.Exists then + RaiseBadIncludeException( "The file at path " + path + " does not exist", match.SubExpressionString( 0 ) ) + elseif not f.IsReadable then + RaiseBadIncludeException( "Can't read the file at path " + path, match.SubExpressionString( 0 ) ) + end if + + dim contents as string = f.TextContents_MTC( Encodings.UTF8 ) + + // + // Replace the Includes line + // + dim startB as integer = match.SubExpressionStartB( 0 ) + 1 + dim prefix as string = src.LeftB( startB - 1 ) + dim suffix as string = src.MidB( startB + match.SubExpressionString( 0 ).LenB ) + src = prefix + contents + suffix + + match = rx.Search( src ) + wend + + src = ReplaceLineEndings( src, &uA ) + return src + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub InsertIncludeDirective() + // Asks for a file and inserts it at the front of the currently selected line + + dim dlg as new OpenDialog + dlg.PromptText = "Select a script that will be included for compilation purposes:" + dlg.Filter = DocumentTypes.XojoScript + dlg.MultiSelect = true + + dim include as FolderItem = dlg.ShowModalWithin( self ) + if include is nil then + return + end if + + // + // Get the insertion point + // + dim curPos as integer = fldCode.SelStart + dim curLine as integer = fldCode.LineNumAtCharPos( curPos ) + fldCode.SelStart = fldCode.CharPosAtLineNum( curLine ) // Front of the line + fldCode.SelLength = 0 + + dim lastFileIndex as integer = dlg.Count - 1 + for i as integer = 0 to lastFileIndex + include = dlg.Item( i ) + dim path as string = include.NativePath + dim insert as string = "'" + kIncludeDirective + " " + path + &uA + fldCode.SelText = insert + next + + // + // Make sure this line is marked dirty + // + fldCode.SelText = " " + fldCode.SelStart = fldCode.SelStart - 1 + fldCode.SelLength = 1 + fldCode.SelText = "" + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function InStrWithOptions(start As Integer = 1, src As String, options As SearchOptions) As Integer + // Like InStr but will honor options and always return char position + + if src = "" or options.FindTerm = "" then + return 0 + end if + + if start < 1 then + start = 1 + end if + + // + // See if we have to convert start into its byte-position equivalent + // + if ( options.IsCaseSensitive or options.IsWholeWord ) then + if start > src.Len then + start = src.LenB + 1 + elseif start > 1 then + dim part as string = src.Left( start - 1 ) + start = part.LenB + 1 + end if + end if + + + if options.IsWholeWord then + // + // Use a regex + // + dim pattern as string = "\b" + StringToRegExCodes( options.FindTerm ) + "\b" + dim rx as new RegEx + rx.SearchPattern = pattern + rx.Options.CaseSensitive = options.IsCaseSensitive + + dim match as RegExMatch + if start = 1 then + match = rx.Search( src ) + else + match = rx.Search( src, start - 1 ) + end if + + if match is nil then + return 0 + else + dim posB as integer = match.SubExpressionStartB( 0 ) + return src.LeftB( posB ).Len + 1 + end if + + else + + if options.IsCaseSensitive then + + dim posB as integer = src.InStrB( start, options.FindTerm ) + if posB < 2 then + return posB + else + return src.LeftB( posB - 1 ).Len + 1 + end if + + else + + return src.InStr( start, options.FindTerm ) + + end if + + end if + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function LineAtLineIndex(lineIndex As Integer) As String + // Returns the text of the line at lineIndex + + if lineIndex >= fldCode.LineCount then + return "" + end if + + dim startCharPos as integer = fldCode.CharPosAtLineNum( lineIndex ) + 1 + dim endCharPos as integer + if lineIndex = ( fldCode.LineCount - 1 ) then + endCharPos = fldCode.Text.Len + else + endCharPos = fldCode.CharPosAtLineNum( lineIndex + 1 ) + 1 + end if + + return fldCode.Text.Mid( startCharPos, endCharPos - startCharPos ).Trim + End Function + #tag EndMethod + + #tag Method, Flags = &h0 + Sub OpenDocument(f As FolderItem) + if f is nil or not f.Exists then + MsgBox "Could not open the document." + self.Close + end if + + MyDocumentAlias = f + fldCode.Text = f.TextContents_MTC( Encodings.UTF8 ) + CodeBeforeChanges = fldCode.Text + + fldCode.ResetUndo + fldCode.ResetUndoDirtyFlag + + self.ContentsChanged = false + SetTitle() + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub PreferencesHaveChanged(prefs As Preferences) + dim xsePrefs as XsEditPreferences = XsEditPreferences( prefs ) + + tbToolbar.Visible = xsePrefs.ShowToolbar + + fldCode.IgnoreRepaint = true + + fldCode.TextFont = xsePrefs.CodeFont + fldCode.TextSize = xsePrefs.CodeFontSize + fldCode.AutocompleteAppliesStandardCase = xsePrefs.AutocompleteAppliesStandardCase + fldCode.AutoCloseBrackets = xsePrefs.AutoCloseBrackets + fldCode.DisplayInvisibleCharacters = xsePrefs.ShowInvisibles + fldCode.DisplayLineNumbers = xsePrefs.ShowLineNumbers + + dim hd as HighlightDefinition = fldCode.SyntaxDefinition + dim interestingPrefs as Dictionary = xsePrefs.InterestingContextPrefsDictionary + + for each context as HighlightContext in hd.Contexts + if interestingPrefs.HasKey( context.Name ) then + dim contextPref as ContextPreferences = interestingPrefs.Value( context.Name ) + contextPref.CopyTo context + end if + next + + // + // Force CustomEditField to update the colors + // + + fldCode.SyntaxDefinition = nil + fldCode.SyntaxDefinition = hd + fldCode.IgnoreRepaint = false + fldCode.Redraw( true ) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub RaiseBadIncludeException(msg As String, faultyLine As String) + dim charStart as integer = fldCode.Text.InStr( faultyLine ) - 1 + dim lineNum as integer = fldCode.LineNumAtCharPos( charStart ) + fldCode.HighlightCharacterRange( charStart, faultyLine.Len, kColorError ) + fldCode.LineIcon( lineNum ) = errordata + fldCode.HelpTag = msg + + LastCompilerErrorCode = kErrorIncludeError + LastCompilerErrorLine = lineNum + + #pragma BreakOnExceptions false + dim err as new XojoScriptException + err.Message = msg + raise err + #pragma BreakOnExceptions default + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function Save() As Boolean + if MyDocument is nil then + return SaveAs() + end if + + MyDocument.TextContents_MTC = fldCode.Text + ContentsChanged = false + fldCode.ClearDirtyLines + + CodeBeforeChanges = fldCode.Text + SetTitle() + + return true + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function SaveAs() As Boolean + dim dlg as new SaveAsDialog + dlg.PromptText = "Save the script:" + dlg.Filter = DocumentTypes.XojoScript + dlg.SuggestedFileName = "Script.xojo_script" + + dim f as FolderItem = dlg.ShowModalWithin( self ) + if f is nil then + return false + end if + + MyDocumentAlias = f + return Save() + + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ScriptCompile() + LastCompilerErrorCode = -1 + LastCompilerErrorLine = -1 + + dim src as string = fldCode.Text + + try + src = InsertIncludedFiles( src ) + catch err as XojoScriptException + return + end try + + fldCode.ClearLineIcons + fldCode.ClearHighlightedCharacterRanges + fldCode.HelpTag = "" + + // + // Handle the compiling first + // + + XS.Reset + XS.Source = src + XS.Context = new IDEEmulator + + call XS.Precompile( XojoScript.OptimizationLevels.None ) + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ScriptRunInIDE() + ScriptCompile + if LastCompilerErrorCode <> -1 then + return + end if + + try + dim endTime as Integer = Ticks + 120 + IDESocket.Path = IDECommunicator.FindIPCPath + + if IDESocket.Path = "" then + dim ex as new RuntimeException + ex.Message = "Could not find XojoIDE IPC socket" + raise ex + end if + + IDESocket.Connect + + while Ticks < endTime + while not IDESocket.IsConnected and IDESocket.LastErrorCode = 0 + IDESocket.Poll + wend + + if IDESocket.IsConnected then + exit + end if + wend + + if IDESocket.LastErrorCode <> 0 then + dim ex as new RuntimeException + ex.ErrorNumber = IDESocket.LastErrorCode + ex.Message = "Could not connect to IDE" + raise ex + end if + + IDESocket.Write XS.Source + while IDESocket.BytesLeftToSend <> 0 + IDESocket.Poll + if IDESocket.LastErrorCode <> 0 then + dim ex as new RuntimeException + ex.ErrorNumber = IDESocket.LastErrorCode + ex.Message = "Could not communicate with IDE" + raise ex + end if + wend + + IDESocket.Close + + // + // If we get here, we assume it worked, so activate it + // + + #if TargetMacOS then + dim sh as new Shell + sh.Execute "osascript -e 'tell app id ""com.xojo.xojo"" to activate'" + #endif + + catch ex as RuntimeException + if IDESocket.IsConnected then + IDESocket.Close + end if + + dim dlg as new MessageDialog + dlg.CancelButton.Visible = false + dlg.Message = "Could not connect to the IDE. Restarting it might help." + dlg.Explanation = ex.Message + ": " + Str(ex.ErrorNumber) + call dlg.ShowModal + + end try + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ScriptTestRun() + ScriptCompile + + if LastCompilerErrorCode = -1 then + #pragma BreakOnExceptions false + XS.Run + #pragma BreakOnExceptions default + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ScrollToErrorLine() + if LastCompilerErrorLine < 1 then + beep + else + fldCode.ScrollPosition = LastCompilerErrorLine - 1 // The line before + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub SelectAfterLineIndex(lineIndex As Integer) + lineIndex = lineIndex + 1 + + dim charPos as integer + if lineIndex >= fldCode.LineCount then + charPos = fldCode.Text.Len + else + charPos = fldCode.CharPosAtLineNum( lineIndex ) + end if + fldCode.SelStart = charPos + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function SelectedLineIndexes() As Integer() + // Returns an array of the line indexes that cover the current selection + + dim startLine as integer = fldCode.LineNumAtCharPos( fldCode.SelStart ) + dim endLine as integer = fldCode.LineNumAtCharPos( fldCode.SelStart + fldCode.SelLength ) + + dim r() as integer + for i as integer = startLine to endLine + r.Append i + next + + return r + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub SetAutocompleteWords() + if AutocompleterKeywords is nil then + + AutocompleterKeywords = new PaTrie + + dim contexts() as HighlightContext = fldCode.SyntaxDefinition.Contexts + for each context as HighlightContext in contexts + dim keywords() as string + context.ListKeywords( keywords ) + for each keyword as string in keywords + keyword = keyword.Trim + + // + // The automcomplete engine only consideres word characters so + // strip the "#" for now + // + if keyword.Left( 1 ) = "#" then + keyword = keyword.Mid( 2 ) + end if + + if keyword <> "" then + call AutocompleterKeywords.AddKey( keyword, nil ) + end if + next + next + + // + // Add all methods and properties from IDEEmulator + // + + dim ti as Introspection.TypeInfo = GetTypeInfo( IDEEmulator ) + + // + // Properties + // + dim props() as Introspection.PropertyInfo = ti.GetProperties + for each prop as Introspection.PropertyInfo in props + call AutocompleterKeywords.AddKey( prop.Name, nil ) + next + + // + // Methods + // + dim methods() as Introspection.MethodInfo = ti.GetMethods + for each method as Introspection.MethodInfo in methods + call AutocompleterKeywords.AddKey( method.Name, nil ) + next + + // + // Additional + // + dim addl as string = kAdditionalKeywords + addl = ReplaceLineEndings( addl, &uA ).Trim + for each keyword as string in addl.Split( &uA ) + keyword = keyword.Trim + if keyword <> "" then + call AutocompleterKeywords.AddKey( keyword, nil ) + end if + next + end if + + // + // Get all the keywords it can from the script up to that point + // + + AutocompleterScript = new PaTrie + ResumeSetAutocompleteAtLine = 0 + tmrSetAutocompleteScript.Mode = Timer.ModeSingle + tmrSetAutocompleteScript.Reset + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub SetCEFPrefs() + // + // Set the static prefs here + // + + fldCode.IgnoreRepaint = true + + fldCode.AutoIndentNewLines = true + fldCode.IndentVisually = true + + fldCode.Border = false + fldCode.UseFocusRing = false + + fldCode.DisplayDirtyLines = true + fldCode.EnableLineFoldings = true + + fldCode.HighlightMatchingBrackets = true + fldCode.HighlightMatchingBracketsMode = 2 + fldCode.EnableAutocomplete = true + + fldCode.ClearHighlightedRangesOnTextChange = true + + fldCode.IgnoreRepaint = false + + // + // Load the dynamic prefs + // (will also call Invalidate) + // + + PreferencesHaveChanged( App.Prefs ) + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub SetContentsChanged() + ContentsChanged = StrComp( CodeBeforeChanges, fldCode.Text, 0 ) <> 0 + if not ContentsChanged then + fldCode.ClearDirtyLines + end if + + tmrSetContentsChanged.Mode = Timer.ModeOff + + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub SetTitle() + if MyDocument is nil then + self.Title = "Untitled" + else + self.Title = MyDocument.Name + end if + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function StringToRegExCodes(s As String) As String + // Converts a string to it's equivalent in regex codes + + dim chars() as string = s.Split( "" ) + for i as integer = 0 to chars.Ubound + chars( i ) = hex( chars( i ).Asc ) + next + + dim r as string = join( chars, "}\x{" ) + r = "\x{" + r + "}" + return r + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private AutocompleterKeywords As PaTrie + #tag EndProperty + + #tag Property, Flags = &h21 + Private AutocompleterScript As PaTrie + #tag EndProperty + + #tag Property, Flags = &h21 + Private CodeBeforeChanges As String + #tag EndProperty + + #tag ComputedProperty, Flags = &h21 + #tag Getter + Get + dim isAvailable as boolean = true // Assume this + dim ipcPath as string = IDECommunicator.FindIPCPath + if ipcPath = "" then + isAvailable = false + else + dim f as new FolderItem( ipcPath, FolderItem.PathTypeNative ) + isAvailable = f isa FolderItem and f.Exists + end if + + return isAvailable + End Get + #tag EndGetter + Private IsIDEAvailable As Boolean + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private LastCompilerErrorCode As Integer = -1 + #tag EndProperty + + #tag Property, Flags = &h21 + Private LastCompilerErrorLine As Integer = -1 + #tag EndProperty + + #tag Property, Flags = &h21 + Private LineNumberAtLastSetAutocomplete As Integer + #tag EndProperty + + #tag ComputedProperty, Flags = &h0 + #tag Getter + Get + if MyDocumentAlias is nil then + return nil + else + return MyDocumentAlias.Resolve + end if + + End Get + #tag EndGetter + MyDocument As FolderItem + #tag EndComputedProperty + + #tag Property, Flags = &h21 + Private MyDocumentAlias As FolderItemAlias + #tag EndProperty + + #tag Property, Flags = &h21 + Private ResumeSetAutocompleteAtLine As Integer = 0 + #tag EndProperty + + + #tag Constant, Name = kAdditionalKeywords, Type = String, Dynamic = False, Default = \"Abs\nAcos\nAppend\nAsc\nAscB\nAsin\nAtan\nAtan2\nBin\nCdbl\nCeil\nChr\nChrB\nCMY\nCos\nCountFields\nCStr\nEndOfLine\nExp\nFloor\nFormat\nHex\nHSV\nInput\nInStr\nInStrB\nLeft\nLeftB\nLen\nLenB\nLog\nLowercase\nLTrim\nMax\nMicroseconds\nMid\nMidB\nMin\nNthField\nOct\nPow\nPrint\nReplace\nReplaceAll\nReplaceAllB\nReplaceB\nRGB\nRight\nRightB\nRnd\nRound\nRTrim\nSin\nSqrt\nStr\nStrComp\nTan\nTicks\nTitlecase\nTrim\nUbound\nUppercase\nVal", Scope = Protected + #tag EndConstant + + #tag Constant, Name = kColorError, Type = Color, Dynamic = False, Default = \"&cFF00007F", Scope = Private + #tag EndConstant + + #tag Constant, Name = kColorFindAll, Type = Color, Dynamic = False, Default = \"&c00FF00", Scope = Private + #tag EndConstant + + #tag Constant, Name = kColorWarning, Type = Color, Dynamic = False, Default = \"&cDCE83D7F", Scope = Private + #tag EndConstant + + #tag Constant, Name = kCommentToken, Type = String, Dynamic = False, Default = \"\'", Scope = Private + #tag EndConstant + + #tag Constant, Name = kErrorIncludeError, Type = Double, Dynamic = False, Default = \"-99", Scope = Private + #tag EndConstant + + #tag Constant, Name = kIncludeDirective, Type = String, Dynamic = False, Default = \"#include", Scope = Private + #tag EndConstant + + +#tag EndWindowCode + +#tag Events tmrReindent + #tag Event + Sub Action() + fldCode.ReindentText + End Sub + #tag EndEvent +#tag EndEvents +#tag Events fldCode + #tag Event + Sub TextChanged() + tmrReindent.Mode = Timer.ModeSingle + tmrReindent.Reset + + me.ClearLineIcons + me.HelpTag = "" + + tmrSetContentsChanged.Mode = Timer.ModeSingle + tmrSetContentsChanged.Reset + End Sub + #tag EndEvent + #tag Event + Function ShouldTriggerAutocomplete(Key as string, hasAutocompleteOptions as boolean) As boolean + return Key.Asc = 9 and hasAutocompleteOptions + End Function + #tag EndEvent + #tag Event + Function AutocompleteOptionsForPrefix(prefix as string) As AutocompleteOptions + // + // If there is a dot in the prefix, strip the text before it + // + if prefix <> "" then + dim prefixParts() as string = prefix.Split( "." ) + prefix = prefixParts( prefixParts.Ubound ) + end if + + dim options as new AutocompleteOptions + options.Prefix = prefix + dim commonPrefixKeywords as string + dim keywords() as string = AutocompleterKeywords.WordsForPrefix( prefix, commonPrefixKeywords ) + dim commonPrefixScript as string + dim scriptwords() as string = AutocompleterScript.WordsForPrefix( prefix, commonPrefixScript ) + + // + // Combine them + // + dim commonPraTrie as new PaTrie + for each word as string in keywords + call commonPraTrie.AddKey( word ) + next + for each word as string in scriptwords + call commonPraTrie.AddKey( word ) + next + + dim commonPrefix as string + dim words() as string = commonPraTrie.WordsForPrefix( prefix, commonPrefix ) + + options.Options = words + options.LongestCommonPrefix = commonPrefix + + return options + End Function + #tag EndEvent + #tag Event + Function KeyDown(key as string) As boolean + #pragma unused key + + // + // Ignore the command keys + // + + #if TargetMacOS then + + if Keyboard.CommandKey then + beep + return true + end if + + #else + + if Keyboard.ControlKey then + beep + return true + end if + + #endif + End Function + #tag EndEvent + #tag Event + Function UseBackgroundColorForLine(lineIndex as integer, byref lineBackgroundColor as color) As boolean + if not App.Prefs.UseActiveLineHighlight then + return false + end if + + dim startLine as integer = me.LineNumAtCharPos( me.SelStart ) + dim endLine as integer = me.LineNumAtCharPos( me.SelStart + me.SelLength ) + + if lineIndex >= startLine and lineIndex <= endLine then + lineBackgroundColor = App.Prefs.ActiveLineHighlightColor + return true + end if + End Function + #tag EndEvent + #tag Event + Sub SelChanged(line as integer, column as integer, length as integer) + #pragma unused column + #pragma unused length + + // + // This "line" is one-based + // + line = line - 1 + if line <> LineNumberAtLastSetAutocomplete then + SetAutocompleteWords + end if + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events sbVertical + #tag Event + Sub ValueChanged() + if me.Value < 0 then + me.Value = 0 + end if + + fldCode.ScrollPosition = me.Value + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events sbHorizontal + #tag Event + Sub ValueChanged() + if me.Value < 0 then + me.Value = 0 + end if + + fldCode.ScrollPositionX = me.Value + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events tbToolbar + #tag Event + Sub Action(item As ToolItem) + select case item.Caption + case kTBEditorLabelCompile + ScriptCompile + + case kTBEditorLabelTestRun + ScriptTestRun + + case kTBEditorLabelRunInIDE + ScriptRunInIDE + end select + End Sub + #tag EndEvent + #tag Event + Sub Open() + dim btn as ToolButton + + btn = new ToolButton + btn.Caption = kTBEditorLabelCompile + btn.Name = "btnCompile" + #if TargetMacOS + btn.Icon = systemrun3 + #else + btn.Icon = systemrun3small + #endif + me.Append btn + + // + // Add a space + // + btn = new ToolButton + #if TargetMacOS + btn.Style = ToolButton.ToolStyleSpace + #else + btn.Style = ToolButton.ToolStyleSeparator + #endif + me.Append btn + + btn = new ToolButton + btn.Caption = kTBEditorLabelRunInIDE + btn.Name = "btnRunInIDE" + #if TargetMacOS + btn.Icon = arrowright3 + #else + btn.Icon = arrowright3small + #endif + me.Append btn + + btn = new ToolButton + btn.Caption = kTBEditorLabelTestRun + btn.Name = "btnTestRun" + #if TargetMacOS + btn.Icon = arrowrightdouble3 + #else + btn.Icon = arrowrightdouble3small + #endif + me.Append btn + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events XS + #tag Event + Sub RuntimeError(error As RuntimeException) + if error isa EndException or error isa ThreadEndException then + #pragma BreakOnExceptions false + raise error // Pass it on + #pragma BreakOnExceptions default + end if + + dim ti as Introspection.TypeInfo = Introspection.GetType( error ) + dim type as string = ti.Name + + dim msg as string = "A runtime exception of type " + type + " has been raised. I wish I knew where." + + dim dlg as new MessageDialog + dlg.Message = msg + dlg.CancelButton.Visible = false + dlg.ActionButton.Caption = "Darn" + + call dlg.ShowModalWithin( self ) + + #pragma warning "Finish this!" + End Sub + #tag EndEvent + #tag Event + Sub CompilerWarning(location As XojoScriptLocation, warning As XojoScript.Warnings, warningInfo As Dictionary) + #if false then + dim msg as string = M_XojoScript.WarningToString( warning, warningInfo ) + HighlightCode( location, msg, kColorWarning, nil ) + #endif + + #pragma warning "Not handling compiler warnings at all right now" + #pragma unused location + #pragma unused warning + #pragma unused warningInfo + End Sub + #tag EndEvent + #tag Event + Function CompilerError(location As XojoScriptLocation, error As XojoScript.Errors, errorInfo As Dictionary) As Boolean + 'select case integer( error ) + 'case 4, 87 // Obsolete, ignore it + 'return false + ' + 'case else + LastCompilerErrorCode = integer( error ) + HighlightCode( location, error, errorInfo, kColorError ) + return true // One at a time + + 'end select + End Function + #tag EndEvent + #tag Event + Sub Print(msg As String) + dim dlg as new MessageDialog + dlg.Title = "Print" + dlg.Message = msg + dlg.CancelButton.Visible = false + + call dlg.ShowModalWithin( self ) + End Sub + #tag EndEvent + #tag Event + Function Input(prompt As String) As String + dim dlg as new DlgInput + return dlg.ShowModalWithin( self, prompt ) + End Function + #tag EndEvent +#tag EndEvents +#tag Events tmrSetAutocompleteScript + #tag Event + Sub Action() + // + // Set up the regexes + // + static rxDimFinder as RegEx + if rxDimFinder is nil then + rxDimFinder = new RegEx + rxDimFinder.SearchPattern = "(?mi-Us)^\s*(?:dim|private|public|protected)\s+(?!sub\s+|function\s+|class\s+|module\s+|interface\s+)([^\s].*)" + end if + + static rxAssignmentRemover as RegEx + if rxAssignmentRemover is nil then + rxAssignmentRemover = new RegEx + rxAssignmentRemover.SearchPattern = "(?mi-Us)=\s*(?:""(?:""""|[^""])*""|[^\s,]+)?" + rxAssignmentRemover.ReplacementPattern = "" + rxAssignmentRemover.Options.ReplaceAllMatches = true + end if + + static rxVariableFinder as RegEx + if rxVariableFinder is nil then + rxVariableFinder = new RegEx + rxVariableFinder.SearchPattern = "(?Umi-s)((?:[^,]+,?)+)\s+as\s+(new\s+\w+|\w+)(?:,|\s|$)" + end if + + static rxConstantFinder as RegEx + if rxConstantFinder is nil then + rxConstantFinder = new RegEx + rxConstantFinder.SearchPattern = "^\s*const\s+([^\s]+)" + end if + + static rxSubFinder as RegEx + if rxSubFinder is nil then + rxSubFinder = new RegEx + rxSubFinder.SearchPattern = "^\s*(?:private\s+|public\s+|protected\s+)?(?:sub\s+|function\s+|module\s+|class\s+|interface\s+)([^\s()]+)" + end if + + dim startTicks as integer = Ticks() + const kThresholdTicks = 2 + dim targetTicks as integer = startTicks + kThresholdTicks + + dim curLineIndex as integer = fldCode.LineNumAtCharPos( fldCode.SelStart ) + dim lastLineIndex as integer = curLineIndex - 1 + for lineIndex as integer = ResumeSetAutocompleteAtLine to lastLineIndex + if Ticks > targetTicks then + ResumeSetAutocompleteAtLine = lineIndex + me.Mode = Timer.ModeSingle + return + end if + + dim thisLine as string = LineAtLineIndex( lineIndex ) + dim match as RegExMatch + + match = rxDimFinder.Search( thisLine ) + if match IsA RegExMatch then + dim part as string = match.SubExpressionString( 1 ) + part = rxAssignmentRemover.Replace( part ) + dim varMatch as RegExMatch = rxVariableFinder.Search( part ) + while varMatch IsA RegExMatch + dim vars() as string = varMatch.SubExpressionString( 1 ).Split( "," ) + for each var as string in vars + var = var.NthField( "(", 1 ) + call AutocompleterScript.AddKey( var.Trim ) + next + + varMatch = rxVariableFinder.Search + wend + + continue for lineIndex + end if + + match = rxConstantFinder.Search( thisLine ) + if match IsA RegExMatch then + call AutocompleterScript.AddKey( match.SubExpressionString( 1 ) ) + continue for lineIndex + end if + + match = rxSubFinder.Search( thisLine ) + if match IsA RegExMatch then + call AutocompleterScript.AddKey( match.SubExpressionString( 1 ) ) + continue for lineIndex + end if + + next + + LineNumberAtLastSetAutocomplete = curLineIndex + + End Sub + #tag EndEvent +#tag EndEvents +#tag Events tmrSetContentsChanged + #tag Event + Sub Action() + SetContentsChanged() + End Sub + #tag EndEvent +#tag EndEvents +#tag Events tmrCheckForXojoIDE + #tag Event + Sub Action() + AdjustRunInIDEButton + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Windows/WndGoToLine.xojo_window b/Windows/WndGoToLine.xojo_window new file mode 100644 index 0000000..c9a03dc --- /dev/null +++ b/Windows/WndGoToLine.xojo_window @@ -0,0 +1,443 @@ +#tag Window +Begin Window WndGoToLine + BackColor = &cFFFFFF00 + Backdrop = 0 + CloseButton = False + Compatibility = "" + Composite = False + Frame = 8 + FullScreen = False + FullScreenButton= False + HasBackColor = False + Height = 118 + ImplicitInstance= True + LiveResize = True + MacProcID = 0 + MaxHeight = 32000 + MaximizeButton = False + MaxWidth = 32000 + MenuBar = 0 + MenuBarVisible = True + MinHeight = 64 + MinimizeButton = True + MinWidth = 64 + Placement = 0 + Resizeable = False + Title = "Go To Line" + Visible = True + Width = 268 + Begin Label Label1 + AutoDeactivate = True + Bold = False + DataField = "" + DataSource = "" + Enabled = True + Height = 20 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Italic = False + Left = 20 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Multiline = False + Scope = 2 + Selectable = False + TabIndex = 0 + TabPanelIndex = 0 + TabStop = True + Text = "Line Number:" + TextAlign = 0 + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 20 + Transparent = False + Underline = False + Visible = True + Width = 100 + End + Begin TextField fldLineNumber + AcceptTabs = False + Alignment = 0 + AutoDeactivate = True + AutomaticallyCheckSpelling= False + BackColor = &cFFFFFF00 + Bold = False + Border = True + CueText = "" + DataField = "" + DataSource = "" + Enabled = True + Format = "" + Height = 22 + HelpTag = "" + Index = -2147483648 + Italic = False + Left = 152 + LimitText = 0 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + Mask = "9" + Password = False + ReadOnly = False + Scope = 2 + TabIndex = 1 + TabPanelIndex = 0 + TabStop = True + Text = "" + TextColor = &c00000000 + TextFont = "System" + TextSize = 0.0 + TextUnit = 0 + Top = 19 + Underline = False + UseFocusRing = True + Visible = True + Width = 80 + End + Begin DialogButtonContainer DialogButtonContainer1 + AcceptFocus = False + AcceptTabs = True + AlternateCaption= "" + AutoDeactivate = True + BackColor = &cFFFFFF00 + Backdrop = 0 + CancelCaption = "Cancel" + Enabled = True + EraseBackground = True + HasBackColor = False + Height = 54 + HelpTag = "" + Index = -2147483648 + InitialParent = "" + Left = -148 + LockBottom = False + LockedInPosition= False + LockLeft = True + LockRight = False + LockTop = True + OkCaption = "OK" + Scope = 0 + TabIndex = 2 + TabPanelIndex = 0 + TabStop = True + Top = 64 + Transparent = True + UseFocusRing = False + Visible = True + Width = 416 + End +End +#tag EndWindow + +#tag WindowCode + #tag Method, Flags = &h21 + Private Sub Show() + raise new RuntimeException + // Shouldn't be used + End Sub + #tag EndMethod + + #tag Method, Flags = &h21 + Private Function ShowModal() As Integer + raise new RuntimeException + // Shouldn't be used + End Function + #tag EndMethod + + #tag Method, Flags = &h21 + Private Sub ShowModalWithin(parentWindow As Window) + #pragma unused parentWindow + + raise new RuntimeException + // Shouldn't be used + End Sub + #tag EndMethod + + #tag Method, Flags = &h0 + Function ShowModalWithin(parentWindow As Window, maxValue As Integer) As Integer + self.MaxValue = maxValue + + // + // Set the mask + // + dim mask as string = str( maxValue ) + dim maskLen as integer = mask.Len + fldLineNumber.Mask = Left( "9999999999999999", maskLen ) + + super.ShowModalWithin( parentWindow ) + return Result + End Function + #tag EndMethod + + + #tag Property, Flags = &h21 + Private MaxValue As Integer + #tag EndProperty + + #tag Property, Flags = &h21 + Private Result As Integer = -1 + #tag EndProperty + + +#tag EndWindowCode + +#tag Events DialogButtonContainer1 + #tag Event + Sub CancelAction() + Result = -1 + self.Close + End Sub + #tag EndEvent + #tag Event + Sub OkAction() + dim lineNum as integer = fldLineNumber.Text.Val + if lineNum < 1 then + beep + fldLineNumber.SelectAll + elseif lineNum > MaxValue then + beep + fldLineNumber.Text = str( MaxValue ) + fldLineNumber.SelectAll + else + Result = lineNum + self.Close + end if + End Sub + #tag EndEvent +#tag EndEvents +#tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty +#tag EndViewBehavior diff --git a/Windows/XsEditWindowBase.xojo_code b/Windows/XsEditWindowBase.xojo_code new file mode 100644 index 0000000..21bef28 --- /dev/null +++ b/Windows/XsEditWindowBase.xojo_code @@ -0,0 +1,256 @@ +#tag Class +Protected Class XsEditWindowBase +Inherits Window + #tag Event + Sub Close() + RaiseEvent Close + App.Prefs.Store self + End Sub + #tag EndEvent + + #tag Event + Sub Open() + App.Prefs.Restore self + RaiseEvent Open() + End Sub + #tag EndEvent + + + #tag Hook, Flags = &h0 + Event Close() + #tag EndHook + + #tag Hook, Flags = &h0 + Event Open() + #tag EndHook + + + #tag ViewBehavior + #tag ViewProperty + Name="BackColor" + Visible=true + Group="Appearance" + InitialValue="&hFFFFFF" + Type="Color" + #tag EndViewProperty + #tag ViewProperty + Name="Backdrop" + Visible=true + Group="Appearance" + Type="Picture" + EditorType="Picture" + #tag EndViewProperty + #tag ViewProperty + Name="CloseButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Composite" + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Frame" + Visible=true + Group="Appearance" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Document" + "1 - Movable Modal" + "2 - Modal Dialog" + "3 - Floating Window" + "4 - Plain Box" + "5 - Shadowed Box" + "6 - Rounded Window" + "7 - Global Floating Window" + "8 - Sheet Window" + "9 - Metal Window" + "10 - Drawer Window" + "11 - Modeless Dialog" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="FullScreen" + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="FullScreenButton" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="HasBackColor" + Visible=true + Group="Appearance" + InitialValue="False" + Type="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Height" + Visible=true + Group="Position" + InitialValue="400" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="ImplicitInstance" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Interfaces" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="LiveResize" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MacProcID" + Group="Appearance" + InitialValue="0" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaxHeight" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MaximizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MaxWidth" + Visible=true + Group="Position" + InitialValue="32000" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBar" + Visible=true + Group="Appearance" + Type="MenuBar" + EditorType="MenuBar" + #tag EndViewProperty + #tag ViewProperty + Name="MenuBarVisible" + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinHeight" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="MinimizeButton" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="MinWidth" + Visible=true + Group="Position" + InitialValue="64" + Type="Integer" + #tag EndViewProperty + #tag ViewProperty + Name="Name" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Placement" + Visible=true + Group="Position" + InitialValue="0" + Type="Integer" + EditorType="Enum" + #tag EnumValues + "0 - Default" + "1 - Parent Window" + "2 - Main Screen" + "3 - Parent Window Screen" + "4 - Stagger" + #tag EndEnumValues + #tag EndViewProperty + #tag ViewProperty + Name="Resizeable" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Super" + Visible=true + Group="ID" + Type="String" + EditorType="String" + #tag EndViewProperty + #tag ViewProperty + Name="Title" + Visible=true + Group="Appearance" + InitialValue="Untitled" + Type="String" + #tag EndViewProperty + #tag ViewProperty + Name="Visible" + Visible=true + Group="Appearance" + InitialValue="True" + Type="Boolean" + EditorType="Boolean" + #tag EndViewProperty + #tag ViewProperty + Name="Width" + Visible=true + Group="Position" + InitialValue="600" + Type="Integer" + #tag EndViewProperty + #tag EndViewBehavior +End Class +#tag EndClass diff --git a/WndEditor.xojo_window b/WndEditor.xojo_window deleted file mode 100644 index 41a79e8..0000000 --- a/WndEditor.xojo_window +++ /dev/null @@ -1,34 +0,0 @@ -#tag Window -Begin Window WndEditor - BackColor = &cFFFFFF00 - Backdrop = 0 - CloseButton = True - Compatibility = "" - Composite = False - Frame = 0 - FullScreen = False - FullScreenButton= False - HasBackColor = False - Height = 400 - ImplicitInstance= True - LiveResize = True - MacProcID = 0 - MaxHeight = 32000 - MaximizeButton = True - MaxWidth = 32000 - MenuBar = 503097343 - MenuBarVisible = True - MinHeight = 64 - MinimizeButton = True - MinWidth = 64 - Placement = 0 - Resizeable = True - Title = "Untitled" - Visible = True - Width = 600 -End -#tag EndWindow - -#tag WindowCode -#tag EndWindowCode - diff --git a/XsEdit.xojo_project b/XsEdit.xojo_project index 71f56e5..531e92e 100644 --- a/XsEdit.xojo_project +++ b/XsEdit.xojo_project @@ -2,23 +2,129 @@ Type=Desktop RBProjectVersion=2015.022 MinIDEVersion=20070100 Class=App;App.xojo_code;&h21983FFF;&h0;false -Window=WndEditor;WndEditor.xojo_window;&h6086C7FF;&h0;false -MenuBar=MainMenuBar;MainMenuBar.xojo_menu;&h1DFCA7FF;&h0;false +Folder=Menu Stuff;Menu Stuff;&h5C6C67FF;&h0;false +MenuBar=MainMenuBar;Menu Stuff/MainMenuBar.xojo_menu;&h1DFCA7FF;&h5C6C67FF;false +Folder=FileTypes;FileTypes;&h58FAEFFF;&h0;false +FileTypeSet=DocumentTypes;FileTypes/DocumentTypes.xojo_filetypeset;&h2C926FFF;&h58FAEFFF;false +Folder=Windows;Windows;&h1B897FFF;&h0;false +Folder=Dialogs;Dialogs;&h11A577FF;&h0;false +Toolbar=TBEditor;Windows/TBEditor.xojo_toolbar;&h28F3D7FF;&h1B897FFF;false +Folder=Classes;Classes;&h8E0CFFF;&h0;false +Folder=Search Stuff;Search Stuff;&h758907FF;&h0;false +Window=WndSearch;Search Stuff/WndSearch.xojo_window;&h43CF17FF;&h758907FF;false +Class=SearchReceiverWindowBase;Search Stuff/SearchReceiverWindowBase.xojo_code;&h4F462FFF;&h758907FF;false +Folder=Widgets;Widgets;&h65D33FFF;&h0;false +Class=ColorPicker_MTC;Widgets/ColorPicker_MTC.xojo_code;&h62D379D8;&h65D33FFF;false +Window=DialogButtonContainer;Widgets/DialogButtonContainer.xojo_window;&h250AC3B6;&h65D33FFF;false +Class=PlatformPushButton;Widgets/PlatformPushButton.xojo_code;&h3578F1B0;&h65D33FFF;false +Module=IDECommunicator;Classes/IDECommunicator.xojo_code;&h715E068E;&h8E0CFFF;false +Window=WndEditor;Windows/WndEditor.xojo_window;&h6086C7FF;&h1B897FFF;false +Folder=Helpers;Helpers;&h6F7957FF;&h0;false +Folder=Preference Stuff;Preference Stuff;&h1BFAEFFF;&h0;false +Interface=PreferenceWatcher;Preference Stuff/PreferenceWatcher.xojo_code;&h145547FF;&h1BFAEFFF;false +Class=Preferences;Preference Stuff/Preferences.xojo_code;&h23C0F1ED;&h1BFAEFFF;false +Class=FolderItemAlias;Helpers/FolderItemAlias.xojo_code;&h53EF1AE5;&h6F7957FF;false +Module=FormatCodePreferences;Helpers/FormatCodePreferences.xojo_code;&h2977F6F1;&h6F7957FF;false +Module=M_FolderItem;Helpers/M_FolderItem.xojo_code;&h43B8CAD2;&h6F7957FF;false BuildSteps=Build Automation;Build Automation.xojo_code;&h5B5DD7FF;&h0;false -DefaultWindow=WndEditor +Picture=bookmarksimg;Third Party Classes/CustomEditField/imgs/bookmarksimg.bmp;&h3F11E6A8;&h505F6EB;false;0;&h0 +Picture=blockEndMarker;Third Party Classes/CustomEditField/imgs/blockEndMarker.bmp;&h303E8197;&h505F6EB;false;0;&h0 +Picture=blockFoldedMarker;Third Party Classes/CustomEditField/imgs/blockFoldedMarker.bmp;&h57AE3AE5;&h505F6EB;false;0;&h0 +Picture=blockFoldedTrailMarker;Third Party Classes/CustomEditField/imgs/blockFoldedTrailMarker.bmp;&h6F9CFF01;&h505F6EB;false;0;&h0 +Picture=blockStartMarker;Third Party Classes/CustomEditField/imgs/blockStartMarker.bmp;&h2C2A158C;&h505F6EB;false;0;&h0 +Folder=Images;Images;&h3F391FFF;&h0;false +Picture=arrowright3;Images/arrow-right-3.png;&h39EE6FFF;&h3F391FFF;false;0;&h0 +Picture=systemrun3;Images/system-run-3.png;&h3A451FFF;&h3F391FFF;false;0;&h0 +Picture=arrowrightdouble3;Images/arrow-right-double-3.png;&h33A1B7FF;&h3F391FFF;false;0;&h0 +Folder=Plists;Plists;&h73DF87FF;&h0;false +Folder=Third Party Classes;Third Party Classes;&h344E0FFF;&h0;false +Plist=Retinize;Plists/Retinize.plist;&h11A9A7FF;&h73DF87FF;false +Module=M_XojoScript;Helpers/M_XojoScript.xojo_code;&h3EA6C7FF;&h6F7957FF;false +Picture=errordata;Images/errordata.bmp;&h173F2FFF;&h3F391FFF;false;0;&h0 +Picture=errormask;Images/errormask.bmp;&h8E58FFF;&h3F391FFF;false;0;&h0 +Picture=knobImage;Images/knobImage.bmp;&h504EDFFF;&h3F391FFF;false;0;&h0 +Picture=markerdata;Images/markerdata.bmp;&h1EF50FFF;&h3F391FFF;false;0;&h0 +Picture=markermask;Images/markermask.bmp;&h1A6677FF;&h3F391FFF;false;0;&h0 +Picture=plasticBack;Images/plasticBack.bmp;&h525D87FF;&h3F391FFF;false;0;&h0 +Picture=rowicondata;Images/rowicondata.bmp;&h42A3EFFF;&h3F391FFF;false;0;&h0 +Picture=rowiconmask;Images/rowiconmask.bmp;&h7938A7FF;&h3F391FFF;false;0;&h0 +Window=DlgInput;Dialogs/DlgInput.xojo_window;&h21CFB7FF;&h11A577FF;false +Class=IDEEmulator;Classes/IDEEmulator.xojo_code;&h2FC4B7FF;&h8E0CFFF;false +Window=WndAbout;Windows/WndAbout.xojo_window;&h7C1EAFFF;&h1B897FFF;false +Window=WndGoToLine;Windows/WndGoToLine.xojo_window;&h60BCFFF;&h1B897FFF;false +Class=SearchOptions;Search Stuff/SearchOptions.xojo_code;&h51513FFF;&h758907FF;false +Class=XsEditWindowBase;Windows/XsEditWindowBase.xojo_code;&h30D0D7FF;&h1B897FFF;false +Folder=CustomEditField;Third Party Classes/CustomEditField;&h258C7F1B;&h344E0FFF;false +Folder=imgs;Third Party Classes/CustomEditField/imgs;&h505F6EB;&h258C7F1B;false +Module=Info;Third Party Classes/CustomEditField/Info.xojo_code;&h2AED1A85;&h258C7F1B;false +Module=EditFieldGlobals;Third Party Classes/CustomEditField/EditFieldGlobals.xojo_code;&h3468696D;&h258C7F1B;false +Class=CustomEditField;Third Party Classes/CustomEditField/CustomEditField.xojo_code;&h32E1CFA2;&h258C7F1B;false +Window=CustomScrollableEditField;Third Party Classes/CustomEditField/CustomScrollableEditField.xojo_window;&h706F06D;&h258C7F1B;false +Class=CustomEditFieldPrinter;Third Party Classes/CustomEditField/CustomEditFieldPrinter.xojo_code;&h5892C95B;&h258C7F1B;false +Window=FindWindow;Third Party Classes/CustomEditField/FindWindow.xojo_window;&hCE1E471;&h258C7F1B;false +Folder=TextStorage;Third Party Classes/CustomEditField/TextStorage;&h425CFEF8;&h258C7F1B;false +Interface=IBufferStorage;Third Party Classes/CustomEditField/TextStorage/IBufferStorage.xojo_code;&h4A960286;&h425CFEF8;false +Class=MemoryBlockStorageWide;Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorageWide.xojo_code;&h3E801351;&h425CFEF8;false +Class=MemoryBlockStorage;Third Party Classes/CustomEditField/TextStorage/MemoryBlockStorage.xojo_code;&h4A748C8B;&h425CFEF8;false +Class=ArrayStorage;Third Party Classes/CustomEditField/TextStorage/ArrayStorage.xojo_code;&h29B76183;&h425CFEF8;false +Class=GapBuffer;Third Party Classes/CustomEditField/TextStorage/GapBuffer.xojo_code;&h46633680;&h425CFEF8;false +Folder=LineManager;Third Party Classes/CustomEditField/LineManager;&h6767AF77;&h258C7F1B;false +Class=LineManager;Third Party Classes/CustomEditField/LineManager/LineManager.xojo_code;&h1B8F7340;&h6767AF77;false +Class=TextSegment;Third Party Classes/CustomEditField/LineManager/TextSegment.xojo_code;&h66D7BE88;&h6767AF77;false +Class=TextLine;Third Party Classes/CustomEditField/LineManager/TextLine.xojo_code;&h5E1E491D;&h6767AF77;false +Class=TextPlaceholder;Third Party Classes/CustomEditField/LineManager/TextPlaceholder.xojo_code;&h165280D7;&h6767AF77;false +Class=TextLineAttributes;Third Party Classes/CustomEditField/LineManager/TextLineAttributes.xojo_code;&h288F05A;&h6767AF77;false +Class=LinesLock;Third Party Classes/CustomEditField/LineManager/LinesLock.xojo_code;&h6C5BE7A8;&h6767AF77;false +Folder=Autocomplete;Third Party Classes/CustomEditField/Autocomplete;&h48312450;&h258C7F1B;false +Class=AutocompleteOptions;Third Party Classes/CustomEditField/Autocomplete/AutocompleteOptions.xojo_code;&h5C63FD2;&h48312450;false +Window=SuggestionWindow;Third Party Classes/CustomEditField/Autocomplete/SuggestionWindow.xojo_window;&h1A88389A;&h48312450;false +Folder=PatriciaTrie;Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie;&h653C55B7;&h48312450;false +Class=PaTrie;Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrie.xojo_code;&h7EECF2A0;&h653C55B7;false +Class=PaTrieNode;Third Party Classes/CustomEditField/Autocomplete/PatriciaTrie/PaTrieNode.xojo_code;&h762D691F;&h653C55B7;false +Folder=SyntaxHighlightEngine;Third Party Classes/CustomEditField/SyntaxHighlightEngine;&h124766DD;&h258C7F1B;false +Class=LineHighlighter;Third Party Classes/CustomEditField/SyntaxHighlightEngine/LineHighlighter.xojo_code;&h5C1FD6F3;&h124766DD;false +Class=HighlightDefinition;Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightDefinition.xojo_code;&h2E595C6B;&h124766DD;false +Class=HighlightContext;Third Party Classes/CustomEditField/SyntaxHighlightEngine/HighlightContext.xojo_code;&h6CC2CE38;&h124766DD;false +Class=SymbolsDefinition;Third Party Classes/CustomEditField/SyntaxHighlightEngine/SymbolsDefinition.xojo_code;&h697137F6;&h124766DD;false +Class=DocumentSymbol;Third Party Classes/CustomEditField/SyntaxHighlightEngine/DocumentSymbol.xojo_code;&h8116F35;&h124766DD;false +Folder=Undo;Third Party Classes/CustomEditField/Undo;&hDB28231;&h258C7F1B;false +Interface=UndoableAction;Third Party Classes/CustomEditField/Undo/UndoableAction.xojo_code;&h309C465C;&hDB28231;false +Class=UndoableDelete;Third Party Classes/CustomEditField/Undo/UndoableDelete.xojo_code;&h63CF9C98;&hDB28231;false +Class=UndoableReplace;Third Party Classes/CustomEditField/Undo/UndoableReplace.xojo_code;&h533233FF;&hDB28231;false +Class=UndoManager;Third Party Classes/CustomEditField/Undo/UndoManager.xojo_code;&h4F416AB;&hDB28231;false +Folder=Extras;Third Party Classes/CustomEditField/Extras;&h4281A7A3;&h258C7F1B;false +Class=CharSelection;Third Party Classes/CustomEditField/Extras/CharSelection.xojo_code;&h2B5B1D1E;&h4281A7A3;false +Class=CharSelectionManager;Third Party Classes/CustomEditField/Extras/CharSelectionManager.xojo_code;&h6AD4F474;&h4281A7A3;false +Class=CaretBlinker;Third Party Classes/CustomEditField/Extras/CaretBlinker.xojo_code;&h42FD641D;&h4281A7A3;false +Class=DataRange;Third Party Classes/CustomEditField/Extras/DataRange.xojo_code;&h1D743151;&h4281A7A3;false +Class=ModifiedLineRangeManager;Third Party Classes/CustomEditField/Extras/ModifiedLineRangeManager.xojo_code;&h7B4EFD0D;&h4281A7A3;false +Folder=MessageCenter;Third Party Classes/CustomEditField/MessageCenter;&h4E252217;&h258C7F1B;false +Class=Message;Third Party Classes/CustomEditField/MessageCenter/Message.xojo_code;&hC1E313F;&h4E252217;false +Module=MessageCenter;Third Party Classes/CustomEditField/MessageCenter/MessageCenter.xojo_code;&h123366F6;&h4E252217;false +Class=MessageQueue;Third Party Classes/CustomEditField/MessageCenter/MessageQueue.xojo_code;&h6CACEB75;&h4E252217;false +Interface=MessageReceiver;Third Party Classes/CustomEditField/MessageCenter/MessageReceiver.xojo_code;&h4C96C7B6;&h4E252217;false +Module=DragImage;Third Party Classes/CustomEditField/DragImage.xojo_code;&h67E17DBB;&h258C7F1B;false +Class=XsEditPreferences;Preference Stuff/XsEditPreferences.xojo_code;&h1E13C7FF;&h1BFAEFFF;false +Window=WndPreferences;Preference Stuff/WndPreferences.xojo_window;&hCA3DFFF;&h1BFAEFFF;false +Window=TextContextControl;Preference Stuff/TextContextControl.xojo_window;&h7D3FFF;&h1BFAEFFF;false +Class=ContextPreferences;Preference Stuff/ContextPreferences.xojo_code;&h2E33FFF;&h1BFAEFFF;false +Module=TBConstants;Windows/TBConstants.xojo_code;&h18D68FFF;&h1B897FFF;false +Picture=arrowright3small;Images/arrow-right-3-small.png;&h4AC07FFF;&h3F391FFF;false;0;&h0 +Picture=arrowrightdouble3small;Images/arrow-right-double-3-small.png;&h1866C7FF;&h3F391FFF;false;0;&h0 +Picture=systemrun3small;Images/system-run-3-small.png;&h4075DFFF;&h3F391FFF;false;0;&h0 AppMenuBar=MainMenuBar MajorVersion=1 MinorVersion=0 SubVersion=0 NonRelease=0 -Release=0 -InfoVersion= -LongVersion= -ShortVersion= +Release=3 +InfoVersion=Advanced XojoScript editor +LongVersion=v.1.0. ©2015 by Kem Tekinay and Jeremy Cowgar. All rights reserved. +ShortVersion=1.0 WinCompanyName=MacTechnologies Consulting -WinInternalName= -WinProductName= -WinFileDescription= +WinInternalName=XsEdit +WinProductName=XsEdit +WinFileDescription=Advanced XojoScript editor AutoIncrementVersionInformation=False BuildFlags=&h4900 BuildLanguage=&h0 @@ -34,6 +140,6 @@ DefaultEncoding=&h0 AppIcon=XsEdit.xojo_resources;&h0 OSXBundleID=com.mactechnologiesconsulting.xsedit DebuggerCommandLine= -UseGDIPlus=False +UseGDIPlus=True UseBuildsFolder=True IsWebProject=False