// // SolutionExplorerPanel.cs // // Author: // Michael Becker // // Copyright (c) 2019 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System; using MBS.Framework; using MBS.Framework.UserInterface; using MBS.Framework.UserInterface.Controls; using MBS.Framework.UserInterface.Controls.ListView; using MBS.Framework.UserInterface.Dialogs; using MBS.Framework.UserInterface.Layouts; using UniversalEditor.ObjectModels.Project; using UniversalEditor.ObjectModels.Solution; namespace UniversalEditor.UserInterface.Panels { public class SolutionExplorerPanel : Panel, IDocumentPropertiesProviderControl { public static readonly Guid ID = new Guid("{1767c74b-b3ea-4919-a5cf-28433e4f6485}"); private DefaultTreeModel tmSolutionExplorer = null; private ListViewControl tvSolutionExplorer = new ListViewControl(); private SolutionObjectModel _Solution = null; public SolutionObjectModel Solution { get { return _Solution; } set { _Solution = value; UpdateSolutionExplorer(); } } public ProjectObjectModel Project { get { if (tvSolutionExplorer.SelectedRows.Count > 0) { TreeModelRow row = tvSolutionExplorer.SelectedRows[0]; while (row != null) { ProjectObjectModel proj = row.GetExtraData("project"); if (proj != null) return proj; row = row.ParentRow; } } else { if (Solution != null) { if (Solution.Projects.Count > 0) { return Solution.Projects[0]; } } } return null; } set { } } private TreeModelRow LoadSolutionExplorerFolder(ObjectModels.Project.ProjectFolder fold) { TreeModelRow rowFolder = new TreeModelRow(new TreeModelRowColumn[] { new TreeModelRowColumn(tmSolutionExplorer.Columns[0], fold.Name) }); rowFolder.SetExtraData("folder", fold); foreach (ObjectModels.Project.ProjectFolder fold2 in fold.Folders) { TreeModelRow row = LoadSolutionExplorerFolder(fold2); rowFolder.Rows.Add(row); } foreach (ObjectModels.Project.ProjectFile file in fold.Files) { if (file.Dependents.Count > 0) continue; TreeModelRow rowFile = LoadSolutionExplorerFile(file); rowFolder.Rows.Add(rowFile); } return rowFolder; } private TreeModelRow LoadSolutionExplorerFile(ProjectFile file) { TreeModelRow rowFile = new TreeModelRow(new TreeModelRowColumn[] { new TreeModelRowColumn(tmSolutionExplorer.Columns[0], file.DestinationFileName) }); rowFile.SetExtraData("file", file); foreach (ProjectFile file2 in file.Files) { TreeModelRow rowFile2 = LoadSolutionExplorerFile(file2); rowFile2.SetExtraData("file", file2); rowFile.Rows.Add(rowFile2); } return rowFile; } private void UpdateSolutionExplorer() { tmSolutionExplorer.Rows.Clear(); if (_Solution != null) { TreeModelRow rowSolution = new TreeModelRow(new TreeModelRowColumn[] { new TreeModelRowColumn(tmSolutionExplorer.Columns[0], _Solution.Title) }); rowSolution.SetExtraData("solution", _Solution); foreach (ObjectModels.Project.ProjectObjectModel proj in _Solution.Projects) { TreeModelRow rowProject = new TreeModelRow(new TreeModelRowColumn[] { new TreeModelRowColumn(tmSolutionExplorer.Columns[0], proj.Title) }); rowProject.SetExtraData("project", proj); TreeModelRow rowReferences = new TreeModelRow(new TreeModelRowColumn[] { new TreeModelRowColumn(tmSolutionExplorer.Columns[0], "References") }); foreach (ObjectModels.Project.Reference reff in proj.References) { rowReferences.Rows.Add(new TreeModelRow(new TreeModelRowColumn[] { new TreeModelRowColumn(tmSolutionExplorer.Columns[0], reff.Title) })); } rowProject.Rows.Add(rowReferences); foreach (ObjectModels.Project.ProjectFolder fold in proj.FileSystem.Folders) { TreeModelRow row = LoadSolutionExplorerFolder(fold); rowProject.Rows.Add(row); } foreach (ObjectModels.Project.ProjectFile file in proj.FileSystem.Files) { TreeModelRow row = new TreeModelRow(new TreeModelRowColumn[] { new TreeModelRowColumn(tmSolutionExplorer.Columns[0], file.DestinationFileName) }); row.SetExtraData("file", file); rowProject.Rows.Add(row); } rowSolution.Rows.Add(rowProject); } tmSolutionExplorer.Rows.Add(rowSolution); } } public SolutionExplorerPanel() { this.Layout = new BoxLayout(Orientation.Vertical); tmSolutionExplorer = new DefaultTreeModel(new Type[] { typeof(string) }); tvSolutionExplorer.Model = tmSolutionExplorer; tvSolutionExplorer.BeforeContextMenu += tvSolutionExplorer_BeforeContextMenu; tvSolutionExplorer.SelectionChanged += tvSolutionExplorer_SelectionChanged; tvSolutionExplorer.RowActivated += tvSolutionExplorer_RowActivated; // (UniversalEditor.exe:24867): Gtk-CRITICAL **: 21:28:56.913: gtk_tree_store_set_value: assertion 'G_IS_VALUE (value)' failed tvSolutionExplorer.Columns.Add(new ListViewColumn("File name", new CellRenderer[] { new CellRendererText(tmSolutionExplorer.Columns[0]) })); this.Controls.Add(tvSolutionExplorer, new BoxLayout.Constraints(true, true)); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_OpenContainingFolder", delegate(object sender, EventArgs e) { if (tvSolutionExplorer.SelectedRows.Count != 1) return; ProjectObjectModel project = tvSolutionExplorer.SelectedRows[0].GetExtraData("project"); ProjectFile file = tvSolutionExplorer.SelectedRows[0].GetExtraData("file"); ProjectFolder folder = tvSolutionExplorer.SelectedRows[0].GetExtraData("folder"); TreeModelRow prow = tvSolutionExplorer.SelectedRows[0].ParentRow; while (project == null && prow != null) { project = prow.GetExtraData("project"); prow = prow.ParentRow; } if (project != null) { if (project.BasePath != null) { string fullpath = System.IO.Path.Combine(project.BasePath, tvSolutionExplorer.SelectedRows[0].RowColumns[0].Value.ToString()); if (file == null && folder == null) { fullpath = fullpath + ".ueproj"; } // not sure if this should be made into a UWT convenience function or not... try { if (Environment.OSVersion.Platform == PlatformID.Unix) { ((UIApplication)Application.Instance).Launch("nautilus", String.Format("-s \"{0}\"", fullpath)); } else if (Environment.OSVersion.Platform == PlatformID.Win32Windows) { ((UIApplication)Application.Instance).Launch("explorer", String.Format("/select \"{0}\"", fullpath)); } else { ((UIApplication)Application.Instance).Launch(System.IO.Path.GetDirectoryName(fullpath)); } } catch (Exception ex) { // not using nautilus, just launch the folder ((UIApplication)Application.Instance).Launch(System.IO.Path.GetDirectoryName(fullpath)); } } } }); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Project_Add_ExistingFiles", mnuContextProjectAddExistingFiles_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Project_Add_NewFolder", mnuContextProjectAddNewFolder_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Solution_Add_ExistingFiles", mnuContextSolutionAddExistingProject_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Solution_Add_ExistingProject", mnuContextSolutionAddExistingProject_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Solution_Add_NewProject", mnuContextSolutionAddNewProject_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_File_Open", mnuContextFileOpen_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Folder_Add_NewFile", mnuContextFolderAddNewFile_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Folder_Add_ExistingFiles", mnuContextFolderAddExistingFile_Click); Application.Instance.AttachCommandEventHandler("SolutionExplorer_ContextMenu_Folder_Add_NewFolder", mnuContextFolderAddNewFolder_Click); } private void tvSolutionExplorer_SelectionChanged(object sender, EventArgs e) { bool hasSettingsProvider = false; if (tvSolutionExplorer.LastHitTest.Row != null) { ProjectObjectModel project = tvSolutionExplorer.LastHitTest.Row.GetExtraData("project"); SolutionObjectModel solution = tvSolutionExplorer.LastHitTest.Row.GetExtraData("solution"); ProjectFolder folder = tvSolutionExplorer.LastHitTest.Row.GetExtraData("folder"); ProjectFile file = tvSolutionExplorer.LastHitTest.Row.GetExtraData("file"); if (project != null) { for (int i = 0; i < project.ProjectTypes.Count; i++) { if (project.ProjectTypes[i].SettingsProvider != null) { hasSettingsProvider = true; break; } } } } Application.Instance.FindCommand("FileProperties").Enabled = hasSettingsProvider; } private void mnuContextFileOpen_Click(object sender, EventArgs e) { } private void mnuContextFolderAddNewFile_Click(object sender, EventArgs e) { } private void mnuContextFolderAddExistingFile_Click(object sender, EventArgs e) { TreeModelRow row = tvSolutionExplorer.LastHitTest.Row; if (row == null) return; ProjectFolder fldr = row.GetExtraData("folder"); if (fldr == null) return; FileDialog dlg = new FileDialog(); dlg.Mode = FileDialogMode.Open; dlg.MultiSelect = true; if (dlg.ShowDialog() == DialogResult.OK) { foreach (string filename in dlg.SelectedFileNames) { ObjectModels.Project.ProjectFile pf = new ObjectModels.Project.ProjectFile(); pf.SourceFileName = filename; pf.DestinationFileName = System.IO.Path.GetFileName(filename); fldr.Files.Add(pf); } UpdateSolutionExplorer(); } } void tvSolutionExplorer_RowActivated(object sender, ListViewRowActivatedEventArgs e) { // TODO: implement what happens when we activate a row in the solution explorer list // typically: // - if it is a ProjectFile we want to open the respective file in a new document tab - DONE // - if it is a ProjectFolder we just want to expand/collapse tree (default action) - DONE // - if it is a special folder named "Properties", or the project item itself, it should display the special project properties dialog // - if it is a special folder named "Properties/Resources", the resource editor shall be shown // these last two conditions can be simplified by simply having the Properties folder open the project file in an editor, and implementing // an editor for ProjectObjectModel which is essentially the "project properties" window (a la VS) ProjectObjectModel project = e.Row.GetExtraData("project"); ProjectFile file = e.Row.GetExtraData("file"); ProjectFolder folder = e.Row.GetExtraData("folder"); if (project != null) { Accessors.MemoryAccessor ma = new Accessors.MemoryAccessor(new byte[0], String.Format("{0} Properties", project.Title)); Document d = new Document(project, null, ma); d.Title = String.Format("{0} Properties", project.Title); ((Application.Instance as UIApplication).CurrentWindow as IHostApplicationWindow).OpenFile(d); } else if (file != null) { if (file.SourceFileAccessor != null) { ((EditorApplication)Application.Instance).LastWindow.OpenFile(new Document(file.SourceFileAccessor)); } else { ((EditorApplication)Application.Instance).LastWindow.OpenFile(file.SourceFileName); } } else if (folder != null) { return; } else { MessageDialog.ShowDialog(String.Format("Activated row {0}", e.Row.RowColumns[0].Value), "Info", MessageDialogButtons.OK); } } private void mnuContextProjectAddNewFolder_Click(object sender, EventArgs e) { TreeModelRow row = tvSolutionExplorer.LastHitTest.Row; if (row == null) return; ProjectObjectModel proj = row.GetExtraData("project"); if (proj == null) return; string folderName = "New folder"; ProjectFolder folder = proj.FileSystem.Folders.Add(folderName); TreeModelRow rowFolder = new TreeModelRow(); rowFolder.RowColumns.Add(new TreeModelRowColumn(tmSolutionExplorer.Columns[0], folderName)); rowFolder.SetExtraData("folder", folder); row.Rows.Add(rowFolder); UpdateSolutionExplorer(); tvSolutionExplorer.Focus(rowFolder, null, null); } private void mnuContextFolderAddNewFolder_Click(object sender, EventArgs e) { TreeModelRow row = tvSolutionExplorer.LastHitTest.Row; if (row == null) return; ProjectFolder proj = row.GetExtraData("folder"); if (proj == null) return; string folderName = "New folder"; ProjectFolder folder = proj.Folders.Add(folderName); TreeModelRow rowFolder = new TreeModelRow(); rowFolder.RowColumns.Add(new TreeModelRowColumn(tmSolutionExplorer.Columns[0], folderName)); rowFolder.SetExtraData("folder", folder); row.Rows.Add(rowFolder); UpdateSolutionExplorer(); } private void mnuContextProjectAddExistingFiles_Click(object sender, EventArgs e) { TreeModelRow row = tvSolutionExplorer.LastHitTest.Row; if (row == null) return; ObjectModels.Project.ProjectObjectModel proj = row.GetExtraData("project"); if (proj == null) return; FileDialog dlg = new FileDialog(); dlg.Mode = FileDialogMode.Open; dlg.MultiSelect = true; if (dlg.ShowDialog() == DialogResult.OK) { foreach (string filename in dlg.SelectedFileNames) { ProjectFile pf = new ProjectFile(); pf.SourceFileName = filename; pf.DestinationFileName = System.IO.Path.GetFileName(filename); proj.FileSystem.Files.Add(pf); } UpdateSolutionExplorer(); } } private void mnuContextSolutionAddNewProject_Click(object sender, EventArgs e) { EditorWindow mw = (((EditorApplication)Application.Instance).LastWindow as EditorWindow); if (mw == null) return; mw.NewProject(true); } private void mnuContextSolutionAddExistingProject_Click(object sender, EventArgs e) { EditorWindow mw = (((EditorApplication)Application.Instance).LastWindow as EditorWindow); if (mw == null) return; ProjectObjectModel proj = mw.ShowOpenProjectDialog(); if (proj == null) return; _Solution.Projects.Add(proj); Solution = _Solution; // update the UI } private void tvSolutionExplorer_BeforeContextMenu(object sender, EventArgs e) { if (tvSolutionExplorer.LastHitTest.Row != null) { ProjectObjectModel project = tvSolutionExplorer.LastHitTest.Row.GetExtraData("project"); SolutionObjectModel solution = tvSolutionExplorer.LastHitTest.Row.GetExtraData("solution"); ProjectFolder folder = tvSolutionExplorer.LastHitTest.Row.GetExtraData("folder"); ProjectFile file = tvSolutionExplorer.LastHitTest.Row.GetExtraData("file"); if (project != null) { tvSolutionExplorer.ContextMenuCommandID = "SolutionExplorer_ContextMenu_Project"; } else if (solution != null) { tvSolutionExplorer.ContextMenuCommandID = "SolutionExplorer_ContextMenu_Solution"; } else if (folder != null) { tvSolutionExplorer.ContextMenuCommandID = "SolutionExplorer_ContextMenu_Folder"; } else if (file != null) { tvSolutionExplorer.ContextMenuCommandID = "SolutionExplorer_ContextMenu_File"; } } } /// /// Deletes the selected item in the Solution Explorer panel. /// public void Delete() { if (tvSolutionExplorer.SelectedRows.Count > 0) { /* // does not work - we need to fix this in UWT while (tvSolutionExplorer.SelectedRows.Count > 0) { tmSolutionExplorer.Rows.Remove(tvSolutionExplorer.SelectedRows[0]); } */ ProjectFolder folder = tvSolutionExplorer.SelectedRows[0].GetExtraData("folder"); ProjectFile file = tvSolutionExplorer.SelectedRows[0].GetExtraData("file"); if (file != null) { file.Parent.Files.Remove(file); } if (folder != null) { folder.Parent.Folders.Remove(folder); } UpdateSolutionExplorer(); } } void IDocumentPropertiesProvider.ShowDocumentPropertiesDialog() { if (tvSolutionExplorer.SelectedRows.Count == 1) { TreeModelRow row = tvSolutionExplorer.SelectedRows[0]; ProjectObjectModel project = row.GetExtraData("project"); ProjectFile file = row.GetExtraData("file"); ProjectFolder folder = row.GetExtraData("folder"); if (project != null) { ((EditorApplication)Application.Instance).ShowProjectSettings(project); return; } if (file != null) { } } } } }