See More

using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using CSScriptLibrary; using FlashDevelop.Controls; using FlashDevelop.Dialogs; using FlashDevelop.Docking; using FlashDevelop.Helpers; using FlashDevelop.Managers; using FlashDevelop.Settings; using FlashDevelop.Utilities; using ICSharpCode.SharpZipLib.Zip; using PluginCore; using PluginCore.Controls; using PluginCore.Helpers; using PluginCore.Localization; using PluginCore.Managers; using PluginCore.Utilities; using ScintillaNet; using ScintillaNet.Configuration; using WeifenLuo.WinFormsUI.Docking; namespace FlashDevelop { public class MainForm : Form, IMainForm, IMessageFilter { #region Constructor public MainForm() { Globals.MainForm = this; PluginBase.Initialize(this); DoubleBuffered = true; InitializeErrorLog(); InitializeSettings(); InitializeLocalization(); if (InitializeFirstRun() != DialogResult.Abort) { // Suspend layout! SuspendLayout(); InitializeConfig(); InitializeRendering(); InitializeComponents(); InitializeProcessRunner(); InitializeSmartDialogs(); InitializeMainForm(); InitializeGraphics(); Application.AddMessageFilter(this); } else Load += MainFormLoaded; } ///

/// Initializes some extra error logging /// static void InitializeErrorLog() => AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; /// /// Handles the catched unhandled exception and logs it /// static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { var exception = new Exception(e.ExceptionObject.ToString()); ErrorManager.AddToLog("Unhandled exception: ", exception); } /// /// Exit nicely after the form has been loaded /// void MainFormLoaded(object sender, EventArgs e) => Close(); #endregion #region Private Properties /* AppMan */ FileSystemWatcher amWatcher; /* Components */ QuickFind quickFind; ToolStripProgressBarEx toolStripProgressBar; ToolStripButton restartButton; ProcessRunner processRunner; /* Dialogs */ PrintDialog printDialog; ColorDialog colorDialog; OpenFileDialog openFileDialog; SaveFileDialog saveFileDialog; PrintPreviewDialog printPreviewDialog; FRInFilesDialog frInFilesDialog; FRInDocDialog frInDocDialog; GoToDialog gotoDialog; /* Working Dir */ string workingDirectory = string.Empty; /* Form State */ FormState formState; Hashtable fullScreenDocks; bool notifyOpenFile; bool closingForOpenFile; bool closingAll; /* Singleton */ public static bool Silent; public static bool IsFirst; public static string[] Arguments; #endregion #region Public Properties /// /// Gets the DockPanel /// public DockPanel DockPanel { get; private set; } /// /// Gets the Scintilla configuration /// public Scintilla SciConfig => ScintillaManager.SciConfig; /// /// Gets the menu strip /// public MenuStrip MenuStrip { get; private set; } /// /// Gets the tool strip /// public ToolStrip ToolStrip { get; private set; } /// /// Gets the tool strip panel /// public ToolStripPanel ToolStripPanel { get; private set; } /// /// Gets the toolStripStatusLabel /// public ToolStripStatusLabel StatusLabel { get; private set; } /// /// Gets the toolStripProgressLabel /// public ToolStripStatusLabel ProgressLabel { get; private set; } /// /// Gets the ToolStripProgressBarEx /// public ToolStripProgressBar ProgressBar => toolStripProgressBar; /// /// Gets the TabMenu /// public ContextMenuStrip TabMenu { get; private set; } /// /// Gets the EditorMenu /// public ContextMenuStrip EditorMenu { get; private set; } /// /// Gets the StatusStrip /// public StatusStrip StatusStrip { get; private set; } /// /// Gets the IgnoredKeys /// public List IgnoredKeys => ShortcutManager.AllShortcuts; /// /// Gets the Settings interface /// public ISettings Settings => AppSettings; /// /// Gets or sets the actual Settings /// public SettingObject AppSettings { get; set; } /// /// Gets the CurrentDocument /// public ITabbedDocument? CurrentDocument => DockPanel.ActiveDocument as ITabbedDocument; /// /// Is FlashDevelop closing? /// public bool ClosingEntirely { get; private set; } /// /// Is this first MainForm instance? /// public bool IsFirstInstance => IsFirst; /// /// Is FlashDevelop in multi instance mode? /// public bool MultiInstanceMode => Program.MultiInstanceMode; /// /// Is FlashDevelop in standalone mode? /// public bool StandaloneMode => File.Exists(Path.Combine(PathHelper.AppDir, ".local")); /// /// Gets the all available documents /// public ITabbedDocument[] Documents { get { var documents = new List(); foreach (var pane in DockPanel.Panes) { if (pane.DockState != DockState.Document) continue; foreach (var content in pane.Contents) { if (content is TabbedDocument document) { documents.Add(document); } } } return documents.ToArray(); } } public int DocumentsLength() { var result = 0; foreach (var pane in DockPanel.Panes) { if (pane.DockState != DockState.Document) continue; foreach (var content in pane.Contents) { if (content is TabbedDocument) { result++; } } } return result; } /// /// Does FlashDevelop hold modified documents? /// public bool HasModifiedDocuments => Documents.Any(document => document.IsModified); /// /// Gets or sets the WorkingDirectory /// public string WorkingDirectory { get { if (!Directory.Exists(workingDirectory)) workingDirectory = GetWorkingDirectory(); return workingDirectory; } set => workingDirectory = value; } /// /// Gets or sets the ProcessIsRunning /// public bool ProcessIsRunning => processRunner.IsRunning; /// /// Gets the panelIsActive /// public bool PanelIsActive { get; private set; } /// /// Gets the isFullScreen /// public bool IsFullScreen { get; private set; } /// /// Gets or sets the ReloadingDocument /// public bool ReloadingDocument { get; set; } /// /// Gets or sets the CloseAllCanceled /// public bool CloseAllCanceled { get; set; } /// /// Gets or sets the ProcessingContents /// public bool ProcessingContents { get; set; } = false; /// /// Gets or sets the RestoringContents /// public bool RestoringContents { get; set; } = false; /// /// Gets or sets the SavingMultiple /// public bool SavingMultiple { get; set; } /// /// Gets or sets the RestartRequested /// public bool RestartRequested { get; set; } /// /// Gets whether the application requires a restart to apply changes. /// public bool RequiresRestart { get; private set; } /// /// Gets or sets the RefreshConfig /// public bool RefreshConfig { get; set; } /// /// Gets the application start args /// public string[] StartArguments => Arguments.ToArray(); /// /// Gets the application custom args /// public List CustomArguments => ArgumentDialog.CustomArguments; /// /// Gets the application's version /// public new string ProductVersion => Application.ProductVersion; /// /// Gets the full human readable version string /// public new string ProductName => Application.ProductName; /// /// Gets the command prompt executable (custom or cmd.exe by default). /// public string CommandPromptExecutable => File.Exists(Settings.CustomCommandPrompt) ? Settings.CustomCommandPrompt : "cmd.exe"; /// /// Gets the version of the operating system /// public Version OSVersion => Environment.OSVersion.Version; #endregion #region Component Creation /// /// Creates a new custom document /// public DockContent CreateCustomDocument(Control ctrl) { try { var result = new TabbedDocument(); result.Closing += OnDocumentClosing; result.Closed += OnDocumentClosed; result.Text = TextHelper.GetString("Title.CustomDocument"); result.TabPageContextMenuStrip = TabMenu; result.Controls.Add(ctrl); result.Show(); return result; } catch (Exception ex) { ErrorManager.ShowError(ex); return null; } } /// /// Creates a new empty document /// public DockContent CreateEditableDocument(string file, string text, int codepage) { try { notifyOpenFile = true; var result = new TabbedDocument(); result.Closing += OnDocumentClosing; result.Closed += OnDocumentClosed; result.TabPageContextMenuStrip = TabMenu; result.ContextMenuStrip = EditorMenu; result.Text = Path.GetFileName(file); result.AddEditorControls(file, text, codepage); result.Show(); return result; } catch (Exception ex) { ErrorManager.ShowError(ex); return null; } } /// /// Creates a floating panel for the plugin /// public DockContent CreateDockablePanel(Control ctrl, string guid, Image image, DockState defaultDockState) { try { var result = new DockablePanel(ctrl, guid); result.Show(); result.Image = image; result.DockState = defaultDockState; LayoutManager.PluginPanels.Add(result); return result; } catch (Exception ex) { ErrorManager.ShowError(ex); return null; } } /// /// Creates a dynamic persist panel for plugins. /// public DockContent CreateDynamicPersistDockablePanel(Control ctrl, string guid, string id, Image image, DockState defaultDockState) { try { var result = new DockablePanel(ctrl, guid + ":" + id) {Image = image, DockState = defaultDockState}; LayoutManager.SetContentLayout(result, result.GetPersistString()); LayoutManager.PluginPanels.Add(result); return result; } catch (Exception ex) { ErrorManager.ShowError(ex); return null; } } /// /// Opens the specified file and creates an editable document /// public DockContent? OpenEditableDocument(string file) => OpenEditableDocument(file, null, true); /// /// Opens the specified file and creates an editable document /// public DockContent? OpenEditableDocument(string file, bool restorePosition) => OpenEditableDocument(file, null, restorePosition); /// /// Opens the specified file and creates an editable document /// public DockContent? OpenEditableDocument(string org, Encoding encoding, bool restorePosition) { var file = PathHelper.GetPhysicalPathName(org); var te = new TextEvent(EventType.FileOpening, file); EventManager.DispatchEvent(this, te); if (te.Handled) { if (DocumentsLength() == 0) SmartNew(null, null); return null; } if (file.EndsWithOrdinal(".delete.fdz")) { CallCommand("RemoveZip", file); return null; } if (file.EndsWithOrdinal(".fdz")) { CallCommand("ExtractZip", file); if (file.IndexOf("theme", StringComparison.OrdinalIgnoreCase) == -1) return null; var path = Path.Combine(PathHelper.ThemesDir, "CURRENT"); if (!File.Exists(path)) return null; ThemeManager.LoadTheme(path); ThemeManager.WalkControls(this); RefreshSciConfig(); return null; } try { foreach (var doc in Documents) { if (doc.SciControl?.FileName is { } fileName && string.Equals(fileName, file, StringComparison.CurrentCultureIgnoreCase)) { doc.Activate(); return doc as DockContent; } } } catch { } DockContent createdDoc; EncodingFileInfo info; if (encoding is null) { info = FileHelper.GetEncodingFileInfo(file); if (info.CodePage == -1) return null; // If the file is locked, stop. } else { info = FileHelper.GetEncodingFileInfo(file); if (info.CodePage == -1) return null; // If the file is locked, stop. info.Contents = FileHelper.ReadFile(file, encoding); info.CodePage = encoding.CodePage; } var de = new DataEvent(EventType.FileDecode, file, null); EventManager.DispatchEvent(this, de); // Lets ask if a plugin wants to decode the data.. if (de.Handled) { info.Contents = de.Data as string; info.CodePage = Encoding.UTF8.CodePage; // assume plugin always return UTF8 } try { if (CurrentDocument is { } doc && doc.IsUntitled && !doc.IsModified && DocumentsLength() == 1) { closingForOpenFile = true; doc.Close(); closingForOpenFile = false; createdDoc = CreateEditableDocument(file, info.Contents, info.CodePage); } else createdDoc = CreateEditableDocument(file, info.Contents, info.CodePage); ButtonManager.AddNewReopenMenuItem(file); } catch { createdDoc = CreateEditableDocument(file, info.Contents, info.CodePage); ButtonManager.AddNewReopenMenuItem(file); } var sci = ((TabbedDocument)createdDoc).SciControl; sci.SaveBOM = info.ContainsBOM; sci.BeginInvoke((Action)(() => { if (AppSettings.RestoreFileStates) FileStateManager.ApplyFileState(sci, restorePosition); })); ButtonManager.UpdateFlaggedButtons(); return createdDoc; } #endregion #region Construct Components /// /// Initializes the graphics /// void InitializeGraphics() => Icon = printPreviewDialog.Icon = new Icon(ResourceHelper.GetStream("FlashDevelopIcon.ico")); /// /// Initializes the theme and config detection /// void InitializeConfig() { try { // Check for FD update var update = Path.Combine(PathHelper.BaseDir, ".update"); if (File.Exists(update)) { File.Delete(update); RefreshConfig = true; } // Check for appman update var appman = Path.Combine(PathHelper.BaseDir, ".appman"); if (File.Exists(appman)) { File.Delete(appman); RefreshConfig = true; } // Load platform data from user files PlatformData.Load(Path.Combine(PathHelper.SettingDir, "Platforms")); // Load current theme for applying later var currentTheme = Path.Combine(PathHelper.ThemesDir, "CURRENT"); if (File.Exists(currentTheme)) ThemeManager.LoadTheme(currentTheme); // Apply FD dir and appman dir to PATH var amPath = Path.Combine(PathHelper.ToolDir, "AppMan"); var oldPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process); var newPath = oldPath + ";" + amPath + ";" + PathHelper.AppDir; Environment.SetEnvironmentVariable("PATH", newPath, EnvironmentVariableTarget.Process); // Watch for appman update notifications amWatcher = new FileSystemWatcher(PathHelper.BaseDir, ".appman"); amWatcher.Changed += AppManUpdate; amWatcher.Created += AppManUpdate; amWatcher.IncludeSubdirectories = false; amWatcher.EnableRaisingEvents = true; } catch {} // No errors... } /// /// When AppMan installs something it notifies of changes. Forward notifications. /// void AppManUpdate(object sender, FileSystemEventArgs e) { try { var ne = new NotifyEvent(EventType.AppChanges); EventManager.DispatchEvent(this, ne); // Notify plugins... var appMan = Path.Combine(PathHelper.BaseDir, ".appman"); var contents = File.ReadAllText(appMan); if (contents == "restart") RestartRequired(); } catch {} // No errors... } /// /// Initializes the restart button /// void InitializeRestartButton() { restartButton = new ToolStripButton { Image = FindImage("73|6|3|3"), Alignment = ToolStripItemAlignment.Right, Text = TextHelper.GetString("Label.Restart"), ToolTipText = TextHelper.GetString("Info.RequiresRestart"), Visible = false }; restartButton.Click += Restart; ToolStrip.Items.Add(restartButton); } /// /// Initializes the smart dialogs /// public void InitializeSmartDialogs() { formState = new FormState(); gotoDialog = new GoToDialog(); frInFilesDialog = new FRInFilesDialog(); frInDocDialog = new FRInDocDialog(); } /// /// Initializes the First Run dialog /// DialogResult InitializeFirstRun() => !StandaloneMode && IsFirst && FirstRunDialog.ShouldProcessCommands() ? FirstRunDialog.Show() : DialogResult.None; /// /// Initializes the UI rendering /// static void InitializeRendering() { if (PluginBase.Settings.RenderMode == UiRenderMode.System) { ToolStripManager.VisualStylesEnabled = true; ToolStripManager.RenderMode = ToolStripManagerRenderMode.System; } else if (PluginBase.Settings.RenderMode == UiRenderMode.Professional) { ToolStripManager.VisualStylesEnabled = false; ToolStripManager.RenderMode = ToolStripManagerRenderMode.Professional; } } /// /// Initializes the application settings /// void InitializeSettings() { AppSettings = SettingObject.GetDefaultSettings(); if (File.Exists(FileNameHelper.SettingData)) AppSettings = ObjectSerializer.Deserialize(FileNameHelper.SettingData, AppSettings, false); SettingObject.EnsureValidity(AppSettings); FileStateManager.RemoveOldStateFiles(); } /// /// Initializes the localization from .locale file /// void InitializeLocalization() { try { var filePath = Path.Combine(PathHelper.BaseDir, ".locale"); if (!File.Exists(filePath)) return; var enumData = File.ReadAllText(filePath).Trim(); var localeVersion = (LocaleVersion)Enum.Parse(typeof(LocaleVersion), enumData); AppSettings.LocaleVersion = localeVersion; File.Delete(filePath); } catch {} // No errors... } /// /// Initializes the process runner /// public void InitializeProcessRunner() { processRunner = new ProcessRunner {RedirectInput = true}; processRunner.ProcessEnded += ProcessEnded; processRunner.Output += ProcessOutput; processRunner.Error += ProcessError; } /// /// Checks for updates in specified schedule /// public void CheckForUpdates() { try { var last = new DateTime(AppSettings.LastUpdateCheck); var elapsed = DateTime.UtcNow.Subtract(last); switch (AppSettings.CheckForUpdates) { case UpdateInterval.Weekly: if (elapsed.TotalDays >= 7) { AppSettings.LastUpdateCheck = DateTime.UtcNow.Ticks; UpdateDialog.Show(true); } break; case UpdateInterval.Monthly: if (elapsed.TotalDays >= 30) { AppSettings.LastUpdateCheck = DateTime.UtcNow.Ticks; UpdateDialog.Show(true); } break; } } catch { /* NO ERRORS PLEASE */ } } /// /// Initializes the window position and size /// public void InitializeWindow() { WindowState = AppSettings.WindowState; Rectangle bounds = new Rectangle(AppSettings.WindowPosition, AppSettings.WindowSize); bounds.Inflate(-4, -25); bool validPosition = false; foreach (Screen screen in Screen.AllScreens) { if (screen.Bounds.IntersectsWith(bounds)) { Location = AppSettings.WindowPosition; validPosition = true; break; } } if (!validPosition) Location = new Point(0, 0); // Continue/perform layout! ResumeLayout(false); PerformLayout(); } /// /// Initialises the plugins, restores the layout and sets an fixed position /// public void InitializeMainForm() { try { string pluginDir = PathHelper.PluginDir; // Plugins of all users if (Directory.Exists(pluginDir)) PluginServices.FindPlugins(pluginDir); if (!StandaloneMode) // No user plugins on standalone... { string userPluginDir = PathHelper.UserPluginDir; if (Directory.Exists(userPluginDir)) PluginServices.FindPlugins(userPluginDir); else Directory.CreateDirectory(userPluginDir); } LayoutManager.BuildLayoutSystems(FileNameHelper.LayoutData); ShortcutManager.LoadCustomShortcuts(); ArgumentDialog.LoadCustomArguments(); ClipboardManager.Initialize(this); UITools.Init(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Initializes the form components /// void InitializeComponents() { quickFind = new QuickFind(); DockPanel = new DockPanel(); StatusStrip = new StatusStrip(); ToolStripPanel = new ToolStripPanel(); MenuStrip = StripBarManager.GetMenuStrip(FileNameHelper.MainMenu); ToolStrip = StripBarManager.GetToolStrip(FileNameHelper.ToolBar); EditorMenu = StripBarManager.GetContextMenu(FileNameHelper.ScintillaMenu); TabMenu = StripBarManager.GetContextMenu(FileNameHelper.TabMenu); StatusLabel = new ToolStripStatusLabel(); ProgressLabel = new ToolStripStatusLabel(); toolStripProgressBar = new ToolStripProgressBarEx(); printPreviewDialog = new PrintPreviewDialog(); saveFileDialog = new SaveFileDialog(); openFileDialog = new OpenFileDialog(); colorDialog = new ColorDialog(); printDialog = new PrintDialog(); // // toolStripPanel // ToolStripPanel.Dock = DockStyle.Top; if (PlatformHelper.IsRunningOnMono()) { ToolStripPanel.Controls.Add(MenuStrip); ToolStripPanel.Controls.Add(ToolStrip); } else { ToolStripPanel.Controls.Add(ToolStrip); ToolStripPanel.Controls.Add(MenuStrip); } TabMenu.Font = PluginBase.Settings.DefaultFont; ToolStrip.Font = PluginBase.Settings.DefaultFont; MenuStrip.Font = PluginBase.Settings.DefaultFont; EditorMenu.Font = PluginBase.Settings.DefaultFont; TabMenu.Renderer = new DockPanelStripRenderer(false); EditorMenu.Renderer = new DockPanelStripRenderer(false); MenuStrip.Renderer = new DockPanelStripRenderer(false); ToolStrip.Renderer = new DockPanelStripRenderer(false); ToolStrip.Padding = new Padding(0, 1, 0, 0); ToolStrip.Size = new Size(500, 26); ToolStrip.Stretch = true; // // openFileDialog // openFileDialog.Title = " " + TextHelper.GetString("Title.OpenFileDialog"); openFileDialog.Filter = TextHelper.GetString("Info.FileDialogFilter") + "|*.*"; openFileDialog.RestoreDirectory = true; // // colorDialog // colorDialog.FullOpen = true; colorDialog.ShowHelp = false; // // printPreviewDialog // printPreviewDialog.Enabled = true; printPreviewDialog.Name = "printPreviewDialog"; printPreviewDialog.StartPosition = FormStartPosition.CenterParent; printPreviewDialog.TransparencyKey = Color.Empty; printPreviewDialog.Visible = false; // // saveFileDialog // saveFileDialog.Title = " " + TextHelper.GetString("Title.SaveFileDialog"); saveFileDialog.Filter = TextHelper.GetString("Info.FileDialogFilter") + "|*.*"; saveFileDialog.RestoreDirectory = true; // // dockPanel // DockPanel.TabIndex = 2; DockPanel.DocumentStyle = DocumentStyle.DockingWindow; DockPanel.DockWindows[DockState.Document].Controls.Add(quickFind); DockPanel.Dock = DockStyle.Fill; DockPanel.Name = "dockPanel"; // // toolStripStatusLabel // StatusLabel.Name = "toolStripStatusLabel"; StatusLabel.TextAlign = ContentAlignment.MiddleLeft; StatusLabel.Spring = true; // // toolStripProgressLabel // ProgressLabel.AutoSize = true; ProgressLabel.Name = "toolStripProgressLabel"; ProgressLabel.TextAlign = ContentAlignment.MiddleRight; ProgressLabel.Visible = false; // // toolStripProgressBar // toolStripProgressBar.Name = "toolStripProgressBar"; toolStripProgressBar.ControlAlign = ContentAlignment.MiddleRight; toolStripProgressBar.ProgressBar.Width = 120; toolStripProgressBar.Visible = false; // // statusStrip // StatusStrip.TabIndex = 3; StatusStrip.Name = "statusStrip"; StatusStrip.Items.Add(StatusLabel); StatusStrip.Items.Add(ProgressLabel); StatusStrip.Items.Add(toolStripProgressBar); StatusStrip.Font = PluginBase.Settings.DefaultFont; StatusStrip.Renderer = new DockPanelStripRenderer(false); StatusStrip.Stretch = true; // // MainForm // AllowDrop = true; Name = "MainForm"; Text = DistroConfig.DISTRIBUTION_NAME; Controls.Add(DockPanel); Controls.Add(ToolStripPanel); Controls.Add(StatusStrip); MainMenuStrip = MenuStrip; Size = AppSettings.WindowSize; Font = AppSettings.DefaultFont; StartPosition = FormStartPosition.Manual; Closing += OnMainFormClosing; FormClosed += OnMainFormClosed; Activated += OnMainFormActivate; Shown += OnMainFormShow; Load += OnMainFormLoad; LocationChanged += OnMainFormLocationChange; GotFocus += OnMainFormGotFocus; Resize += OnMainFormResize; ScintillaManager.ConfigurationLoaded += ApplyAllSettings; } #endregion #region Event Handlers /// /// Checks the file changes and activates /// void OnMainFormActivate(object sender, EventArgs e) { var document = CurrentDocument; if (document is null) return; document.Activate(); // Activate the current document ButtonManager.UpdateFlaggedButtons(); } /// /// Checks the file changes when recieving focus /// void OnMainFormGotFocus(object sender, EventArgs e) { if (CurrentDocument is null) return; ButtonManager.UpdateFlaggedButtons(); } /// /// Initalizes the windows state after show is called and /// check if we need to notify user for recovery files /// static void OnMainFormShow(object sender, EventArgs e) { if (RecoveryDialog.ShouldShowDialog()) RecoveryDialog.Show(); } /// /// Saves the window size as it's being resized /// void OnMainFormResize(object sender, EventArgs e) { if (WindowState != FormWindowState.Maximized && WindowState != FormWindowState.Minimized) { AppSettings.WindowSize = Size; } } /// /// Saves the window location as it's being moved /// void OnMainFormLocationChange(object sender, EventArgs e) { if (WindowState != FormWindowState.Maximized && WindowState != FormWindowState.Minimized) { AppSettings.WindowSize = Size; AppSettings.WindowPosition = Location; } } /// /// Setups misc stuff when MainForm is loaded /// void OnMainFormLoad(object sender, EventArgs e) { /** * DockPanel events */ DockPanel.ActivePaneChanged += OnActivePaneChanged; DockPanel.ActiveContentChanged += OnActiveContentChanged; DockPanel.ActiveDocumentChanged += OnActiveDocumentChanged; DockPanel.ContentRemoved += OnContentRemoved; /** * Populate menus and check buttons */ ButtonManager.PopulateReopenMenu(); ButtonManager.UpdateFlaggedButtons(); /** * Set the initial directory for file dialogs */ string path = AppSettings.LatestDialogPath; openFileDialog.InitialDirectory = path; saveFileDialog.InitialDirectory = path; workingDirectory = path; /** * Open document[s] in startup */ if (!Arguments.IsNullOrEmpty()) { ProcessParameters(Arguments); Arguments = null; } else if (AppSettings.RestoreFileSession) { string file = FileNameHelper.SessionData; SessionManager.RestoreSession(file, SessionType.Startup); } if (DocumentsLength() == 0) { NotifyEvent ne = new NotifyEvent(EventType.FileEmpty); EventManager.DispatchEvent(this, ne); if (!ne.Handled) SmartNew(null, null); } /** * Apply the default loaded theme */ ThemeManager.WalkControls(this); /** * Notify plugins that the application is ready */ EventManager.DispatchEvent(this, new NotifyEvent(EventType.UIStarted)); EventManager.DispatchEvent(this, new NotifyEvent(EventType.Completion)); /** * Start polling for file changes outside of the editor */ FilePollManager.InitializePolling(); /** * Apply all settings to all documents */ ApplyAllSettings(); /** * Initialize window and continue layout */ InitializeWindow(); /** * Initializes the restart button */ InitializeRestartButton(); /** * Check for updates when needed */ CheckForUpdates(); } /// /// Checks that if there are modified documents when the MainForm is closing /// public void OnMainFormClosing(object sender, CancelEventArgs e) { ClosingEntirely = true; Session session = SessionManager.GetCurrentSession(); NotifyEvent ne = new NotifyEvent(EventType.UIClosing); EventManager.DispatchEvent(this, ne); if (ne.Handled) { ClosingEntirely = false; e.Cancel = true; } if (!e.Cancel && PluginBase.Settings.ConfirmOnExit) { var title = TextHelper.GetString("Title.ConfirmDialog"); var message = TextHelper.GetString("Info.AreYouSureToExit"); var result = MessageBox.Show(this, message, " " + title, MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.No) e.Cancel = true; } if (!e.Cancel) CloseAllDocuments(false); if (CloseAllCanceled) { CloseAllCanceled = false; ClosingEntirely = false; e.Cancel = true; } if (!e.Cancel && IsFullScreen) { ToggleFullScreen(null, null); } if (!e.Cancel && DocumentsLength() == 0) { var fe = new NotifyEvent(EventType.FileEmpty); EventManager.DispatchEvent(this, fe); if (!fe.Handled) SmartNew(null, null); } if (!e.Cancel) { SessionManager.SaveSession(FileNameHelper.SessionData, session); ShortcutManager.SaveCustomShortcuts(); ArgumentDialog.SaveCustomArguments(); ClipboardManager.Dispose(); PluginServices.DisposePlugins(); KillProcess(); SaveAllSettings(); } else RestartRequested = false; } /// /// When form is closed restart if requested. /// public void OnMainFormClosed(object sender, FormClosedEventArgs e) { if (!RestartRequested) return; RestartRequested = false; Process.Start(Application.ExecutablePath); Process.GetCurrentProcess().Kill(); } /// /// When dock changes, applies the padding to documents /// void OnActivePaneChanged(object sender, EventArgs e) => quickFind.ApplyFixedDocumentPadding(); /// /// When document is removed update tab texts /// public void OnContentRemoved(object sender, DockContentEventArgs e) => TabTextManager.UpdateTabTexts(); /// /// Dispatch UIRefresh event and focus scintilla control /// void OnActiveContentChanged(object sender, EventArgs e) { if (DockPanel.ActiveContent is null) return; if (DockPanel.ActiveContent.GetType() == typeof(TabbedDocument)) { PanelIsActive = false; ((TabbedDocument) DockPanel.ActiveContent).Activate(); } else PanelIsActive = true; NotifyEvent ne = new NotifyEvent(EventType.UIRefresh); EventManager.DispatchEvent(this, ne); } /// /// Updates the UI, tabbing, working directory and the button states. /// Also notifies the plugins for the FileOpen and FileSwitch events. /// public void OnActiveDocumentChanged(object sender, EventArgs e) { try { var document = CurrentDocument; if (document is null) return; var sci = document.SciControl; OnScintillaControlUpdateControl(sci); quickFind.CanSearch = sci != null; /** * Bring this newly active document to the top of the tab history * unless you're currently cycling through tabs with the keyboard */ TabbingManager.UpdateSequentialIndex(document); if (!TabbingManager.TabTimer.Enabled) { TabbingManager.TabHistory.Remove(document); TabbingManager.TabHistory.Insert(0, document); } if (sci != null) { /** * Apply correct extension when saving */ if (AppSettings.ApplyFileExtension) { var extension = Path.GetExtension(sci.FileName); if (extension != string.Empty) saveFileDialog.DefaultExt = extension; } /** * Set current working directory */ var path = Path.GetDirectoryName(sci.FileName); if (!document.IsUntitled && Directory.Exists(path)) { workingDirectory = path; } /** * Checks the file changes */ ((TabbedDocument)document).Activate(); /** * Processes the opened file */ if (notifyOpenFile) { ScintillaManager.UpdateControlSyntax(sci); if (File.Exists(sci.FileName)) { var te = new TextEvent(EventType.FileOpen, sci.FileName); EventManager.DispatchEvent(this, te); } notifyOpenFile = false; } } TabTextManager.UpdateTabTexts(); var ne = new NotifyEvent(EventType.FileSwitch); EventManager.DispatchEvent(this, ne); var ce = new NotifyEvent(EventType.Completion); EventManager.DispatchEvent(this, ce); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Checks that if the are any modified documents when closing. /// public void OnDocumentClosing(object sender, CancelEventArgs e) { var document = (ITabbedDocument)sender; if (CloseAllCanceled && closingAll) e.Cancel = true; else if (document.IsModified) { var saveChanges = TextHelper.GetString("Info.SaveChanges"); var saveChangesTitle = TextHelper.GetString("Title.SaveChanges"); var result = MessageBox.Show(this, saveChanges, saveChangesTitle + " " + document.Text, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (result == DialogResult.Yes) { if (document.IsUntitled) { saveFileDialog.FileName = document.FileName; if (saveFileDialog.ShowDialog(this) == DialogResult.OK && saveFileDialog.FileName.Length != 0) { ButtonManager.AddNewReopenMenuItem(saveFileDialog.FileName); document.Save(saveFileDialog.FileName); } else { if (closingAll) CloseAllCanceled = true; e.Cancel = true; } } else document.Save(); } else if (result == DialogResult.Cancel) { if (closingAll) CloseAllCanceled = true; e.Cancel = true; } else if (result == DialogResult.No) { RecoveryManager.RemoveTemporaryFile(document.FileName); } } var documentsLength = DocumentsLength(); if (documentsLength == 1 && document.IsUntitled && !document.IsModified && document.SciControl.Length == 0 && !e.Cancel && !closingForOpenFile && !RestoringContents) { e.Cancel = true; } if (documentsLength == 1 && !e.Cancel && !closingForOpenFile && !ClosingEntirely && !RestoringContents) { NotifyEvent ne = new NotifyEvent(EventType.FileEmpty); EventManager.DispatchEvent(this, ne); if (!ne.Handled) SmartNew(null, null); } } /// /// Activates the previous document when document is closed /// public void OnDocumentClosed(object sender, EventArgs e) { var document = (ITabbedDocument) sender; TabbingManager.TabHistory.Remove(document); var ne = new TextEvent(EventType.FileClose, document.FileName); EventManager.DispatchEvent(this, ne); if (AppSettings.SequentialTabbing) { if (TabbingManager.SequentialIndex == 0) Documents[0].Activate(); else TabbingManager.NavigateTabsSequentially(-1); } else TabbingManager.NavigateTabHistory(0); if (document.SciControl is {} sci && !document.IsUntitled) { if (AppSettings.RestoreFileStates) FileStateManager.SaveFileState(sci); RecoveryManager.RemoveTemporaryFile(sci.FileName); OldTabsManager.SaveOldTabDocument(sci.FileName); } ButtonManager.UpdateFlaggedButtons(); } /// /// Refreshes the statusbar display and updates the important edit buttons /// public void OnScintillaControlUpdateControl(ScintillaControl sci) { if (InvokeRequired) { BeginInvoke((Action)(() => OnScintillaControlUpdateControl(sci))); return; } if (sci != null && DocumentManager.FindDocument(sci) != null) { var statusText = " " + TextHelper.GetString("Info.StatusText"); var line = sci.CurrentLine + 1 + " / " + sci.LineCount; var column = sci.Column(sci.CurrentPos) + 1 + " / " + (sci.Column(sci.LineEndPosition(sci.CurrentLine)) + 1); var oldOS = OSVersion.Major < 6; // Vista is 6.0 and ok... var file = oldOS ? PathHelper.GetCompactPath(sci.FileName) : sci.FileName; var eol = sci.EOLMode switch { 0 => "CR+LF", 1 => "CR", _ => "LF" }; var encoding = ButtonManager.GetActiveEncodingName(); StatusLabel.Text = string.Format(statusText, line, column, eol, encoding, file); } else StatusLabel.Text = " "; OnUpdateMainFormDialogTitle(); ButtonManager.UpdateFlaggedButtons(); var ne = new NotifyEvent(EventType.UIRefresh); EventManager.DispatchEvent(this, ne); } /// /// Opens the selected files on drop /// public void OnScintillaControlDropFiles(ScintillaControl sci, string data) { if (InvokeRequired) { BeginInvoke((Action)(() => OnScintillaControlDropFiles(null, data))); return; } var files = Regex.Split(data.Substring(1, data.Length - 2), "\" \""); foreach (var file in files) { if (File.Exists(file)) { var doc = OpenEditableDocument(file); if (doc is null || ModifierKeys == Keys.Control) return; var drop = DocumentManager.FindDocument(sci) as DockContent; if (drop?.Pane is null) continue; doc.DockTo(drop.Pane, DockStyle.Fill, -1); doc.Activate(); } else if (Directory.Exists(file)) { var de = new TextEvent(EventType.FolderOpen, file); EventManager.DispatchEvent(this, de); } } } /// /// Notifies when the user is trying to modify a read only file /// public void OnScintillaControlModifyRO(ScintillaControl sci) { if (!sci.Enabled || !File.Exists(sci.FileName)) return; var te = new TextEvent(EventType.FileModifyRO, sci.FileName); EventManager.DispatchEvent(this, te); if (te.Handled) return; // Let plugin handle this... var dlgTitle = TextHelper.GetString("Title.ConfirmDialog"); var message = TextHelper.GetString("Info.MakeReadOnlyWritable"); if (MessageBox.Show(this, message, dlgTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { ScintillaManager.MakeFileWritable(sci); } } /// /// Handles the modified event /// public void OnScintillaControlModified(ScintillaControl sender, int pos, int modType, string text, int length, int lAdded, int line, int fLevelNow, int fLevelPrev) { var document = DocumentManager.FindDocument(sender); if (document is null || !document.IsEditable) return; OnDocumentModify(document); if (!AppSettings.ViewModifiedLines) return; int flags = sender.ModEventMask; sender.ModEventMask = flags & ~(int)ScintillaNet.Enums.ModificationFlags.ChangeMarker; int modLine = sender.LineFromPosition(pos); sender.MarkerAdd(modLine, 2); for (int i = 0; i <= lAdded; i++) { sender.MarkerAdd(modLine + i, 2); } sender.ModEventMask = flags; } /// /// Provides a basic folding service and notifies the plugins for the MarginClick event /// public void OnScintillaControlMarginClick(ScintillaControl sci, int modifiers, int position, int margin) { if (margin != ScintillaManager.FoldingMargin) return; var line = sci.LineFromPosition(position); if (ModifierKeys == Keys.Control) MarkerManager.ToggleMarker(sci, 0, line); else sci.ToggleFold(line); } /// /// Handles the mouse wheel on hover /// public bool PreFilterMessage(ref Message m) { if (Win32.ShouldUseWin32() && m.Msg == 0x20a) { int x = unchecked((short)(long)m.LParam); int y = unchecked((short)((long)m.LParam >> 16)); var hWnd = Win32.WindowFromPoint(new Point(x, y)); if (hWnd == IntPtr.Zero) return false; var doc = PluginBase.MainForm.CurrentDocument; if (FromHandle(hWnd) != null) { Win32.SendMessage(hWnd, m.Msg, m.WParam, m.LParam); return true; } if (doc != null && doc.IsEditable && (hWnd == doc.SplitSci1.HandleSci || hWnd == doc.SplitSci2.HandleSci)) { Win32.SendMessage(hWnd, m.Msg, m.WParam, m.LParam); return true; } } return false; } /// /// Handles the application shortcuts /// protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { /** * Notify plugins. Don't notify ControlKey or ShiftKey as it polls a lot */ var ke = new KeyEvent(EventType.Keys, keyData); var keyCode = keyData & Keys.KeyCode; if ((keyCode != Keys.ControlKey) && (keyCode != Keys.ShiftKey)) { EventManager.DispatchEvent(this, ke); } if (!ke.Handled) { /** * Ignore basic control keys if sci doesn't have focus. */ if (CurrentDocument.SciControl is null || !CurrentDocument.SciControl.IsFocus) { switch (keyData) { case Keys.Control | Keys.C: case Keys.Control | Keys.V: case Keys.Control | Keys.X: case Keys.Control | Keys.A: case Keys.Control | Keys.Z: case Keys.Control | Keys.Y: return false; } } /** * Process special key combinations and allow "chaining" of * Ctrl-Tab commands if you keep holding control down. */ if ((keyData & Keys.Control) != 0) { bool sequentialTabbing = AppSettings.SequentialTabbing; if ((keyData == (Keys.Control | Keys.Next)) || (keyData == (Keys.Control | Keys.Tab))) { TabbingManager.TabTimer.Enabled = true; if (keyData == (Keys.Control | Keys.Next) || sequentialTabbing) { TabbingManager.NavigateTabsSequentially(1); } else TabbingManager.NavigateTabHistory(1); return true; } if ((keyData == (Keys.Control | Keys.Prior)) || (keyData == (Keys.Control | Keys.Shift | Keys.Tab))) { TabbingManager.TabTimer.Enabled = true; if (keyData == (Keys.Control | Keys.Prior) || sequentialTabbing) { TabbingManager.NavigateTabsSequentially(-1); } else TabbingManager.NavigateTabHistory(-1); return true; } } return base.ProcessCmdKey(ref msg, keyData); } return true; } /// /// Notifies the plugins for the SyntaxChange event /// public void OnSyntaxChange(string language) { var te = new TextEvent(EventType.SyntaxChange, language); EventManager.DispatchEvent(this, te); } /// /// Updates the MainForm's title automatically /// public void OnUpdateMainFormDialogTitle() { if (PluginBase.CurrentProject is {} project) Text = project.Name + " - " + DistroConfig.DISTRIBUTION_NAME; else if (CurrentDocument?.FileName is {} fileName) Text = Path.GetFileName(fileName) + " - " + DistroConfig.DISTRIBUTION_NAME; else Text = DistroConfig.DISTRIBUTION_NAME; } /// /// Sets the current document unmodified and updates it /// public void OnDocumentReload(ITabbedDocument document) { document.IsModified = false; ReloadingDocument = false; OnUpdateMainFormDialogTitle(); document.SciControl?.MarkerDeleteAll(2); ButtonManager.UpdateFlaggedButtons(); } /// /// Sets the current document modified /// public void OnDocumentModify(ITabbedDocument document) { var sci = document.SciControl; if (sci is null || document.IsModified || ReloadingDocument || ProcessingContents) return; document.IsModified = true; var te = new TextEvent(EventType.FileModify, sci.FileName); EventManager.DispatchEvent(this, te); } /// /// Notifies the plugins for the FileSave event and includes the given reason for the save. /// public void OnFileSave(ITabbedDocument document, string oldFile, string reason) { if (oldFile != null) { var args = document.FileName + ";" + oldFile; var rename = new TextEvent(EventType.FileRename, args); EventManager.DispatchEvent(this, rename); var open = new TextEvent(EventType.FileOpen, document.FileName); EventManager.DispatchEvent(this, open); } OnUpdateMainFormDialogTitle(); document.SciControl?.MarkerDeleteAll(2); var save = new TextDataEvent(EventType.FileSave, document.FileName, reason); EventManager.DispatchEvent(this, save); ButtonManager.UpdateFlaggedButtons(); TabTextManager.UpdateTabTexts(); } /// /// Notifies the plugins for the FileSave event /// public void OnFileSave(ITabbedDocument document, string oldFile) => OnFileSave(document, oldFile, null); /// /// Handles clipboard updates. /// protected override void WndProc(ref Message m) { if (ClipboardManager.HandleWndProc(ref m)) ClipboardHistoryDialog.UpdateHistory(); base.WndProc(ref m); } #endregion #region General Methods /// /// Finds the specified plugin /// public IPlugin FindPlugin(string guid) => PluginServices.Find(guid).Instance; /// /// Finds the specified composed/ready image that is automatically adjusted according to the theme. /// /// If you make a copy of the image returned by this method, the copy will not be automatically adjusted. /// public Image FindImage(string data) => FindImage(data, true); /// /// Finds the specified composed/ready image. /// /// If you make a copy of the image returned by this method, the copy will not be automatically adjusted, even if autoAdjusted is true. /// public Image FindImage(string data, bool autoAdjusted) { try { lock (this) return ImageManager.GetComposedBitmap(data, autoAdjusted); } catch (Exception ex) { ErrorManager.ShowError(ex); return null; } } /// /// Finds the specified composed/ready image that is automatically adjusted according to the theme. /// The image size is always 16x16. /// /// If you make a copy of the image returned by this method, the copy will not be automatically adjusted. /// public Image FindImage16(string data) => FindImage16(data, true); /// /// Finds the specified composed/ready image. The image size is always 16x16. /// /// If you make a copy of the image returned by this method, the copy will not be automatically adjusted, even if autoAdjusted is true. /// public Image FindImage16(string data, bool autoAdjusted) { try { lock (this) return ImageManager.GetComposedBitmapSize16(data, autoAdjusted); } catch (Exception ex) { ErrorManager.ShowError(ex); return null; } } /// /// Finds the specified composed/ready image and returns a copy of the image that has its color adjusted. /// This method is typically used for populating a object. /// /// Equivalent to calling ImageSetAdjust(FindImage(data, false)). /// public Image FindImageAndSetAdjust(string data) => ImageSetAdjust(FindImage(data, false)); /// /// Returns a copy of the specified image that has its color adjusted. /// public Image ImageSetAdjust(Image image) => ImageManager.SetImageAdjustment(image); /// /// Gets a copy of the image that gets automatically adjusted according to the theme. /// public Image GetAutoAdjustedImage(Image image) => ImageManager.GetAutoAdjustedImage(image); /// /// Adjusts all images for different themes. /// public void AdjustAllImages() { ImageManager.AdjustAllImages(); ImageListManager.RefreshAll(); foreach (var it in LayoutManager.PluginPanels) { var panel = it as DockablePanel; panel?.RefreshIcon(); } } /// /// Themes the controls from the parent /// public void ThemeControls(object parent) => ThemeManager.WalkControls(parent); /// /// Gets a theme property color /// public Color GetThemeColor(string id) => ThemeManager.GetThemeColor(id); /// /// Gets a theme property color with a fallback /// public Color GetThemeColor(string id, Color fallback) => ThemeManager.GetThemeColor(id) is {} color && color != Color.Empty ? color : fallback; /// /// Gets a theme property value /// public string GetThemeValue(string id) => ThemeManager.GetThemeValue(id); /// /// Gets a theme property value with a fallback /// public string GetThemeValue(string id, string fallback) { var value = ThemeManager.GetThemeValue(id); return !string.IsNullOrEmpty(value) ? value : fallback; } /// /// Gets a theme flag value. /// public bool GetThemeFlag(string id) => GetThemeFlag(id, false); /// /// Gets a theme flag value with a fallback. /// public bool GetThemeFlag(string id, bool fallback) { var value = ThemeManager.GetThemeValue(id); if (string.IsNullOrEmpty(value)) return fallback; return value.ToLower() switch { "true" => true, "false" => false, _ => fallback, }; } /// /// Sets if child controls should use theme. /// public void SetUseTheme(object obj, bool use) => ThemeManager.SetUseTheme(obj, use); /// /// Finds the specified menu item by name /// public ToolStripItem FindMenuItem(string name) => StripBarManager.FindMenuItem(name); /// /// Finds the menu items that have the specified name /// public List FindMenuItems(string name) => StripBarManager.FindMenuItems(name); /// /// Lets you update menu items using the flag functionality /// public void AutoUpdateMenuItem(ToolStripItem item, string action) { bool value = ButtonManager.ValidateFlagAction(item, action); ButtonManager.ExecuteFlagAction(item, action, value); } /// /// Gets the specified item's shortcut keys. /// public Keys GetShortcutItemKeys(string id) => ShortcutManager.GetRegisteredItem(id)?.Custom ?? Keys.None; /// /// Gets the specified item's id. /// public string GetShortcutItemId(Keys keys) => ShortcutManager.GetRegisteredItem(keys)?.Id ?? string.Empty; /// /// Registers a new menu item with the shortcut manager /// public void RegisterShortcutItem(string id, Keys keys) => ShortcutManager.RegisterItem(id, keys); /// /// Registers a new menu item with the shortcut manager /// public void RegisterShortcutItem(string id, ToolStripMenuItem item) => ShortcutManager.RegisterItem(id, item); /// /// Registers a new secondary menu item with the shortcut manager /// public void RegisterSecondaryItem(string id, ToolStripItem item) => ShortcutManager.RegisterSecondaryItem(id, item); /// /// Updates a registered secondary menu item in the shortcut manager /// - should be called when the tooltip changes. /// public void ApplySecondaryShortcut(ToolStripItem item) => ShortcutManager.ApplySecondaryShortcut(item); /// /// Shows the settings dialog /// public void ShowSettingsDialog(string itemName) => SettingDialog.Show(itemName, ""); public void ShowSettingsDialog(string itemName, string filter) => SettingDialog.Show(itemName, filter); /// /// Shows the error dialog if the sender is ErrorManager /// public void ShowErrorDialog(object sender, Exception ex) { if (sender.GetType().ToString() != "PluginCore.Managers.ErrorManager") { string message = TextHelper.GetString("Info.OnlyErrorManager"); ErrorDialog.Show(new Exception(message)); } else ErrorDialog.Show(ex); } /// /// Show a message to the user to restart FD /// public void RestartRequired() { if (restartButton != null) restartButton.Visible = true; RequiresRestart = true; TraceManager.Add(TextHelper.GetString("Info.RequiresRestart")); } /// /// Refreshes the main form /// public void RefreshUI() { if (CurrentDocument?.SciControl is {} sci) OnScintillaControlUpdateControl(sci); } /// /// Clears the temporary files from disk /// public void ClearTemporaryFiles(string file) { RecoveryManager.RemoveTemporaryFile(file); FileStateManager.RemoveStateFile(file); } /// /// Refreshes the scintilla configuration /// public void RefreshSciConfig() => ScintillaManager.LoadConfiguration(); /// /// Processes the argument string variables /// public string ProcessArgString(string args) => ArgsProcessor.ProcessString(args, true); public string ProcessArgString(string args, bool dispatch) => ArgsProcessor.ProcessString(args, dispatch); /// /// Processes the incoming arguments /// public void ProcessParameters(string[] args) { if (InvokeRequired) { BeginInvoke((Action)(() => ProcessParameters(args))); return; } Activate(); Focus(); if (!args.IsNullOrEmpty()) { Silent = args.Contains("-silent"); args.ForEach(OpenDocumentFromParameters); } if (Win32.ShouldUseWin32()) Win32.RestoreWindow(Handle); /** * Notify plugins about start arguments */ var ne = new NotifyEvent(EventType.StartArgs); EventManager.DispatchEvent(this, ne); } void OpenDocumentFromParameters(string file) { var openParams = Regex.Match(file, "@([0-9]+)($|:([0-9]+)$)"); // path@line:col if (openParams.Success) { file = file.Substring(0, openParams.Index); file = PathHelper.GetLongPathName(file); if (!File.Exists(file)) return; if (OpenEditableDocument(file, false) is TabbedDocument doc) ApplyOpenParams(openParams, doc.SciControl); else if (CurrentDocument?.SciControl is { } sci && sci.FileName == file) ApplyOpenParams(openParams, sci); } else if (File.Exists(file)) { file = PathHelper.GetLongPathName(file); OpenEditableDocument(file); } } static void ApplyOpenParams(Match openParams, ScintillaControl sci) { if (sci is null) return; int col = 0; int line = Math.Min(sci.LineCount - 1, Math.Max(0, int.Parse(openParams.Groups[1].Value) - 1)); if (openParams.Groups.Count > 3 && openParams.Groups[3].Value.Length > 0) { col = int.Parse(openParams.Groups[3].Value); } int position = sci.PositionFromLine(line) + col; sci.SetSel(position, position); } /// /// Closes all open documents with an option: exceptCurrent /// public void CloseAllDocuments(bool exceptCurrent) => CloseAllDocuments(exceptCurrent, false); public void CloseAllDocuments(bool exceptCurrent, bool exceptOtherPanes) { var current = CurrentDocument; var currentPane = current?.DockHandler.PanelPane; CloseAllCanceled = false; closingAll = true; foreach (var document in Documents) { var close = !(exceptCurrent && document == current); if (exceptOtherPanes && document.DockHandler.PanelPane != currentPane) close = false; if (close) document.Close(); } closingAll = false; } /// /// Updates all needed settings after modification /// public void ApplyAllSettings() { if (InvokeRequired) { BeginInvoke((Action) ApplyAllSettings); return; } ShortcutManager.ApplyAllShortcuts(); EventManager.DispatchEvent(this, new NotifyEvent(EventType.ApplySettings)); foreach (var document in Documents) { if (document.SciControl is null) continue; ScintillaManager.ApplySciSettings(document.SplitSci1, true); ScintillaManager.ApplySciSettings(document.SplitSci2, true); } frInFilesDialog.UpdateSettings(); StatusStrip.Visible = AppSettings.ViewStatusBar; ToolStrip.Visible = !IsFullScreen && AppSettings.ViewToolBar; ButtonManager.UpdateFlaggedButtons(); TabTextManager.UpdateTabTexts(); ClipboardManager.ApplySettings(); TraceManager.ApplySettings(AppSettings.MaxTraceLines); } /// /// Saves all settings of the FlashDevelop /// public void SaveAllSettings() { SaveSettings(); SaveLayout(); } /// /// Saves settings to a file. /// public void SaveSettings() { try { AppSettings.WindowState = WindowState; AppSettings.LatestDialogPath = workingDirectory; if (WindowState != FormWindowState.Maximized && WindowState != FormWindowState.Minimized) { AppSettings.WindowSize = Size; AppSettings.WindowPosition = Location; } if (!File.Exists(FileNameHelper.SettingData)) { var folder = Path.GetDirectoryName(FileNameHelper.SettingData); if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); } ObjectSerializer.Serialize(FileNameHelper.SettingData, AppSettings); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Saves layout to a file. /// public void SaveLayout() { try { DockPanel.SaveAsXml(FileNameHelper.LayoutData); } catch (Exception ex) { // Ignore errors on multi instance full close... if (MultiInstanceMode && ClosingEntirely) return; ErrorManager.ShowError(ex); } } /// /// Resolves the working directory /// public string GetWorkingDirectory() { if (CurrentDocument?.SciControl?.FileName is {} fileName && File.Exists(fileName)) return Path.GetDirectoryName(fileName); if (PluginBase.CurrentProject?.ProjectPath is {} projectPath && File.Exists(projectPath)) return Path.GetDirectoryName(projectPath); return PathHelper.AppDir; } /// /// Gets the amount instances running /// public int GetInstanceCount() { var current = Process.GetCurrentProcess(); return Process.GetProcessesByName(current.ProcessName).Length; } /// /// Sets the text to find globally /// public void SetFindText(object sender, string text) { if (sender != quickFind) quickFind.SetFindText(text); if (sender != frInDocDialog) frInDocDialog.SetFindText(text); } /// /// Sets the case setting to find globally /// public void SetMatchCase(object sender, bool matchCase) { if (sender != quickFind) quickFind.SetMatchCase(matchCase); if (sender != frInDocDialog) frInDocDialog.SetMatchCase(matchCase); } /// /// Sets the whole word setting to find globally /// public void SetWholeWord(object sender, bool wholeWord) { if (sender != quickFind) quickFind.SetWholeWord(wholeWord); if (sender != frInDocDialog) frInDocDialog.SetWholeWord(wholeWord); } #endregion #region Click Handlers /// /// Creates a new blank document /// public void New(object sender, EventArgs e) { var fileName = DocumentManager.GetNewDocumentName(null); var te = new TextEvent(EventType.FileNew, fileName); EventManager.DispatchEvent(this, te); if (!te.Handled) CreateEditableDocument(fileName, "", (int) AppSettings.DefaultCodePage); } /// /// Creates a new blank document tracking current project /// public void SmartNew(object sender, EventArgs e) { var ext = string.Empty; if (PluginBase.CurrentProject != null) { try { var filter = PluginBase.CurrentProject.DefaultSearchFilter; var tempExt = filter.Split(';')[0].Replace("*.", ""); if (Regex.Match(tempExt, "^[A-Za-z0-9]+$").Success) ext = tempExt; } catch { /* NO ERRORS */ } } var fileName = DocumentManager.GetNewDocumentName(ext); var te = new TextEvent(EventType.FileNew, fileName); EventManager.DispatchEvent(this, te); if (!te.Handled) CreateEditableDocument(fileName, "", (int) AppSettings.DefaultCodePage); } /// /// Create a new document from a template /// public void NewFromTemplate(object sender, EventArgs e) { try { var button = (ToolStripItem)sender; var args = ProcessArgString(((ItemData)button.Tag).Tag).Split(';'); var encoding = Encoding.GetEncoding((int)AppSettings.DefaultCodePage); var fileName = DocumentManager.GetNewDocumentName(args[0]); var contents = FileHelper.ReadFile(args[1], encoding); var lineEndChar = LineEndDetector.GetNewLineMarker((int)Settings.EOLMode); contents = Regex.Replace(contents, @"\r\n?|\n", lineEndChar); var processed = ProcessArgString(contents); var actionPoint = SnippetHelper.ProcessActionPoint(processed); if (DocumentsLength() == 1 && Documents[0] is { } doc && doc.IsUntitled) { closingForOpenFile = true; doc.Close(); closingForOpenFile = false; } var te = new TextEvent(EventType.FileTemplate, fileName); EventManager.DispatchEvent(this, te); if (!te.Handled) { var document = (ITabbedDocument) CreateEditableDocument(fileName, actionPoint.Text, encoding.CodePage); if (document is not null) SnippetHelper.ExecuteActionPoint(actionPoint, document.SciControl); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Create the specified new document from the given template /// public void FileFromTemplate(string templatePath, string newFilePath) { try { var encoding = Encoding.GetEncoding((int)AppSettings.DefaultCodePage); var contents = FileHelper.ReadFile(templatePath); var processed = ProcessArgString(contents); var lineEndChar = LineEndDetector.GetNewLineMarker((int)Settings.EOLMode); processed = Regex.Replace(processed, @"\r\n?|\n", lineEndChar); var actionPoint = SnippetHelper.ProcessActionPoint(processed); FileHelper.WriteFile(newFilePath, actionPoint.Text, encoding, PluginBase.Settings.SaveUnicodeWithBOM); if (actionPoint.EntryPosition != -1) { var documents = Documents; if (documents.Length == 1 && documents[0].IsUntitled) { closingForOpenFile = true; documents[0].Close(); closingForOpenFile = false; } var te = new TextEvent(EventType.FileTemplate, newFilePath); EventManager.DispatchEvent(this, te); if (!te.Handled) { var document = (ITabbedDocument) CreateEditableDocument(newFilePath, actionPoint.Text, encoding.CodePage); if (document is not null) SnippetHelper.ExecuteActionPoint(actionPoint, document.SciControl); } } else { var te = new TextEvent(EventType.FileTemplate, newFilePath); EventManager.DispatchEvent(this, te); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Opens the open file dialog and opens the selected files /// public void Open(object sender, EventArgs e) { openFileDialog.Multiselect = true; openFileDialog.InitialDirectory = WorkingDirectory; if (openFileDialog.ShowDialog(this) == DialogResult.OK) openFileDialog.FileNames.ForEach(OpenEditableDocument); openFileDialog.Multiselect = false; } /// /// Opens the open file dialog and opens the selected files in specific encoding /// public void OpenIn(object sender, EventArgs e) { var button = (ToolStripItem)sender; var encMode = Convert.ToInt32(((ItemData)button.Tag).Tag); var encoding = Encoding.GetEncoding(encMode); openFileDialog.Multiselect = true; // Allow multiple... openFileDialog.InitialDirectory = WorkingDirectory; if (openFileDialog.ShowDialog(this) == DialogResult.OK && openFileDialog.FileName.Length != 0) { foreach (var it in openFileDialog.FileNames) { if (encMode == 0) // Detect 8bit encoding... { int codepage = FileHelper.GetFileCodepage(it); encoding = Encoding.GetEncoding(codepage); } OpenEditableDocument(it, encoding, false); } } openFileDialog.Multiselect = false; } /// /// Opens a the specified file in the UI /// public void Edit(object sender, EventArgs e) { var file = ProcessArgString(((ItemData)((ToolStripItem)sender).Tag).Tag); if (File.Exists(file)) OpenEditableDocument(file); } /// /// Reopens a file from the old documents list /// public void Reopen(object sender, EventArgs e) { var file = ((ToolStripItem)sender).Tag.ToString(); if (File.Exists(file)) { OpenEditableDocument(file); ButtonManager.AddNewReopenMenuItem(file); } else { var message = TextHelper.GetString("Info.InvalidFileOnReopen"); Settings.PreviousDocuments.Remove(file); ButtonManager.PopulateReopenMenu(); ErrorManager.ShowInfo(message); } } /// /// Opens the last closed tabs if they are not open /// public void ReopenClosed(object sender, EventArgs e) => OldTabsManager.OpenOldTabDocument(); /// /// Clears invalid entries from the old documents list /// public void CleanReopenList(object sender, EventArgs e) { FileHelper.FilterByExisting(AppSettings.PreviousDocuments, true); ButtonManager.PopulateReopenMenu(); } /// /// Clears all entries from the old documents list /// public void ClearReopenList(object sender, EventArgs e) { AppSettings.PreviousDocuments.Clear(); ButtonManager.PopulateReopenMenu(); } /// /// Views the current clipboard history /// public void ClipboardHistory(object sender, EventArgs e) { if (ClipboardHistoryDialog.Show(out var data)) CurrentDocument?.SciControl?.ReplaceSel(data.Text); } /// /// Pastes lines at the correct indent level /// public void SmartPaste(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null || !sci.CanPaste) return; // if clip is not line-based, then just do simple paste if ((sci.SelTextSize > 0 && !sci.SelText.EndsWith('\n')) || !Clipboard.GetText().EndsWith('\n') || Clipboard.ContainsData("MSDEVColumnSelect")) sci.Paste(); else { sci.BeginUndoAction(); try { if (sci.SelTextSize > 0) sci.Clear(); sci.Home(); var pos = sci.CurrentPos; var lines = sci.LineCount; sci.Paste(); lines = sci.LineCount - lines; // = # of lines in the pasted text sci.ReindentLines(sci.LineFromPosition(pos), lines); } finally { sci.EndUndoAction(); } } } /// /// Saves the current file session /// public void SaveSession(object sender, EventArgs e) { try { var file = ((ItemData)((ToolStripItem)sender).Tag).Tag; SessionManager.SaveSession(file); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Restores the specified file session /// public void RestoreSession(object sender, EventArgs e) { try { var file = ((ItemData)((ToolStripItem)sender).Tag).Tag; SessionManager.RestoreSession(file, SessionType.External); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Restores the specified panel layout /// public void RestoreLayout(object sender, EventArgs e) { try { var file = ((ItemData)((ToolStripItem)sender).Tag).Tag; LayoutManager.RestoreLayout(file); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Saves the current file or opens a save file dialog /// public void Save(object sender, EventArgs e) { try { var document = CurrentDocument; if (document is null) return; if (document.IsUntitled) SaveAs(); else if (document.IsModified) { var reason = ((ItemData)((ToolStripItem)sender).Tag).Tag; document.Save(document.FileName, reason); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Saves the current document with the specified file name /// public void SaveAs(object sender, EventArgs e) => SaveAs(); /// /// Saves the current document with the specified file name /// void SaveAs() { saveFileDialog.FileName = CurrentDocument.FileName; saveFileDialog.InitialDirectory = WorkingDirectory; if (saveFileDialog.ShowDialog(this) != DialogResult.OK || saveFileDialog.FileName.Length == 0) return; ButtonManager.AddNewReopenMenuItem(saveFileDialog.FileName); CurrentDocument.Save(saveFileDialog.FileName); var ne = new NotifyEvent(EventType.FileSwitch); EventManager.DispatchEvent(this, ne); var ce = new NotifyEvent(EventType.Completion); EventManager.DispatchEvent(this, ce); } /// /// Saves the selected text as a snippet /// public void SaveAsSnippet(object sender, EventArgs e) { try { saveFileDialog.FileName = ""; saveFileDialog.DefaultExt = ".fds"; string prevFilter = saveFileDialog.Filter; string snippetFilter = TextHelper.GetString("Info.SnippetFilter"); saveFileDialog.Filter = snippetFilter + "|*.fds"; string prevRootPath = saveFileDialog.InitialDirectory; saveFileDialog.InitialDirectory = PathHelper.SnippetDir; if (saveFileDialog.ShowDialog(this) == DialogResult.OK && saveFileDialog.FileName.Length != 0) { string file = saveFileDialog.FileName; FileHelper.WriteFile(file, CurrentDocument?.SciControl?.SelText, Encoding.UTF8); } saveFileDialog.InitialDirectory = prevRootPath; saveFileDialog.Filter = prevFilter; } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Saves the selected text as a template /// public void SaveAsTemplate(object sender, EventArgs e) { try { saveFileDialog.FileName = ""; saveFileDialog.DefaultExt = ".fdt"; string prevFilter = saveFileDialog.Filter; string templateFilter = TextHelper.GetString("Info.TemplateFilter"); saveFileDialog.Filter = templateFilter + "|*.fdt"; string prevRootPath = saveFileDialog.InitialDirectory; saveFileDialog.InitialDirectory = PathHelper.TemplateDir; if (saveFileDialog.ShowDialog(this) == DialogResult.OK && saveFileDialog.FileName.Length != 0) { string file = saveFileDialog.FileName; FileHelper.WriteFile(file, CurrentDocument?.SciControl?.SelText, Encoding.UTF8); } saveFileDialog.InitialDirectory = prevRootPath; saveFileDialog.Filter = prevFilter; } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Saves all modified documents or opens a save file dialog /// public void SaveAll(object sender, EventArgs e) { try { SavingMultiple = true; var active = CurrentDocument; foreach (var document in Documents) { if (document.SciControl is { } sci && document.IsModified) { if (document.IsUntitled) { saveFileDialog.FileName = sci.FileName; saveFileDialog.InitialDirectory = WorkingDirectory; if (saveFileDialog.ShowDialog(this) == DialogResult.OK && saveFileDialog.FileName.Length != 0) { ButtonManager.AddNewReopenMenuItem(saveFileDialog.FileName); document.Save(saveFileDialog.FileName); } } else document.Save(); } } SavingMultiple = false; active.Activate(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Saves only modified documents with extension filter /// public void SaveAllModified(object sender, EventArgs e) { try { var filter = "*"; SavingMultiple = true; filter = ((ItemData)((ToolStripItem)sender).Tag).Tag + filter; var active = CurrentDocument; foreach (var document in Documents) { if (document.IsEditable && document.IsModified && !document.IsUntitled && document.Text.EndsWithOrdinal(filter)) { document.Save(); document.IsModified = false; } } SavingMultiple = false; active.Activate(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Closes the current document /// public void Close(object sender, EventArgs e) => CurrentDocument?.Close(); /// /// Closes all open documents in the current pane /// public void CloseAll(object sender, EventArgs e) => CloseAllDocuments(false, true); /// /// Closes all open documents except the current in the current pane /// public void CloseOthers(object sender, EventArgs e) => CloseAllDocuments(true, true); /// /// Exits the application /// public void Exit(object sender, EventArgs e) => Close(); /// /// Duplicates the current document /// public void Duplicate(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; var extension = Path.GetExtension(sci.FileName); var filename = DocumentManager.GetNewDocumentName(extension); var document = CreateEditableDocument(filename, sci.Text, sci.Encoding.CodePage); ((TabbedDocument)document).IsModified = true; } /// /// Reverts the document to the default state /// public void Revert(object sender, EventArgs e) => CurrentDocument?.Revert(true); /// /// Reloads the current document /// public void Reload(object sender, EventArgs e) => CurrentDocument?.Reload(true); /// /// Prints the current document /// public void Print(object sender, EventArgs e) { if (CurrentDocument?.SciControl?.TextLength == 0) { var message = TextHelper.GetString("Info.NothingToPrint"); ErrorManager.ShowInfo(message); return; } try { printDialog.PrinterSettings = PrintingManager.GetPrinterSettings(); printDialog.Document = PrintingManager.CreatePrintDocument(); if (printDialog.ShowDialog(this) == DialogResult.OK) { printDialog.Document.Print(); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Views the current print document /// public void PrintPreview(object sender, EventArgs e) { if (CurrentDocument?.SciControl?.TextLength == 0) { string message = TextHelper.GetString("Info.NothingToPrint"); ErrorManager.ShowInfo(message); return; } try { printDialog.PrinterSettings = PrintingManager.GetPrinterSettings(); printPreviewDialog.Document = PrintingManager.CreatePrintDocument(); printPreviewDialog.ShowDialog(this); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Opens a goto dialog /// public void GoTo(object sender, EventArgs e) { if (!gotoDialog.Visible) gotoDialog.Show(); else gotoDialog.Activate(); } /// /// Displays the next result /// public void FindNext(object sender, EventArgs e) { bool update = !PluginBase.Settings.DisableFindTextUpdating; bool simple = !PluginBase.Settings.DisableSimpleQuickFind && !quickFind.Visible; frInDocDialog.FindNext(true, update, simple); } /// /// Displays the previous result /// public void FindPrevious(object sender, EventArgs e) { bool update = !PluginBase.Settings.DisableFindTextUpdating; bool simple = !PluginBase.Settings.DisableSimpleQuickFind && !quickFind.Visible; frInDocDialog.FindNext(false, update, simple); } /// /// Opens a find and replace dialog /// public void FindAndReplace(object sender, EventArgs e) { if (!frInDocDialog.Visible) frInDocDialog.Show(); else { frInDocDialog.InitializeFindText(); frInDocDialog.Activate(); } } /// /// Opens a find and replace dialog with a location /// public void FindAndReplaceFrom(object sender, EventArgs e) { var file = ((ItemData)((ToolStripItem)sender).Tag).Tag; BeginInvoke((Action)(() => OpenEditableDocument(file))); if (!frInDocDialog.Visible) frInDocDialog.Show(); else frInDocDialog.Activate(); } /// /// Opens a find and replace in files dialog /// public void FindAndReplaceInFiles(object sender, EventArgs e) { if (!frInFilesDialog.Visible) frInFilesDialog.Show(); else { frInFilesDialog.UpdateFindText(); frInFilesDialog.Activate(); } } /// /// Opens a find and replace in files dialog with a location /// public void FindAndReplaceInFilesFrom(object sender, EventArgs e) { var path = ((ItemData)((ToolStripItem)sender).Tag).Tag; if (!frInFilesDialog.Visible) frInFilesDialog.Show(); // Show first.. else frInFilesDialog.Activate(); frInFilesDialog.SetFindPath(path); } /// /// Shows the quick find control /// public void QuickFind(object sender, EventArgs e) => quickFind.ShowControl(); /// /// Opens the edit shortcut dialog /// public void EditShortcuts(object sender, EventArgs e) => ShortcutDialog.Show(); /// /// Opens the edit snippet dialog /// public void EditSnippets(object sender, EventArgs e) => SnippetDialog.Show(); /// /// Opens the edit syntax dialog /// public void EditSyntax(object sender, EventArgs e) => EditorDialog.Show(); /// /// Opens the about dialog /// public void About(object sender, EventArgs e) => AboutDialog.Show(); /// /// Opens the settings dialog /// public void ShowSettings(object sender, EventArgs e) => SettingDialog.Show(DistroConfig.DISTRIBUTION_NAME, ""); /// /// Shows the application in fullscreen or normal mode /// public void ToggleFullScreen(object sender, EventArgs e) { if (IsFullScreen) { formState.Restore(this); if (AppSettings.ViewToolBar) ToolStrip.Visible = true; foreach (DockPane pane in DockPanel.Panes) { if (fullScreenDocks[pane] != null) { pane.DockState = (DockState)fullScreenDocks[pane]; } } IsFullScreen = false; } else { formState.Maximize(this); ToolStrip.Visible = false; fullScreenDocks = new Hashtable(); foreach (DockPane pane in DockPanel.Panes) { fullScreenDocks[pane] = pane.DockState; pane.DockState = pane.DockState switch { DockState.DockLeft => DockState.DockLeftAutoHide, DockState.DockRight => DockState.DockRightAutoHide, DockState.DockBottom => DockState.DockBottomAutoHide, DockState.DockTop => DockState.DockTopAutoHide, _ => pane.DockState, }; } IsFullScreen = true; } ButtonManager.UpdateFlaggedButtons(); } /// /// Extracts a zip file by extending paths with fd args /// public void ExtractZip(object sender, EventArgs e) { try { string zipLog = string.Empty; string zipFile = string.Empty; bool requiresRestart = false; bool silentInstall = Silent; var button = (ToolStripItem)sender; var chunks = ((ItemData)button.Tag).Tag.Split(';'); if (chunks.Length > 1) { zipFile = chunks[0]; silentInstall = chunks[1] == "true"; } else zipFile = chunks[0]; if (!File.Exists(zipFile)) return; // Skip missing file... string caption = TextHelper.GetString("Title.ConfirmDialog"); string message = TextHelper.GetString("Info.ZipConfirmExtract") + "\n" + zipFile; if (silentInstall || MessageBox.Show(message, caption, MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) { ZipEntry entry; zipLog += "FDZ: " + zipFile + "\r\n"; var zis = new ZipInputStream(new FileStream(zipFile, FileMode.Open, FileAccess.Read)); while ((entry = zis.GetNextEntry()) != null) { byte[] data = new byte[2048]; string fdpath = ProcessArgString(entry.Name, false).Replace("/", "\\"); if (entry.IsFile) { string ext = Path.GetExtension(fdpath); if (File.Exists(fdpath) && (ext == ".dll" || ext == ".fdb" || ext == ".fdl")) { fdpath += ".new"; requiresRestart = true; } else if (ext == ".dll" || ext == ".fdb" || ext == ".fdl") { requiresRestart = true; } zipLog += "Extract: " + fdpath + "\r\n"; string dirPath = Path.GetDirectoryName(fdpath); if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); FileStream extracted = new FileStream(fdpath, FileMode.Create); while (true) { var size = zis.Read(data, 0, data.Length); if (size > 0) extracted.Write(data, 0, size); else break; } extracted.Close(); extracted.Dispose(); } else if (!Directory.Exists(fdpath)) { zipLog += "Create: " + fdpath + "\r\n"; Directory.CreateDirectory(fdpath); } } string finish = TextHelper.GetString("Info.ZipExtractDone"); string restart = TextHelper.GetString("Info.RequiresRestart"); if (requiresRestart) { zipLog += "Restart required.\r\n"; if (!silentInstall) finish += "\n" + restart; RestartRequired(); } string logFile = Path.Combine(PathHelper.BaseDir, "Extensions.log"); File.AppendAllText(logFile, zipLog + "Done.\r\n\r\n", Encoding.UTF8); if (!silentInstall) ErrorManager.ShowInfo(finish); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Removes a zip file by extending paths with fd args /// public void RemoveZip(object sender, EventArgs e) { try { string zipFile = string.Empty; bool requiresRestart = false; bool silentRemove = Silent; var button = (ToolStripItem)sender; var chunks = ((ItemData)button.Tag).Tag.Split(';'); if (chunks.Length > 1) { zipFile = chunks[0]; silentRemove = chunks[1] == "true"; } else zipFile = chunks[0]; if (!File.Exists(zipFile)) return; // Skip missing file... var zipLog = string.Empty; var removeDirs = new List(); string caption = TextHelper.GetString("Title.ConfirmDialog"); string message = TextHelper.GetString("Info.ZipConfirmRemove") + "\n" + zipFile; if (silentRemove || MessageBox.Show(message, caption, MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) { ZipEntry entry; zipLog += "FDZ: " + zipFile + "\r\n"; ZipInputStream zis = new ZipInputStream(new FileStream(zipFile, FileMode.Open, FileAccess.Read)); while ((entry = zis.GetNextEntry()) != null) { string fdpath = ProcessArgString(entry.Name, false).Replace("/", "\\"); if (entry.IsFile) { string ext = Path.GetExtension(fdpath); if (File.Exists(fdpath)) { zipLog += "Delete: " + fdpath + "\r\n"; if (ext == ".dll" || ext == ".fdb" || ext == ".fdl") { requiresRestart = true; File.Copy(fdpath, fdpath + ".del", true); } else { try { File.Delete(fdpath); } catch { requiresRestart = true; File.Copy(fdpath, fdpath + ".del", true); } } } } else removeDirs.Add(fdpath); } // Remove empty dirs removeDirs.Reverse(); foreach (string dir in removeDirs) { if (FolderHelper.IsDirectoryEmpty(dir) && !DirIsImportant(dir)) { zipLog += "Remove: " + dir + "\r\n"; try { Directory.Delete(dir); } catch { /* NO ERRORS */ } } } string finish = TextHelper.GetString("Info.ZipRemoveDone"); string restart = TextHelper.GetString("Info.RequiresRestart"); if (requiresRestart) { zipLog += "Restart required.\r\n"; if (!silentRemove) finish += "\n" + restart; RestartRequired(); } string logFile = Path.Combine(PathHelper.BaseDir, "Extensions.log"); File.AppendAllText(logFile, zipLog + "Done.\r\n\r\n", Encoding.UTF8); if (!silentRemove) ErrorManager.ShowInfo(finish); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } static bool DirIsImportant(string dir) => Path.GetDirectoryName(dir) is { } full && (full == PathHelper.UserPluginDir || full == PathHelper.UserLibraryDir || full == PathHelper.UserProjectsDir); /// /// Opens the browser with the specified file /// public void Browse(object sender, EventArgs e) { var browser = new Browser {Dock = DockStyle.Fill}; if (sender != null) { var button = (ToolStripItem)sender; var url = ProcessArgString(((ItemData)button.Tag).Tag); CreateCustomDocument(browser); if (url.Trim().Length > 0) browser.WebBrowser.Navigate(url); else browser.WebBrowser.GoHome(); } else browser.WebBrowser.GoHome(); } /// /// Opens the home page in browser /// public void ShowHome(object sender, EventArgs e) => CallCommand("Browse", DistroConfig.DISTRIBUTION_HOME); /// /// Opens the help page in browser /// public void ShowHelp(object sender, EventArgs e) => CallCommand("Browse", DistroConfig.DISTRIBUTION_HELP); /// /// Opens the arguments dialog /// public void ShowArguments(object sender, EventArgs e) => ArgumentDialog.Show(); /// /// Checks for updates from flashdevelop.org /// public void CheckUpdates(object sender, EventArgs e) => UpdateDialog.Show(false); /// /// Toggles the current document to and from split view /// public void ToggleSplitView(object sender, EventArgs e) { if (CurrentDocument?.SciControl is null) return; CurrentDocument.IsSplitted = !CurrentDocument.IsSplitted; ButtonManager.UpdateFlaggedButtons(); } /// /// Moves the user to the matching brace /// public void GoToMatchingBrace(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; var curPos = sci.CurrentPos; char c; if (curPos > 0) { c = (char) sci.CharAt(curPos - 1); if (c == '{' || c == '[' || c == '(' || c == ')' || c == ']' || c == '}') curPos--; } c = (char)sci.CharAt(curPos); if (c != '{' && c != '[' && c != '(' && c != '}' && c != ']' && c != ')') curPos--; var bracePosEnd = sci.BraceMatch(curPos); if (bracePosEnd != -1) sci.GotoPos(bracePosEnd + 1); } /// /// Adds or removes a bookmark /// public void ToggleBookmark(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is not null) MarkerManager.ToggleMarker(sci, 0, sci.CurrentLine); } /// /// Moves the cursor to the next bookmark /// public void NextBookmark(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is not null) MarkerManager.NextMarker(sci, 0, sci.CurrentLine); } /// /// Moves the cursor to the previous bookmark /// public void PrevBookmark(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is not null) MarkerManager.PreviousMarker(sci, 0, sci.CurrentLine); } /// /// Removes all bookmarks /// public void ClearBookmarks(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; sci.MarkerDeleteAll(0); UITools.Manager.MarkerChanged(sci, -1); ButtonManager.UpdateFlaggedButtons(); } /// /// Converts all end of line characters /// public void ConvertEOL(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var eolMode = Convert.ToInt32(((ItemData)((ToolStripItem)sender).Tag).Tag); sci.ConvertEOLs(eolMode); sci.EOLMode = eolMode; OnScintillaControlUpdateControl(sci); OnDocumentModify(CurrentDocument); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Toggles the folding of the editor /// public void ToggleFold(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; sci?.ToggleFold(sci.CurrentLine); } /// /// Toggles a boolean setting /// public void ToggleBooleanSetting(object sender, EventArgs e) { try { var key = ((ItemData)((ToolStripItem)sender).Tag).Tag; var value = (bool)AppSettings.GetValue(key); AppSettings.SetValue(key, !value); ApplyAllSettings(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Changes the encoding of the current document /// public void ChangeEncoding(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var encMode = Convert.ToInt32(((ItemData)((ToolStripItem)sender).Tag).Tag); sci.Encoding = Encoding.GetEncoding(encMode); OnScintillaControlUpdateControl(sci); OnDocumentModify(CurrentDocument); } catch (Exception ex) { ErrorManager.ShowError(ex); } } public void ToggleSaveBOM(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; sci.SaveBOM = !sci.SaveBOM; OnScintillaControlUpdateControl(sci); OnDocumentModify(CurrentDocument); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Converts the encoding of the current document /// public void ConvertEncoding(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var button = (ToolStripItem)sender; var encMode = Convert.ToInt32(((ItemData)button.Tag).Tag); var curMode = sci.Encoding.CodePage; // From current.. var converted = DataConverter.ChangeEncoding(sci.Text, curMode, encMode); sci.Encoding = Encoding.GetEncoding(encMode); sci.Text = converted; // Set after codepage change OnScintillaControlUpdateControl(sci); OnDocumentModify(CurrentDocument); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Inserts a snippet to the current position /// public void InsertSnippet(object sender, EventArgs e) { try { string word = ((ItemData)((ToolStripItem)sender).Tag).Tag; SnippetManager.InsertTextByWord(word != "null" ? word : null); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Inserts a new GUID to the editor /// public void InsertGUID(object sender, EventArgs e) => CurrentDocument?.SciControl?.ReplaceSel(Guid.NewGuid().ToString()); /// /// Inserts a custom hash to the editor /// public void InsertHash(object sender, EventArgs e) { using var dialog = new HashDialog(); if (dialog.ShowDialog() == DialogResult.OK) CurrentDocument?.SciControl?.ReplaceSel(dialog.HashResultText); } /// /// Inserts a color to the editor /// public void InsertColor(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; bool hasPrefix = true; bool isAsterisk = false; if (sci.SelTextSize > 0) { var selText = sci.SelText; isAsterisk = selText.StartsWith('#'); if (selText.StartsWithOrdinal("0x") && sci.SelTextSize == 8) { int convertedColor = DataConverter.StringToColor(selText); colorDialog.Color = ColorTranslator.FromWin32(convertedColor); } else if (selText.StartsWith('#') && sci.SelTextSize == 7) { string foundColor = selText.Replace("#", "0x"); int convertedColor = DataConverter.StringToColor(foundColor); colorDialog.Color = ColorTranslator.FromWin32(convertedColor); } else if (sci.SelTextSize == 6) { hasPrefix = false; int convertedColor = DataConverter.StringToColor("0x" + selText); colorDialog.Color = ColorTranslator.FromWin32(convertedColor); } } if (colorDialog.ShowDialog(this) == DialogResult.OK) { string colorText = DataConverter.ColorToHex(colorDialog.Color); if (!hasPrefix) colorText = colorText.Replace("0x", ""); else if (isAsterisk || sci.ConfigurationLanguage == "css") { colorText = colorText.Replace("0x", "#"); } sci.ReplaceSel(colorText); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Inserts the file info to the current position /// public void InsertFileDetails(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var fileInfo = new FileInfo(sci.FileName); var message = TextHelper.GetString("Info.FileDetails"); var newline = LineEndDetector.GetNewLineMarker(sci.EOLMode); var path = fileInfo.FullName; var created = fileInfo.CreationTime.ToString(); var modified = fileInfo.LastWriteTime.ToString(); var size = fileInfo.Length.ToString(); var info = string.Format(message, newline, path, created, modified, size); sci.ReplaceSel(info); } catch { string message = TextHelper.GetString("Info.NoFileInfoAvailable"); ErrorManager.ShowInfo(message); } } /// /// Inserts a global timestamp to the current position /// public void InsertTimestamp(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var button = (ToolStripItem)sender; var date = (((ItemData)button.Tag).Tag); var currentDate = DateTime.Now.ToString(date); sci.ReplaceSel(currentDate); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Inserts text from the specified file to the current position /// public void InsertFile(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var button = (ToolStripItem)sender; var file = ProcessArgString(((ItemData)button.Tag).Tag); if (!File.Exists(file)) return; var to = sci.Encoding; var info = FileHelper.GetEncodingFileInfo(file); if (info.CodePage == -1) return; // If the file is locked, stop. var contents = DataConverter.ChangeEncoding(info.Contents, info.CodePage, to.CodePage); sci.ReplaceSel(contents); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Changes the language of the current document /// public void ChangeSyntax(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var button = (ToolStripItem)sender; var language = ((ItemData) button.Tag).Tag; if (sci.ConfigurationLanguage.Equals(language)) return; // already using this syntax ScintillaManager.ChangeSyntax(language, sci); var extension = sci.GetFileExtension(); if (string.IsNullOrEmpty(extension)) return; var title = TextHelper.GetString("Title.RememberExtensionDialog"); var message = TextHelper.GetString("Info.RememberExtensionDialog"); if (MessageBox.Show(message, title, MessageBoxButtons.YesNo) == DialogResult.Yes) { sci.SaveExtensionToSyntaxConfig(extension); } } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Sorts the currently selected lines /// public void SortLines(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; var curLine = sci.LineFromPosition(sci.SelectionStart); var endLine = sci.LineFromPosition(sci.SelectionEnd); var count = endLine + 1; var lines = new List(count - curLine); for (var line = curLine; line < count; ++line) { lines.Add(sci.GetLine(line)); } lines.Sort(CompareLines); var sb = new StringBuilder(); foreach (var s in lines) { sb.Append(s); } int selStart = sci.PositionFromLine(curLine); int selEnd = sci.PositionFromLine(endLine) + sci.MBSafeTextLength(sci.GetLine(endLine)); sci.SetSel(selStart, selEnd); sci.ReplaceSel(sb.ToString()); } /// /// Sorts the currently selected lines in groups /// public void SortLineGroups(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; var curLine = sci.LineFromPosition(sci.SelectionStart); var endLine = sci.LineFromPosition(sci.SelectionEnd); var lineLists = new List>(); var curList = new List(); lineLists.Add(curList); for (int line = curLine; line < endLine + 1; ++line) { var lineText = sci.GetLine(line); if (lineText.Trim().Length == 0) { curList.Sort(CompareLines); curList.Add(lineText); curList = new List(); lineLists.Add(curList); continue; } curList.Add(lineText); } curList.Sort(CompareLines); var sb = new StringBuilder(); foreach (var l in lineLists) { foreach (var s in l) { sb.Append(s); } } int selStart = sci.PositionFromLine(curLine); int selEnd = sci.PositionFromLine(endLine) + sci.MBSafeTextLength(sci.GetLine(endLine)); sci.SetSel(selStart, selEnd); sci.ReplaceSel(sb.ToString()); } static int CompareLines(string a, string b) { char[] whitespace = {'\t', ' '}; a = a.TrimStart(whitespace); b = b.TrimStart(whitespace); return a.CompareTo(b); } /// /// Line-comment the selected text /// public void CommentLine(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; var lineComment = ScintillaManager.GetLineComment(sci.ConfigurationLanguage); if (lineComment.Length == 0) return; int position = sci.CurrentPos; int curLine = sci.LineFromPosition(position); int startPosInLine = position - sci.PositionFromLine(curLine); int finalPos = position; int startLine = sci.LineFromPosition(sci.SelectionStart); int line = startLine; int endLine = sci.LineFromPosition(sci.SelectionEnd); if (endLine > line && curLine == endLine && startPosInLine == 0) { curLine--; endLine--; finalPos = sci.PositionFromLine(curLine); } bool afterIndent = AppSettings.LineCommentsAfterIndent; sci.BeginUndoAction(); try { while (line <= endLine) { position = (afterIndent) ? sci.LineIndentPosition(line) : sci.PositionFromLine(line); sci.InsertText(position, lineComment); if (line == curLine) { finalPos = sci.PositionFromLine(line) + startPosInLine; if (finalPos >= position) finalPos += lineComment.Length; } line++; } sci.SetSel(finalPos, finalPos); if (startLine == endLine && (endLine < sci.LineCount) && AppSettings.MoveCursorAfterComment) { sci.LineDown(); } } finally { sci.EndUndoAction(); } sci.Colourise(0, -1); } /// /// Line-uncomment the selected text /// public void UncommentLine(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; var lineComment = ScintillaManager.GetLineComment(sci.ConfigurationLanguage); if (lineComment.Length == 0) return; int position = sci.CurrentPos; int curLine = sci.LineFromPosition(position); int startPosInLine = position - sci.PositionFromLine(curLine); int finalPos = position; int startLine = sci.LineFromPosition(sci.SelectionStart); int line = startLine; int endLine = sci.LineFromPosition(sci.SelectionEnd); if (endLine > line && curLine == endLine && startPosInLine == 0) { curLine--; endLine--; finalPos = sci.PositionFromLine(curLine); } sci.BeginUndoAction(); try { while (line <= endLine) { var text = sci.LineLength(line) == 0 ? "" : sci.GetLine(line).TrimStart(); if (text.StartsWithOrdinal(lineComment)) { position = sci.LineIndentPosition(line); sci.SetSel(position, position + lineComment.Length); sci.ReplaceSel(""); if (line == curLine) { finalPos = sci.PositionFromLine(line) + Math.Min(startPosInLine, sci.LineLength(line)); if (finalPos >= position + lineComment.Length) finalPos -= lineComment.Length; } } line++; } sci.SetSel(finalPos, finalPos); if (startLine == endLine && (endLine < sci.LineCount) && AppSettings.MoveCursorAfterComment) { sci.LineDown(); } } finally { sci.EndUndoAction(); } } /// /// Box-comments or uncomments the selected text /// public void CommentSelection(object sender, EventArgs e) => CommentSelection(); bool? CommentSelection() { var sci = CurrentDocument?.SciControl; if (sci is null) return null; var selEnd = sci.SelectionEnd; var selStart = sci.SelectionStart; var commentEnd = ScintillaManager.GetCommentEnd(sci.ConfigurationLanguage); var commentStart = ScintillaManager.GetCommentStart(sci.ConfigurationLanguage); var selText = sci.SelText; if (selText.StartsWithOrdinal(commentStart) && selText.EndsWithOrdinal(commentEnd)) { sci.BeginUndoAction(); try { var indexLength = sci.SelTextSize - commentStart.Length - commentEnd.Length; var withoutComment = selText.Substring(commentStart.Length, indexLength); sci.ReplaceSel(withoutComment); } finally { sci.EndUndoAction(); } return false; } if (sci.SelTextSize == 0) return null; sci.BeginUndoAction(); try { sci.InsertText(selStart, commentStart); sci.InsertText(selEnd + commentStart.Length, commentEnd); } finally { sci.EndUndoAction(); } return true; } /// /// Uncomments a comment block from current position /// public void UncommentBlock(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; sci.Colourise(0, -1); // update coloring int selEnd = sci.SelectionEnd; int selStart = sci.SelectionStart; string commentEnd = ScintillaManager.GetCommentEnd(sci.ConfigurationLanguage); string commentStart = ScintillaManager.GetCommentStart(sci.ConfigurationLanguage); if ((sci.PositionIsOnComment(selStart) && (sci.PositionIsOnComment(selEnd)) || sci.PositionIsOnComment(selEnd - 1)) || (selEnd == selStart && sci.PositionIsOnComment(selStart - 1))) { sci.BeginUndoAction(); try { int lexer = sci.Lexer; // find selection bounds if (!sci.PositionIsOnComment(selStart, lexer)) selStart--; int scrollTop = sci.FirstVisibleLine; int initPos = sci.CurrentPos; int start = selStart; while (start > 0 && sci.PositionIsOnComment(start, lexer)) start--; int end = selEnd; int length = sci.TextLength; while (end < length && sci.PositionIsOnComment(end, lexer)) end++; sci.SetSel(start + 1, end); // remove comment string selText = sci.SelText; if (selText.StartsWithOrdinal(commentStart) && selText.EndsWithOrdinal(commentEnd)) { sci.SetSel(end - commentEnd.Length, end); sci.ReplaceSel(""); sci.SetSel(start + 1, start + commentStart.Length + 1); sci.ReplaceSel(""); // fix caret pos if (initPos > end - commentEnd.Length) initPos = end - commentEnd.Length; if (initPos <= start + commentStart.Length) initPos = start + 1; else initPos -= commentStart.Length; } sci.SetSel(initPos, initPos); if (scrollTop != sci.FirstVisibleLine) // fix page scrolling { sci.LineScroll(0, scrollTop - sci.FirstVisibleLine); } } finally { sci.EndUndoAction(); } } } /// /// Toggles the line comment in a smart way /// public void ToggleLineComment(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; var lineComment = ScintillaManager.GetLineComment(sci.ConfigurationLanguage); var position = sci.CurrentPos; // try doing a block comment on the current line instead (xml, html...) if (lineComment.Length == 0) { ToggleBlockOnCurrentLine(sci); return; } int curLine = sci.LineFromPosition(position); int startPosInLine = position - sci.PositionFromLine(curLine); int startLine = sci.LineFromPosition(sci.SelectionStart); int line = startLine; int endLine = sci.LineFromPosition(sci.SelectionEnd); if (endLine > line && curLine == endLine && startPosInLine == 0) endLine--; bool containsCodeLine = false; while (line <= endLine) { var text = sci.LineLength(line) == 0 ? "" : sci.GetLine(line).TrimStart(); if (!text.StartsWithOrdinal(lineComment)) { containsCodeLine = true; break; } line++; } if (containsCodeLine) CommentLine(null, null); else UncommentLine(null, null); } void ToggleBlockOnCurrentLine(ScintillaControl sci) { int selStart = sci.SelectionStart; int indentPos = sci.LineIndentPosition(sci.CurrentLine); int lineEndPos = sci.LineEndPosition(sci.CurrentLine); bool afterBlockStart = sci.CurrentPos > indentPos; bool afterBlockEnd = sci.CurrentPos >= lineEndPos; sci.SelectionStart = indentPos; sci.SelectionEnd = lineEndPos; var added = CommentSelection(); if (added is null) return; var factor = (bool)added ? 1 : -1; var commentEnd = ScintillaManager.GetCommentEnd(sci.ConfigurationLanguage); var commentStart = ScintillaManager.GetCommentStart(sci.ConfigurationLanguage); // preserve cursor pos if (afterBlockStart) selStart += commentStart.Length * factor; if (afterBlockEnd) selStart += commentEnd.Length * factor; sci.SetSel(selStart, selStart); } /// /// Toggles the block comment in a smart way /// public void ToggleBlockComment(object sender, EventArgs e) { var sci = CurrentDocument?.SciControl; if (sci is null) return; if (sci.SelTextSize > 0) CommentSelection(null, null); else UncommentBlock(null, null); } /// /// Lets user browse for an theme file /// public void SelectTheme(object sender, EventArgs e) { using var dialog = new OpenFileDialog { InitialDirectory = PathHelper.ThemesDir, Title = " " + TextHelper.GetString("Title.OpenFileDialog"), Filter = TextHelper.GetString("Info.ThemesFilter") }; if (dialog.ShowDialog(this) != DialogResult.OK) return; var ext = Path.GetExtension(dialog.FileName).ToLower(); if (ext == ".fdi") { ThemeManager.LoadTheme(dialog.FileName); ThemeManager.WalkControls(this); } else { CallCommand("ExtractZip", dialog.FileName + ";true"); var currentTheme = Path.Combine(PathHelper.ThemesDir, "CURRENT"); if (File.Exists(currentTheme)) ThemeManager.LoadTheme(currentTheme); ThemeManager.WalkControls(this); RefreshSciConfig(); } } /// /// Loads an theme file and applies it /// public void LoadThemeFile(object sender, EventArgs e) { var button = (ToolStripItem)sender; var file = ProcessArgString(((ItemData)button.Tag).Tag); ThemeManager.LoadTheme(file); } /// /// Invokes the specified registered menu item /// public void InvokeMenuItem(object sender, EventArgs e) { try { var button = (ToolStripItem)sender; var registeredItem = ((ItemData)button.Tag).Tag; var item = ShortcutManager.GetRegisteredItem(registeredItem); item?.Item?.PerformClick(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Calls a ScintillaControl command /// public void ScintillaCommand(object sender, EventArgs e) { try { var sci = CurrentDocument?.SciControl; if (sci is null) return; var button = (ToolStripItem)sender; var command = ((ItemData)button.Tag).Tag; var mfType = sci.GetType(); var method = mfType.GetMethod(command, Array.Empty()); method?.Invoke(sci, null); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Calls a custom plugin command /// public void PluginCommand(object sender, EventArgs e) { try { var item = (ToolStripItem) sender; var args = ((ItemData) item.Tag).Tag.Split(new[] { ';' }, 2); var action = args[0]; // Action of the command var data = args.Length > 1 ? args[1] : null; EventManager.DispatchEvent(this, new DataEvent(EventType.Command, action, data)); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Calls a normal MainForm method /// public bool CallCommand(string command, string args) { if (IsDisposed) return false; try { var method = GetType().GetMethod(command); if (method is null) throw new MethodAccessException(); var item = new ToolStripMenuItem(); item.Tag = new ItemData(null, args, null); // Tag is used for args method.Invoke(this, new[] { item, null }); return true; } catch (Exception ex) { ErrorManager.ShowError(ex); return false; } } /// /// Runs a simple process /// public void RunProcess(object sender, EventArgs e) { try { var button = (ToolStripItem)sender; var args = ProcessArgString(((ItemData)button.Tag).Tag); var position = args.IndexOf(';'); // Position of the arguments var ne = new NotifyEvent(EventType.ProcessStart); EventManager.DispatchEvent(this, ne); if (position > -1) { var message = TextHelper.GetString("Info.RunningProcess"); TraceManager.Add(message + " " + args.Substring(0, position) + " " + args.Substring(position + 1), TraceType.ProcessStart); var psi = new ProcessStartInfo { WorkingDirectory = WorkingDirectory, Arguments = args.Substring(position + 1), FileName = args.Substring(0, position) }; ProcessHelper.StartAsync(psi); } else { string message = TextHelper.GetString("Info.RunningProcess"); TraceManager.Add(message + " " + args, (int)TraceType.ProcessStart); if (args.ToLower().EndsWithOrdinal(".bat")) { var bp = new Process {StartInfo = {WindowStyle = ProcessWindowStyle.Hidden, FileName = args}}; bp.Start(); } else { var psi = new ProcessStartInfo(args) {WorkingDirectory = WorkingDirectory}; ProcessHelper.StartAsync(psi); } } ButtonManager.UpdateFlaggedButtons(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Runs a process and tracks it's progress /// public void RunProcessCaptured(object sender, EventArgs e) { try { var button = (ToolStripItem)sender; var args = ProcessArgString(((ItemData)button.Tag).Tag); var position = args.IndexOf(';'); // Position of the arguments var ne = new NotifyEvent(EventType.ProcessStart); EventManager.DispatchEvent(this, ne); if (position == -1) { var message = TextHelper.GetString("Info.NotEnoughArguments"); TraceManager.Add($"{message} {args}", (int)TraceType.Error); return; } var message2 = TextHelper.GetString("Info.RunningProcess"); var fileName = args.Substring(0, position); var arguments = args.Substring(position + 1); TraceManager.Add($"{message2} {fileName} {arguments}", (int)TraceType.ProcessStart); processRunner.Run(fileName, arguments); ButtonManager.UpdateFlaggedButtons(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Handles the incoming info output /// static void ProcessOutput(object sender, string line) => TraceManager.AddAsync(line, TraceType.Info); /// /// Handles the incoming error output /// static void ProcessError(object sender, string line) => TraceManager.AddAsync(line, TraceType.ProcessError); /// /// Handles the ending of a process /// void ProcessEnded(object sender, int exitCode) { if (InvokeRequired) BeginInvoke((Action)(() => ProcessEnded(sender, exitCode))); else { var result = $"Done({exitCode})"; TraceManager.Add(result, TraceType.ProcessEnd); var te = new TextEvent(EventType.ProcessEnd, result); EventManager.DispatchEvent(this, te); ButtonManager.UpdateFlaggedButtons(); } } /// /// Stop the currently running process /// public void KillProcess(object sender, EventArgs e) => KillProcess(); /// /// Stop the currently running process /// public void KillProcess() { if (processRunner.IsRunning) processRunner.KillProcess(); } /// /// Backups users setting files to a FDZ file /// public void BackupSettings(object sender, EventArgs e) { try { using var dialog = new SaveFileDialog { AddExtension = true, DefaultExt = "fdz", Filter = TextHelper.GetString("FlashDevelop.Info.ZipFilter") }; if (dialog.ShowDialog(this) != DialogResult.OK) return; var settingFiles = new List(); settingFiles.AddRange(Directory.GetFiles(PathHelper.DataDir, "*.*", SearchOption.AllDirectories)); settingFiles.AddRange(Directory.GetFiles(PathHelper.SnippetDir, "*.*", SearchOption.AllDirectories)); settingFiles.AddRange(Directory.GetFiles(PathHelper.SettingDir, "*.*", SearchOption.AllDirectories)); settingFiles.AddRange(Directory.GetFiles(PathHelper.TemplateDir, "*.*", SearchOption.AllDirectories)); settingFiles.AddRange(Directory.GetFiles(PathHelper.UserLibraryDir, "*.*", SearchOption.AllDirectories)); settingFiles.AddRange(Directory.GetFiles(PathHelper.UserProjectsDir, "*.*", SearchOption.AllDirectories)); const string dirMarker = "\\" + DistroConfig.DISTRIBUTION_NAME + "\\"; var zipFile = ZipFile.Create(dialog.FileName); zipFile.BeginUpdate(); foreach (string settingFile in settingFiles) { int index = settingFile.IndexOfOrdinal(dirMarker) + dirMarker.Length; zipFile.Add(settingFile, "$(BaseDir)\\" + settingFile.Substring(index)); } zipFile.CommitUpdate(); zipFile.Close(); } catch (Exception ex) { ErrorManager.ShowError(ex); } } /// /// Executes the specified C# Script file /// public void ExecuteScript(object sender, EventArgs e) { var button = (ToolStripItem)sender; var file = ProcessArgString(((ItemData)button.Tag).Tag); try { var args = file.Split(';'); if (args.Length == 1 || string.IsNullOrEmpty(args[1])) return; // no file selected / the open file dialog was cancelled var host = new Host(); switch (args[0]) { case "Internal": host.ExecuteScriptInternal(args[1], false); break; case "Development": host.ExecuteScriptInternal(args[1], true); break; default: host.ExecuteScriptExternal(file); break; } } catch (Exception ex) { var message = TextHelper.GetString("Info.CouldNotExecuteScript"); ErrorManager.ShowWarning(message + "\r\n" + ex.Message, null); } } /// /// Test the controls in a dedicated form /// public void TestControls(object sender, EventArgs e) { using var dialog = new ControlDialog(); dialog.Show(this); } /// /// Outputs the supplied argument string /// public void Debug(object sender, EventArgs e) { var button = (ToolStripItem)sender; var args = ProcessArgString(((ItemData)button.Tag).Tag); if (args.Length == 0) ErrorManager.ShowError(new Exception("Debug")); else ErrorManager.ShowInfo(args); } /// /// Restarts FlashDevelop /// public void Restart(object sender, EventArgs e) { if (GetInstanceCount() != 1) return; RestartRequested = true; Close(); } #endregion } #region Script Host public class Host : MarshalByRefObject { /// /// Executes the script in a separate appdomain and then unloads it /// NOTE: This is more suitable for one pass processes /// public void ExecuteScriptExternal(string script) { if (!File.Exists(script)) throw new FileNotFoundException(); using var helper = new AsmHelper(CSScript.CompileFile(script, null, true), null, true); helper.Invoke("*.Execute"); } /// /// Executes the script and adds it to the current app domain /// NOTE: This locks the assembly script file /// public void ExecuteScriptInternal(string script, bool random) { if (!File.Exists(script)) throw new FileNotFoundException(); var file = random ? Path.GetTempFileName() : null; var helper = new AsmHelper(CSScript.Load(script, file, false, null)); helper.Invoke("*.Execute"); } } #endregion }