using BookmarkManager; using bzit.bomg.Models; using FizzyLauncher.Models; using Newtonsoft.Json; using RyzStudio.Drawing; using RyzStudio.Windows.Forms; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace FizzyLauncher { public partial class MainForm : Form { public enum AppMode { Clear = 0, Open, New } protected IconDatabase iconDatabase = null; protected UpdateIconsForm updateIconsForm = null; protected OptionsForm optionsForm = null; protected FindForm findForm = null; protected AppMode appMode = AppMode.Clear; protected string sessionFilename = null; protected string sessionPassword = null; protected bool isBusy = false; protected readonly string jsonfigFilename; public MainForm() { InitializeComponent(); jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig"); if (iconDatabase == null) iconDatabase = new IconDatabase(Path.ChangeExtension(Application.ExecutablePath, "db")); this.AutoScaleMode = AutoScaleMode.None; this.StartPosition = FormStartPosition.WindowsDefaultLocation; this.ClientSize = new System.Drawing.Size(300, 580); //this.Visible = false; treeView1.IconDatabase = iconDatabase; treeView1.RootContextMenu = rootContextMenu; treeView1.FolderContextMenu = folderContextMenu; treeView1.PageContextMenu = pageContextMenu; treeView1.NodeMouseDoubleClick += treeView1_NodeMouseDoubleClick; treeView1.OnNodeChanged += treeView1_OnNodeChanged; treeView1.PreviewKeyDown += treeView1_PreviewKeyDown; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); //ThreadControl.SetVisible(this, false); UIControl.SetSize(this, 300, 580); List supportedFileExtensions = GetSupportedFileTypes(); openFileDialog1.Filter = BuildOpenFileSupportedFiles(supportedFileExtensions); saveFileDialog1.Filter = BuildSaveFileSupportedFiles(supportedFileExtensions); ApplicationMode = AppMode.Clear; } protected async override void OnShown(EventArgs e) { base.OnShown(e); //ThreadControl.SetVisible(this, false); await LoadAppSession(jsonfigFilename); InvalidateAppSession(); string[] commandLineArgs = Environment.GetCommandLineArgs(); bool hasCommands = await LoadCommandLine(commandLineArgs); if (!hasCommands) { newToolStripMenuItem_Click(null, null); } } protected async override void OnClosing(CancelEventArgs e) { e.Cancel = true; if (this.IsBusy) { return; } this.IsBusy = true; Result result = await CloseFile(); if (!result.IsSuccess) { if (!string.IsNullOrWhiteSpace(result.Message)) { MessageBox.Show(result.Message, "Close", MessageBoxButtons.OK, MessageBoxIcon.Error); } this.IsBusy = false; e.Cancel = true; return; } sessionFilename = null; sessionPassword = null; treeView1.Clear(); iconDatabase?.Close(); ApplicationMode = AppMode.Clear; this.IsBusy = false; Application.Exit(); } public AppSession CurrentSession { get; set; } = null; public bool IsBusy { get => isBusy; set { treeView1.Enabled = !value; } } protected AppMode ApplicationMode { get => appMode; set { appMode = value; switch (value) { case AppMode.Clear: UIControl.SetText(this, AppResource.app_name); UIControl.SetEnable(false, closeToolStripMenuItem, saveToolStripMenuItem, saveAsToolStripMenuItem, findToolStripMenuItem, toolStripMenuItem9, expandAllToolStripMenuItem, collapseAllToolStripMenuItem); UIControl.SetEnable(false, toolStripButton3, toolStripButton4); break; case AppMode.Open: UIControl.SetEnable(true, closeToolStripMenuItem, saveToolStripMenuItem, saveAsToolStripMenuItem, findToolStripMenuItem, toolStripMenuItem9, expandAllToolStripMenuItem, collapseAllToolStripMenuItem); UIControl.SetEnable(true, toolStripButton3, toolStripButton4); break; case AppMode.New: UIControl.SetText(this, AppResource.app_name); UIControl.SetEnable(true, closeToolStripMenuItem, saveAsToolStripMenuItem, findToolStripMenuItem, toolStripMenuItem9, expandAllToolStripMenuItem, collapseAllToolStripMenuItem); UIControl.SetEnable(saveToolStripMenuItem, false); UIControl.SetEnable(toolStripButton3, false); UIControl.SetEnable(toolStripButton4, true); break; default: break; } } } #region main menu /// /// New /// /// /// private async void newToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } this.IsBusy = true; Result result = await CloseFile(); if (!result.IsSuccess) { if (!string.IsNullOrWhiteSpace(result.Message)) { MessageBox.Show(result.Message, "New Session", MessageBoxButtons.OK, MessageBoxIcon.Error); } this.IsBusy = false; return; } sessionFilename = null; sessionPassword = null; treeView1.Clear("Untitled"); ApplicationMode = AppMode.New; this.IsBusy = false; } /// /// Open file /// /// /// private async void openToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } this.IsBusy = true; Result result = await CloseFile(); if (!result.IsSuccess) { if (!string.IsNullOrWhiteSpace(result.Message)) { MessageBox.Show(result.Message, "Open File", MessageBoxButtons.OK, MessageBoxIcon.Error); } this.IsBusy = false; return; } if (openFileDialog1.ShowDialog() == DialogResult.OK) { result = await LoadFile(openFileDialog1.FileName); if (result.IsSuccess) { UIControl.SetText(this, Path.GetFileNameWithoutExtension(openFileDialog1.FileName) + " - " + AppResource.app_name); } else { MessageBox.Show(result.Message, "Open File", MessageBoxButtons.OK, MessageBoxIcon.Error); } } this.IsBusy = false; } /// /// Close /// /// /// private async void closeToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } this.IsBusy = true; Result result = await CloseFile(); if (!result.IsSuccess) { if (!string.IsNullOrWhiteSpace(result.Message)) { MessageBox.Show(result.Message, "Close File", MessageBoxButtons.OK, MessageBoxIcon.Error); } this.IsBusy = false; return; } sessionFilename = null; sessionPassword = null; treeView1.Clear(); ApplicationMode = AppMode.Clear; this.IsBusy = false; } /// /// Save /// /// /// private async void saveToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (ApplicationMode == AppMode.Clear) { return; } this.IsBusy = true; Result result = await SaveFile(sessionFilename, sessionPassword); if (result.IsSuccess) { MessageBox.Show("File saved!", "Save File", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show(result.Message, "Save File", MessageBoxButtons.OK, MessageBoxIcon.Error); } this.IsBusy = false; } /// /// Save As /// /// /// private async void saveAsToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (ApplicationMode == AppMode.Clear) { return; } this.IsBusy = true; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { Result result = await SaveAsFile(saveFileDialog1.FileName); if (result.IsSuccess) { MessageBox.Show("File saved!", "Save As File", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show(result.Message, "Save As File", MessageBoxButtons.OK, MessageBoxIcon.Error); } } this.IsBusy = false; } /// /// Exit /// /// /// private void exitToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } this.Close(); } /// /// Find /// /// /// private void findToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (findForm == null) findForm = new FindForm(treeView1); findForm.Show(); } /// /// Expand all /// /// /// private void expandAllToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) return; if (ApplicationMode == AppMode.Clear) return; if (treeView1.SelectedNode == null) { treeView1.ExpandAll(); } else { treeView1.SelectedNode.ExpandAll(); } } /// /// Collapse all /// /// /// private void collapseAllToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) return; if (ApplicationMode == AppMode.Clear) return; if (treeView1.SelectedNode == null) { treeView1.CollapseAll(); } else { treeView1.SelectedNode.Collapse(false); } } /// /// Always on top /// /// /// private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e) { this.TopMost = !this.TopMost; alwaysOnTopToolStripMenuItem.Checked = this.TopMost; } /// /// Update icons /// /// /// private void toolStripMenuItem9_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (updateIconsForm == null) updateIconsForm = new UpdateIconsForm(this.CurrentSession, treeView1, iconDatabase); updateIconsForm.ShowDialog(); } /// /// Options /// /// /// private async void optionsToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (optionsForm == null) optionsForm = new OptionsForm(this); if (optionsForm.ShowDialog() == DialogResult.OK) { await SaveAppSession(jsonfigFilename); } InvalidateAppSession(); } /// /// View help /// /// /// private void viewHelpToolStripMenuItem1_Click(object sender, EventArgs e) { try { System.Diagnostics.Process.Start("https://www.hiimray.co.uk/software-bookmark-manager"); } catch { // do nothing } } /// /// About /// /// /// private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) { MessageBox.Show(Application.ProductName + " v" + Application.ProductVersion, "About", MessageBoxButtons.OK, MessageBoxIcon.Information); } #endregion #region context menu /// /// Add page /// /// /// private void addPageToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (treeView1.GetNodeType() == BookmarkTreeView.NodeType.Page) { return; } if (treeView1.SelectedNode == null) { return; } // add placeholder TreeNode node = treeView1.AddItem(treeView1.SelectedNode, new BookmarkItem() { SiteName = "New Bookmark" }); node.EnsureVisible(); treeView1.SelectedNode = node; UpdateBookmarkNode(true); } /// /// Add folder /// /// /// private void addFolderToolStripMenuItem_Click(object sender, EventArgs e) => treeView1.AddFolder(); /// /// Edit root node /// /// /// private void editToolStripMenuItem_Click(object sender, EventArgs e) => treeView1.EditNode(); /// /// Sort /// /// /// private void sortToolStripMenuItem_Click(object sender, EventArgs e) => treeView1.Sort(); /// /// Add page /// /// /// private void addPageToolStripMenuItem1_Click(object sender, EventArgs e) => addPageToolStripMenuItem_Click(sender, e); /// /// Add folder /// /// /// private void addFolderToolStripMenuItem1_Click(object sender, EventArgs e) => treeView1.AddFolder(); /// /// Open all pages /// /// /// private async void openAllToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (treeView1.SelectedNode == null) { return; } if (treeView1.SelectedNode.Nodes.Count <= 0) { return; } foreach (TreeNode item in treeView1.SelectedNode.Nodes) { await OpenBookmark(item); } } /// /// Edit folder name /// /// /// private void editToolStripMenuItem1_Click(object sender, EventArgs e) => treeView1.EditNode(); /// /// Delete folder and contents /// /// /// private void deleteToolStripMenuItem_Click(object sender, EventArgs e) => treeView1.DeleteNode(); /// /// Sort children /// /// /// private void sortToolStripMenuItem1_Click(object sender, EventArgs e) => treeView1.Sort(); /// /// Move up /// /// /// private void moveUpToolStripMenuItem_Click(object sender, EventArgs e) => treeView1.MoveUp(); /// /// Move down /// /// /// private void moveDownToolStripMenuItem_Click(object sender, EventArgs e) => treeView1.MoveDown(); /// /// Open page /// /// /// private async void openToolStripMenuItem1_Click(object sender, EventArgs e) => await OpenBookmark(treeView1.SelectedNode); /// /// Edit page /// /// /// private void editToolStripMenuItem2_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } BookmarkTreeView.NodeType nodeType = treeView1.GetNodeType(); if (nodeType == BookmarkTreeView.NodeType.Page) { UpdateBookmarkNode(false); } } /// /// Delete page /// /// /// private void deleteToolStripMenuItem1_Click(object sender, EventArgs e) => treeView1.DeleteNode(); /// /// Move up /// /// /// private void moveUpToolStripMenuItem1_Click(object sender, EventArgs e) => treeView1.MoveUp(); /// /// Move down /// /// /// private void moveDownToolStripMenuItem1_Click(object sender, EventArgs e) => treeView1.MoveDown(); #endregion private async void treeView1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { TreeNode tn = treeView1.SelectedNode; if (tn == null) { return; } BookmarkTreeView.NodeType nodeType = treeView1.GetNodeType(); switch (e.KeyCode) { case Keys.Enter: await OpenBookmark(tn); break; case Keys.F2: editToolStripMenuItem2_Click(sender, null); break; case Keys.Insert: if (e.Modifiers != Keys.Shift) { if ((nodeType == BookmarkTreeView.NodeType.Root) || (nodeType == BookmarkTreeView.NodeType.Folder)) { // do nothing } else if (nodeType == BookmarkTreeView.NodeType.Page) { treeView1.SelectedNode = tn.Parent; } addPageToolStripMenuItem_Click(sender, null); } break; default: break; } } private async void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) => await OpenBookmark(e.Node); private void treeView1_OnNodeChanged(object sender, EventArgs e) { bool state = (treeView1.HasChanged && ApplicationMode == AppMode.Open); UIControl.SetEnable(saveToolStripMenuItem, state); UIControl.SetEnable(toolStripButton3, state); } protected string BuildOpenFileSupportedFiles(List supportList) { StringBuilder rs = new StringBuilder(); rs.Append("All supported files (*" + string.Join("; *", supportList) + ")"); rs.Append("|*" + string.Join(";*", supportList)); for (int i = 0; i < supportList.Count; i++) { rs.Append("|" + supportList[i].TrimStart('.')?.ToUpper() + " Bookmark files"); rs.Append("|*" + supportList[i]); } return rs.ToString(); } protected string BuildSaveFileSupportedFiles(List supportList) { StringBuilder rs = new StringBuilder(); for (int i = 0; i < supportList.Count; i++) { if (i > 0) rs.Append("|"); rs.Append(supportList[i].TrimStart('.')?.ToUpper() + " Bookmark file"); rs.Append("|*" + supportList[i]); } return rs.ToString(); } protected async Task CloseFile() { return await Task.Run(async () => { if (this.ApplicationMode == AppMode.Clear) { return Result.Create(true); } if (!treeView1.HasChanged) { return Result.Create(true); } if (this.ApplicationMode == AppMode.New) { DialogResult response = MessageBox.Show("Save bookmarks", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (response == DialogResult.Yes) { Result result = await SaveAsFile(sessionFilename); if (!result.IsSuccess) { return Result.Create(false, result.Message); } } else if (response == DialogResult.Cancel) { return Result.Create(false, ""); } } else if (this.ApplicationMode == AppMode.Open) { DialogResult response = MessageBox.Show("Save changes to open bookmarks", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (response == DialogResult.Yes) { Result result = await SaveFile(sessionFilename, sessionPassword); if (!result.IsSuccess) { return Result.Create(false, result.Message); } } else if (response == DialogResult.Cancel) { return Result.Create(false, ""); } } return Result.Create(true); }); } protected SupportedFileBase GetSupportedFileHandler(string filename) { SupportedFileBase rs = null; List typeList = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsSubclassOf(typeof(SupportedFileBase))).ToList(); foreach (Type t in typeList) { rs = (SupportedFileBase)Activator.CreateInstance(t); if (rs == null) { continue; } if (rs.IsSupported(filename)) { break; } rs = null; } return rs; } protected List GetSupportedFileTypes() { List rs = new List(); List typeList = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsSubclassOf(typeof(SupportedFileBase))).ToList(); foreach (Type t in typeList) { SupportedFileBase fileHandler = (SupportedFileBase)Activator.CreateInstance(t); if (fileHandler == null) { continue; } rs.AddRange(fileHandler.SupportedExtensions ?? new List()); } return rs; } protected void InvalidateAppSession() { if (CurrentSession == null) CurrentSession = new AppSession(); if (CurrentSession.EnableAutoPosition) { this.Height = Screen.PrimaryScreen.WorkingArea.Height; this.Location = Screen.PrimaryScreen.WorkingArea.Location; } toolStrip1.Visible = CurrentSession.ShowToolbar; this.TopMost = CurrentSession.AlwaysOnTop; } protected async Task LoadAppSession(string filename) { return await Task.Run(() => { if (File.Exists(filename)) { string sourceCode = null; try { sourceCode = File.ReadAllText(filename); } catch (Exception) { // do nothing } if (string.IsNullOrWhiteSpace(sourceCode)) { this.CurrentSession = new AppSession(); return false; } try { this.CurrentSession = JsonConvert.DeserializeObject(sourceCode); } catch (Exception) { this.CurrentSession = null; } if (this.CurrentSession == null) { this.CurrentSession = new AppSession(); return false; } } else { this.CurrentSession = new AppSession(); try { File.WriteAllText(filename, JsonConvert.SerializeObject(this.CurrentSession)); } catch (Exception) { // do nothing } } return true; }); } protected async Task LoadCommandLine(string[] args) { bool rs = false; if (args.Length > 1) { int i = 1; while (true) { if (i > (args.Length - 1)) { break; } switch (args[i].Trim().ToLower()) { case "-o": case "-open": if ((i + 1) > (args.Length - 1)) break; string openFilename = args[(i + 1)]; if (string.IsNullOrWhiteSpace(openFilename)) break; if (!File.Exists(openFilename)) break; await LoadFile(openFilename); //LoadBookmarksForm loadFileForm = new LoadBookmarksForm(this, openFilename); //loadFileForm.ShowDialog(); rs = true; i++; break; } i++; } } return rs; } protected async Task LoadFile(string filename) { return await Task.Run(() => { sessionFilename = null; sessionPassword = null; ApplicationMode = AppMode.Clear; treeView1.Clear(); if (string.IsNullOrWhiteSpace(filename)) { return Result.Create(false, "Filename is empty"); } if (!File.Exists(filename)) { return Result.Create(false, "File not found"); } sessionFilename = filename; SupportedFileBase fileHandler = GetSupportedFileHandler(sessionFilename); if (fileHandler == null) { return Result.Create(false, "Handler not found for this file type"); } if (fileHandler.IsEncrypted(sessionFilename)) { PasswordForm passwordForm = new PasswordForm(); if (passwordForm.ShowDialog() == DialogResult.OK) { sessionPassword = passwordForm.Password; } } Result result = fileHandler.Load(treeView1, sessionFilename, sessionPassword); if (!result.IsSuccess) { return result; } if (treeView1.Nodes.Count > 0) { UIControl.Expand(treeView1.Nodes[0]); } ApplicationMode = AppMode.Open; if (result.IsSuccess) { treeView1.SetNoChanges(); } UIControl.SetFocus(treeView1); return result; }); } protected async Task OpenBookmark(TreeNode node) { await Task.Run(() => { if (BookmarkTreeView.GetNodeType(node) != BookmarkTreeView.NodeType.Page) { return; } BookmarkItem viewModel = (BookmarkItem)node.Tag; if (viewModel == null) { return; } if (string.IsNullOrWhiteSpace(viewModel.SiteAddress)) { return; } string cmd = (string.IsNullOrWhiteSpace(CurrentSession.RunCommand) ? viewModel.SiteAddress : CurrentSession.RunCommand.Replace("{0}", viewModel.SiteAddress)); try { System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo { FileName = cmd, UseShellExecute = true }; System.Diagnostics.Process.Start(psi); } catch (Exception exc) { #if DEBUG MessageBox.Show(exc.Message); #endif } }); } protected async Task SaveFile(string filename, string password = null) { return await Task.Run(() => { if (string.IsNullOrWhiteSpace(filename)) { return Result.Create(false, "Filename is empty"); } SupportedFileBase fileHandler = GetSupportedFileHandler(filename); if (fileHandler == null) { return Result.Create(false, "Handler not found for this file type"); } Result result = fileHandler.Save(treeView1, filename, password); if (result.IsSuccess) { treeView1.SetNoChanges(); } return result; }); } protected async Task SaveAppSession(string filename) { return await Task.Run(() => { if (this.CurrentSession == null) this.CurrentSession = new AppSession(); try { File.WriteAllText(filename, JsonConvert.SerializeObject(this.CurrentSession)); } catch (Exception) { return false; } return true; }); } protected async Task SaveAsFile(string filename) { return await Task.Run(() => { if (string.IsNullOrWhiteSpace(filename)) { return Result.Create(false, "Filename is empty"); } SupportedFileBase fileHandler = GetSupportedFileHandler(filename); if (fileHandler == null) { return Result.Create(false, "Handler not found for this file type"); } string password = null; if (fileHandler.IsEncryptionSupported) { PasswordForm passwordForm = new PasswordForm(); if (passwordForm.ShowDialog() == DialogResult.OK) { if (!string.IsNullOrWhiteSpace(passwordForm.Password)) { password = passwordForm.Password; } } } Result result = fileHandler.Save(treeView1, filename, password); if (result.IsSuccess) { this.ApplicationMode = AppMode.Open; sessionFilename = filename; sessionPassword = password; treeView1.SetNoChanges(); } return result; }); } protected void UpdateBookmarkNode(bool deleteOnCancel) { BookmarkItem model = treeView1.GetNodeModel(); Image icon = null; // retrieve icon from DB string iconID = model?.ToHash(); icon = iconDatabase.FindIcon(iconID); BookmarkForm bookmarkForm = new BookmarkForm(this.CurrentSession, model, icon); if (bookmarkForm.ShowDialog() == DialogResult.OK) { //string iconID = bookmarkForm.Model.Item?.ToHash(); iconID = bookmarkForm.Model.Item?.ToHash(); if (!string.IsNullOrWhiteSpace(iconID)) { iconDatabase.AddIcon(iconID, bookmarkForm.Model.Icon); } treeView1.UpdateItem(treeView1.SelectedNode, bookmarkForm.Model.Item); } else { if (deleteOnCancel) { treeView1.SelectedNode.Remove(); } } } } }