using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.IO.Compression; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using BookmarkManager; using BookmarkManager.Models; using bzit.bomg.Models; using FizzyLauncher.Models; using RyzStudio; using RyzStudio.Windows.Forms; using RyzStudio.Windows.ThemedForms; using static RyzStudio.Windows.Forms.BookmarkTreeView; namespace FizzyLauncher { public partial class MainForm : Form { private readonly FileSessionManager _fileSessionManager; private bool _isBusy = false; public MainForm() { InitializeComponent(); this.Text = Application.ProductName; newToolStripMenuItem.Image = RyzStudio.Windows.ThemedForms.DefaultVisualStyle.GetImage("a", Color.Black, 2); openToolStripMenuItem.Image = RyzStudio.Windows.ThemedForms.DefaultVisualStyle.GetImage("b", Color.Black, 2); saveToolStripMenuItem.Image = RyzStudio.Windows.ThemedForms.DefaultVisualStyle.GetImage("c", Color.Black, 2); findToolStripMenuItem.Image = RyzStudio.Windows.ThemedForms.DefaultVisualStyle.GetImage("d", Color.Black, 2); optionsToolStripMenuItem.Image = RyzStudio.Windows.ThemedForms.DefaultVisualStyle.GetImage("i", Color.Black, 2); viewHelpToolStripMenuItem1.Image = RyzStudio.Windows.ThemedForms.DefaultVisualStyle.GetImage("j", Color.Black, 2); //aboutToolStripMenuItem1.Image = RyzStudio.Windows.ThemedForms.DefaultVisualStyle.GetImage("k", Color.Black, 2); _fileSessionManager = new FileSessionManager(); _fileSessionManager.OpenFileDialog = openFileDialog1; _fileSessionManager.SaveFileDialog = saveFileDialog1; _fileSessionManager.OnNewing += fileSessionManager_OnNewSession; _fileSessionManager.OnLoading += fileSessionManager_OnLoadSession; _fileSessionManager.OnSaving += fileSessionManager_OnSaveSession; _fileSessionManager.OnClearing += fileSessionManager_OnClearSession; _fileSessionManager.OnFilenameChanged += fileSessionManager_OnFilenameChanged; this.AutoScaleMode = AutoScaleMode.None; this.StartPosition = FormStartPosition.WindowsDefaultLocation; treeView1.RootContextMenu = rootContextMenu; treeView1.FolderContextMenu = folderContextMenu; treeView1.PageContextMenu = pageContextMenu; treeView1.NodeMouseDoubleClick += treeView1_NodeMouseDoubleClick; treeView1.PreviewKeyDown += treeView1_PreviewKeyDown; } protected async override void OnShown(EventArgs e) { base.OnShown(e); var args = WinApplication.GetCommandLine(); string jsonfigFilename = args.Where(x => (x.Key.Equals("o") || x.Key.Equals("open"))).Select(x => x.Value).FirstOrDefault(); //if (string.IsNullOrWhiteSpace(jsonfigFilename)) //{ // jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig"); //} if (!string.IsNullOrWhiteSpace(jsonfigFilename) && File.Exists(jsonfigFilename)) { await _fileSessionManager.OpenSession(jsonfigFilename); } else { this.CurrentSession = new AppOptions(); InvalidateOptions(); } } protected async override void OnClosing(CancelEventArgs e) { base.OnClosing(e); await _fileSessionManager.CloseSession(); } public AppOptions CurrentSession { get; set; } = null; public bool IsBusy { get => _isBusy; set { treeView1.Enabled = !value; } } private void InvalidateOptions() { UIControl.SetTopMost(this, this.CurrentSession.AlwaysOnTop); } private void menuStrip1_MenuActivate(object sender, EventArgs e) { closeToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close); saveToolStripMenuItem.Enabled = (_fileSessionManager.SessionState == FileSessionManager.SessionStateEnum.Open) && treeView1.HasChanged; saveAsToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close); findToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close); collapseAllToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close); expandAllToolStripMenuItem.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close); alwaysOnTopToolStripMenuItem.Checked = this.CurrentSession?.AlwaysOnTop ?? false; toolStripMenuItem9.Enabled = (_fileSessionManager.SessionState != FileSessionManager.SessionStateEnum.Close); } #region Main Menu /// /// New /// /// /// private async void newToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } await _fileSessionManager.NewSession(); } /// /// Open file /// /// /// private async void openToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } await _fileSessionManager.OpenSession(); } /// /// Close /// /// /// private async void closeToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } await _fileSessionManager.CloseSession(); } /// /// Save /// /// /// private async void saveToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } await _fileSessionManager.SaveSession(); } /// /// Save As /// /// /// private async void saveAsToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } await _fileSessionManager.SaveAsSession(); } /// /// 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; } var form = new FindForm(treeView1); form.ShowDialog(); } /// /// Expand all /// /// /// private void expandAllToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (_fileSessionManager.SessionState == FileSessionManager.SessionStateEnum.Close) { 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 (_fileSessionManager.SessionState == FileSessionManager.SessionStateEnum.Close) { return; } if (treeView1.SelectedNode == null) { treeView1.CollapseAll(); } else { treeView1.SelectedNode.Collapse(false); } } /// /// Always on top /// /// /// private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e) { if (this.CurrentSession == null) { return; } this.CurrentSession.AlwaysOnTop = !this.CurrentSession.AlwaysOnTop; this.TopMost = this.CurrentSession.AlwaysOnTop; } /// /// Update icons /// /// /// private void toolStripMenuItem9_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } var form = new UpdateIconsForm(treeView1); form.ShowDialog(); } /// /// Options /// /// /// private void optionsToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } var form = new OptionsForm(this.CurrentSession); if (form.ShowDialog() == DialogResult.OK) { this.CurrentSession = form.Result; InvalidateOptions(); } } /// /// View help /// /// /// private void viewHelpToolStripMenuItem1_Click(object sender, EventArgs e) { RyzStudio.Diagnostics.Process.Execute(AppResource.AppHelpURL); } /// /// About /// /// /// private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) { var form = new RyzStudio.Windows.ThemedForms.AboutForm(); form.ProductURL = AppResource.AppProductURL; form.AuthorURL = AppResource.AppAuthorURL; form.CompanyURL = AppResource.AppCompanyURL; form.ProductCopyrightStartYear = 2012; form.ProductLogo = AppResource.icon_64; form.ShowDialog(); } #endregion #region Context Menu - Root /// /// Add page /// /// /// private void addPageToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } var nodeType = treeView1.GetNodeType(); if ((nodeType == BookmarkTreeView.NodeType.Root) || (nodeType == BookmarkTreeView.NodeType.Folder)) { treeView1.AddNode(); } } /// /// Add folder /// /// /// private void addFolderToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } var nodeType = treeView1.GetNodeType(); if ((nodeType == BookmarkTreeView.NodeType.Root) || (nodeType == BookmarkTreeView.NodeType.Folder)) { treeView1.AddFolder(); } } /// /// Edit root node /// /// /// private void editToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } treeView1.EditNode(); } /// /// Sort /// /// /// private void sortToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } treeView1.Sort(); } #endregion #region Context Menu - Folder /// /// Add page /// /// /// private void addPageToolStripMenuItem1_Click(object sender, EventArgs e) { addPageToolStripMenuItem_Click(sender, e); } /// /// Add folder /// /// /// private void addFolderToolStripMenuItem1_Click(object sender, EventArgs e) { addFolderToolStripMenuItem_Click(sender, e); } /// /// 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) { editToolStripMenuItem_Click(sender, e); } /// /// Delete folder /// /// /// private void deleteToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } if (MessageBox.Show("Delete?", "Delete?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes) { treeView1.DeleteNode(); } } /// /// Sort children /// /// /// private void sortToolStripMenuItem1_Click(object sender, EventArgs e) { sortToolStripMenuItem_Click(sender, e); } /// /// Move up /// /// /// private void moveUpToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } treeView1.MoveUp(); } /// /// Move down /// /// /// private void moveDownToolStripMenuItem_Click(object sender, EventArgs e) { if (this.IsBusy) { return; } treeView1.MoveDown(); } #endregion #region Context Menu - Bookmark /// /// 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) { editToolStripMenuItem_Click(sender, e); } /// /// Delete page /// /// /// private void deleteToolStripMenuItem1_Click(object sender, EventArgs e) { deleteToolStripMenuItem_Click(sender, e); } /// /// Move up /// /// /// private void moveUpToolStripMenuItem1_Click(object sender, EventArgs e) { moveUpToolStripMenuItem_Click(sender, e); } /// /// Move down /// /// /// private void moveDownToolStripMenuItem1_Click(object sender, EventArgs e) { moveDownToolStripMenuItem_Click(sender, e); } #endregion #region File Session Manager private async Task fileSessionManager_OnNewSession(FileSessionManager sender) { return await Task.Run(() => { treeView1.Clear("New Session"); if (treeView1.Nodes.Count >= 0) { UIControl.Invoke(treeView1, (x) => { treeView1.Nodes[0].Expand(); treeView1.SelectedNode = treeView1.Nodes[0]; }); } UIControl.SetFocus(treeView1); return true; }); } private async Task fileSessionManager_OnLoadSession(FileSessionManager sender, string filename, int formatType) { return await Task.Run(async () => { var result = GenericResult.Create(); switch (Path.GetExtension(filename?.ToLower()?.Trim() ?? string.Empty)) { case ".json": this.CurrentSession = RyzStudio.Text.Json.JsonSerialiser.DeserialiseFile(filename); if (this.CurrentSession == null) { this.CurrentSession = LoadR4SaveFile(filename); } break; case ".jsnx": this.CurrentSession = await RyzStudio.IO.Compression.ZFile.ReadFile(filename, "Document.json"); if (this.CurrentSession == null) { this.CurrentSession = new AppOptions(); } // Load icons this.CurrentSession = await LoadIconsFromZipFile(filename, this.CurrentSession); break; default: this.CurrentSession = null; break; } if (this.CurrentSession == null) { ThMessageBox.Show(this, "Unable to read session", "Load session", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } if (this.CurrentSession == null) { this.CurrentSession = new AppOptions(); } // Reposition + resize if (!this.CurrentSession.StartPosition.IsEmpty) { UIControl.SetLocation(this, this.CurrentSession.StartPosition); } if (this.CurrentSession.Height > 0) { UIControl.SetHeight(this, this.CurrentSession.Height); } if (this.CurrentSession.Width > 0) { UIControl.SetWidth(this, this.CurrentSession.Width); } InvalidateOptions(); // Load items treeView1.Clear("New Session"); foreach (var item in this.CurrentSession.Items ?? new List()) { treeView1.AddNode(item); } if (treeView1.Nodes.Count >= 0) { UIControl.Invoke(treeView1, (x) => { treeView1.Nodes[0].Expand(); treeView1.SelectedNode = treeView1.Nodes[0]; }); } UIControl.SetFocus(treeView1); return true; }); } private async Task fileSessionManager_OnSaveSession(FileSessionManager sender, string filename, int formatType, bool showNotices) { if (string.IsNullOrWhiteSpace(filename)) { return false; } return await Task.Run(async () => { if (_isBusy) { return false; } _isBusy = true; // update session if (this.CurrentSession == null) { this.CurrentSession = new AppOptions(); } this.CurrentSession.StartPosition = this.Location; this.CurrentSession.Width = this.Width; this.CurrentSession.Height = this.Height; var nodeList = treeView1.ToNodeList() ?? new List>(); foreach (var node in nodeList) { node.Value.Path = treeView1.GetNodePath(node.Key); if (node.Value.Path.Contains('\n')) { node.Value.Path = node.Value.Path.Substring(0, node.Value.Path.LastIndexOf('\n')); } } this.CurrentSession.Items = nodeList.Select(x => x.Value).ToList(); var result = GenericResult.Create(); switch (Path.GetExtension(filename?.ToLower()?.Trim() ?? string.Empty)) { case ".json": case ".jsonfig": result = RyzStudio.Text.Json.JsonSerialiser.SerialiseFile(filename, this.CurrentSession); break; case ".jsnx": result = await RyzStudio.IO.Compression.ZFile.WriteFile(filename, "Document.json", this.CurrentSession); if (result.IsSuccess) { // Add icons to save file var result2 = AddImagesToZipFile(filename, nodeList); if (!result2.IsSuccess) { if (showNotices) { ThMessageBox.Show(this, "Unable to save icons", "Save session", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } break; default: result = GenericResult.Fault("Format not supported"); break; } if (result.IsSuccess) { if (showNotices) { ThMessageBox.Show(this, "Session saved!", "Save session", MessageBoxButtons.OK, MessageBoxIcon.Information); } } else { if (showNotices) { ThMessageBox.Show(this, result.Message, "Save session"); } } _isBusy = false; return result.IsSuccess; }); } private async Task fileSessionManager_OnClearSession(FileSessionManager sender) { return await Task.Run(() => { UIControl.Clear(treeView1); return true; }); } private async Task fileSessionManager_OnFilenameChanged(FileSessionManager sender, string filename) { await Task.Run(() => { switch (sender.SessionState) { case FileSessionManager.SessionStateEnum.New: UIControl.SetText(this, "New Session - " + Application.ProductName); break; case FileSessionManager.SessionStateEnum.Open: UIControl.SetText(this, Path.GetFileNameWithoutExtension(filename) + " - " + Application.ProductName); break; case FileSessionManager.SessionStateEnum.Close: UIControl.SetText(this, Application.ProductName); break; default: break; } treeView1.HasChanged = false; }); } #endregion private async void treeView1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { TreeNode node = treeView1.SelectedNode; if (node == null) { return; } BookmarkTreeView.NodeType nodeType = treeView1.GetNodeType(); switch (e.KeyCode) { case Keys.Enter: await OpenBookmark(node); break; default: break; } } private async void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) { await OpenBookmark(e.Node); } private AppOptions LoadR4SaveFile(string filename) { var session = RyzStudio.Text.Json.JsonSerialiser.DeserialiseFile>(filename); if (session == null) { return null; } var result = new AppOptions(); result.Items = new List(); foreach (var item in session) { result.Items.Add(new BookmarkModel() { Title = item.SiteName, Address = item.SiteAddress, Description = item.SiteDescription, Notes = item.Notes, Path = item.Path }); } return result; } private async Task LoadIconsFromZipFile(string filename, AppOptions session) { if (string.IsNullOrWhiteSpace(filename)) { return session; } return await Task.Run(() => { try { using (var archive = ZipFile.Open(filename, ZipArchiveMode.Read)) { foreach (var item in session.Items) { if (item.Id == Guid.Empty) { continue; } var key = "icon\\" + item.Id.ToString() + ".png"; var zipEntry = archive.GetEntry(key); if (zipEntry == null) { continue; } using (Stream entryStream = zipEntry.Open()) { item.Icon = Image.FromStream(entryStream); } } } } catch (Exception) { // do nothing } return session; }); } private async Task OpenBookmark(TreeNode node) { await Task.Run(() => { if (treeView1.GetNodeType(node) != BookmarkTreeView.NodeType.Page) { return; } var model = UIControl.GetTag(node); if (model == null) { return; } if (string.IsNullOrWhiteSpace(model.Address)) { return; } string cmd = (string.IsNullOrWhiteSpace(CurrentSession.RunCommand) ? model.Address : CurrentSession.RunCommand.Replace("{0}", model.Address)); RyzStudio.Diagnostics.Process.Execute(cmd); }); } private GenericResult AddImagesToZipFile(string zipFilename, List> items) { if (string.IsNullOrWhiteSpace(zipFilename)) { return GenericResult.Fault("Filename is blank"); } var path = Path.GetDirectoryName(zipFilename); if (!System.IO.Directory.Exists(path)) { try { System.IO.Directory.CreateDirectory(path); } catch (Exception exc) { return GenericResult.Fault(exc.Message); } } var options = RyzStudio.Text.Json.JsonSerialiser.GetPreferredOptions(); try { using (var archive = ZipFile.Open(zipFilename, ZipArchiveMode.Update)) { foreach (var item in items) { var key = "icon\\" + item.Value.Id.ToString() + ".png"; var zipEntry = archive.GetEntry(key); if (zipEntry != null) { zipEntry.Delete(); } if (item.Key.ImageIndex == (int)NodeIcon.Default) { continue; } if (!treeView1.ImageList.Images.ContainsKey(item.Value.Id.ToString())) { continue; } var icon = treeView1.ImageList.Images[item.Value.Id.ToString()]; if (icon == null) { continue; } try { if (icon.Width <= 0) { continue; } } catch (Exception) { continue; } zipEntry = archive.CreateEntry(key, CompressionLevel.SmallestSize); try { using (Stream entryStream = zipEntry.Open()) { using (Image image = icon) { image.Save(entryStream, ImageFormat.Png); } } } catch (Exception) { zipEntry.Delete(); continue; } } } } catch (Exception exc) { return GenericResult.Fault(exc.Message); } return GenericResult.Create(); } } }