Imports EgtUILib Imports EgtWPFLib5 Imports System.IO Imports System.Text Imports System.Text.RegularExpressions Imports System.Windows.Threading Public Class ScriptWindowVM Inherits VMBase #Region "FIELD & PROPERTIES" Public textChangedTimer As DispatcherTimer #Region "Script Color" ' Dizionario con Regex Precompilate e Colori Associati Private patterns As New Dictionary(Of Regex, System.Windows.Media.Color) From { {RegexM.KeywordRegex, ColorScriptM.KeywordColor}, {RegexM.ValueRegex, ColorScriptM.ValueColor}, {RegexM.FunctionRegex, ColorScriptM.FunctionColor}, {RegexM.InfoRegex, ColorScriptM.InfoColor}, {RegexM.EgtRegex, ColorScriptM.EgtColor}, {RegexM.BracketRegex, ColorScriptM.BracketColor}, {RegexM.TextRegex, ColorScriptM.TextColor}, {RegexM.NumberRegex, ColorScriptM.NumberColor} } #End Region ' Script Color Private m_sNameFile As String = String.Empty Public Property sNameFile As String Get Return m_sNameFile End Get Set(value As String) m_sNameFile = value NotifyPropertyChanged(NameOf(sNameFile)) End Set End Property Private m_sRichTextParagraph As String = String.Empty Public Property sRichTextParagraph As String Get Return m_sRichTextParagraph End Get Set(value As String) m_sRichTextParagraph = value NotifyPropertyChanged(NameOf(sRichTextParagraph)) End Set End Property Private m_nFontSizeList As New List(Of Integer) Public Property FontSizeList As List(Of Integer) Get Return m_nFontSizeList End Get Set(value As List(Of Integer)) m_nFontSizeList = value NotifyPropertyChanged(NameOf(FontSizeList)) End Set End Property Private m_nSelFontSize As Integer Public Property SelFontSize As Integer Get Return m_nSelFontSize End Get Set(value As Integer) m_nSelFontSize = value If Not IsNothing(m_nSelFontSize) Then ChangeFontSize(m_nSelFontSize) NotifyPropertyChanged(NameOf(SelFontSize)) End Set End Property Friend Sub SetSelFontSize(value As Integer) m_nSelFontSize = value NotifyPropertyChanged(NameOf(SelFontSize)) End Sub Private m_sFontFamilyList As New List(Of FontFamily) Public Property FontFamilyList As List(Of FontFamily) Get Return m_sFontFamilyList End Get Set(value As List(Of FontFamily)) m_sFontFamilyList = value NotifyPropertyChanged(NameOf(FontFamilyList)) End Set End Property Private m_sSelFontFamily As FontFamily Public Property SelFontFamily As FontFamily Get Return m_sSelFontFamily End Get Set(value As FontFamily) m_sSelFontFamily = value If Not IsNothing(m_sSelFontFamily) Then ChangeFontFamily(m_sSelFontFamily) NotifyPropertyChanged(NameOf(SelFontFamily)) End Set End Property Friend Sub SetSelFontFamily(value As FontFamily) m_sSelFontFamily = value NotifyPropertyChanged(NameOf(SelFontFamily)) End Sub Private m_bBoldIsChecked As Boolean = False Public Property BoldIsChecked As Boolean Get Return m_bBoldIsChecked End Get Set(value As Boolean) m_bBoldIsChecked = value NotifyPropertyChanged(NameOf(BoldIsChecked)) End Set End Property Private m_bItalicIsChecked As Boolean = False Public Property ItalicIsChecked As Boolean Get Return m_bItalicIsChecked End Get Set(value As Boolean) m_bItalicIsChecked = value NotifyPropertyChanged(NameOf(ItalicIsChecked)) End Set End Property Private m_bUnderlineChecked As Boolean = False Public Property UnderlineChecked As Boolean Get Return m_bUnderlineChecked End Get Set(value As Boolean) m_bUnderlineChecked = value NotifyPropertyChanged(NameOf(UnderlineChecked)) End Set End Property Private m_bLeftIsChecked As Boolean = False Public Property LeftIsChecked As Boolean Get Return m_bLeftIsChecked End Get Set(value As Boolean) m_bLeftIsChecked = value SetCenterIsChecked(False) SetJustifyIsChecked(False) NotifyPropertyChanged(NameOf(LeftIsChecked)) End Set End Property Friend Sub SetLeftIsChecked(value As Boolean) m_bLeftIsChecked = value NotifyPropertyChanged(NameOf(LeftIsChecked)) End Sub Private m_bCenterIsChecked As Boolean = False Public Property CenterIsChecked As Boolean Get Return m_bCenterIsChecked End Get Set(value As Boolean) m_bCenterIsChecked = value SetLeftIsChecked(False) SetJustifyIsChecked(False) NotifyPropertyChanged(NameOf(CenterIsChecked)) End Set End Property Friend Sub SetCenterIsChecked(value As Boolean) m_bCenterIsChecked = value NotifyPropertyChanged(NameOf(CenterIsChecked)) End Sub Private m_bJustifyIsChecked As Boolean = False Public Property JustifyIsChecked As Boolean Get Return m_bJustifyIsChecked End Get Set(value As Boolean) m_bJustifyIsChecked = value SetLeftIsChecked(False) SetCenterIsChecked(False) NotifyPropertyChanged(NameOf(JustifyIsChecked)) End Set End Property Friend Sub SetJustifyIsChecked(value As Boolean) m_bJustifyIsChecked = value NotifyPropertyChanged(NameOf(JustifyIsChecked)) End Sub Private m_FileAlignLeftSVG As String = String.Empty Public Property FileAlignLeftSVG As String Get Return m_FileAlignLeftSVG End Get Set(value As String) m_FileAlignLeftSVG = value NotifyPropertyChanged(NameOf(FileAlignLeftSVG)) End Set End Property Friend Sub SetFileAlignLeftSVG(sFileAlignLeftSVG As String) m_FileAlignLeftSVG = sFileAlignLeftSVG NotifyPropertyChanged(NameOf(FileAlignLeftSVG)) End Sub Private m_FileAlignCenterSVG As String = String.Empty Public Property FileAlignCenterSVG As String Get Return m_FileAlignCenterSVG End Get Set(value As String) m_FileAlignCenterSVG = value NotifyPropertyChanged(NameOf(FileAlignCenterSVG)) End Set End Property Friend Sub SetFileAlignCenterSVG(sFileAlignCenterSVG As String) m_FileAlignCenterSVG = sFileAlignCenterSVG NotifyPropertyChanged(NameOf(FileAlignCenterSVG)) End Sub Private m_FileAlignJustifySVG As String = String.Empty Public Property FileAlignJustifySVG As String Get Return m_FileAlignJustifySVG End Get Set(value As String) m_FileAlignJustifySVG = value NotifyPropertyChanged(NameOf(FileAlignJustifySVG)) End Set End Property Friend Sub SetFileAlignJustifySVG(sFileAlignJustifySVG As String) m_FileAlignJustifySVG = sFileAlignJustifySVG NotifyPropertyChanged(NameOf(FileAlignJustifySVG)) End Sub #Region "Messages" Public ReadOnly Property Title As String Get Return "Script File" ' Opzioni End Get End Property Public ReadOnly Property Conferma_Msg As String Get Return EgtMsg(110003) ' Conferma End Get End Property Public ReadOnly Property Salva_Msg As String Get Return EgtMsg(110013) ' Salva End Get End Property Public ReadOnly Property Annulla_Msg As String Get Return EgtMsg(110004) ' Annulla End Get End Property #End Region ' Messages ' Definizione Comandi Private m_ConfermaCmd As ICommand Private m_cmdSaveAs As ICommand Private m_cmdAnnulla As ICommand Private m_cmdTextColor As ICommand #End Region ' Fields & Properties #Region "CONSTRUCTOR" Sub New() Map.SetRefScriptWindowVM(Me) If m_nFontSizeList.Count <= 0 Then CreateFontSizeList() If m_sFontFamilyList.Count <= 0 Then CreateFontFamilyList() SetToolbar() SetFileAlignLeftSVG(Map.refMainWindowVM.MainWindowM.sResourcesDir & "\alignleft.svg") SetFileAlignCenterSVG(Map.refMainWindowVM.MainWindowM.sResourcesDir & "\aligncenter.svg") SetFileAlignJustifySVG(Map.refMainWindowVM.MainWindowM.sResourcesDir & "\alignjustify.svg") End Sub #End Region ' Constructor #Region "METHODS" Public Function SaveProject() As Boolean ' Determina il nome del file di default Dim sFile As String = If(String.IsNullOrWhiteSpace(m_sNameFile), "New.lua", $"{m_sNameFile}.lua") sFile = IO.Path.ChangeExtension(sFile, "lua") ' Assegnazione nome file con dialogo Dim SaveFileDialog As New EgtManageFileDialogV(Application.Current.MainWindow, New EgtManageFileDialogVM()) With { .Title = EgtMsg(110013), ' Salva .Filter = "lua files (*.lua)|*.lua", .FileName = sFile, .FilterIndex = 1, .InitialDirectory = Map.refMainWindowVM.MainWindowM.sTempDir, .ValidateNames = False, .OverwritePrompt = True, .Mode = 1 } ' Mostra la finestra di dialogo e ottieni il percorso del file selezionato Dim sFileName As String = String.Empty If SaveFileDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then Dim fileExtension = IO.Path.GetExtension(SaveFileDialog.SafeFileName) sFileName = IO.Path.Combine(SaveFileDialog.InitialDirectory, SaveFileDialog.SafeFileName & If(String.IsNullOrEmpty(fileExtension), SaveFileDialog.SelFilter.sExstension.Trim("*"c), String.Empty)) End If ' Verifica se il nome del file è stato selezionato If String.IsNullOrWhiteSpace(sFileName) Then Return False ' Salva il progetto e scrive lo script Lua Dim bOk = Map.refSceneHostVM.SaveProj(sFileName) WriteTextScriptLua(sFileName) ' Imposta lo stato della scena Map.refSceneHostVM.MainScene.SetStatusNull() Return bOk End Function Private Sub WriteTextScriptLua(sFileName As String) If String.IsNullOrWhiteSpace(sFileName) Then Return ' Validazione input Dim sFileScript As String = RichTextBox(sFileName) ' Ottieni contenuto da RichTextBox File.WriteAllText(sFileName, sFileScript, Encoding.UTF8) ' Salva il file con codifica UTF-8 End Sub Private Sub SaveScript(sPathFileScript As String, sRichTextParagraph As String) If String.IsNullOrWhiteSpace(sPathFileScript) OrElse String.IsNullOrWhiteSpace(sRichTextParagraph) Then Return ' Validazione input Map.refSceneHostVM.SaveProj(sPathFileScript) ' Salva il progetto nel percorso specificato File.WriteAllText(sPathFileScript, sRichTextParagraph, Encoding.UTF8) ' Salva il contenuto con codifica UTF-8 End Sub Public Function RichTextBox(sPathFileScript As String) As String ' Recupero il RichTextBox e il FlowDocument Dim myRichTextBox As RichTextBox = Map.refScriptWindowV.sNameFile_RichTxBx Dim myFlowDoc As FlowDocument = Map.refScriptWindowV.FDocumentFile ' Cancella il contenuto del documento e assegna un nuovo FlowDocument myRichTextBox.Document.Blocks.Clear() myRichTextBox.Document = myFlowDoc ' Ottiene il testo completo del documento Dim textRange As New TextRange(myRichTextBox.Document.ContentStart, myRichTextBox.Document.ContentEnd) Return textRange.Text End Function Private Sub CreateFontSizeList() ' Aggiunge solo font-size pari alla lista, con un semplice iteratore m_nFontSizeList.AddRange(Enumerable.Range(8, 13).Where(Function(size) size Mod 2 = 0)) End Sub Private Sub ChangeFontSize(nSelFontSize As Integer) If nSelFontSize = GDB_ID.NULL Then Return Dim pixelSize As Double = nSelFontSize * (96.0 / 72.0) Dim textRange = New TextRange(Map.refScriptWindowV.sNameFile_RichTxBx.Selection.Start, Map.refScriptWindowV.sNameFile_RichTxBx.Selection.End) textRange.ApplyPropertyValue(TextElement.FontSizeProperty, pixelSize) End Sub Private Sub CreateFontFamilyList() ' Aggiunge direttamente tutte le famiglie di font alla lista m_sFontFamilyList.AddRange(Fonts.SystemFontFamilies) End Sub Private Sub ChangeFontFamily(sSelFontFamily As FontFamily) If sSelFontFamily Is Nothing Then Return Dim textRange = New TextRange(Map.refScriptWindowV.sNameFile_RichTxBx.Selection.Start, Map.refScriptWindowV.sNameFile_RichTxBx.Selection.End) textRange.ApplyPropertyValue(TextElement.FontFamilyProperty, sSelFontFamily) End Sub Friend Sub SetToolbar() Dim textRange As New TextRange(Map.refScriptWindowV.sNameFile_RichTxBx.Selection.Start, Map.refScriptWindowV.sNameFile_RichTxBx.Selection.[End]) ' Recupera font e dimensione SetSelFontFamily(textRange.GetPropertyValue(TextElement.FontFamilyProperty)) SetSelFontSize(textRange.GetPropertyValue(TextElement.FontSizeProperty)) If Not String.IsNullOrWhiteSpace(textRange.Text) Then ' Recupera proprietà di formattazione (Bold, Italic, Underline) m_bBoldIsChecked = Equals(textRange.GetPropertyValue(TextElement.FontWeightProperty), FontWeights.Bold) m_bItalicIsChecked = Equals(textRange.GetPropertyValue(TextElement.FontStyleProperty), FontStyles.Italic) m_bUnderlineChecked = Equals(textRange.GetPropertyValue(Inline.TextDecorationsProperty), TextDecorations.Underline) End If ' Recupera proprietà di allineamento Dim alignment = textRange.GetPropertyValue(FlowDocument.TextAlignmentProperty) m_bLeftIsChecked = Equals(alignment, TextAlignment.Left) m_bCenterIsChecked = Equals(alignment, TextAlignment.Center) m_bJustifyIsChecked = Equals(alignment, TextAlignment.Justify) End Sub Public Sub InsertText(ByVal sRichTextBox As RichTextBox, ByVal content As String) ' Verifica nullità e contenuto non vuoto If String.IsNullOrWhiteSpace(content) OrElse sRichTextBox Is Nothing Then Return ' Sospende gli aggiornamenti visivi per migliorare le prestazioni sRichTextBox.BeginChange() Try ' Rimuove il testo selezionato (se esiste) If Not String.IsNullOrEmpty(sRichTextBox.Selection.Text) Then sRichTextBox.Selection.Text = String.Empty ' Inserisce il contenuto e riposiziona il cursore Dim textPointer As TextPointer = sRichTextBox.CaretPosition textPointer.InsertTextInRun(content) sRichTextBox.CaretPosition = textPointer.GetPositionAtOffset(content.Length, LogicalDirection.Forward) Finally ' Ripristina gli aggiornamenti visivi sRichTextBox.EndChange() ' Imposta il focus sulla RichTextBox Keyboard.Focus(sRichTextBox) End Try End Sub Public Sub SyntaxHighlighting(rtb As RichTextBox) ' Sospendere gli aggiornamenti visivi per migliorare le prestazioni rtb.BeginChange() Try ' Recupera il testo dalla RichTextBox Dim textRange As New TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd) Dim text As String = textRange.Text ' Applicare evidenziazione per ogni regex nel dizionario For Each kvp In patterns Dim regex As Regex = kvp.Key Dim color As Color = kvp.Value ' Evidenzia le corrispondenze Dim matches As MatchCollection = regex.Matches(text) For Each match As Match In matches HighlightText(rtb, match.Index, match.Length, color) Next Next ' Evidenzia commenti (esempio di commento Lua: "--") Dim commentMatches = patterns.FirstOrDefault(Function(p) p.Key.ToString().Contains("--")) If commentMatches.Key IsNot Nothing Then HighlightComments(rtb, text, "--", ColorScriptM.CommentColor) End If Finally ' Riprendere gli aggiornamenti visivi rtb.EndChange() End Try End Sub Private Sub HighlightText(rtb As RichTextBox, startIndex As Integer, length As Integer, color As Color) Dim startPointer As TextPointer = GetTextPosition(rtb.Document.ContentStart, startIndex) Dim endPointer As TextPointer = GetTextPosition(startPointer, length) Dim range As New TextRange(startPointer, endPointer) range.ApplyPropertyValue(TextElement.ForegroundProperty, New SolidColorBrush(color)) End Sub Private Sub HighlightComments(rtb As RichTextBox, text As String, commentSymbol As String, color As Color) Dim startIndex As Integer = 0 While startIndex < text.Length startIndex = text.IndexOf(commentSymbol, startIndex) If startIndex = -1 Then Exit While Dim endIndex As Integer = text.IndexOf(Environment.NewLine, startIndex) If endIndex = -1 Then endIndex = text.Length HighlightText(rtb, startIndex, endIndex - startIndex, color) startIndex = endIndex End While End Sub Private Function GetTextPosition(start As TextPointer, position As Integer) As TextPointer Dim navigator As TextPointer = start While navigator IsNot Nothing AndAlso position > 0 Dim context = navigator.GetPointerContext(LogicalDirection.Forward) If context = TextPointerContext.Text Then Dim run As String = navigator.GetTextInRun(LogicalDirection.Forward) Dim count As Integer = Math.Min(run.Length, position) navigator = navigator.GetPositionAtOffset(count) position -= count Else navigator = navigator.GetPositionAtOffset(1) End If End While Return navigator End Function Public Sub ProcessTextChanges() SyntaxHighlighting(Map.refScriptWindowV.sNameFile_RichTxBx) End Sub Public Sub ChangeTimer() If Not IsNothing(Map.refScriptWindowVM) AndAlso textChangedTimer Is Nothing Then textChangedTimer = New DispatcherTimer() textChangedTimer.Interval = TimeSpan.FromMilliseconds(500) AddHandler textChangedTimer.Tick, AddressOf OnTextChangedTimerTick End If textChangedTimer?.Stop() textChangedTimer?.Start() End Sub Public Sub Tabulation(ByVal sRichTextBox As RichTextBox) If sRichTextBox Is Nothing Then Return ' Verifica nullità dell'oggetto ' Inserisci un carattere di tabulazione (3 spazi) Dim tabString As String = " " ' Gestisci il punto di inserimento del cursore Dim caretPosition As TextPointer = sRichTextBox.CaretPosition If caretPosition IsNot Nothing Then caretPosition.InsertTextInRun(tabString) ' Aggiorna la posizione del cursore dopo il testo inserito sRichTextBox.CaretPosition = caretPosition.GetPositionAtOffset(tabString.Length, LogicalDirection.Forward) End If End Sub #End Region ' Methods #Region "EVENTS" Private Sub OnTextChangedTimerTick(sender As Object, e As EventArgs) textChangedTimer.Stop() ProcessTextChanges() End Sub #End Region ' Events #Region "COMMANDS" #Region "ConfermCmd" Public ReadOnly Property ConfermaCmd As ICommand Get If m_ConfermaCmd Is Nothing Then m_ConfermaCmd = New Command(AddressOf Conferma) End If Return m_ConfermaCmd End Get End Property Public Sub Conferma() ' Genera il percorso del file script Dim scriptFilePath As String = IO.Path.Combine(Map.refMainWindowVM.MainWindowM.sTempDir, $"{m_sNameFile}.lua") ' Recupera il contenuto del file script Dim sFileScript As String = RichTextBox(scriptFilePath) ' Salva il file script SaveScript(scriptFilePath, sFileScript) ' Esegue il file script EgtLuaExecFile(scriptFilePath) ' Imposta lo stato della gestione mouse diretto della scena a "nessuno" Map.refSceneHostVM.MainScene.SetStatusNull() End Sub #End Region ' ConfermaCmd #Region "SaveAsCommand" Public ReadOnly Property SalvaCmd As ICommand Get If m_cmdSaveAs Is Nothing Then m_cmdSaveAs = New Command(AddressOf SaveAs) End If Return m_cmdSaveAs End Get End Property Public Sub SaveAs() SaveProject() End Sub #End Region ' SaveAsCommand #Region "AnnullaCmd" Public ReadOnly Property AnnullaCmd As ICommand Get If m_cmdAnnulla Is Nothing Then m_cmdAnnulla = New Command(AddressOf Annulla) End If Return m_cmdAnnulla End Get End Property Public Sub Annulla() Map.refScriptWindowV.Close() End Sub #End Region ' AnnullaCmd #Region "TextColorCmd" Public ReadOnly Property TextColorCmd As ICommand Get If m_cmdTextColor Is Nothing Then m_cmdTextColor = New Command(AddressOf TextColor) End If Return m_cmdTextColor End Get End Property Public Sub TextColor() ' Recupero colori custom Dim defaultColor As New Color3d(10, 122, 150) Dim sCustomColors As String = String.Empty GetMainPrivateProfileString(S_COLORS, K_CUSTOMCOLORS, "", sCustomColors) ' Parsing dei colori custom in una lista di interi Dim nCustomColors = sCustomColors.Split(","c). Select(Function(color) Dim nColor As Integer Return If(Integer.TryParse(color, nColor), nColor, Nothing) End Function). Where(Function(nColor) nColor > 0).Cast(Of Integer)().ToList() ' Configurazione dialogo colori Dim colorDialog As New EgtColorPickerV(Application.Current.MainWindow, New EgtColorPickerVM()) With { .CustomColors = nCustomColors.ToArray(), .Color = defaultColor.ToColor() } ' Visualizzo il dialogo e gestisco l'output If colorDialog.ShowDialog() <> Windows.Forms.DialogResult.OK Then Return ' Conversione colore selezionato Dim selectedColor As System.Windows.Media.Color = System.Windows.Media.Color.FromRgb( CByte(colorDialog.Color.R), CByte(colorDialog.Color.G), CByte(colorDialog.Color.B) ) Dim selColorBrush As New SolidColorBrush(selectedColor) ' Applicazione del colore al testo selezionato Dim textRange As New TextRange(Map.refScriptWindowV.sNameFile_RichTxBx.Selection.Start, Map.refScriptWindowV.sNameFile_RichTxBx.Selection.End) textRange.ApplyPropertyValue(TextElement.ForegroundProperty, selColorBrush) End Sub #End Region ' TextColorCmd #End Region ' Commands End Class