1 module dlangide.ui.wspanel;
2 
3 import dlangui;
4 import dlangide.workspace.workspace;
5 import dlangide.workspace.project;
6 import dlangide.ui.commands;
7 
8 enum ProjectItemType : int {
9     None,
10     SourceFile,
11     SourceFolder,
12     Project,
13     Workspace
14 }
15 
16 interface SourceFileSelectionHandler {
17     bool onSourceFileSelected(ProjectSourceFile file, bool activate);
18 }
19 
20 interface WorkspaceActionHandler {
21     bool onWorkspaceAction(const Action a);
22 }
23 
24 class WorkspacePanel : DockWindow {
25     protected Workspace _workspace;
26     protected TreeWidget _tree;
27 
28     /// handle source file selection change
29     Signal!SourceFileSelectionHandler sourceFileSelectionListener;
30     Signal!WorkspaceActionHandler workspaceActionListener;
31 
32     this(string id) {
33         super(id);
34         workspace = null;
35         //layoutWidth = 200;
36         _caption.text = "Workspace Explorer"d;
37     }
38 
39     bool selectItem(ProjectItem projectItem) {
40         if (projectItem) {
41             TreeItem item = _tree.findItemById(projectItem.filename);
42             if (item) {
43                 _tree.selectItem(item);
44                 return true;
45             }
46         } else {
47             _tree.clearSelection();
48             return true;
49         }
50         return false;
51     }
52 
53     void onTreeItemSelected(TreeItems source, TreeItem selectedItem, bool activated) {
54         if (!selectedItem)
55             return;
56         if (selectedItem.intParam == ProjectItemType.SourceFile) {
57             // file selected
58             if (sourceFileSelectionListener.assigned) {
59                 ProjectSourceFile sourceFile = cast(ProjectSourceFile)selectedItem.objectParam;
60                 if (sourceFile) {
61                     sourceFileSelectionListener(sourceFile, activated);
62                 }
63             }
64         } else if (selectedItem.intParam == ProjectItemType.SourceFolder) {
65             // folder selected
66         }
67     }
68 
69     override protected Widget createBodyWidget() {
70         _tree = new TreeWidget("wstree");
71         _tree.layoutHeight(FILL_PARENT).layoutHeight(FILL_PARENT);
72         _tree.selectionChange = &onTreeItemSelected;
73         _tree.fontSize = 16;
74         _tree.noCollapseForSingleTopLevelItem = true;
75         _tree.popupMenu = &onTreeItemPopupMenu;
76 
77         _workspacePopupMenu = new MenuItem();
78         _workspacePopupMenu.add(ACTION_PROJECT_FOLDER_REFRESH, 
79                                 ACTION_FILE_WORKSPACE_CLOSE);
80 
81         _projectPopupMenu = new MenuItem();
82         _projectPopupMenu.add(ACTION_PROJECT_SET_STARTUP,
83                               ACTION_PROJECT_FOLDER_REFRESH,
84                               ACTION_FILE_NEW_SOURCE_FILE,
85                               //ACTION_PROJECT_FOLDER_OPEN_ITEM,
86                               ACTION_PROJECT_BUILD,
87                               ACTION_PROJECT_REBUILD,
88                               ACTION_PROJECT_CLEAN,
89                               ACTION_PROJECT_UPDATE_DEPENDENCIES,
90                               ACTION_PROJECT_REVEAL_IN_EXPLORER,
91                               ACTION_PROJECT_SETTINGS,
92                               //ACTION_PROJECT_FOLDER_REMOVE_ITEM
93                               );
94 
95         _folderPopupMenu = new MenuItem();
96         _folderPopupMenu.add(ACTION_FILE_NEW_SOURCE_FILE, ACTION_PROJECT_FOLDER_REFRESH, ACTION_PROJECT_FOLDER_OPEN_ITEM, 
97                              //ACTION_PROJECT_FOLDER_REMOVE_ITEM, 
98                              //ACTION_PROJECT_FOLDER_RENAME_ITEM
99                              );
100 
101         _filePopupMenu = new MenuItem();
102         _filePopupMenu.add(ACTION_FILE_NEW_SOURCE_FILE, ACTION_PROJECT_FOLDER_REFRESH, 
103                            ACTION_PROJECT_FOLDER_OPEN_ITEM, 
104                            ACTION_PROJECT_FOLDER_REMOVE_ITEM, 
105                            //ACTION_PROJECT_FOLDER_RENAME_ITEM
106                            );
107         return _tree;
108     }
109 
110     protected MenuItem _workspacePopupMenu;
111     protected MenuItem _projectPopupMenu;
112     protected MenuItem _folderPopupMenu;
113     protected MenuItem _filePopupMenu;
114     protected string _popupMenuSelectedItemId;
115     protected void onPopupMenuItem(MenuItem item) {
116         if (item.action)
117             handleAction(item.action);
118     }
119 
120     protected MenuItem onTreeItemPopupMenu(TreeItems source, TreeItem selectedItem) {
121         MenuItem menu = null;
122         _popupMenuSelectedItemId = selectedItem.id;
123         if (selectedItem.intParam == ProjectItemType.SourceFolder) {
124             menu = _folderPopupMenu;
125         } else if (selectedItem.intParam == ProjectItemType.SourceFile) {
126             menu = _filePopupMenu;
127         } else if (selectedItem.intParam == ProjectItemType.Project) {
128             menu = _projectPopupMenu;
129         } else if (selectedItem.intParam == ProjectItemType.Workspace) {
130             menu = _workspacePopupMenu;
131         }
132         if (menu && menu.subitemCount) {
133             for (int i = 0; i < menu.subitemCount; i++) {
134                 Action a = menu.subitem(i).action.clone();
135                 a.objectParam = selectedItem.objectParam;
136                 menu.subitem(i).action = a;
137                 //menu.subitem(i).menuItemAction = &handleAction;
138             }
139             //menu.onMenuItem = &onPopupMenuItem;
140             //menu.menuItemClick = &onPopupMenuItem;
141             menu.menuItemAction = &handleAction;
142             menu.updateActionState(this);
143             return menu;
144         }
145         return null;
146     }
147 
148     @property Workspace workspace() {
149         return _workspace;
150     }
151 
152     ProjectSourceFile findSourceFileItem(string filename, bool fullFileName=true) {
153         if (_workspace)
154             return _workspace.findSourceFileItem(filename, fullFileName);
155         return null;
156     }
157 
158     void addProjectItems(TreeItem root, ProjectItem items) {
159         for (int i = 0; i < items.childCount; i++) {
160             ProjectItem child = items.child(i);
161             if (child.isFolder) {
162                 TreeItem p = root.newChild(child.filename, child.name, "folder");
163                 p.intParam = ProjectItemType.SourceFolder;
164                 p.objectParam = child;
165                 addProjectItems(p, child);
166             } else {
167                 string icon = "text-other";
168                 if (child.isDSourceFile)
169                     icon = "text-d";
170                 if (child.isJsonFile)
171                     icon = "text-json";
172                 if (child.isDMLFile)
173                     icon = "text-dml";
174                 TreeItem p = root.newChild(child.filename, child.name, icon);
175                 p.intParam = ProjectItemType.SourceFile;
176                 p.objectParam = child;
177             }
178         }
179     }
180 
181     void updateDefault() {
182         TreeItem defaultItem = null;
183         if (_workspace && _tree.items.childCount && _workspace.startupProject) {
184             for (int i = 0; i < _tree.items.child(0).childCount; i++) {
185                 TreeItem p = _tree.items.child(0).child(i);
186                 if (p.objectParam is _workspace.startupProject)
187                     defaultItem = p;
188             }
189         }
190         _tree.items.setDefaultItem(defaultItem);
191     }
192 
193     void reloadItems() {
194         _tree.clearAllItems();
195         if (_workspace) {
196             TreeItem defaultItem = null;
197             TreeItem root = _tree.items.newChild(_workspace.filename, _workspace.name, "project-development");
198             root.intParam = ProjectItemType.Workspace;
199             foreach(project; _workspace.projects) {
200                 TreeItem p = root.newChild(project.filename, project.name, project.isDependency ? "project-d-dependency" : "project-d");
201                 p.intParam = ProjectItemType.Project;
202                 p.objectParam = project;
203                 if (project && _workspace.startupProject is project)
204                     defaultItem = p;
205                 addProjectItems(p, project.items);
206             }
207             _tree.items.setDefaultItem(defaultItem);
208         } else {
209             _tree.items.newChild("none", "No workspace"d, "project-development");
210         }
211         _tree.onTreeContentChange(null);
212         if (_workspace) {
213             TreeItem root = _tree.items.child(0);
214             for (int i = 0; i < root.childCount; i++) {
215                 TreeItem child = root.child(i);
216                 if (child.intParam == ProjectItemType.Project) {
217                     Object obj = child.objectParam;
218                     Project prj = cast(Project)obj;
219                     if (prj && prj.isDependency)
220                         child.collapse();
221                 }
222             }
223         }
224         updateDefault();
225     }
226 
227     @property void workspace(Workspace w) {
228         _workspace = w;
229         reloadItems();
230     }
231 
232     /// override to handle specific actions
233     override bool handleAction(const Action a) {
234         if (workspaceActionListener.assigned)
235             return workspaceActionListener(a);
236         return false;
237     }
238 }