Code::Blocks  SVN r11506
projectmanagerui.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3  * http://www.gnu.org/licenses/gpl-3.0.html
4  *
5  * $Revision: 11413 $
6  * $Id: projectmanagerui.cpp 11413 2018-05-28 15:44:10Z pecanh $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/src/src/projectmanagerui.cpp $
8  */
9 
10 #include "sdk.h"
11 #include "projectmanagerui.h"
12 
13 #ifndef CB_PRECOMP
14  #include <algorithm>
15 
16  #include <wx/checkbox.h>
17  #include <wx/choicdlg.h>
18  #include <wx/dir.h>
19  #include <wx/filedlg.h>
20  #include <wx/imaglist.h>
21  #include <wx/listctrl.h>
22  #include <wx/menu.h>
23  #include <wx/settings.h>
24  #include <wx/textdlg.h>
25  #include <wx/xrc/xmlres.h>
26 
27  #include "cbeditor.h"
28  #include "cbproject.h"
29  #include "cbworkspace.h"
30  #include "configmanager.h"
31  #include "editormanager.h"
32  #include "logmanager.h"
33 #endif
34 
35 #include <unordered_map>
36 #include <wx/dataobj.h>
37 #include <wx/dnd.h>
38 
39 #include "cbauibook.h"
40 #include "cbcolourmanager.h"
41 #include "confirmreplacedlg.h"
42 #include "filefilters.h"
43 #include "filegroupsandmasks.h"
44 #include "multiselectdlg.h"
45 #include "projectdepsdlg.h"
46 #include "projectfileoptionsdlg.h"
47 #include "projectoptionsdlg.h"
48 #include "projectsfilemasksdlg.h"
49 
50 #include "goto_file.h"
51 #include "startherepage.h"
52 
53 namespace
54 {
55 
56 // maximum number of items in "Open with" context menu
57 static const unsigned int MAX_OPEN_WITH_ITEMS = 20; // keep it in sync with below array!
58 static const int idOpenWith[] =
59 {
60  static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
61  static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
62  static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
63  static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
64 };
65 // special entry: force open with internal editor
66 static const int idOpenWithInternal = wxNewId();
67 
68 const int ID_ProjectManager = wxNewId();
69 const int idMenuSetActiveProject = wxNewId();
70 const int idMenuOpenFile = wxNewId();
71 const int idMenuSaveProject = wxNewId();
72 const int idMenuSaveFile = wxNewId();
73 const int idMenuCloseProject = wxNewId();
74 const int idMenuCloseFile = wxNewId();
75 const int idMenuAddFilePopup = wxNewId();
76 const int idMenuAddFilesRecursivelyPopup = wxNewId();
77 const int idMenuAddFile = wxNewId();
78 const int idMenuAddFilesRecursively = wxNewId();
79 const int idMenuRemoveFolderFilesPopup = wxNewId();
80 const int idMenuOpenFolderFilesPopup = wxNewId();
81 const int idMenuRemoveFilePopup = wxNewId();
82 const int idMenuRemoveFile = wxNewId();
83 const int idMenuRenameFile = wxNewId();
84 const int idMenuRenameVFolder = wxNewId();
85 const int idMenuProjectNotes = wxNewId();
86 const int idMenuProjectProperties = wxNewId();
87 const int idMenuFileProperties = wxNewId();
88 const int idMenuTreeProjectProperties = wxNewId();
89 const int idMenuTreeFileProperties = wxNewId();
90 const int idMenuTreeOptionsCompile = wxNewId();
91 const int idMenuTreeOptionsLink = wxNewId();
92 const int idMenuTreeOptionsDisableBoth = wxNewId();
93 const int idMenuTreeOptionsEnableBoth = wxNewId();
94 const int idMenuGotoFile = wxNewId();
95 const int idMenuExecParams = wxNewId();
96 const int idMenuViewCategorize = wxNewId();
97 const int idMenuViewUseFolders = wxNewId();
98 const int idMenuViewHideFolderName = wxNewId();
99 const int idMenuViewFileMasks = wxNewId();
100 const int idMenuNextProject = wxNewId();
101 const int idMenuPriorProject = wxNewId();
102 const int idMenuProjectTreeProps = wxNewId();
103 const int idMenuProjectUp = wxNewId();
104 const int idMenuProjectDown = wxNewId();
105 const int idMenuViewCategorizePopup = wxNewId();
106 const int idMenuViewUseFoldersPopup = wxNewId();
107 const int idMenuViewHideFolderNamePopup = wxNewId();
108 const int idMenuTreeRenameWorkspace = wxNewId();
109 const int idMenuTreeSaveWorkspace = wxNewId();
110 const int idMenuTreeSaveAsWorkspace = wxNewId();
111 const int idMenuTreeCloseWorkspace = wxNewId();
112 const int idMenuAddVirtualFolder = wxNewId();
113 const int idMenuDeleteVirtualFolder = wxNewId();
114 const int idMenuFindFile = wxNewId();
115 const int idNB = wxNewId();
116 const int idNB_TabTop = wxNewId();
117 const int idNB_TabBottom = wxNewId();
118 } // anonymous namespace
119 
120 namespace
121 {
122 bool ProjectCanDragNode(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node);
123 bool ProjectNodeDragged(cbProject* project, wxTreeCtrl* tree, wxArrayTreeItemIds& fromArray, wxTreeItemId to);
124 bool ProjectVirtualFolderAdded(cbProject* project, wxTreeCtrl* tree, wxTreeItemId parent_node,
125  const wxString& virtual_folder);
126 void ProjectVirtualFolderDeleted(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node);
127 bool ProjectVirtualFolderRenamed(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node, const wxString& new_name);
128 bool ProjectVirtualFolderDragged(cbProject* project, wxTreeCtrl* tree, wxTreeItemId from, wxTreeItemId to);
129 bool ProjectShowOptions(cbProject* project);
130 } // anonymous namespace
131 
132 
133 BEGIN_EVENT_TABLE(ProjectManagerUI, wxEvtHandler)
134  EVT_TREE_BEGIN_DRAG(ID_ProjectManager, ProjectManagerUI::OnTreeBeginDrag)
135  EVT_TREE_END_DRAG(ID_ProjectManager, ProjectManagerUI::OnTreeEndDrag)
136 
137  EVT_TREE_BEGIN_LABEL_EDIT(ID_ProjectManager, ProjectManagerUI::OnBeginEditNode)
138  EVT_TREE_END_LABEL_EDIT(ID_ProjectManager, ProjectManagerUI::OnEndEditNode)
139 
140  EVT_TREE_ITEM_ACTIVATED(ID_ProjectManager, ProjectManagerUI::OnProjectFileActivated)
141  EVT_TREE_ITEM_RIGHT_CLICK(ID_ProjectManager, ProjectManagerUI::OnTreeItemRightClick)
142  EVT_TREE_KEY_DOWN(ID_ProjectManager, ProjectManagerUI::OnKeyDown)
143  EVT_COMMAND_RIGHT_CLICK(ID_ProjectManager, ProjectManagerUI::OnRightClick)
144 
145  EVT_AUINOTEBOOK_TAB_RIGHT_UP(idNB, ProjectManagerUI::OnTabContextMenu)
146 
147  EVT_MENU_RANGE(idOpenWith[0], idOpenWith[MAX_OPEN_WITH_ITEMS - 1], ProjectManagerUI::OnOpenWith)
148  EVT_MENU(idOpenWithInternal, ProjectManagerUI::OnOpenWith)
149  EVT_MENU(idNB_TabTop, ProjectManagerUI::OnTabPosition)
150  EVT_MENU(idNB_TabBottom, ProjectManagerUI::OnTabPosition)
151  EVT_MENU(idMenuSetActiveProject, ProjectManagerUI::OnSetActiveProject)
152  EVT_MENU(idMenuNextProject, ProjectManagerUI::OnSetActiveProject)
153  EVT_MENU(idMenuPriorProject, ProjectManagerUI::OnSetActiveProject)
154  EVT_MENU(idMenuProjectUp, ProjectManagerUI::OnSetActiveProject)
155  EVT_MENU(idMenuProjectDown, ProjectManagerUI::OnSetActiveProject)
156  EVT_MENU(idMenuTreeRenameWorkspace, ProjectManagerUI::OnRenameWorkspace)
157  EVT_MENU(idMenuTreeSaveWorkspace, ProjectManagerUI::OnSaveWorkspace)
158  EVT_MENU(idMenuTreeSaveAsWorkspace, ProjectManagerUI::OnSaveAsWorkspace)
159  EVT_MENU(idMenuTreeCloseWorkspace, ProjectManagerUI::OnCloseWorkspace)
160  EVT_MENU(idMenuAddVirtualFolder, ProjectManagerUI::OnAddVirtualFolder)
161  EVT_MENU(idMenuDeleteVirtualFolder, ProjectManagerUI::OnDeleteVirtualFolder)
162  EVT_MENU(idMenuAddFile, ProjectManagerUI::OnAddFileToProject)
163  EVT_MENU(idMenuAddFilesRecursively, ProjectManagerUI::OnAddFilesToProjectRecursively)
164  EVT_MENU(idMenuRemoveFile, ProjectManagerUI::OnRemoveFileFromProject)
165  EVT_MENU(idMenuAddFilePopup, ProjectManagerUI::OnAddFileToProject)
166  EVT_MENU(idMenuAddFilesRecursivelyPopup, ProjectManagerUI::OnAddFilesToProjectRecursively)
167  EVT_MENU(idMenuRemoveFolderFilesPopup, ProjectManagerUI::OnRemoveFileFromProject)
168  EVT_MENU(idMenuOpenFolderFilesPopup, ProjectManagerUI::OnOpenFolderFiles)
169  EVT_MENU(idMenuRemoveFilePopup, ProjectManagerUI::OnRemoveFileFromProject)
170  EVT_MENU(idMenuRenameFile, ProjectManagerUI::OnRenameFile)
171  EVT_MENU(idMenuRenameVFolder, ProjectManagerUI::OnRenameVirtualFolder)
172  EVT_MENU(idMenuSaveProject, ProjectManagerUI::OnSaveProject)
173  EVT_MENU(idMenuSaveFile, ProjectManagerUI::OnSaveFile)
174  EVT_MENU(idMenuCloseProject, ProjectManagerUI::OnCloseProject)
175  EVT_MENU(idMenuCloseFile, ProjectManagerUI::OnCloseFile)
176  EVT_MENU(idMenuOpenFile, ProjectManagerUI::OnOpenFile)
177  EVT_MENU(idMenuProjectNotes, ProjectManagerUI::OnNotes)
178  EVT_MENU(idMenuProjectProperties, ProjectManagerUI::OnProperties)
179  EVT_MENU(idMenuFileProperties, ProjectManagerUI::OnProperties)
180  EVT_MENU(idMenuTreeOptionsCompile, ProjectManagerUI::OnFileOptions)
181  EVT_MENU(idMenuTreeOptionsLink, ProjectManagerUI::OnFileOptions)
182  EVT_MENU(idMenuTreeOptionsEnableBoth, ProjectManagerUI::OnFileOptions)
183  EVT_MENU(idMenuTreeOptionsDisableBoth, ProjectManagerUI::OnFileOptions)
184  EVT_MENU(idMenuTreeProjectProperties, ProjectManagerUI::OnProperties)
185  EVT_MENU(idMenuTreeFileProperties, ProjectManagerUI::OnProperties)
186  EVT_MENU(idMenuGotoFile, ProjectManagerUI::OnGotoFile)
187  EVT_MENU(idMenuExecParams, ProjectManagerUI::OnExecParameters)
188  EVT_MENU(idMenuViewCategorize, ProjectManagerUI::OnViewCategorize)
189  EVT_MENU(idMenuViewUseFolders, ProjectManagerUI::OnViewUseFolders)
190  EVT_MENU(idMenuViewHideFolderName, ProjectManagerUI::OnViewHideFolderName)
191  EVT_MENU(idMenuViewCategorizePopup, ProjectManagerUI::OnViewCategorize)
192  EVT_MENU(idMenuViewUseFoldersPopup, ProjectManagerUI::OnViewUseFolders)
193  EVT_MENU(idMenuViewHideFolderNamePopup, ProjectManagerUI::OnViewHideFolderName)
194  EVT_MENU(idMenuViewFileMasks, ProjectManagerUI::OnViewFileMasks)
195  EVT_MENU(idMenuFindFile, ProjectManagerUI::OnFindFile)
196  EVT_IDLE( ProjectManagerUI::OnIdle)
197 
198  EVT_UPDATE_UI(idMenuFileProperties, ProjectManagerUI::OnUpdateUI)
199  EVT_UPDATE_UI(idMenuProjectProperties, ProjectManagerUI::OnUpdateUI)
200  EVT_UPDATE_UI(idMenuAddFile, ProjectManagerUI::OnUpdateUI)
201  EVT_UPDATE_UI(idMenuAddFilesRecursively, ProjectManagerUI::OnUpdateUI)
202  EVT_UPDATE_UI(idMenuRemoveFile, ProjectManagerUI::OnUpdateUI)
203  EVT_UPDATE_UI(idMenuProjectTreeProps, ProjectManagerUI::OnUpdateUI)
204  EVT_UPDATE_UI(idMenuAddVirtualFolder, ProjectManagerUI::OnUpdateUI)
205  EVT_UPDATE_UI(idMenuDeleteVirtualFolder, ProjectManagerUI::OnUpdateUI)
206 
207 END_EVENT_TABLE()
208 
210  m_pTree(nullptr),
211  m_TreeFreezeCounter(0),
212  m_isCheckingForExternallyModifiedProjects(false)
213 {
214  m_pNotebook = new cbAuiNotebook(Manager::Get()->GetAppWindow(), idNB,
215  wxDefaultPosition, wxDefaultSize, wxAUI_NB_WINDOWLIST_BUTTON);
216  if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/project_tabs_bottom"), false))
217  m_pNotebook->SetWindowStyleFlag(m_pNotebook->GetWindowStyleFlag() | wxAUI_NB_BOTTOM);
218 
219  InitPane();
220 
221  ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("project_manager"));
222  m_TreeVisualState = ptvsNone;
223  m_TreeVisualState |= (cfg->ReadBool(_T("/categorize_tree"), true) ? ptvsCategorize : ptvsNone);
224  m_TreeVisualState |= (cfg->ReadBool(_T("/use_folders"), true) ? ptvsUseFolders : ptvsNone);
225  m_TreeVisualState |= (cfg->ReadBool(_T("/hide_folder_name"), false) ? ptvsHideFolderName : ptvsNone);
226  // fix invalid combination, "use folders" has precedence
227  if ( (m_TreeVisualState&ptvsUseFolders) && (m_TreeVisualState&ptvsHideFolderName) )
228  {
229  m_TreeVisualState &= ~ptvsHideFolderName;
230  cfg->Write(_T("/hide_folder_name"), false);
231  }
232 
233  RebuildTree();
234 
235  Manager::Get()->GetColourManager()->RegisterColour(_("Project Tree"), _("Not-compiled files (headers/resources)"),
236  wxT("project_tree_non_source_files"),
238 
239  // Event handling. This must be THE LAST THING activated on startup.
240  // Constructors and destructors must always follow the LIFO rule:
241  // Last in, first out.
242  Manager::Get()->GetAppWindow()->PushEventHandler(this);
243 }
244 
246 {
247  delete m_pImages;
248  m_pImages = nullptr;
249  m_pNotebook->Destroy();
250 }
251 
253 {
255  return;
256  if (m_pTree)
257  return;
258  BuildTree();
259  m_pNotebook->AddPage(m_pTree, _("Projects"));
260 }
261 
263 {
264  m_pTree = new cbTreeCtrl(m_pNotebook, ID_ProjectManager);
265 
266  static const wxString imgs[] =
267  {
268  // NOTE: Keep in sync with FileVisualState in globals.h!
269 
270  // The following are related to (editable, source-) file states
271  _T("file.png"), // fvsNormal
272  _T("file-missing.png"), // fvsMissing,
273  _T("file-modified.png"), // fvsModified,
274  _T("file-readonly.png"), // fvsReadOnly,
275 
276  // The following are related to version control systems (vc)
277  _T("rc-file-added.png"), // fvsVcAdded,
278  _T("rc-file-conflict.png"), // fvsVcConflict,
279  _T("rc-file-missing.png"), // fvsVcMissing,
280  _T("rc-file-modified.png"), // fvsVcModified,
281  _T("rc-file-outofdate.png"), // fvsVcOutOfDate,
282  _T("rc-file-uptodate.png"), // fvsVcUpToDate,
283  _T("rc-file-requireslock.png"), // fvsVcRequiresLock,
284  _T("rc-file-external.png"), // fvsVcExternal,
285  _T("rc-file-gotlock.png"), // fvsVcGotLock,
286  _T("rc-file-lockstolen.png"), // fvsVcLockStolen,
287  _T("rc-file-mismatch.png"), // fvsVcMismatch,
288  _T("rc-file-noncontrolled.png"), // fvsVcNonControlled,
289 
290  // The following are related to C::B workspace/project/folder/virtual
291  _T("workspace.png"), // fvsWorkspace, WorkspaceIconIndex()
292  _T("workspace-readonly.png"), // fvsWorkspaceReadOnly, WorkspaceIconIndex(true)
293  _T("project.png"), // fvsProject, ProjectIconIndex()
294  _T("project-readonly.png"), // fvsProjectReadOnly, ProjectIconIndex(true)
295  _T("folder_open.png"), // fvsFolder, FolderIconIndex()
296  _T("vfolder_open.png"), // fvsVirtualFolder, VirtualFolderIconIndex()
297 
299  };
300  wxBitmap bmp;
301  m_pImages = new wxImageList(16, 16);
302  wxString prefix = ConfigManager::ReadDataPath() + _T("/images/");
303 
304  int i = 0;
305  while (!imgs[i].IsEmpty())
306  {
307  bmp = cbLoadBitmap(prefix + imgs[i], wxBITMAP_TYPE_PNG); // workspace
308  m_pImages->Add(bmp);
309  ++i;
310  }
312 }
313 
315 {
316  if (Manager::IsAppShuttingDown()) // saves a lot of time at startup for large projects
317  return;
318 
319  FreezeTree();
321  ProjectsArray* pa = pm->GetProjects();
322  int count = pa->GetCount();
323  for (int i = 0; i < count; ++i)
324  {
325  if ( cbProject* prj = pa->Item(i) )
326  prj->SaveTreeState(m_pTree);
327  }
329  wxString title;
330  bool read_only = false;
331  cbWorkspace* wkspc = pm->GetWorkspace();
332  if (wkspc)
333  {
334  title = wkspc->GetTitle();
335  wxString ws_file = wkspc->GetFilename();
336  read_only = ( !ws_file.IsEmpty()
337  && wxFile::Exists(ws_file.c_str())
338  && !wxFile::Access(ws_file.c_str(), wxFile::write) );
339  }
340  if (title.IsEmpty())
341  title = _("Workspace");
343  for (int i = 0; i < count; ++i)
344  {
345  if ( cbProject* prj = pa->Item(i) )
346  {
348  m_pTree->SetItemBold(prj->GetProjectNode(), prj == pm->GetActiveProject());
349  }
350  }
352 
353  for (int i = 0; i < count; ++i)
354  {
355  if ( cbProject* prj = pa->Item(i) )
356  prj->RestoreTreeState(m_pTree);
357  }
358  UnfreezeTree();
359 }
360 
362 {
363  if (!m_pTree)
364  return;
365 
367  m_pTree->Freeze();
368 }
369 
370 void ProjectManagerUI::UnfreezeTree(cb_unused bool force)
371 {
372  if (!m_pTree)
373  return;
374 
376  {
378  m_pTree->Thaw();
379  }
380 }
381 
382 void ProjectManagerUI::UpdateActiveProject(cbProject* oldProject, cbProject* newProject, bool refresh)
383 {
384  if (oldProject)
385  m_pTree->SetItemBold(oldProject->GetProjectNode(), false);
386  if (newProject)
387  {
388  wxTreeItemId tid = newProject->GetProjectNode();
389  if (tid)
390  m_pTree->SetItemBold(newProject->GetProjectNode(), true);
391  }
392 
393  if (refresh)
394  RebuildTree();
395  if (newProject)
396  m_pTree->EnsureVisible(newProject->GetProjectNode());
397 
398  m_pTree->Refresh();
399 }
400 
402 {
403  m_pTree->Delete(project->GetProjectNode());
404 }
405 
407 {
408  // User may have selected several items and right-clicked on one,
409  // so return the right-click item instead in that case.
410  if (m_RightClickItem.IsOk())
411  return m_RightClickItem;
412 
413  wxArrayTreeItemIds selections;
414  unsigned int sel = m_pTree->GetSelections(selections);
415 
416  if (sel)
417  // Usually return the first item in the selection list.
418  return selections[0];
419 
420  return wxTreeItemId();
421 }
422 
424 {
425  FreezeTree();
426  m_pTree->AppendItem(m_pTree->GetRootItem(), _("Loading workspace..."));
428  UnfreezeTree();
429 }
430 
432 {
433  if (m_pTree)
434  {
435  m_pTree->SetItemText(m_TreeRoot, _("Workspace"));
437  RebuildTree(); // update the workspace icon if required
438  }
439 }
440 
441 void ProjectManagerUI::FinishLoadingProject(cbProject* project, bool newAddition, cb_unused FilesGroupsAndMasks* fgam)
442 {
443  if (newAddition)
444  {
447  }
448  else
449  RebuildTree();
450  m_pTree->Expand(project->GetProjectNode());
451  m_pTree->Expand(m_TreeRoot); // make sure the root node is open
452 }
453 
454 void ProjectManagerUI::FinishLoadingWorkspace(cbProject* activeProject, const wxString &workspaceTitle)
455 {
456  RebuildTree();
457  if (activeProject)
458  m_pTree->Expand(activeProject->GetProjectNode());
459  m_pTree->Expand(m_TreeRoot); // make sure the root node is open
460  m_pTree->SetItemText(m_TreeRoot, workspaceTitle);
461 
462  UnfreezeTree(true);
463 }
464 
466 {
468  showEvent.pWindow = m_pNotebook;
469  Manager::Get()->ProcessEvent(showEvent);
470 
471  int page = m_pNotebook->GetPageIndex(m_pTree);
472  if (page != wxNOT_FOUND)
473  m_pNotebook->SetSelection(page);
474 }
475 
477 {
478  // first unselect previous selected item if any, needed because of wxTR_MULTIPLE flag
479  m_pTree->UnselectAll();
480 
481  const wxTreeItemId &itemId = projectFile.GetTreeItemId();
482  if (itemId.IsOk())
483  {
484  m_pTree->EnsureVisible(itemId);
485  m_pTree->SelectItem(itemId, true);
486  }
487 }
488 
490 {
491 /* TODO (mandrav#1#): Move menu items from main.cpp, here */
492  if (menuBar)
493  {
494  int pos = menuBar->FindMenu(_("Sea&rch"));
495  wxMenu* menu = menuBar->GetMenu(pos);
496  if (menu)
497  menu->Append(idMenuGotoFile, _("Goto file...\tAlt-G"));
498 
499  pos = menuBar->FindMenu(_("&File"));
500  menu = menuBar->GetMenu(pos);
501  if (menu)
502  {
503  menu->Insert(menu->GetMenuItemCount() - 1, idMenuFileProperties, _("Properties..."));
504  menu->Insert(menu->GetMenuItemCount() - 1, wxID_SEPARATOR, _T("")); // instead of AppendSeparator();
505  }
506 
507  pos = menuBar->FindMenu(_("&Project"));
508  menu = menuBar->GetMenu(pos);
509  if (menu)
510  {
511  if (menu->GetMenuItemCount())
512  menu->AppendSeparator();
513  menu->Append(idMenuAddFile, _("Add files..."), _("Add files to the project"));
514  menu->Append(idMenuAddFilesRecursively, _("Add files recursively..."), _("Add files recursively to the project"));
515  menu->Append(idMenuRemoveFile, _("Remove files..."), _("Remove files from the project"));
516 
517  menu->AppendSeparator();
518  CreateMenuTreeProps(menu, false);
519 
520  menu->Append(idMenuExecParams, _("Set &programs' arguments..."), _("Set execution parameters for the targets of this project"));
521  menu->Append(idMenuProjectNotes, _("Notes..."));
522  menu->Append(idMenuProjectProperties, _("Properties..."));
523  }
524  }
525 }
526 
528 {
529  wxMenu* treeprops = new wxMenu;
530  treeprops->Append(idMenuProjectUp, _("Move project up\tCtrl-Shift-Up"),
531  _("Move project up in project tree"));
532  treeprops->Append(idMenuProjectDown, _("Move project down\tCtrl-Shift-Down"),
533  _("Move project down in project tree"));
534 
535  treeprops->AppendSeparator();
536 
537  treeprops->Append(idMenuPriorProject, _("Activate prior project\tAlt-F5"),
538  _("Activate prior project in open projects list"));
539  treeprops->Append(idMenuNextProject, _("Activate next project\tAlt-F6"),
540  _("Activate next project in open projects list"));
541 
542  treeprops->AppendSeparator();
543 
544  treeprops->AppendCheckItem((popup ? idMenuViewCategorizePopup : idMenuViewCategorize),
545  _("Categorize by file types"));
546  treeprops->AppendCheckItem((popup ? idMenuViewUseFoldersPopup : idMenuViewUseFolders),
547  _("Display folders as on disk"));
548  treeprops->AppendCheckItem((popup ? idMenuViewHideFolderNamePopup : idMenuViewHideFolderName),
549  _("Hide folder name"));
550 
551  ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("project_manager"));
552  bool do_categorise = cfg->ReadBool(_T("/categorize_tree"), true);
553  bool do_use_folders = cfg->ReadBool(_T("/use_folders"), true);
554  bool do_hide_folder_name = !do_use_folders && cfg->ReadBool(_T("/hide_folder_name"), false); // "use folders" has precedence
555  cfg->Write(_T("/hide_folder_name"), do_hide_folder_name); // make sure that configuration is consistent
556 
557  treeprops->Check((popup ? idMenuViewCategorizePopup : idMenuViewCategorize), do_categorise);
558  treeprops->Check((popup ? idMenuViewUseFoldersPopup : idMenuViewUseFolders), do_use_folders);
559  treeprops->Check((popup ? idMenuViewHideFolderNamePopup : idMenuViewHideFolderName), do_hide_folder_name);
560 
561  treeprops->Enable((popup ? idMenuViewUseFoldersPopup : idMenuViewUseFolders), !do_hide_folder_name);
562  treeprops->Enable((popup ? idMenuViewHideFolderNamePopup : idMenuViewHideFolderName), !do_use_folders);
563 
564  treeprops->Append(idMenuViewFileMasks, _("Edit file types && categories..."));
565 
566  menu->Append(idMenuProjectTreeProps, _("Project tree"), treeprops);
567 }
568 
570 {
571  if ( !id.IsOk() )
572  return;
573 
574  wxString caption;
575  wxMenu menu;
576 
578  bool is_vfolder = ftd && ftd->GetKind() == FileTreeData::ftdkVirtualFolder;
579  /* Following code will check for currently compiling project.
580  * If it finds the selected is project is currently compiling,
581  * then it will disable some of the options */
582  bool PopUpMenuOption = true;
583  ProjectsArray* pa = Manager::Get()->GetProjectManager()->GetProjects();
584  if ( pa && ftd
585  && ( ftd->GetKind() == FileTreeData::ftdkProject
586  || ftd->GetKind() == FileTreeData::ftdkFile
587  || ftd->GetKind() == FileTreeData::ftdkFolder ) )
588  {
589  PopUpMenuOption = !cbHasRunningCompilers(Manager::Get()->GetPluginManager());
590  }
591 
592  // if it is not the workspace, add some more options
594  if (ftd)
595  {
596  // if it is a project...
597  if (ftd->GetKind() == FileTreeData::ftdkProject)
598  {
599  if (ftd->GetProject() != pm->GetActiveProject())
600  {
601  menu.Append(idMenuSetActiveProject, _("Activate project"));
602  menu.Enable(idMenuSetActiveProject, PopUpMenuOption);
603  }
604  menu.Append(idMenuSaveProject, _("Save project"));
605  menu.Enable(idMenuSaveProject, PopUpMenuOption);
606  menu.Append(idMenuCloseProject, _("Close project"));
607  menu.Enable(idMenuCloseProject, PopUpMenuOption);
608  menu.AppendSeparator();
609  menu.Append(idMenuAddFilePopup, _("Add files..."));
610  menu.Enable(idMenuAddFilePopup, PopUpMenuOption);
611  menu.Append(idMenuAddFilesRecursivelyPopup, _("Add files recursively..."));
612  menu.Enable(idMenuAddFilesRecursivelyPopup, PopUpMenuOption);
613  menu.Append(idMenuRemoveFile, _("Remove files..."));
614  menu.AppendSeparator();
615  menu.Append(idMenuFindFile, _("Find file..."));
616  menu.AppendSeparator();
617  CreateMenuTreeProps(&menu, true);
618  menu.Append(idMenuAddVirtualFolder, _("Add new virtual folder..."));
619  if (is_vfolder)
620  menu.Append(idMenuDeleteVirtualFolder, _("Delete this virtual folder..."));
621  }
622 
623  // if it is a file...
624  else if (ftd->GetKind() == FileTreeData::ftdkFile)
625  {
626  // selected project file
627  ProjectFile* pf = ftd->GetProjectFile();
628 
629  // is it already open in the editor?
631  if (eb)
632  {
633  // is it already active?
634  bool active = Manager::Get()->GetEditorManager()->GetActiveEditor() == eb;
635 
636  if (!active)
637  {
638  caption.Printf(_("Switch to %s"), m_pTree->GetItemText(id).c_str());
639  menu.Append(idMenuOpenFile, caption);
640  }
641  caption.Printf(_("Save %s"), m_pTree->GetItemText(id).c_str());
642  menu.Append(idMenuSaveFile, caption);
643  caption.Printf(_("Close %s"), m_pTree->GetItemText(id).c_str());
644  menu.Append(idMenuCloseFile, caption);
645  }
646  else
647  {
648  caption.Printf(_("Open %s"), m_pTree->GetItemText(id).c_str());
649  menu.Append(idMenuOpenFile, caption);
650  }
651 
652  // add "Open with" menu
653  wxMenu* openWith = new wxMenu;
654  PluginsArray mimes = Manager::Get()->GetPluginManager()->GetMimeOffers();
655  for (unsigned int i = 0; i < mimes.GetCount() && i < MAX_OPEN_WITH_ITEMS; ++i)
656  {
657  cbMimePlugin* plugin = (cbMimePlugin*)mimes[i];
658  if (plugin && plugin->CanHandleFile(m_pTree->GetItemText(id)))
659  {
660  const PluginInfo* info = Manager::Get()->GetPluginManager()->GetPluginInfo(plugin);
661  openWith->Append(idOpenWith[i], info ? info->title : wxString(_("<Unknown plugin>")));
662  }
663  }
664  openWith->AppendSeparator();
665  openWith->Append(idOpenWithInternal, _("Internal editor"));
666  menu.Append(wxID_ANY, _("Open with"), openWith);
667 
669  {
670  menu.AppendSeparator();
671  menu.Append(idMenuRenameFile, _("Rename file..."));
672  menu.Enable(idMenuRenameFile, PopUpMenuOption);
673  }
674  menu.AppendSeparator();
675  menu.Append(idMenuRemoveFilePopup, _("Remove file from project"));
676  menu.Enable(idMenuRemoveFilePopup, PopUpMenuOption);
677  }
678 
679  // if it is a folder...
680  else if (ftd->GetKind() == FileTreeData::ftdkFolder)
681  {
682  menu.Append(idMenuAddFilePopup, _("Add files..."));
683  menu.Enable(idMenuAddFilePopup, PopUpMenuOption);
684  menu.Append(idMenuAddFilesRecursivelyPopup, _("Add files recursively..."));
685  menu.Enable(idMenuAddFilesRecursivelyPopup, PopUpMenuOption);
686  menu.AppendSeparator();
687  menu.Append(idMenuRemoveFile, _("Remove files..."));
688  menu.AppendSeparator();
689  menu.Append(idMenuFindFile, _("Find file..."));
690  menu.AppendSeparator();
691  wxFileName f(ftd->GetFolder());
693  menu.Append(idMenuRemoveFolderFilesPopup, wxString::Format(_("Remove %s*"), f.GetFullPath().c_str()));
694  menu.Enable(idMenuRemoveFolderFilesPopup, PopUpMenuOption);
695  menu.Append(idMenuOpenFolderFilesPopup, wxString::Format(_("Open %s*"), f.GetFullPath().c_str()));
696  }
697 
698  // if it is a virtual folder
699  else if (is_vfolder)
700  {
701  menu.Append(idMenuAddVirtualFolder, _("Add new virtual folder..."));
702  menu.Append(idMenuDeleteVirtualFolder, _("Delete this virtual folder"));
703  menu.Append(idMenuRenameVFolder, _("Rename this virtual folder"));
704  menu.AppendSeparator();
705  menu.Append(idMenuRemoveFile, _("Remove files..."));
706  menu.Append(idMenuRemoveFolderFilesPopup, wxString::Format(_("Remove %s*"), ftd->GetFolder().c_str()));
707  menu.Append(idMenuOpenFolderFilesPopup, wxString::Format(_("Open %s*"), ftd->GetFolder().c_str()));
708  menu.AppendSeparator();
709  menu.Append(idMenuFindFile, _("Find file..."));
710  }
711 
712  // if it is a virtual group (wild-card matching)
713  else if (ftd->GetKind() == FileTreeData::ftdkVirtualGroup)
714  {
715  menu.Append(idMenuFindFile, _("Find file..."));
716  }
717 
718  // ask any plugins to add items in this menu
720 
721  // more project options
722  if (ftd->GetKind() == FileTreeData::ftdkProject)
723  {
724  menu.Append(idMenuTreeProjectProperties, _("Properties..."));
725  menu.Enable(idMenuTreeProjectProperties, PopUpMenuOption);
726  }
727 
728  // more file options
729  else if (ftd->GetKind() == FileTreeData::ftdkFile)
730  {
731  menu.AppendSeparator();
732  wxMenu *options = new wxMenu;
733  wxMenuItem *optionsItem = menu.AppendSubMenu(options, _("Options"));
734  optionsItem->Enable(PopUpMenuOption);
735 
736  options->AppendCheckItem(idMenuTreeOptionsCompile, _("Compile file"));
737  options->AppendCheckItem(idMenuTreeOptionsLink, _("Link file"));
738  options->AppendSeparator();
739  options->Append(idMenuTreeOptionsEnableBoth, _("Enable both"));
740  options->Append(idMenuTreeOptionsDisableBoth, _("Disable both"));
741 
742  if ( ProjectFile* pf = ftd->GetProjectFile() )
743  {
744  menu.Check(idMenuTreeOptionsCompile, pf->compile);
745  menu.Check(idMenuTreeOptionsLink, pf->link);
746  }
747  menu.Append(idMenuTreeFileProperties, _("Properties..."));
748  menu.Enable(idMenuTreeFileProperties, PopUpMenuOption);
749  }
750  }
751  else if (!ftd && pm->GetWorkspace())
752  {
753  wxCommandEvent event;
754  OnRightClick(event);
755  return;
756  }
757 
758  if (menu.GetMenuItemCount() != 0)
759  m_pTree->PopupMenu(&menu, pt);
760 }
761 
763 {
764  // Basic stuff: We can only open files that are still present
765  wxFileName the_file(filename);
766  if (!the_file.FileExists())
767  {
768  wxString msg;
769  msg.Printf(_("Could not open the file '%s'.\nThe file does not exist."), filename.c_str());
770  cbMessageBox(msg, _("Error"));
773  return;
774  }
775 
776  FileType ft = FileTypeOf(filename);
777  if (ft == ftHeader || ft == ftSource || ft == ftTemplateSource)
778  {
779  // C/C++ header/source files, always get opened inside Code::Blocks
780  if ( cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filename) )
781  {
782  ed->SetProjectFile(pf);
783  ed->Activate();
784  }
785  else
786  {
787  wxString msg;
788  msg.Printf(_("Failed to open '%s'."), filename.c_str());
790  }
791  }
792  else
793  {
794  // first look for custom editors
795  // if that fails, try MIME handlers
796  EditorBase* eb = Manager::Get()->GetEditorManager()->IsOpen(filename);
797  if (eb && !eb->IsBuiltinEditor())
798  {
799  // custom editors just get activated
800  eb->Activate();
801  return;
802  }
803 
804  // not a recognized file type
806  if (!plugin)
807  {
808  wxString msg;
809  msg.Printf(_("Could not open file '%s'.\nNo handler registered for this type of file."), filename.c_str());
811  }
812  else if (plugin->OpenFile(filename) != 0)
813  {
814  const PluginInfo* info = Manager::Get()->GetPluginManager()->GetPluginInfo(plugin);
815  wxString msg;
816  msg.Printf(_("Could not open file '%s'.\nThe registered handler (%s) could not open it."), filename.c_str(), info ? info->title.c_str() : wxString(_("<Unknown plugin>")).c_str());
818  }
819  }
820 }
821 
823 {
825  if (!sel.IsOk())
826  return;
827 
828  if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
829  {
830  if ( ProjectFile* pf = ftd->GetProjectFile() )
831  DoOpenFile(pf, pf->file.GetFullPath());
832  }
833 }
834 
836 {
837  wxTreeItemIdValue cookie;
838  wxTreeItemId child;
839  wxString filename;
840  size_t i = 0;
841  while (i < m_pTree->GetChildrenCount(sel_id))
842  {
843  if (i == 0)
844  child = m_pTree->GetFirstChild(sel_id, cookie);
845  else
846  child = m_pTree->GetNextChild(sel_id, cookie);
847  if (child.IsOk())
848  {
849  if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(child) )
850  {
851  cbProject* prj = ftd->GetProject();
852  if (prj && ftd->GetKind() == FileTreeData::ftdkFile)
853  {
854  if ( ProjectFile* pf = ftd->GetProjectFile() )
856  }
857  else if ( ftd->GetKind() == FileTreeData::ftdkFolder
858  || ftd->GetKind() == FileTreeData::ftdkVirtualFolder)
859  {
860  RemoveFilesRecursively(child);
861  }
862  }
863  ++i;
864  }
865  else
866  break;
867  }
868 }
869 
871 {
872  wxTreeItemIdValue cookie;
873  wxTreeItemId child;
874  wxString filename;
875  size_t i = 0;
876  while (i < m_pTree->GetChildrenCount(sel_id))
877  {
878  if (i == 0)
879  child = m_pTree->GetFirstChild(sel_id, cookie);
880  else
881  child = m_pTree->GetNextChild(sel_id, cookie);
882  if (child.IsOk())
883  {
884  FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(child);
885  if (ftd)
886  {
887  cbProject* prj = ftd->GetProject();
888  if (prj && ftd->GetKind() == FileTreeData::ftdkFile)
889  {
890  if ( ProjectFile* pf = ftd->GetProjectFile() )
891  DoOpenFile(pf, pf->file.GetFullPath());
892  }
893  else if ( ftd->GetKind() == FileTreeData::ftdkFolder
895  {
896  OpenFilesRecursively(child);
897  }
898  }
899  ++i;
900  }
901  else
902  break;
903  }
904 }
905 
906 
907 // events
908 
910 {
911  wxMenu* NBmenu = new wxMenu();
912  if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/project_tabs_bottom"), false))
913  NBmenu->Append(idNB_TabTop, _("Tabs at top"));
914  else
915  NBmenu->Append(idNB_TabBottom, _("Tabs at bottom"));
916  m_pNotebook->PopupMenu(NBmenu);
917  delete NBmenu;
918 }
919 
921 {
922  long style = m_pNotebook->GetWindowStyleFlag();
923  style &= ~wxAUI_NB_BOTTOM;
924 
925  if (event.GetId() == idNB_TabBottom)
926  style |= wxAUI_NB_BOTTOM;
927  m_pNotebook->SetWindowStyleFlag(style);
928  m_pNotebook->Refresh();
929  // (style & wxAUI_NB_BOTTOM) saves info only about the the tabs position
930  Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/environment/project_tabs_bottom"), (bool)(style & wxAUI_NB_BOTTOM));
931 }
932 
934 {
935  wxArrayString fileList;
936 
937  size_t count = m_pTree->GetSelections(m_DraggingSelection);
938 
939  for (size_t i = 0; i < count; i++)
940  {
941  //what item do we start dragging?
943 
944  if (!id.IsOk())
945  return;
946 
947  // if no data associated with it, disallow
949  if (!ftd)
950  return;
951 
952  // if no project, disallow
953  cbProject* prj = ftd->GetProject();
954  if (!prj)
955  return;
956 
957  // allow only if the project approves
958  if ( ! ProjectCanDragNode(prj, m_pTree, id))
959  continue;
960 
961  // We allow drag and drop for normal files or projects, //(pecan 2018/03/22)+
962  // but not for mixed selection, or any other project items
963  if ( ftd->GetKind() == FileTreeData::ftdkFile )
964  {
965  fileList.Add(ftd->GetProjectFile()->file.GetLongPath());
966  }
967  else if ( ftd->GetKind() == FileTreeData::ftdkProject )
968  {
969  fileList.Add(ftd->GetProject()->GetFilename());
970  }
971  }
972 
973  // wxTreeCtrl Internal vs External DragAndDrop are incompatible.
974  // To do an external DnD here, we have to test the mouse position
975  // and verify that the cursor is outside the wxTreeCtrl
976  m_pTree->SetCursor(wxCursor(wxCURSOR_HAND)); //show feedback to user
977  bool isExternalDrag = false;
978  for (int ii=0; ii<8; ++ii)
979  {
980  // wait max 800 milliseconds for cursor move outside the tree
981  wxMilliSleep(100); //wait awhile for possible mouse move outside tree ctrl
983  wxString winName = pWin ? pWin->GetName().Lower(): _T("unkwn");
984  if ( (not pWin) or (_T("treectrl") != winName) )
985  {
986  isExternalDrag = true;
987  break;
988  }
989  if (not wxGetMouseState().LeftIsDown())
990  break; //internal tree drag
991  }
992  if ( (! fileList.IsEmpty()) and isExternalDrag )
993  {
994  // create a drop object of file paths
995  wxTextDataObject dropObject( GetStringFromArray(fileList , wxT("\n"), false));
996  wxDropSource dragSource(m_pTree);
997  dragSource.SetData(dropObject);
998  dragSource.DoDragDrop();
999  m_pTree->SetCursor(wxCursor(wxNullCursor));
1000  return;
1001  }
1002 
1003  m_pTree->SetCursor(wxCursor(wxNullCursor));
1004 
1005  // allowed
1006  event.Allow();
1007 }
1008 
1010 {
1011  m_pTree->SetCursor(wxCursor(wxNullCursor));
1012 
1013  wxTreeItemId to = event.GetItem();
1014 
1015  // is the drag target valid?
1016  if (!to.IsOk())
1017  return;
1018 
1019  // if no data associated with any of them, disallow
1020  FileTreeData* ftdTo = (FileTreeData*)m_pTree->GetItemData(to);
1021  if (!ftdTo)
1022  return;
1023 
1024  // if no project or different projects, disallow
1025  cbProject* prjTo = ftdTo->GetProject();
1026  if (!prjTo)
1027  return;
1028 
1029  size_t count = m_DraggingSelection.Count();
1030  for (size_t i = 0; i < count; i++)
1031  {
1033 
1034  // is the item valid?
1035  if (!from.IsOk())
1036  return;
1037 
1038  // if no data associated with any of them, disallow
1039  FileTreeData* ftdFrom = (FileTreeData*)m_pTree->GetItemData(from);
1040  if (!ftdFrom)
1041  return;
1042 
1043  // if no project or different projects, disallow
1044  cbProject* prjFrom = ftdTo->GetProject();
1045  if (prjFrom != prjTo)
1046  return;
1047  }
1048 
1049  // allow only if the project approves
1050  if (!ProjectNodeDragged(prjTo, m_pTree, m_DraggingSelection, to))
1051  return;
1052 
1053  event.Allow();
1054 }
1055 
1057 {
1058  wxTreeItemId id = event.GetItem();
1061 
1062  if (ftd && ftd->GetKind() == FileTreeData::ftdkProject)
1063  {
1064  if (ftd->GetProject() != pm->GetActiveProject())
1065  pm->SetProject(ftd->GetProject(), false);
1066 
1067  // prevent item expand state toggle when project is activated
1068  // toggle it one time so that it is toggled back by wx
1069  m_pTree->IsExpanded(id) ? m_pTree->Collapse(id) : m_pTree->Expand(id);
1070  }
1071  else if ( ftd
1072  && ( (ftd->GetKind() == FileTreeData::ftdkVirtualGroup)
1074  || (ftd->GetKind() == FileTreeData::ftdkFolder) ) )
1075  m_pTree->IsExpanded(id) ? m_pTree->Collapse(id) : m_pTree->Expand(id);
1076  else if (!ftd && pm->GetWorkspace())
1078  else
1080 }
1081 
1083 {
1084  if (Manager::Get()->GetProjectManager()->GetActiveProject())
1086 }
1087 
1089 {
1091  if (!pm)
1092  return;
1093 
1094  bool notCompilingProject = true;
1095  cbProject *project = pm->GetActiveProject();
1096  if (project && project->GetCurrentlyCompilingTarget())
1097  notCompilingProject = false;
1098 
1099  wxMenu menu;
1100  if (pm->GetWorkspace())
1101  {
1102  menu.Append(idMenuTreeRenameWorkspace, _("Rename workspace..."));
1103  menu.Enable(idMenuTreeRenameWorkspace, notCompilingProject);
1104  menu.AppendSeparator();
1105  menu.Append(idMenuTreeSaveWorkspace, _("Save workspace"));
1106  menu.Enable(idMenuTreeSaveWorkspace, notCompilingProject);
1107  menu.Append(idMenuTreeSaveAsWorkspace, _("Save workspace as..."));
1108  menu.Enable(idMenuTreeSaveAsWorkspace, notCompilingProject);
1109  menu.AppendSeparator();
1110  menu.Append(idMenuFindFile, _("Find file..."));
1111  }
1112 
1113  // ask any plugins to add items in this menu
1115 
1116  // if plugins added to this menu, add a separator
1117  if (menu.GetMenuItemCount() != 0)
1118  menu.AppendSeparator();
1119 
1120  menu.AppendCheckItem(idMenuViewCategorizePopup, _("Categorize by file types"));
1121  menu.AppendCheckItem(idMenuViewUseFoldersPopup, _("Display folders as on disk"));
1122  menu.AppendCheckItem(idMenuViewHideFolderNamePopup, _("Hide folder name"));
1123 
1124  bool do_categorise = (m_TreeVisualState&ptvsCategorize);
1125  bool do_use_folders = (m_TreeVisualState&ptvsUseFolders);
1126  bool do_hide_folder_name = !do_use_folders && (m_TreeVisualState&ptvsHideFolderName); // "use folders" has precedence
1127 
1128  menu.Check(idMenuViewCategorizePopup, do_categorise);
1129  menu.Check(idMenuViewUseFoldersPopup, do_use_folders);
1130  menu.Check(idMenuViewHideFolderNamePopup, do_hide_folder_name);
1131 
1132  menu.Enable(idMenuViewUseFoldersPopup, !do_hide_folder_name);
1133  menu.Enable(idMenuViewHideFolderNamePopup, !do_use_folders);
1134 
1135  menu.AppendSeparator();
1136  menu.Append(idMenuViewFileMasks, _("Edit file types && categories..."));
1137  menu.Enable(idMenuViewFileMasks, notCompilingProject);
1138 
1139  if (pm->GetWorkspace())
1140  {
1141  // this menu items should be always the last one
1142  menu.AppendSeparator();
1143  menu.Append(idMenuTreeCloseWorkspace, _("Close workspace"));
1144  menu.Enable(idMenuTreeCloseWorkspace, notCompilingProject);
1145  }
1146 
1147  wxPoint pt = wxGetMousePosition();
1148  pt = m_pTree->ScreenToClient(pt);
1149  m_pTree->PopupMenu(&menu, pt);
1150 }
1151 
1153 {
1154  if (Manager::Get()->GetProjectManager()->IsLoadingProject())
1155  {
1156  wxBell();
1157  return;
1158  }
1159 
1160  // We have a popup menu, so we will use the right-click item instead of the first tree selection.
1161  m_RightClickItem = event.GetItem();
1162 
1163  m_pTree->SelectItem(event.GetItem());
1164  ShowMenu(event.GetItem(), event.GetPoint());
1165 
1166  // Unset it so that we go back to using the first tree selection again.
1168 }
1169 
1171 {
1173  if (wkspc)
1174  {
1175  wxString text = cbGetTextFromUser(_("Please enter the new name for the workspace:"),
1176  _("Rename workspace"),
1177  wkspc->GetTitle());
1178  if (!text.IsEmpty())
1179  {
1180  wkspc->SetTitle(text);
1181  m_pTree->SetItemText(m_TreeRoot, wkspc->GetTitle());
1182  }
1183  }
1184 }
1185 
1187 {
1189  if (pm->GetWorkspace())
1190  pm->SaveWorkspace();
1191 }
1192 
1194 {
1196  if (pm->GetWorkspace())
1197  pm->SaveWorkspaceAs(_T(""));
1198 }
1199 
1201 {
1203  if (pm->GetWorkspace())
1204  pm->CloseWorkspace();
1205 }
1206 
1208 {
1210  ProjectsArray* pa = pm->GetProjects();
1211 
1212  if (event.GetId() == idMenuSetActiveProject)
1213  {
1215  if (!sel.IsOk())
1216  return;
1217 
1219  if (!ftd)
1220  return;
1221 
1222  pm->SetProject(ftd->GetProject(), false);
1223  }
1224  else if (event.GetId() == idMenuPriorProject)
1225  {
1226  int index = pa->Index(pm->GetActiveProject());
1227  if (index == wxNOT_FOUND)
1228  return;
1229  --index;
1230  if (index < 0)
1231  index = pa->GetCount() - 1;
1232  pm->SetProject(pa->Item(index), false);
1233  }
1234  else if (event.GetId() == idMenuNextProject)
1235  {
1236  int index = pa->Index(pm->GetActiveProject());
1237  if (index == wxNOT_FOUND)
1238  return;
1239  ++index;
1240  if (index == (int)pa->GetCount())
1241  index = 0;
1242  pm->SetProject(pa->Item(index), false);
1243  }
1244  else if (event.GetId() == idMenuProjectUp)
1245  {
1247  if (!sel.IsOk())
1248  return;
1249  if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
1250  MoveProjectUp(ftd->GetProject());
1251  }
1252  else if (event.GetId() == idMenuProjectDown)
1253  {
1255  if (!sel.IsOk())
1256  return;
1257 
1258  if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
1259  MoveProjectDown(ftd->GetProject());
1260  }
1261 }
1262 
1264 {
1266  cbProject* prj = nullptr;
1267  wxString basePath;
1268 
1269  if (event.GetId() == idMenuAddFilesRecursively)
1270  {
1271  prj = pm->GetActiveProject();
1272  if (prj)
1273  basePath = prj->GetBasePath();
1274  }
1275  else
1276  {
1278  if (!sel.IsOk())
1279  return;
1280 
1281  if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
1282  {
1283  if ( (prj = ftd->GetProject()) )
1284  {
1285  basePath = ftd->GetFolder();
1286  if (!wxDirExists(basePath))
1287  basePath = prj->GetBasePath();
1288  }
1289  }
1290  }
1291  if (!prj)
1292  return;
1293 
1295  _("Add files recursively..."),
1296  basePath,
1297  wxEmptyString,
1298  false,
1299  false);
1300  if (dir.IsEmpty())
1301  return;
1302 
1303  wxArrayInt targets;
1304  // ask for target only if more than one
1305  if (prj->GetBuildTargetsCount() == 1)
1306  targets.Add(0);
1307 
1308  // generate list of files to add
1309  wxArrayString array;
1311  if (array.GetCount() == 0)
1312  return;
1313 
1314  // for usability reasons, remove any directory entries from the list...
1315  unsigned int i = 0;
1316  while (i < array.GetCount())
1317  {
1318  // discard directories, as well as some well known SCMs control folders ;)
1319  // also discard C::B project files
1320  if (wxDirExists(array[i]) ||
1321  array[i].Contains(_T("/.git/")) ||
1322  array[i].Contains(_T("\\.git\\")) ||
1323  array[i].Contains(_T("\\.hg\\")) ||
1324  array[i].Contains(_T("/.hg/")) ||
1325  array[i].Contains(_T("\\.svn\\")) ||
1326  array[i].Contains(_T("/.svn/")) ||
1327  array[i].Contains(_T("\\CVS\\")) ||
1328  array[i].Contains(_T("/CVS/")) ||
1329  array[i].Lower().Matches(_T("*.cbp")))
1330  {
1331  array.RemoveAt(i);
1332  }
1333  else
1334  ++i;
1335  }
1336 
1337  wxString wild;
1338  const FilesGroupsAndMasks* fgam = pm->GetFilesGroupsAndMasks();
1339  for (unsigned fm_idx = 0; fm_idx < fgam->GetGroupsCount(); fm_idx++)
1340  wild += fgam->GetFileMasks(fm_idx);
1341 
1342  MultiSelectDlg dlg(nullptr, array, wild, _("Select the files to add to the project:"));
1343  PlaceWindow(&dlg);
1344  if (dlg.ShowModal() != wxID_OK)
1345  return;
1346  array = dlg.GetSelectedStrings();
1347 
1348  // finally add the files
1349  pm->AddMultipleFilesToProject(array, prj, targets);
1350  RebuildTree();
1351 }
1352 
1354 {
1356  cbProject* prj = nullptr;
1357  wxString basePath;
1358 
1359  if (event.GetId() == idMenuAddFile)
1360  {
1361  prj = pm->GetActiveProject();
1362  if (prj)
1363  basePath = prj->GetBasePath();
1364  }
1365  else
1366  {
1368  if (!sel.IsOk())
1369  return;
1370 
1371  if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
1372  {
1373  if ( (prj = ftd->GetProject()) )
1374  {
1375  basePath = ftd->GetFolder();
1376  if (!wxDirExists(basePath))
1377  basePath = prj->GetBasePath();
1378  }
1379  }
1380  }
1381  if (!prj)
1382  return;
1383 
1384  wxFileDialog dlg(Manager::Get()->GetAppWindow(),
1385  _("Add files to project..."),
1386  basePath,
1387  wxEmptyString,
1389  wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST | compatibility::wxHideReadonly);
1391 
1392  PlaceWindow(&dlg);
1393  if (dlg.ShowModal() == wxID_OK)
1394  {
1395  wxArrayInt targets;
1396  // ask for target only if more than one
1397  if (prj->GetBuildTargetsCount() == 1)
1398  targets.Add(0);
1399 
1400  wxArrayString array;
1401  dlg.GetPaths(array);
1402  pm->AddMultipleFilesToProject(array, prj, targets);
1403  RebuildTree();
1404  }
1405 }
1406 
1407 namespace
1408 {
1409 void FindFiles(wxArrayString &resultFiles, wxTreeCtrl &tree, wxTreeItemId item)
1410 {
1411  FileTreeData* ftd = static_cast<FileTreeData*>(tree.GetItemData(item));
1412  if (!ftd)
1413  return;
1414 
1415  switch (ftd->GetKind())
1416  {
1418  resultFiles.Add(ftd->GetProjectFile()->relativeFilename);
1419  break;
1421  {
1422  wxTreeItemIdValue cookie;
1423  wxTreeItemId i = tree.GetFirstChild(item, cookie);
1424  while (i.IsOk())
1425  {
1426  FindFiles(resultFiles, tree, i);
1427  i = tree.GetNextChild(item, cookie);
1428  }
1429  }
1430  break;
1431  case FileTreeData::ftdkUndefined: // fall-through
1432  case FileTreeData::ftdkProject: // fall-through
1433  case FileTreeData::ftdkVirtualGroup: // fall-through
1434  case FileTreeData::ftdkVirtualFolder: // fall-through
1435  default:
1436  for (FilesList::iterator it = ftd->GetProject()->GetFilesList().begin(); it != ftd->GetProject()->GetFilesList().end(); ++it)
1437  resultFiles.Add(((ProjectFile*)*it)->relativeFilename);
1438  }
1439 }
1440 } // namespace
1441 
1443 {
1446  if (!sel.IsOk())
1447  return;
1448 
1450  if (!ftd)
1451  return;
1452 
1453  cbProject* prj = ftd->GetProject();
1454  if (!prj)
1455  return;
1456 
1457  wxString oldpath = prj->GetCommonTopLevelPath();
1458 
1459  if (event.GetId() == idMenuRemoveFile)
1460  {
1461  // remove multiple-files
1462  wxArrayString files;
1463  FindFiles(files, *m_pTree, sel);
1464 
1465  if (files.Count()==0)
1466  {
1467  cbMessageBox(_("This project does not contain any files to remove."),
1468  _("Error"), wxICON_WARNING);
1469  return;
1470  }
1471  files.Sort();
1472  wxString msg;
1473  msg.Printf(_("Select files to remove from %s:"), prj->GetTitle().c_str());
1474  MultiSelectDlg dlg(nullptr, files, true, msg);
1475  PlaceWindow(&dlg);
1476  if (dlg.ShowModal() == wxID_OK)
1477  {
1478  wxArrayInt indices = dlg.GetSelectedIndices();
1479  if (indices.GetCount() == 0)
1480  return;
1481  if (cbMessageBox(_("Are you sure you want to remove these files from the project?"),
1482  _("Confirmation"),
1484  {
1485  return;
1486  }
1487  prj->BeginRemoveFiles();
1488  // we iterate the array backwards, because if we iterate it normally,
1489  // when we remove the first index, the rest becomes invalid...
1490  for (int i = (int)indices.GetCount() - 1; i >= 0; --i)
1491  {
1492  Manager::Get()->GetLogManager()->DebugLog(F(_T("Removing index %d"), indices[i]));
1493 
1494  if ( ProjectFile* pf = prj->GetFileByFilename(files[indices[i]]) )
1495  pm->RemoveFileFromProject(pf, prj);
1496  }
1498  prj->EndRemoveFiles();
1499  RebuildTree();
1500  }
1501  }
1502  else if (event.GetId() == idMenuRemoveFilePopup)
1503  {
1504  if ( ProjectFile* pf = ftd->GetProjectFile() )
1505  {
1506  // remove single file
1507  prj->BeginRemoveFiles();
1508  pm->RemoveFileFromProject(pf, prj);
1510  if (prj->GetCommonTopLevelPath() == oldpath)
1511  m_pTree->Delete(sel);
1512  prj->EndRemoveFiles();
1513  RebuildTree();
1514  }
1515  }
1516  else if (event.GetId() == idMenuRemoveFolderFilesPopup)
1517  {
1518  // remove all files from a folder
1519  if (cbMessageBox(_("Are you sure you want to recursively remove from the project all the files under this folder?"),
1520  _("Confirmation"),
1522  {
1523  return;
1524  }
1525  bool is_virtual = ftd->GetKind() == FileTreeData::ftdkVirtualFolder;
1526  if (is_virtual || ftd->GetKind() == FileTreeData::ftdkFolder)
1527  {
1528  prj->BeginRemoveFiles();
1530  prj->EndRemoveFiles();
1531  }
1533  if (prj->GetCommonTopLevelPath() == oldpath && !is_virtual)
1534  m_pTree->Delete(sel);
1535  else if (is_virtual)
1536  ProjectVirtualFolderDeleted(prj, m_pTree, sel);
1537  RebuildTree();
1538  }
1539 }
1540 
1542 {
1544  if (!sel.IsOk())
1545  return;
1546 
1548 
1549  if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
1550  {
1551  if ( cbProject* prj = ftd->GetProject() )
1552  {
1553  // TODO : does it make sense NOT to save project file while compiling ??
1554  if (pm->IsLoadingProject() || prj->GetCurrentlyCompilingTarget())
1555  wxBell();
1556  else
1557  pm->SaveProject(prj);
1558  }
1559  }
1560 }
1561 
1563 {
1565  if (pm->IsLoadingProject())
1566  {
1567  wxBell();
1568  return;
1569  }
1570 
1571  wxArrayTreeItemIds selections;
1572  int count = m_pTree->GetSelections(selections);
1573  if (count == 0)
1574  return;
1575  std::set<cbProject*> projectsToClose;
1576 
1577  for (size_t ii = 0; ii < selections.GetCount(); ++ii)
1578  {
1579  FileTreeData* ftd = reinterpret_cast<FileTreeData*>(m_pTree->GetItemData(selections[ii]));
1580  if (!ftd || ftd->GetKind() != FileTreeData::ftdkProject)
1581  continue;
1582 
1583  cbProject* prj = ftd->GetProject();
1584  if (prj)
1585  {
1586  if (prj->GetCurrentlyCompilingTarget())
1587  wxBell();
1588  else
1589  projectsToClose.insert(prj);
1590  }
1591  }
1592 
1593  for (std::set<cbProject*>::iterator it = projectsToClose.begin(); it != projectsToClose.end(); ++it)
1594  pm->CloseProject(*it);
1595 
1596  if (pm->GetProjects()->GetCount() > 0 && !pm->GetActiveProject())
1597  pm->SetProject(pm->GetProjects()->Item(0), false);
1598 
1599  Manager::Get()->GetAppWindow()->Refresh();
1600 }
1601 
1603 {
1605  if (!sel.IsOk())
1606  return;
1607 
1608  if (FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel))
1609  {
1610  if ( ProjectFile* pf = ftd->GetProjectFile() )
1611  Manager::Get()->GetEditorManager()->Save(pf->file.GetFullPath());
1612  }
1613 }
1614 
1616 {
1618  if (!sel.IsOk())
1619  return;
1620 
1621  if (FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel))
1622  {
1623  if ( ProjectFile* pf = ftd->GetProjectFile() )
1624  Manager::Get()->GetEditorManager()->Close(pf->file.GetFullPath());
1625  }
1626 }
1627 
1629 {
1631 }
1632 
1634 {
1637  if (!ftd)
1638  return;
1639 
1640  // open all files from a folder
1641  if (cbMessageBox(_("Are you sure you want to recursively open from the project all the files under this folder?"),
1642  _("Confirmation"),
1644  {
1645  return;
1646  }
1647 
1648  if ( ftd->GetKind() == FileTreeData::ftdkFolder
1650  {
1651  OpenFilesRecursively(sel);
1652  }
1653 
1654  event.Skip();
1655 }
1656 
1658 {
1660  if (!sel.IsOk())
1661  return;
1662 
1664  if (!ftd)
1665  return;
1666 
1667  if ( ProjectFile* pf = ftd->GetProjectFile() )
1668  {
1669  wxString filename = pf->file.GetFullPath();
1670  if (event.GetId() == idOpenWithInternal)
1671  {
1672  if ( cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filename) )
1673  {
1674  ed->SetProjectFile(pf);
1675  ed->Show(true);
1676  return;
1677  }
1678  }
1679  else
1680  {
1681  PluginsArray mimes = Manager::Get()->GetPluginManager()->GetMimeOffers();
1682  cbMimePlugin* plugin = (cbMimePlugin*)mimes[event.GetId() - idOpenWith[0]];
1683  if (plugin && plugin->OpenFile(filename) == 0)
1684  return;
1685  }
1686  wxString msg;
1687  msg.Printf(_("Failed to open '%s'."), filename.c_str());
1688  Manager::Get()->GetLogManager()->LogError(msg);
1689  }
1690 }
1691 
1693 {
1694  if ( cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject() )
1695  prj->ShowNotes(false, true);
1696 }
1697 
1699 {
1701  cbProject* activePrj = pm->GetActiveProject();
1702  if (event.GetId() == idMenuProjectProperties)
1703  {
1704  wxString backupTitle = activePrj ? activePrj->GetTitle() : _T("");
1705  if (ProjectShowOptions(activePrj))
1706  {
1707  // make sure that cbEVT_PROJECT_ACTIVATE
1708  // is sent (maybe targets have changed)...
1709  // rebuild tree only if title has changed
1710  pm->SetProject(activePrj, backupTitle != activePrj->GetTitle());
1711  }
1712  }
1713  else if (event.GetId() == idMenuTreeProjectProperties)
1714  {
1716  if (!sel.IsOk())
1717  return;
1718 
1720 
1721  cbProject* prj = ftd ? ftd->GetProject() : activePrj;
1722  wxString backupTitle = prj ? prj->GetTitle() : _T("");
1723  if (ProjectShowOptions(prj) && prj == activePrj)
1724  {
1725  // rebuild tree and make sure that cbEVT_PROJECT_ACTIVATE
1726  // is sent (maybe targets have changed)...
1727  // rebuild tree only if title has changed
1728  pm->SetProject(prj, backupTitle != prj->GetTitle());
1729  }
1730  // if project title has changed, update the appropriate tab tooltips
1731  wxString newTitle = prj->GetTitle();
1732  if (backupTitle != newTitle)
1733  {
1735  if (nb)
1736  {
1737  wxString toolTip;
1738  for (size_t i = 0; i < nb->GetPageCount(); ++i)
1739  {
1740  toolTip = nb->GetPageToolTip(i);
1741  if (toolTip.EndsWith(_("Project: ") + backupTitle))
1742  {
1743  toolTip.Replace(_("Project: ") + backupTitle,_("Project: ") + newTitle);
1744  nb->SetPageToolTip(i, toolTip);
1745  }
1746  }
1747  }
1748  }
1749  }
1750  else if (event.GetId() == idMenuTreeFileProperties)
1751  {
1753  if (!sel.IsOk())
1754  return;
1755 
1757 
1758  cbProject* prj = ftd ? ftd->GetProject() : activePrj;
1759  if (prj)
1760  {
1761  if (ftd && ftd->GetFileIndex() != -1)
1762  {
1763  if ( ProjectFile* pf = ftd->GetProjectFile() )
1764  pf->ShowOptions(Manager::Get()->GetAppWindow());
1765  }
1766  }
1767  }
1768  else // active editor properties
1769  {
1771  {
1772  if ( ProjectFile* pf = ed->GetProjectFile() )
1773  pf->ShowOptions(Manager::Get()->GetAppWindow());
1774  else
1775  {
1776  // active editor not-in-project
1777  ProjectFileOptionsDlg dlg(Manager::Get()->GetAppWindow(), ed->GetFilename());
1778  PlaceWindow(&dlg);
1779  dlg.ShowModal();
1780  }
1781  }
1782  }
1783 }
1784 
1789 template<typename Func>
1790 static void applyFileOptionChange(std::set<cbProject*> &modified, wxTreeCtrl &tree, Func func)
1791 {
1792  wxArrayTreeItemIds selected;
1793  size_t count = tree.GetSelections(selected);
1794  for (size_t ii = 0; ii < count; ++ii)
1795  {
1796  wxTreeItemId id = selected[ii];
1797  if (!id.IsOk())
1798  continue;
1799 
1800  FileTreeData* ftd = (FileTreeData*)tree.GetItemData(id);
1801  if (!ftd || ftd->GetKind() != FileTreeData::ftdkFile)
1802  continue;
1803 
1804  ProjectFile* pf = ftd->GetProjectFile();
1805  if (pf && func(*pf))
1806  {
1807  if (pf->GetParentProject())
1808  modified.insert(pf->GetParentProject());
1809  }
1810  }
1811 }
1812 
1814 {
1815  std::set<cbProject*> modified;
1816  if (event.GetId() == idMenuTreeOptionsCompile)
1817  {
1818  const bool checked = event.IsChecked();
1819  applyFileOptionChange(modified, *m_pTree, [checked](ProjectFile &pf) -> bool {
1820  if (pf.compile != checked)
1821  {
1822  pf.compile = checked;
1823  return true;
1824  }
1825  else
1826  return false;
1827  });
1828  }
1829  else if (event.GetId() == idMenuTreeOptionsLink)
1830  {
1831  const bool checked = event.IsChecked();
1832  applyFileOptionChange(modified, *m_pTree, [checked](ProjectFile &pf) -> bool {
1833  if (pf.link != checked)
1834  {
1835  pf.link = checked;
1836  return true;
1837  }
1838  else
1839  return false;
1840  });
1841  }
1842  else if (event.GetId() == idMenuTreeOptionsEnableBoth || event.GetId() == idMenuTreeOptionsDisableBoth)
1843  {
1844  const bool newValue = (event.GetId() == idMenuTreeOptionsEnableBoth);
1845  applyFileOptionChange(modified, *m_pTree, [newValue](ProjectFile &pf) -> bool {
1846  pf.compile = pf.link = newValue;
1847  return true;
1848  });
1849  }
1850 
1851  for (std::set<cbProject*>::iterator it = modified.begin(); it != modified.end(); ++it)
1852  (*it)->SetModified(true);
1853  if (!modified.empty())
1854  RebuildTree();
1855  event.Skip();
1856 }
1857 
1859 {
1860  ProjectFileRelativePathCmp(cbProject* pActiveProject) : m_pActiveProject(pActiveProject) {}
1862  {
1863  if (pf1->GetParentProject() == m_pActiveProject && pf2->GetParentProject() != m_pActiveProject)
1864  return true;
1865  else if (pf1->GetParentProject() != m_pActiveProject && pf2->GetParentProject() == m_pActiveProject)
1866  return false;
1867  else
1868  {
1869  int relCmp = pf1->relativeFilename.Cmp(pf2->relativeFilename);
1870 
1871  if (relCmp == 0)
1872  return pf1 < pf2;
1873  else
1874  return relCmp < 0;
1875  }
1876  }
1877 private:
1879 };
1880 
1882 {
1883  size_t operator()(const wxString& s) const
1884  {
1885 #if wxCHECK_VERSION(3, 0, 0)
1886  return std::hash<std::wstring>()(s.ToStdWstring());
1887 #else
1888  return std::hash<std::wstring>()(s.wc_str());
1889 #endif // wxCHECK_VERSION
1890  }
1891 };
1892 
1894 {
1896  cbProject* activePrj = pm->GetActiveProject();
1897 
1898  if (!activePrj)
1899  {
1900  Manager::Get()->GetLogManager()->DebugLog(_("No active project!"));
1901  return;
1902  }
1903 
1904  ProjectsArray* pa = pm->GetProjects();
1905 
1906  std::unordered_map<wxString, ProjectFile*, cbStringHash> uniqueAbsPathFiles;
1907  for (size_t prjIdx = 0; prjIdx < pa->GetCount(); ++prjIdx)
1908  {
1909  cbProject* prj = (*pa)[prjIdx];
1910  if (!prj) continue;
1911 
1912  for (FilesList::iterator it = prj->GetFilesList().begin(); it != prj->GetFilesList().end(); ++it)
1913  {
1914  ProjectFile *projectFile = *it;
1915  uniqueAbsPathFiles.insert({projectFile->file.GetFullPath(), projectFile});
1916  }
1917  }
1918 
1919  typedef std::vector<ProjectFile*> VProjectFiles;
1920  VProjectFiles pfiles;
1921  if (!uniqueAbsPathFiles.empty())
1922  {
1923  pfiles.reserve(uniqueAbsPathFiles.size());
1924  for (const auto &pf : uniqueAbsPathFiles)
1925  pfiles.push_back(pf.second);
1926  std::sort(pfiles.begin(), pfiles.end(), ProjectFileRelativePathCmp(activePrj));
1927  }
1928 
1929  struct Iterator : IncrementalSelectIteratorIndexed
1930  {
1931  Iterator(VProjectFiles &pfiles, bool showProject) :
1932  m_pfiles(pfiles),
1933  m_ShowProject(showProject),
1934  m_ColumnWidth(300)
1935  {
1936  }
1937 
1938  int GetTotalCount() const override
1939  {
1940  return m_pfiles.size();
1941  }
1942  const wxString& GetItemFilterString(int index) const override
1943  {
1944  return m_pfiles[index]->relativeFilename;
1945  }
1946  wxString GetDisplayText(int index, cb_unused int column) const override
1947  {
1948  ProjectFile* pf = m_pfiles[m_indices[index]];
1949  return MakeDisplayName(*pf);
1950  }
1951  int GetColumnWidth(cb_unused int column) const override
1952  {
1953  return m_ColumnWidth;
1954  }
1955 
1956  void CalcColumnWidth(wxListCtrl &list) override
1957  {
1958  int length = 0;
1959  ProjectFile *pfLongest = nullptr;
1960  for (const auto &pf : m_pfiles)
1961  {
1962  int pfLength = pf->relativeFilename.length();
1963  if (m_ShowProject)
1964  pfLength += pf->GetParentProject()->GetTitle().length() + 3;
1965  if (pfLength > length)
1966  {
1967  length = pfLength;
1968  pfLongest = pf;
1969  }
1970  }
1971  if (pfLongest)
1972  {
1973  const wxString &longestString = MakeDisplayName(*pfLongest);
1974  int yTemp;
1975  list.GetTextExtent(longestString, &m_ColumnWidth, &yTemp);
1976  // just to be safe if the longest string is made of thin letters.
1977  m_ColumnWidth += 50;
1978  }
1979  else
1980  m_ColumnWidth = 300;
1981  }
1982 
1983  private:
1984  wxString MakeDisplayName(ProjectFile &pf) const
1985  {
1986  if (m_ShowProject)
1987  return pf.relativeFilename + wxT(" (") + pf.GetParentProject()->GetTitle() + wxT(")");
1988  else
1989  return pf.relativeFilename;
1990  }
1991  private:
1992  const VProjectFiles &m_pfiles;
1993  wxString temp;
1994  bool m_ShowProject;
1995  int m_ColumnWidth;
1996  };
1997 
1998  Iterator iterator(pfiles, (pa->GetCount() > 1));
1999  GotoFile dlg(Manager::Get()->GetAppWindow(), &iterator, _("Select file..."), _("Please select file to open:"));
2000  PlaceWindow(&dlg);
2001  if (dlg.ShowModal() == wxID_OK)
2002  {
2003  int selection = dlg.GetSelection();
2004  if (selection >= 0 && selection < int(pfiles.size()))
2005  DoOpenFile(pfiles[selection], pfiles[selection]->file.GetFullPath());
2006  }
2007 }
2008 
2010 {
2011  bool do_categorise = event.IsChecked();
2012 
2013  if (do_categorise)
2015  else
2017 
2018  Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewCategorize, do_categorise);
2019  Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/categorize_tree"), do_categorise);
2020 
2021  RebuildTree();
2022 }
2023 
2025 {
2026  bool do_use_folders = event.IsChecked();
2027 
2028  if (do_use_folders)
2030  else
2032 
2033  Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewUseFolders, do_use_folders);
2034  Manager::Get()->GetAppFrame()->GetMenuBar()->Enable(idMenuViewHideFolderName, !do_use_folders);
2035  Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/use_folders"), do_use_folders);
2036 
2037  // Do not create an invalid state
2038  if (do_use_folders)
2039  {
2041  Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewHideFolderName, false);
2042  Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/hide_folder_name"), false);
2043  }
2044 
2045  RebuildTree();
2046 }
2047 
2049 {
2050  bool do_hide_folder_name = event.IsChecked();
2051 
2052  if (do_hide_folder_name)
2054  else
2056 
2057  Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewHideFolderName, do_hide_folder_name);
2058  Manager::Get()->GetAppFrame()->GetMenuBar()->Enable(idMenuViewUseFolders, !do_hide_folder_name);
2059  Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/hide_folder_name"), do_hide_folder_name);
2060 
2061  // Do not create an invalid state
2062  if (do_hide_folder_name)
2063  {
2065  Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewUseFolders, false);
2066  Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/use_folders"), false);
2067  }
2068 
2069  RebuildTree();
2070 }
2071 
2073 {
2075  ProjectsFileMasksDlg dlg(Manager::Get()->GetAppWindow(), fgam);
2076  PlaceWindow(&dlg);
2077  if (dlg.ShowModal() == wxID_OK)
2078  {
2079  fgam->Save();
2080  RebuildTree();
2081  }
2082 }
2083 
2085 {
2086  wxArrayString nodes;
2087  wxTreeItemIdValue cookie;
2088  wxTreeItemId item = m_pTree->GetFirstChild(node, cookie);
2089  while (item.IsOk())
2090  {
2091  nodes.Add(m_pTree->GetItemText(item));
2092  if (m_pTree->ItemHasChildren(item))
2093  {
2094  const wxArrayString& children = ListNodes(item);
2095  const wxString parent = nodes.Last();
2096  for (size_t i = 0; i < children.GetCount(); ++i)
2097  nodes.Add(parent + wxT("/") + children[i]);
2098  }
2099  item = m_pTree->GetNextChild(node, cookie);
2100  }
2101  return nodes;
2102 }
2103 
2105 {
2107  if (!sel.IsOk())
2108  return;
2109  wxArrayString files = ListNodes(sel);
2110  if (files.IsEmpty())
2111  return;
2112 
2114 
2115  // workspace selected, add *.cbp filenames
2117  if ( pm->GetWorkspace() && !(FileTreeData*)m_pTree->GetItemData(sel) )
2118  {
2119  for (size_t i = 0; i < pm->GetProjects()->GetCount(); ++i)
2120  {
2121  const cbProject* prj = pm->GetProjects()->Item(i);
2122  const wxFileName file(prj->GetFilename());
2123  files.Add(file.GetFullName());
2124  fileNameMap[file.GetFullName()] = prj->GetTitle();
2125  }
2126  }
2127 
2128  struct Iterator : IncrementalSelectIteratorIndexed
2129  {
2130  Iterator(const wxArrayString &files) : m_files(files), m_ColumnWidth(300)
2131  {
2132  }
2133 
2134  int GetTotalCount() const override
2135  {
2136  return m_files.size();
2137  }
2138  const wxString& GetItemFilterString(int index) const override
2139  {
2140  return m_files[index];
2141  }
2142  wxString GetDisplayText(int index, cb_unused int column) const override
2143  {
2144  return m_files[m_indices[index]];
2145  }
2146 
2147  int GetColumnWidth(cb_unused int column) const override
2148  {
2149  return m_ColumnWidth;
2150  }
2151 
2152  void CalcColumnWidth(wxListCtrl &list) override
2153  {
2154  int index = -1;
2155  size_t length = 0;
2156  for (size_t ii = 0; ii < m_files.size(); ++ii)
2157  {
2158  size_t itemLength = m_files[ii].length();
2159  if (itemLength > length)
2160  {
2161  index = ii;
2162  length = itemLength;
2163  }
2164  }
2165  if (index >= 0 && index < int(m_files.size()))
2166  {
2167  int yTemp;
2168  list.GetTextExtent(m_files[index], &m_ColumnWidth, &yTemp);
2169  // just to be safe if the longest string is made of thin letters.
2170  m_ColumnWidth += 50;
2171  }
2172  else
2173  m_ColumnWidth = 300;
2174  }
2175 
2176  private:
2177  const wxArrayString &m_files;
2178  int m_ColumnWidth;
2179  };
2180  Iterator iter(files);
2181  GotoFile dlg(Manager::Get()->GetAppWindow(), &iter, _("Find file..."),
2182  _("Please enter the name of the file you are searching:"));
2183 
2184  ConfigManager *cfg = Manager::Get()->GetConfigManager(wxT("project_manager"));
2185 
2186  // Add a checkbox at the bottom that control if the selected file will be opened in an editor.
2187  wxCheckBox *chkOpen = new wxCheckBox(&dlg, wxID_ANY, _("Open file"));
2188  chkOpen->SetValue(cfg->ReadBool(wxT("/find_file_open"), false));
2189  dlg.AddControlBelowList(chkOpen);
2190 
2191  PlaceWindow(&dlg);
2192  if (dlg.ShowModal() != wxID_OK)
2193  return;
2194  const long selection = dlg.GetSelection();
2195  if (selection == wxNOT_FOUND)
2196  return;
2197 
2198  wxString file = files[selection];
2199  ConfigManagerContainer::StringToStringMap::iterator it = fileNameMap.find(file);
2200  if (it != fileNameMap.end())
2201  file = it->second; // resolve .cbp project filename
2202  wxTreeItemIdValue cookie;
2203  wxTreeItemId item = m_pTree->GetFirstChild(sel, cookie);
2204  while (item.IsOk())
2205  {
2206  if (m_pTree->GetItemText(item) == file)
2207  break; // found it, exit
2208  else if (file.StartsWith(m_pTree->GetItemText(item) + wxT("/")))
2209  {
2210  // expand node
2211  file = file.Mid(m_pTree->GetItemText(item).Length() + 1);
2212  sel = item;
2213  item = m_pTree->GetFirstChild(sel, cookie);
2214  }
2215  else // try next node
2216  item = m_pTree->GetNextChild(sel, cookie);
2217  }
2218  if (item.IsOk())
2219  {
2220  m_pTree->UnselectAll();
2221  m_pTree->SelectItem(item);
2222  const FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(item);
2223  if (ftd && chkOpen->IsChecked())
2224  {
2225  if ( ProjectFile* pf = ftd->GetProjectFile() )
2226  DoOpenFile(pf, pf->file.GetFullPath()); // open the file
2227  else if ( ftd->GetKind() == FileTreeData::ftdkProject
2228  && ftd->GetProject() )
2229  {
2230  // change active project
2231  pm->SetProject(ftd->GetProject(), false);
2232  }
2233  }
2234  cfg->Write(wxT("/find_file_open"), chkOpen->IsChecked());
2235  }
2236  else
2237  {
2238  // error ?!
2239  // ... this should not fail (unless the tree was modified during selection)
2240  }
2241 }
2242 
2244 {
2245  wxString fld = cbGetTextFromUser(_("Please enter the new virtual folder path:"), _("New virtual folder"));
2246  if (fld.IsEmpty())
2247  return;
2248 
2250  if (!sel.IsOk())
2251  return;
2252 
2254  if (!ftd)
2255  return;
2256 
2257  cbProject* prj = ftd->GetProject();
2258  if (!prj)
2259  return;
2260 
2261  ProjectVirtualFolderAdded(prj, m_pTree, sel, fld);
2262 // RebuildTree();
2263 }
2264 
2266 {
2268  if (!sel.IsOk())
2269  return;
2270 
2272  if (!ftd)
2273  return;
2274 
2275  cbProject* prj = ftd->GetProject();
2276  if (!prj)
2277  return;
2278 
2279  ProjectVirtualFolderDeleted(prj, m_pTree, sel);
2280  RebuildTree();
2281 }
2282 
2284 {
2286  if (!sel.IsOk())
2287  return;
2288 
2290  if (!ftd)
2291  return;
2292 
2293  cbProject* prj = ftd->GetProject();
2294  if (!prj)
2295  return;
2296 
2297  wxString oldName = ftd->GetFolder();
2298 
2299  if (oldName.EndsWith(_T("/")))
2300  oldName.RemoveLast(1);
2301 
2302  wxTextEntryDialog dlg(Manager::Get()->GetAppWindow(),
2303  _("Please enter the new name for the virtual folder:"),
2304  _("Rename Virtual Folder"),
2305  oldName,
2306  wxOK | wxCANCEL | wxCENTRE);
2307  if (dlg.ShowModal() == wxID_OK)
2308  {
2309  ProjectVirtualFolderRenamed(prj, m_pTree, sel, dlg.GetValue());
2310  RebuildTree();
2311  }
2312 }
2313 
2315 {
2316  // what item do we start editing?
2317  wxTreeItemId id = event.GetItem();
2318  if (!id.IsOk())
2319  {
2320  event.Veto();
2321  return;
2322  }
2323 
2324  // if no data associated with it, disallow
2326  if (!ftd)
2327  {
2328  event.Veto();
2329  return;
2330  }
2331 
2332  // only allow editing virtual folders
2334  event.Veto();
2335 }
2336 
2338 {
2340  if (!ftd)
2341  {
2342  event.Veto();
2343  return;
2344  }
2345  cbProject* prj = ftd->GetProject();
2346  if (!prj)
2347  {
2348  event.Veto();
2349  return;
2350  }
2351 
2352  if (!ProjectVirtualFolderRenamed(prj, m_pTree, event.GetItem(), event.GetLabel()))
2353  event.Veto();
2354 // RebuildTree();
2355 }
2356 
2358 {
2359  if (event.GetId() == idMenuFileProperties)
2360  {
2361  EditorManager *editorManager = Manager::Get()->GetEditorManager();
2362  bool enableProperties;
2363  if (editorManager)
2364  {
2365  EditorBase *editor = editorManager->GetActiveEditor();
2366  EditorBase *startHerePage = editorManager->GetEditor(g_StartHereTitle);
2367 
2368  enableProperties = (editor && editor != startHerePage);
2369  if (enableProperties)
2370  enableProperties = !cbHasRunningCompilers(Manager::Get()->GetPluginManager());
2371  }
2372  else
2373  enableProperties = false;
2374 
2375  event.Enable(enableProperties);
2376  }
2377  else if (event.GetId() == idMenuProjectProperties || event.GetId() == idMenuAddFile
2378  || event.GetId() == idMenuAddFilesRecursively || event.GetId() == idMenuRemoveFile
2379  || event.GetId() == idMenuProjectTreeProps || event.GetId() == idMenuAddVirtualFolder
2380  || event.GetId() == idMenuDeleteVirtualFolder)
2381  {
2382  ProjectManager *projectManager = Manager::Get()->GetProjectManager();
2383  if (!projectManager || (projectManager->GetIsRunning() != nullptr))
2384  event.Enable(false);
2385  else
2386  {
2387  cbProject *project = projectManager->GetActiveProject();
2388  if (!project)
2389  event.Enable(false);
2390  else
2391  {
2392  bool enable = !cbHasRunningCompilers(Manager::Get()->GetPluginManager());
2393  event.Enable(enable);
2394  }
2395  }
2396  }
2397  else
2398  event.Skip();
2399 }
2400 
2402 {
2403  event.Skip();
2404 }
2405 
2407 {
2409  if (!sel.IsOk())
2410  return;
2411 
2413  if (!ftd)
2414  return;
2415 
2416  cbProject* prj = ftd->GetProject();
2417  if (!prj)
2418  return;
2419 
2420  if (ftd->GetProjectFile()->AutoGeneratedBy())
2421  {
2422  cbMessageBox(_("Can't rename file because it is auto-generated..."), _("Error"));
2423  return;
2424  }
2425 
2426  wxString path = ftd->GetProjectFile()->file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
2427  wxString name = ftd->GetProjectFile()->file.GetFullName();
2428 
2429  wxTextEntryDialog dlg(Manager::Get()->GetAppWindow(), _("Please enter the new name:"), _("Rename file"), name, wxOK | wxCANCEL | wxCENTRE);
2430  PlaceWindow(&dlg);
2431  if (dlg.ShowModal() == wxID_OK)
2432  {
2433  wxFileName fn(dlg.GetValue());
2434  wxString new_name = fn.GetFullName();
2435 
2436  if (name != new_name)
2437  {
2438  #ifdef __WXMSW__
2439  // only overwrite files, if the names are the same, but with different cases
2440  if (!wxRenameFile(path + name, path + new_name, (name.Lower() == new_name.Lower())))
2441  #else
2442  if (!wxRenameFile(path + name, path + new_name, false))
2443  #endif
2444  {
2445  wxBell();
2446  return;
2447  }
2448 
2449  if ( ProjectFile* pf = ftd->GetProjectFile() )
2450  {
2451  pf->Rename(new_name);
2452  RebuildTree();
2453  }
2454  }
2455  }
2456 }
2457 
2459 {
2460  const wxKeyEvent& key_event = event.GetKeyEvent();
2461 
2463  if ( prj
2464  && (prj->GetCurrentlyCompilingTarget() == nullptr)
2465  && ( key_event.GetKeyCode() == WXK_DELETE
2466  || key_event.GetKeyCode() == WXK_NUMPAD_DELETE ) )
2467  {
2468  m_DraggingSelection.Clear(); // fix delete while drag crash
2469  wxCommandEvent command(0, idMenuRemoveFilePopup);
2470  OnRemoveFileFromProject(command);
2471  }
2472  else
2473  event.Skip();
2474 }
2475 
2476 void ProjectManagerUI::MoveProjectUp(cbProject* project, bool warpAround)
2477 {
2478  if (!project)
2479  return;
2481  ProjectsArray* pa = pm->GetProjects();
2482 
2483  int idx = pa->Index(project);
2484  if (idx == wxNOT_FOUND)
2485  return; // project not opened in project manager???
2486 
2487  if (idx == 0)
2488  {
2489  if (!warpAround)
2490  return;
2491  else
2492  idx = pa->Count();
2493  }
2494  pa->RemoveAt(idx--);
2495  pa->Insert(project, idx);
2496  RebuildTree();
2497  if (pm->GetWorkspace())
2498  pm->GetWorkspace()->SetModified(true);
2499 
2500  // re-select the project
2501  wxTreeItemId itemId = project->GetProjectNode();
2502  cbAssert(itemId.IsOk());
2503  m_pTree->SelectItem(itemId);
2504  m_pTree->EnsureVisible(itemId);
2505 }
2506 
2507 void ProjectManagerUI::MoveProjectDown(cbProject* project, bool warpAround)
2508 {
2509  if (!project)
2510  return;
2512  ProjectsArray* pa = pm->GetProjects();
2513 
2514  int idx = pa->Index(project);
2515  if (idx == wxNOT_FOUND)
2516  return; // project not opened in project manager???
2517 
2518  if (idx == (int)pa->Count() - 1)
2519  {
2520  if (!warpAround)
2521  return;
2522  else
2523  idx = 0;
2524  }
2525  pa->RemoveAt(idx++);
2526  pa->Insert(project, idx);
2527  RebuildTree();
2528  if (pm->GetWorkspace())
2529  pm->GetWorkspace()->SetModified(true);
2530 
2531  // re-select the project
2532  wxTreeItemId itemId = project->GetProjectNode();
2533  cbAssert(itemId.IsOk());
2534  m_pTree->SelectItem(itemId);
2535  m_pTree->EnsureVisible(itemId);
2536 }
2537 
2538 
2540 {
2541  if (!Manager::Get()->GetEditorManager()->QueryCloseAll())
2542  return false;
2543  ProjectsArray* pa = Manager::Get()->GetProjectManager()->GetProjects();
2544  for (size_t i = 0; i < pa->GetCount(); ++i)
2545  {
2546  // Ask for saving modified projects. However,
2547  // we already asked to save projects' files;
2548  // do not ask again
2549  if (!QueryCloseProject((*pa)[i], true))
2550  return false;
2551  }
2552  return true;
2553 }
2554 
2555 bool ProjectManagerUI::QueryCloseProject(cbProject* proj, bool dontsavefiles)
2556 {
2557  if (!proj)
2558  return true;
2559  if (proj->GetCurrentlyCompilingTarget())
2560  return false;
2561  if (!dontsavefiles)
2562  {
2563  if (!proj->QueryCloseAllFiles())
2564  return false;
2565  }
2566  if (proj->GetModified() && !Manager::IsBatchBuild())
2567  {
2568  wxString msg;
2569  msg.Printf(_("Project '%s' is modified...\nDo you want to save the changes?"), proj->GetTitle().c_str());
2570  switch (cbMessageBox(msg, _("Save project"), wxICON_QUESTION | wxYES_NO | wxCANCEL))
2571  {
2572  case wxID_YES:
2573  if (!proj->Save())
2574  return false;
2575  case wxID_NO:
2576  break;
2577  case wxID_CANCEL: // fall-through
2578  default:
2579  return false;
2580  }
2581  }
2582  return true;
2583 }
2584 
2586 {
2588  if (!wkspc)
2589  return true;
2590 
2591  // Don't ask to save the default workspace, if blank workspace is used on app startup.
2592  bool blankWorkspace = Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/blank_workspace"), true);
2593  if ( !(wkspc->IsDefault() && blankWorkspace) )
2594  {
2595  // always save workspace layout
2596  wkspc->SaveLayout();
2597  if ( wkspc->GetModified() )
2598  {
2599  // workspace needs save
2600  wxString msg;
2601  msg.Printf(_("Workspace '%s' is modified. Do you want to save it?"), wkspc->GetTitle().c_str());
2602  switch (cbMessageBox(msg, _("Save workspace"),
2604  {
2605  case wxID_YES:
2607  break;
2608  case wxID_CANCEL:
2609  return false;
2610  default:
2611  break;
2612  }
2613  }
2614  }
2615 
2616  // We always want to ask to save all loaded projects.
2617  if (!QueryCloseAllProjects())
2618  return false;
2619  return true;
2620 }
2621 
2623 {
2624  cbProject* prj = project;
2625  if (!prj)
2627  if (!prj)
2628  return -1;
2629 
2630  // ask for target
2631  wxArrayString array;
2632  int count = prj->GetBuildTargetsCount();
2633  for (int i = 0; i < count; ++i)
2634  array.Add(prj->GetBuildTarget(i)->GetTitle());
2635  int target = cbGetSingleChoiceIndex(_("Select the target:"), _("Project targets"), array, m_pTree, wxSize(300, 400));
2636 
2637  return target;
2638 }
2639 
2641 {
2642  wxArrayInt indices;
2643  cbProject* prj = project;
2644  if (!prj)
2646  if (!prj)
2647  return indices;
2648 
2649  // ask for target
2650  wxArrayString array;
2651  int count = prj->GetBuildTargetsCount();
2652  for (int i = 0; i < count; ++i)
2653  array.Add(prj->GetBuildTarget(i)->GetTitle());
2654 
2655  MultiSelectDlg dlg(nullptr, array, true, _("Select the targets this file should belong to:"));
2656  PlaceWindow(&dlg);
2657  if (dlg.ShowModal() == wxID_OK)
2658  indices = dlg.GetSelectedIndices();
2659 
2660  return indices;
2661 }
2662 
2664 {
2665  ProjectDepsDlg dlg(Manager::Get()->GetAppWindow(), base);
2666  PlaceWindow(&dlg);
2667  dlg.ShowModal();
2668 }
2669 
2671 {
2672  if (m_isCheckingForExternallyModifiedProjects) // for some reason, a mutex locker does not work???
2673  return;
2675 
2676  // check also the projects (TO DO : what if we gonna reload while compiling/debugging)
2677  // TODO : make sure the same project is the active one again
2679  if ( ProjectsArray* pa = pm->GetProjects())
2680  {
2681  bool reloadAll = false;
2682  // make a copy of all the pointers before we start messing with closing and opening projects
2683  // the hash (pa) could change the order
2684  std::vector<cbProject*> ProjectPointers;
2685  for (unsigned int idxProject = 0; idxProject < pa->Count(); ++idxProject)
2686  ProjectPointers.push_back(pa->Item(idxProject));
2687 
2688  for (unsigned int idxProject = 0; idxProject < ProjectPointers.size(); ++idxProject)
2689  {
2690  cbProject* prj = ProjectPointers[idxProject];
2691  wxFileName fname(prj->GetFilename());
2692  wxDateTime last = fname.GetModificationTime();
2693  if (last.IsLaterThan(prj->GetLastModificationTime()))
2694  { // was modified -> reload
2695  int ret = -1;
2696  if (!reloadAll)
2697  {
2698  Manager::Get()->GetLogManager()->Log(prj->GetFilename());
2699  wxString msg;
2700  msg.Printf(_("Project %s is modified outside the IDE...\nDo you want to reload it (you will lose any unsaved work)?"),
2701  prj->GetFilename().c_str());
2702  ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow(), false, msg);
2703  dlg.SetTitle(_("Reload Project?"));
2704  PlaceWindow(&dlg);
2705 
2706  // Find the window, that actually has the mouse-focus and force a release.
2707  // This prevents crash on windows or hang on wxGTK.
2708  wxWindow* win = wxWindow::GetCapture();
2709  if (win)
2710  win->ReleaseMouse();
2711 
2712  ret = dlg.ShowModal();
2713  reloadAll = ret == crAll;
2714  }
2715  if (reloadAll || ret == crYes)
2716  pm->ReloadProject(prj);
2717  else if (ret == crCancel)
2718  break;
2719  else if (ret == crNo)
2720  prj->Touch();
2721  }
2722  } // end for : idx : idxProject
2723  }
2725 }
2726 
2727 
2728 namespace
2729 {
2730 
2731 static void ProjectTreeSortChildrenRecursive(cbTreeCtrl* tree, const wxTreeItemId& parent)
2732 {
2733  if (!tree->ItemHasChildren(parent))
2734  return;
2735 
2736  tree->SortChildren(parent);
2737 
2738  wxTreeItemIdValue cookie = nullptr;
2739  wxTreeItemId current = tree->GetFirstChild(parent, cookie);
2740  while (current)
2741  {
2742  ProjectTreeSortChildrenRecursive(tree, current);
2743  current = tree->GetNextChild(parent, cookie);
2744  }
2745 }
2746 
2747 // helper function used by AddTreeNode
2748 static wxString GetRelativeFolderPath(wxTreeCtrl* tree, wxTreeItemId parent)
2749 {
2750  wxString fld;
2751  while (parent.IsOk())
2752  {
2753  FileTreeData* ftd = (FileTreeData*)tree->GetItemData(parent);
2754  if ( !ftd
2755  || ( (ftd->GetKind() != FileTreeData::ftdkFolder)
2756  && (ftd->GetKind() != FileTreeData::ftdkVirtualFolder) ) )
2757  break;
2758  fld.Prepend(tree->GetItemText(parent) + wxFILE_SEP_PATH);
2759  parent = tree->GetItemParent(parent);
2760  }
2761  return fld;
2762 }
2763 
2764 wxTreeItemId ProjectFindNodeToInsertAfter(wxTreeCtrl* tree, const wxString& text, const wxTreeItemId& parent, bool in_folders)
2765 {
2766  wxTreeItemId result;
2767 
2768  if (tree && parent.IsOk())
2769  {
2770  wxTreeItemIdValue cookie = nullptr;
2771 
2772  int fldIdx = cbProjectTreeImages::FolderIconIndex();
2774  wxTreeItemId last;
2775  wxTreeItemId child = tree->GetFirstChild(parent, cookie);
2776  while (child)
2777  {
2778  bool is_folder = tree->GetItemImage(child) == fldIdx || tree->GetItemImage(child) == vfldIdx;
2779 
2780  if (in_folders)
2781  {
2782  if (!is_folder || text.CmpNoCase(tree->GetItemText(child)) < 0)
2783  {
2784  result = last;
2785  break;
2786  }
2787  }
2788  else
2789  {
2790  if (!is_folder && text.CmpNoCase(tree->GetItemText(child)) < 0)
2791  {
2792  result = last;
2793  break;
2794  }
2795  }
2796 
2797  last = child;
2798  child = tree->GetNextChild(parent, cookie);
2799  }
2800  if (!result.IsOk())
2801  result = last;
2802  }
2803 
2804  return result;
2805 }
2806 
2807 wxTreeItemId ProjectAddTreeNode(cbProject* project, wxTreeCtrl* tree, const wxString& text, const wxTreeItemId& parent,
2808  bool useFolders, FileTreeData::FileTreeDataKind folders_kind, bool compiles, int image,
2809  FileTreeData* data)
2810 {
2811  // see if the text contains any path info, e.g. plugins/compilergcc/compilergcc.cpp
2812  // in that case, take the first element (plugins in this example), create a sub-folder
2813  // with the same name and recurse with the result...
2814 
2815  wxTreeItemId ret;
2816 
2817  if (text.IsEmpty())
2818  {
2819  delete data;
2820  return ret;
2821  }
2822 
2823  wxString path = text;
2824 
2825  // special case for windows and files on a different drive
2826  if ( platform::windows && (path.Length() > 1) && (path.GetChar(1) == _T(':')) )
2827  path.Remove(1, 1);
2828 
2829  // avoid empty node names in case of UNC paths, then, at least the first two chars are slashes
2830  while ((path.Length() > 1) && (path.GetChar(0) == _T('\\') || path.GetChar(0) == _T('/')) )
2831  path.Remove(0, 1);
2832 
2833  if (path.IsEmpty())
2834  {
2835  delete data;
2836  return ret;
2837  }
2838 
2839  int pos = path.Find(_T('/'));
2840  if (pos == -1)
2841  pos = path.Find(_T('\\'));
2842  if (useFolders && pos >= 0)
2843  {
2844  // ok, we got it. now split it up and re-curse
2845  wxString folder = path.Left(pos);
2846  // avoid consecutive path separators
2847  while (path.GetChar(pos + 1) == _T('/') || path.GetChar(pos + 1) == _T('\\'))
2848  ++pos;
2849  path = path.Right(path.Length() - pos - 1);
2850 
2851  wxTreeItemIdValue cookie = nullptr;
2852 
2853  wxTreeItemId newparent = tree->GetFirstChild(parent, cookie);
2854  while (newparent)
2855  {
2856  wxString itemText = tree->GetItemText(newparent);
2857  if (itemText.Matches(folder))
2858  break;
2859  newparent = tree->GetNextChild(parent, cookie);
2860  }
2861 
2862  if (!newparent)
2863  {
2864  // in order not to override wxTreeCtrl to sort alphabetically but the
2865  // folders be always on top, we just search here where to put the new folder...
2866  int fldIdx = cbProjectTreeImages::FolderIconIndex();
2868 
2869  newparent = ProjectFindNodeToInsertAfter(tree, folder, parent, true);
2870 
2871  FileTreeData* ftd = new FileTreeData(*data);
2872  ftd->SetKind(folders_kind);
2873  if (folders_kind != FileTreeData::ftdkVirtualFolder)
2874  ftd->SetFolder(project->GetCommonTopLevelPath() + GetRelativeFolderPath(tree, parent) + folder + wxFILE_SEP_PATH);
2875  else
2876  ftd->SetFolder(GetRelativeFolderPath(tree, parent) + folder + wxFILE_SEP_PATH);
2877  ftd->SetProjectFile(nullptr);
2878  int idx = folders_kind != FileTreeData::ftdkVirtualFolder ? fldIdx : vfldIdx;
2879  newparent = tree->InsertItem(parent, newparent, folder, idx, idx, ftd);
2880  }
2881  ret = ProjectAddTreeNode(project, tree, path, newparent, true, folders_kind, compiles, image, data);
2882  }
2883  else
2884  {
2885  ret = tree->AppendItem(parent, text, image, image, data);
2886  if (!compiles)
2887  {
2889  tree->SetItemTextColour(ret, manager->GetColour(wxT("project_tree_non_source_files")));
2890  }
2891  }
2892  return ret;
2893 }
2894 
2895 bool ProjectCanDragNode(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node)
2896 {
2897  // what item do we start dragging?
2898  if (!node.IsOk())
2899  return false;
2900 
2901  // if no data associated with it, disallow
2902  FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
2903  if (!ftd)
2904  return false;
2905 
2906  // if not ours, disallow
2907  if (ftd->GetProject() != project)
2908  return false;
2909 
2910  // allow only if it is a file or a virtual folder or project file(.cbp)
2911  return ( (ftd->GetKind() == FileTreeData::ftdkFile)
2913  || (ftd->GetKind() == FileTreeData::ftdkProject) );
2914 }
2915 
2916 void ProjectCopyTreeNodeRecursively(wxTreeCtrl* tree, const wxTreeItemId& item, const wxTreeItemId& new_parent)
2917 {
2918  // first, some sanity checks
2919  if (!tree || !item.IsOk() || !new_parent.IsOk())
2920  return;
2921 
2922  FileTreeData* ftd = (FileTreeData*)tree->GetItemData(item);
2923  FileTreeData* ftd_moved = ftd ? new FileTreeData(*ftd) : nullptr;
2924  int idx = tree->GetItemImage(item); // old image
2925  wxColour col = tree->GetItemTextColour(item); // old colour
2926 
2927  wxTreeItemId insert = ProjectFindNodeToInsertAfter(tree, tree->GetItemText(item), new_parent, ftd && ftd->GetKind() == FileTreeData::ftdkVirtualFolder);
2928  wxTreeItemId target = tree->InsertItem(new_parent, insert, tree->GetItemText(item), idx, idx, ftd_moved);
2929  tree->SetItemTextColour(target, col);
2930 
2931  // recurse for folders
2932  if (tree->ItemHasChildren(item))
2933  {
2934  // vfolder: recurse for files all contained files virtual path
2935  wxTreeItemIdValue cookie;
2936  wxTreeItemId child = tree->GetFirstChild(item, cookie);
2937  while (child.IsOk())
2938  {
2939  ProjectCopyTreeNodeRecursively(tree, child, target);
2940  child = tree->GetNextChild(item, cookie);
2941  }
2942  }
2943 
2944  if (!tree->IsExpanded(new_parent))
2945  tree->Expand(new_parent);
2946 
2947  if (ftd_moved && ftd_moved->GetProjectFile())
2948  ftd_moved->GetProjectFile()->virtual_path = GetRelativeFolderPath(tree, new_parent);
2949 }
2950 
2951 bool ProjectVirtualFolderDragged(cbProject* project, wxTreeCtrl* tree, wxTreeItemId from, wxTreeItemId to)
2952 {
2953  FileTreeData* ftdFrom = static_cast<FileTreeData*>(tree->GetItemData(from));
2954  FileTreeData* ftdTo = static_cast<FileTreeData*>(tree->GetItemData(to) );
2955  if (!ftdFrom || !ftdTo)
2956  return false;
2957 
2960  wxString fromFolderPath = ftdFrom->GetFolder();
2961  wxString toFolderPath = ftdTo->GetFolder();
2962 
2963  wxString fromFolder = fromFolderPath;
2964  fromFolder = fromFolder.RemoveLast();
2965  fromFolder = fromFolder.AfterLast(sepChar) + sep;
2966  wxString toFolder = toFolderPath;
2967  toFolder = toFolder.RemoveLast();
2968  toFolder = toFolder.AfterLast(sepChar) + sep;
2969 
2971  {
2972  const wxArrayString &oldArray = project->GetVirtualFolders();
2973  wxArrayString newFolders;
2974  for (size_t i = 0; i < oldArray.GetCount(); ++i)
2975  {
2976  wxString item = oldArray[i];
2977  wxString toFolderStr;
2978  if (toFolderPath.StartsWith(fromFolderPath.BeforeFirst(sepChar)))
2979  {
2980  // The virtual folder is drageed under same root
2981  int posFrom = item.Find(fromFolderPath);
2982  if (posFrom != wxNOT_FOUND)
2983  {
2984  wxString fromFolderStr = item.Mid(posFrom);
2985  item = item.Left(posFrom);
2986  if (!item.IsEmpty())
2987  newFolders.Add(item);
2988  }
2989  else if (item.IsSameAs(toFolderPath))
2990  {
2991  // First add it to folder structure
2992  newFolders.Add(item);
2993  wxString fromFolderStr = fromFolderPath;
2994  fromFolderStr = fromFolderStr.RemoveLast();
2995  fromFolderStr = fromFolderStr.AfterLast(wxFileName::GetPathSeparator());
2996  newFolders.Add(item + fromFolderStr + sep);
2997  }
2998  else
2999  newFolders.Add(item);
3000  }
3001  else
3002  {
3003  // A virtual folder has been dropped from a different place
3004  if (item.Find(toFolderPath + fromFolder) != wxNOT_FOUND)
3005  {
3006  cbMessageBox(_("Another Virtual folder with same name exists in the destination folder!"),
3007  _("Error"), wxOK | wxICON_ERROR, Manager::Get()->GetAppWindow());
3008  return false;
3009  }
3010  if (item.StartsWith(toFolderPath.BeforeFirst(sepChar)))
3011  newFolders.Add(item);
3012  else if (item.StartsWith(fromFolderPath.BeforeFirst(sepChar)))
3013  {
3014  int pos = item.Find(fromFolder);
3015  if (pos == 0)
3016  {
3017  if (!toFolderPath.IsEmpty())
3018  newFolders.Add(toFolderPath + item);
3019  }
3020  else
3021  {
3022  wxString temp = item.Left(pos);
3023  newFolders.Add(item.Left(pos));
3024  if (!toFolderPath.IsEmpty())
3025  newFolders.Add(toFolderPath + item.Mid(pos));
3026  }
3027  }
3028  }
3029  }
3030 
3031  project->SetVirtualFolders(newFolders);
3032  }
3033  else if (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder && ftdTo->GetKind() == FileTreeData::ftdkProject)
3034  {
3035  const wxArrayString &oldArray = project->GetVirtualFolders();
3036  wxArrayString newFolders;
3037 
3038  newFolders.Add(fromFolder);
3039 
3040  for (size_t i = 0; i < oldArray.GetCount(); ++i)
3041  {
3042  wxString item = oldArray[i];
3043  if (item.StartsWith(fromFolder))
3044  {
3045  // We can't overwrite an existing folder
3046  cbMessageBox(_("Another Virtual folder with same name exists in the Project tree!"),
3047  _("Error"), wxOK | wxICON_ERROR, Manager::Get()->GetAppWindow());
3048  return false;
3049  }
3050  else if (item.StartsWith(fromFolderPath))
3051  {
3052  int pos = item.Find(fromFolderPath);
3053  if (pos != wxNOT_FOUND)
3054  {
3055  item = item.Mid(pos + fromFolderPath.Length());
3056  if (item.Length() > 1)
3057  {
3058  item = item.Prepend(fromFolder);
3059  if (newFolders.Index(item) == wxNOT_FOUND)
3060  newFolders.Add(item);
3061  }
3062  else
3063  continue;
3064  }
3065  else
3066  newFolders.Add(item);
3067  }
3068  else
3069  newFolders.Add(item);
3070  }
3071 
3072  project->SetVirtualFolders(newFolders);
3073  }
3074  return true;
3075 }
3076 
3077 bool ProjectNodeDragged(cbProject* project, wxTreeCtrl* tree, wxArrayTreeItemIds& fromArray, wxTreeItemId to)
3078 {
3079  // what items did we drag?
3080  if (!to.IsOk())
3081  return false;
3082 
3083  // if no data associated with it, disallow
3084  FileTreeData* ftdTo = (FileTreeData*)tree->GetItemData(to);
3085  if (!ftdTo)
3086  return false;
3087 
3088  // if not ours, disallow
3089  if (ftdTo->GetProject() != project)
3090  return false;
3091 
3092  // allow only if a file or vfolder was dragged on a file, another vfolder or the project itself
3093  if ( (ftdTo->GetKind() != FileTreeData::ftdkFile)
3094  && (ftdTo->GetKind() != FileTreeData::ftdkVirtualFolder)
3095  && (ftdTo->GetKind() != FileTreeData::ftdkProject) )
3096  {
3097  return false;
3098  }
3099 
3100  wxTreeItemId parentTo = ftdTo->GetKind() == FileTreeData::ftdkFile ? tree->GetItemParent(to) : to;
3101 
3102  // do all the checking for all selected items first (no movement yet, just checking!)
3103  size_t count = fromArray.Count();
3104  for (size_t i = 0; i < count; i++)
3105  {
3106  wxTreeItemId from = fromArray[i];
3107  if (!from.IsOk())
3108  return false;
3109 
3110  // if no data associated with it, disallow
3111  FileTreeData* ftdFrom = (FileTreeData*)tree->GetItemData(from);
3112  if (!ftdFrom)
3113  return false;
3114 
3115  // if not ours, disallow
3116  if (ftdFrom->GetProject() != project)
3117  return false;
3118 
3119  // allow only if a file or vfolder was dragged on a file, another vfolder or the project itself
3120  if ( (ftdFrom->GetKind() != FileTreeData::ftdkFile)
3121  && (ftdFrom->GetKind() != FileTreeData::ftdkVirtualFolder) )
3122  return false;
3123 
3124  // don't drag under the same parent
3125  wxTreeItemId parentFrom = ftdFrom->GetKind() == FileTreeData::ftdkFile ? tree->GetItemParent(from) : from;
3126  if (parentFrom == parentTo)
3127  return false;
3128 
3129  // A special check for virtual folders.
3130  if ( (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder)
3131  || (ftdTo->GetKind() == FileTreeData::ftdkVirtualFolder) )
3132  {
3133  wxTreeItemId root = tree->GetRootItem();
3134  wxTreeItemId toParent = tree->GetItemParent(to);
3135  while (toParent != root)
3136  {
3137  if (toParent == from)
3138  return false;
3139  toParent = tree->GetItemParent(toParent);
3140  }
3141  if (!ProjectVirtualFolderDragged(project, tree, from, to))
3142  return false;
3143  }
3144  }
3145 
3146  // now that we have successfully done the checking, do the moving
3147  for (size_t i = 0; i < count; i++)
3148  {
3149  wxTreeItemId from = fromArray[i];
3150  // finally; make the move
3151  ProjectCopyTreeNodeRecursively(tree, from, parentTo);
3152  // remove old node
3153  tree->Delete(from);
3154  }
3155 
3156  project->SetModified(true);
3157 
3159 
3160  return true;
3161 }
3162 
3163 bool ProjectHasVirtualFolder(const wxString &folderName, const wxArrayString &virtualFolders)
3164 {
3165  for (size_t i = 0; i < virtualFolders.GetCount(); ++i)
3166  {
3167  if (virtualFolders[i].StartsWith(folderName))
3168  {
3169  cbMessageBox(_("A virtual folder with the same name already exists."),
3170  _("Error"), wxICON_WARNING);
3171  return false;
3172  }
3173  }
3174  return true;
3175 }
3176 
3177 bool ProjectVirtualFolderAdded(cbProject* project, wxTreeCtrl* tree,
3178  wxTreeItemId parent_node, const wxString& virtual_folder)
3179 {
3180  wxString foldername = GetRelativeFolderPath(tree, parent_node);
3181  foldername << virtual_folder;
3182  foldername.Replace(_T("/"), wxString(wxFILE_SEP_PATH), true);
3183  foldername.Replace(_T("\\"), wxString(wxFILE_SEP_PATH), true);
3184  if (foldername.Last() != wxFILE_SEP_PATH)
3185  foldername << wxFILE_SEP_PATH;
3186 
3187  const wxArrayString &virtualFolders = project->GetVirtualFolders();
3188  if (!ProjectHasVirtualFolder(foldername, virtualFolders))
3189  return false;
3190  project->AppendUniqueVirtualFolder(foldername);
3191 
3193  ftd->SetProjectFile(nullptr);
3194  ftd->SetFolder(foldername);
3195 
3197 
3198  ProjectAddTreeNode(project, tree, foldername, project->GetProjectNode(), true,
3199  FileTreeData::ftdkVirtualFolder, true, vfldIdx, ftd);
3200  if (!tree->IsExpanded(parent_node))
3201  tree->Expand(parent_node);
3202 
3203  project->SetModified(true);
3204 
3205 // Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderAdded: %s: %s"), foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
3206  return true;
3207 }
3208 
3209 void ProjectVirtualFolderDeleted(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node)
3210 {
3211  // what item do we start dragging?
3212  if (!node.IsOk())
3213  return;
3214 
3215  // if no data associated with it, disallow
3216  FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
3217  if (!ftd)
3218  return;
3219 
3220  // if not ours, disallow
3221  if (ftd->GetProject() != project)
3222  return;
3223 
3224  wxString foldername = GetRelativeFolderPath(tree, node);
3225  wxString parent_foldername = GetRelativeFolderPath(tree, tree->GetItemParent(node));
3226 
3227  project->RemoveVirtualFolders(foldername);
3228  if (!parent_foldername.IsEmpty())
3229  project->AppendUniqueVirtualFolder(parent_foldername);
3230 // Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderDeleted: %s: %s"), foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
3231 }
3232 
3233 bool ProjectVirtualFolderRenamed(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node, const wxString& new_name)
3234 {
3235  if (new_name.IsEmpty())
3236  return false;
3237 
3238  if (new_name.First(_T(';')) != wxNOT_FOUND ||
3239  new_name.First(_T('/')) != wxNOT_FOUND ||
3240  new_name.First(_T('\\')) != wxNOT_FOUND)
3241  {
3242  cbMessageBox(_("A virtual folder name cannot contain these special characters: \";\", \"\\\" or \"/\"."),
3243  _("Error"), wxICON_WARNING);
3244  return false;
3245  }
3246 
3247  // what item are we renaming?
3248  if (!node.IsOk())
3249  return false;
3250 
3251  // is it a different name?
3252  if (tree->GetItemText(node) == new_name)
3253  return false;
3254 
3255  // if no data associated with it, disallow
3256  FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
3257  if (!ftd)
3258  return false;
3259 
3260  // if not ours, disallow
3261  if (ftd->GetProject() != project)
3262  return false;
3263 
3264  wxString old_foldername = GetRelativeFolderPath(tree, node);
3265  wxString new_foldername = GetRelativeFolderPath(tree, tree->GetItemParent(node)) + new_name + wxFILE_SEP_PATH;
3266 
3267  const wxArrayString &virtualFolders = project->GetVirtualFolders();
3268  if (!ProjectHasVirtualFolder(new_foldername, virtualFolders))
3269  return false;
3270 
3271  project->ReplaceVirtualFolder(old_foldername, new_foldername);
3272 
3273 // Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderRenamed: %s to %s: %s"), old_foldername.c_str(), new_foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
3274  return true;
3275 }
3276 
3280 bool ProjectShowOptions(cbProject* project)
3281 {
3282  if (!project)
3283  return false;
3284  ProjectOptionsDlg dlg(Manager::Get()->GetAppWindow(), project);
3285  PlaceWindow(&dlg);
3286  if (dlg.ShowModal() == wxID_OK)
3287  {
3288  // update file details
3289  FilesList &filesList = project->GetFilesList();
3290  for (FilesList::iterator it = filesList.begin(); it != filesList.end(); ++it)
3291  {
3292  if ( ProjectFile* pf = *it )
3293  pf->UpdateFileDetails();
3294  }
3296  event.SetProject(project);
3297  Manager::Get()->ProcessEvent(event);
3298  return true;
3299  }
3300  return false;
3301 }
3302 } // anonymous namespace
3303 
3305 {
3306  if (!tree)
3307  return;
3308 
3309 #ifdef fileload_measuring
3310  wxStopWatch sw;
3311 #endif
3312  int fldIdx = cbProjectTreeImages::FolderIconIndex();
3314  bool read_only = (!wxFile::Access(project->GetFilename().c_str(), wxFile::write));
3315  int prjIdx = cbProjectTreeImages::ProjectIconIndex(read_only);
3316 
3317  tree->SetCompareFunction(ptvs);
3318 
3319  // add our project's root item
3321  project->SetProjectNode(tree->AppendItem(root, project->GetTitle(), prjIdx, prjIdx, ftd));
3322  wxTreeItemId others, generated;
3323  others = generated = project->GetProjectNode();
3324  wxTreeItemId* pGroupNodes = nullptr; // file group nodes (if enabled)
3325 
3326  // create file-type categories nodes (if enabled)
3327  bool do_categorise = ((ptvs&ptvsCategorize) && fgam);
3328  if (do_categorise)
3329  {
3330  // obtain all group nodes available from "file groups and masks"
3331  pGroupNodes = new wxTreeItemId[fgam->GetGroupsCount()];
3332  for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
3333  {
3334  ftd = new FileTreeData(project, FileTreeData::ftdkVirtualGroup);
3335  ftd->SetFolder(fgam->GetGroupName(i));
3336  pGroupNodes[i] = tree->AppendItem(project->GetProjectNode(), fgam->GetGroupName(i), fldIdx, fldIdx, ftd);
3337  }
3338  // add a default category "Generated" for all auto-generated file types
3339  ftd = new FileTreeData(project, FileTreeData::ftdkVirtualGroup);
3340  generated = tree->AppendItem(project->GetProjectNode(), _("Auto-generated"), fldIdx, fldIdx, ftd);
3341 
3342  // add a default category "Others" for all non-matching file-types
3343  ftd = new FileTreeData(project, FileTreeData::ftdkVirtualGroup);
3344  others = tree->AppendItem(project->GetProjectNode(), _("Others"), fldIdx, fldIdx, ftd);
3345  }
3346 
3347  // Now add any virtual folders
3348  const wxArrayString& virtualFolders = project->GetVirtualFolders();
3349  for (size_t i = 0; i < virtualFolders.GetCount(); ++i)
3350  {
3351  ftd = new FileTreeData(project, FileTreeData::ftdkVirtualFolder);
3352  ftd->SetFolder(virtualFolders[i]);
3353  ProjectAddTreeNode(project, tree, virtualFolders[i], project->GetProjectNode(), true,
3354  FileTreeData::ftdkVirtualFolder, true, vfldIdx, ftd);
3355  }
3356 
3357  // iterate all project files and add them to the tree
3358  int count = 0;
3359  FilesList& fileList = project->GetFilesList();
3360  for (FilesList::iterator it = fileList.begin(); it != fileList.end(); ++it)
3361  {
3362  ProjectFile* pf = *it;
3363  if (!pf)
3364  {
3365  Manager::Get()->GetLogManager()->DebugLogError(_T("Looks like the project's file list is broken?!"));
3366  continue;
3367  }
3368 
3369  ftd = new FileTreeData(project, FileTreeData::ftdkFile);
3370  ftd->SetFileIndex(count++);
3371  ftd->SetProjectFile(pf);
3372  ftd->SetFolder(pf->file.GetFullPath());
3373 
3374  wxString nodetext = pf->relativeToCommonTopLevelPath;
3376 
3377  // by default, the parent node is the project node (in case of no grouping, no virtual folders)
3378  wxTreeItemId parentNode = project->GetProjectNode();
3379 
3380  // now change the parent node for virtual folders and/or if grouping is enabled
3381  // first check, if the file is under a virtual folder
3382  if (!pf->virtual_path.IsEmpty())
3383  {
3384  nodetext = pf->virtual_path + wxFILE_SEP_PATH + pf->file.GetFullName();
3385  folders_kind = FileTreeData::ftdkVirtualFolder;
3386  wxString slash = pf->virtual_path.Last() == wxFILE_SEP_PATH ? _T("") : wxString(wxFILE_SEP_PATH);
3387  ftd->SetFolder(pf->virtual_path);
3388 
3389  project->AppendUniqueVirtualFolder(pf->virtual_path + slash);
3390  }
3391  // second check, if files grouping is enabled and find the group parent
3392  else if (do_categorise && pGroupNodes)
3393  {
3394  bool found = false;
3395 
3396  // auto-generated files end up all together
3397  if (pf->AutoGeneratedBy())
3398  {
3399  parentNode = generated;
3400  found = true;
3401  }
3402  else // else try to match a group
3403  {
3404  const wxFileName fname(pf->relativeToCommonTopLevelPath);
3405  const wxString &fnameFullname = fname.GetFullName();
3406 
3407  for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
3408  {
3409  if (fgam->MatchesMask(fnameFullname, i))
3410  {
3411  parentNode = pGroupNodes[i];
3412  found = true;
3413  break;
3414  }
3415  }
3416  }
3417 
3418  // if not matched a group, put it in "Others" group
3419  if (!found)
3420  parentNode = others;
3421  }
3422 
3423  // probably remove the path of the entry (depending on settings)
3424  if ( !(ptvs&ptvsUseFolders)
3425  && (ptvs&ptvsHideFolderName)
3426  && (folders_kind != FileTreeData::ftdkVirtualFolder) )
3427  {
3428  nodetext = pf->file.GetFullName();
3429  }
3430 
3431  // add file in the tree
3432  bool useFolders = (ptvs&ptvsUseFolders) || (folders_kind == FileTreeData::ftdkVirtualFolder);
3433  pf->SetTreeItemId(ProjectAddTreeNode(project, tree, nodetext, parentNode, useFolders, folders_kind,
3434  pf->compile, (int)pf->GetFileState(), ftd));
3435  }// iteration of project files
3436 
3437  // finally remove empty tree nodes (like empty groups)
3438  if (do_categorise && pGroupNodes)
3439  {
3440  for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
3441  {
3442  if (tree->GetChildrenCount(pGroupNodes[i], false) == 0)
3443  tree->Delete(pGroupNodes[i]);
3444  }
3445  if (tree->GetChildrenCount(others, false) == 0)
3446  tree->Delete(others);
3447  if (tree->GetChildrenCount(generated, false) == 0)
3448  tree->Delete(generated);
3449  }
3450  delete[] pGroupNodes;
3451 
3452  ProjectTreeSortChildrenRecursive(tree, project->GetProjectNode());
3453  tree->Expand(project->GetProjectNode());
3454 #ifdef fileload_measuring
3455  Manager::Get()->GetLogManager()->DebugLogError(F(_T("%s::%s:%d took : %d ms"), cbC2U(__FILE__).c_str(),cbC2U(__PRETTY_FUNCTION__).c_str(), __LINE__, (int)sw.Time()));
3456 #endif
3457 }
wxTreeItemId GetProjectNode()
Definition: cbproject.h:311
wxArrayString GetSelectedStrings() const
ProjectFile * GetFileByFilename(const wxString &filename, bool isRelative=true, bool isUnixFilename=false)
Access a file of the project.
Definition: cbproject.cpp:1049
virtual wxTreeItemId GetNextChild(const wxTreeItemId &item, wxTreeItemIdValue &cookie) const
wxString AfterLast(wxUniChar ch) const
wxString F(const wxChar *msg,...)
sprintf-like function
Definition: logmanager.h:20
static int WorkspaceIconIndex(bool read_only=false)
Definition: globals.cpp:1534
wxPoint wxGetMousePosition()
wxString relativeToCommonTopLevelPath
The relative filename to the common top-level path.
Definition: projectfile.h:135
void FinishLoadingWorkspace(cbProject *activeProject, const wxString &workspaceTitle)
bool IsLoadingProject()
Check if the project manager is loading a project.
int GetKeyCode() const
void OnViewFileMasks(wxCommandEvent &event)
void MoveProjectDown(cbProject *project, bool warpAround=false)
Move a project down in the project manager tree.
void RemoveFilesRecursively(wxTreeItemId &sel_id)
bool QueryCloseWorkspace()
Asks user to save the workspace, projects and files (Yes/No/cancel).
bool wxRenameFile(const wxString &file1, const wxString &file2, bool overwrite=true)
bool AddPage(wxWindow *page, const wxString &caption, bool select=false, const wxBitmap &bitmap=wxNullBitmap)
Add Page.
Definition: cbauibook.cpp:561
const wxTreeItemId & GetTreeItemId() const
Returns the wxTreeItemId for the file.
Definition: projectfile.h:210
const wxString & GetFolder() const
Definition: cbproject.h:62
wxTreeItemId m_TreeRoot
bool Matches(const wxString &mask) const
virtual int ShowModal()
virtual void SetItemBold(const wxTreeItemId &item, bool bold=true)
int wxNewId()
PluginManager * GetPluginManager() const
Definition: manager.cpp:444
#define wxICON_QUESTION
static int ProjectIconIndex(bool read_only=false)
Definition: globals.cpp:1542
bool QueryCloseAllProjects()
Checks whether all projects are saved.
void ShowFileInTree(ProjectFile &projectFile)
void OnSaveProject(wxCommandEvent &event)
void SetVirtualFolders(const wxArrayString &folders)
Set the virtual folders list.
Definition: cbproject.cpp:968
wxString relativeFilename
The relative (to the project) filename of this file.
Definition: projectfile.h:131
virtual wxTreeItemId GetItemParent(const wxTreeItemId &item) const
#define wxICON_WARNING
void OnOpenWith(wxCommandEvent &event)
void OnAddFilesToProjectRecursively(wxCommandEvent &event)
void ReloadProject(cbProject *project)
Reloads a project and tries to keep everything the same (project order, dependencies, active project)
ProjectFileRelativePathCmp(cbProject *pActiveProject)
wxImageList * m_pImages
ConfigManager * GetConfigManager(const wxString &name_space) const
Definition: manager.cpp:474
void DebugLogError(const wxString &msg)
Definition: logmanager.h:147
bool IsOk() const
void SetCompareFunction(const int ptvs)
Definition: cbtreectrl.cpp:44
virtual void RebuildTree()=0
Rebuild the project manager&#39;s tree.
static Manager * Get()
Use Manager::Get() to get a pointer to its instance Manager::Get() is guaranteed to never return an i...
Definition: manager.cpp:182
void OnRenameVirtualFolder(wxCommandEvent &event)
void OnViewCategorize(wxCommandEvent &event)
void Touch()
Sets the last modification time for the project to &#39;now&#39;.
Definition: cbproject.cpp:428
void MoveProjectUp(cbProject *project, bool warpAround=false)
Move a project up in the project manager tree.
bool SaveWorkspaceAs(const wxString &filename)
Save the open workspace under a different filename.
wxString Lower() const
void UnfreezeTree(bool force=false)
Le the tree control be updated again.
int Cmp(const wxString &s) const
void RemoveProject(cbProject *project)
void BeginRemoveFiles()
Notify that file(s) will be removed shortly.
Definition: cbproject.cpp:607
virtual bool GetModified() const
Is this workspace modified?
Definition: cbworkspace.h:102
Information about the plugin.
Definition: pluginmanager.h:38
void OnIdle(wxIdleEvent &event)
static bool IsAppShuttingDown()
Definition: manager.cpp:333
size_t length() const
void CreateMenuTreeProps(wxMenu *menu, bool popup)
#define wxICON_ERROR
void RegisterColour(const wxString &category, const wxString &name, const wxString &id, const wxColour &defaultColour)
wxTreeItemId m_RightClickItem
DLLIMPORT wxBitmap cbLoadBitmap(const wxString &filename, wxBitmapType bitmapType=wxBITMAP_TYPE_PNG)
This function loads a bitmap from disk.
Definition: globals.cpp:1102
FileTreeDataKind
The kind of tree node.
Definition: cbproject.h:41
wxArrayInt AskForMultiBuildTargetIndex(cbProject *project)
Utility function.
virtual void DeleteAllItems()
virtual FilesList & GetFilesList()
Provides an easy way to iterate all the files belonging in this target.
Definition: cbproject.h:685
void Enable(int id, bool enable)
DLLIMPORT wxString cbGetTextFromUser(const wxString &message, const wxString &caption=cbGetTextFromUserPromptStr, const wxString &default_value=wxEmptyString, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord, bool centre=true)
Definition: globals.cpp:1465
wxWindow * pWindow
The window to dock.
Definition: sdk_events.h:137
wxFileName file
The full filename of this file.
Definition: projectfile.h:126
void SetProjectNode(wxTreeItemId node)
Sets the root item of this item, should not be called by user&#39;s code!
Definition: cbproject.h:314
void RemoveFileFromProject(ProjectFile *pfile, cbProject *project)
Remove a file from a project.
static int VirtualFolderIconIndex()
Definition: globals.cpp:1555
bool QueryCloseAllFiles()
Act like closing all project files, but don&#39;t do it.
Definition: cbproject.cpp:1078
virtual void SetTitle(const wxString &title)
Set the workspace&#39;s title.
bool m_isCheckingForExternallyModifiedProjects
virtual size_t GetChildrenCount(const wxTreeItemId &item, bool recursively=true) const
bool compile
Compile flag.
Definition: projectfile.h:138
bool ReadBool(const wxString &name, bool defaultVal=false)
std::wstring ToStdWstring() const
DLLIMPORT wxString ChooseDirectory(wxWindow *parent, const wxString &message=_("Select directory"), const wxString &initialPath=_T(""), const wxString &basePath=_T(""), bool askToMakeRelative=false, bool showCreateDirButton=false)
Definition: globals.cpp:639
virtual wxString GetFilename() const
Get the workspace file&#39;s name.
Definition: cbworkspace.h:60
int Index(const wxString &sz, bool bCase=true, bool bFromEnd=false) const
void SetModified(bool modified=true) override
Mark the project as modified or not.
Definition: cbproject.cpp:179
virtual void SetFilterIndex(int filterIndex)
unsigned int GetGroupsCount() const
Return total number of groups.
virtual bool CanHandleFile(const wxString &filename) const =0
Can a file be handled by this plugin?
size_t GetPageCount() const
void SetData(wxDataObject &data)
virtual bool IsDefault() const
Is this workspace the Code::Blocks default?
Definition: cbworkspace.h:93
#define wxCANCEL
virtual void EnsureVisible(const wxTreeItemId &item)
size_t Length() const
void OnSetActiveProject(wxCommandEvent &event)
Event used to request from the main app to add a window to the docking system.
Definition: sdk_events.h:83
wxCStrData c_str() const
bool wxDirExists(const wxString &dirname)
virtual void Activate()
Activate this editor.
Definition: editorbase.cpp:175
wxMenuItem * Append(int id, const wxString &item=wxEmptyString, const wxString &helpString=wxEmptyString, wxItemKind kind=wxITEM_NORMAL)
long Time() const
DLLIMPORT int cbGetSingleChoiceIndex(const wxString &message, const wxString &caption, const wxArrayString &choices, wxWindow *parent=NULL, const wxSize &size=wxSize(300, 300), int initialSelection=0)
Definition: globals.cpp:1427
#define wxNO_DEFAULT
#define _T(string)
FileType
Known file types.
Definition: globals.h:49
virtual void SetModified(bool modified)
Mark the workspace as modified or not.
void UpdateActiveProject(cbProject *oldProject, cbProject *newProject, bool refresh)
virtual size_t GetSelections(wxArrayTreeItemIds &selection) const
wxString title
Definition: pluginmanager.h:41
#define wxYES_NO
bool Close(const wxString &filename, bool dontsave=false)
virtual int GetItemImage(const wxTreeItemId &item, wxTreeItemIcon which=wxTreeItemIcon_Normal) const
void wxBell()
void OnTreeEndDrag(wxTreeEvent &event)
cbProjectManagerUI & GetUI()
EVTIMPORT const wxEventType cbEVT_PROJECT_OPTIONS_CHANGED
Definition: sdk_events.cpp:110
The default style: All "off".
Definition: globals.h:137
DLLIMPORT wxString GetStringFromArray(const wxArrayString &array, const wxString &separator=DEFAULT_ARRAY_SEP, bool SeparatorAtEnd=true)
Definition: globals.cpp:122
DLLIMPORT FileType FileTypeOf(const wxString &filename)
Definition: globals.cpp:285
void ReplaceVirtualFolder(const wxString &oldFolder, const wxString &newFolder)
Replaced the oldFolder with newFolder or appends newFolder to the end of the list if oldFolder is not...
Definition: cbproject.cpp:945
int AddMultipleFilesToProject(const wxArrayString &filelist, cbProject *project, int target=-1)
Add multiple files to a project.
void OnCloseFile(wxCommandEvent &event)
virtual wxTreeItemData * GetItemData(const wxTreeItemId &item) const
const FilesGroupsAndMasks * GetFilesGroupsAndMasks() const
ProjectFile * GetProjectFile() const
Definition: cbproject.h:61
virtual wxString GetItemText(const wxTreeItemId &item) const
wxString & Remove(size_t pos)
static size_t GetAllFiles(const wxString &dirname, wxArrayString *files, const wxString &filespec=wxEmptyString, int flags=wxDIR_DEFAULT)
void CalculateCommonTopLevelPath()
Calculates the top-level path common to all project files.
Definition: cbproject.cpp:319
void EndRemoveFiles()
Notify that file(s) removal finished.
Definition: cbproject.cpp:614
virtual bool IsExpanded(const wxTreeItemId &item) const
Base class for mime plugins.
Definition: cbplugin.h:684
#define wxT(string)
void OnNotes(wxCommandEvent &event)
wxMouseState wxGetMouseState()
void OnTabPosition(wxCommandEvent &event)
#define wxNOT_FOUND
void OnFileOptions(wxCommandEvent &event)
Represents a file in a Code::Blocks project.
Definition: projectfile.h:39
ProjectBuildTarget * GetCurrentlyCompilingTarget()
Get a pointer to the currently compiling target.
Definition: cbproject.h:478
size_t GetMenuItemCount() const
size_t operator()(const wxString &s) const
A generic Code::Blocks event.
Definition: sdk_events.h:20
int CmpNoCase(const wxString &s) const
wxWindow * GetAppWindow() const
Definition: manager.cpp:424
wxTreeItemId GetItem() const
virtual void SortChildren(const wxTreeItemId &item)
void SwitchToProjectsPage()
Switches the management&#39;s notebook to the Projects tab.
void SetFolder(const wxString &folder)
Definition: cbproject.h:70
const PluginInfo * GetPluginInfo(const wxString &pluginName)
cbMimePlugin * GetMIMEHandlerForFile(const wxString &filename)
EditorManager * GetEditorManager() const
Definition: manager.cpp:434
virtual void SetItemTextColour(const wxTreeItemId &item, const wxColour &col)
void LogError(const wxString &msg, int i=app_log)
Definition: logmanager.h:142
int GetFileIndex() const
Definition: cbproject.h:60
If true, create folders as needed. If false, the list is flat (not compatible with "hie folder name")...
Definition: globals.h:139
void OnSaveFile(wxCommandEvent &event)
wxUSE_UNICODE_dependent wxChar
virtual void Enable(bool enable=true)
void OnCloseWorkspace(wxCommandEvent &event)
DLLIMPORT bool cbHasRunningCompilers(const PluginManager *manager)
wxString BeforeFirst(wxUniChar ch, wxString *rest=NULL) const
void RemoveVirtualFolders(const wxString &folder)
Remove all virtual folders starting with folder.
Definition: cbproject.cpp:924
void OnBeginEditNode(wxTreeEvent &event)
wxString GetFileMasks(unsigned int group) const
Return a specific group file mask.
ProjectManager * GetProjectManager() const
Functions returning pointers to the respective sub-manager instances.
Definition: manager.cpp:429
cbAuiNotebook * m_pNotebook
void BuildProjectTree(cbProject *project, cbTreeCtrl *tree, const wxTreeItemId &root, int ptvs, FilesGroupsAndMasks *fgam)
(Re)build the project tree.
bool MakeRelativeTo(const wxString &pathBase=wxEmptyString, wxPathFormat format=wxPATH_NATIVE)
virtual wxTreeItemId GetFirstChild(const wxTreeItemId &item, wxTreeItemIdValue &cookie) const
void Write(const wxString &name, const wxString &value, bool ignoreEmpty=false)
void OnRightClick(wxCommandEvent &event)
If true, the folder name will be hidden and only the file name will be shown (not compatible with "us...
Definition: globals.h:140
Represents a Code::Blocks project.
Definition: cbproject.h:96
void OnTabContextMenu(wxAuiNotebookEvent &event)
wxWindow * wxFindWindowAtPoint(const wxPoint &pt)
virtual const wxString & GetFilename() const
void Check(int id, bool check)
EditorBase * GetActiveEditor()
wxString & RemoveLast(size_t n=1)
wxArrayInt GetSelectedIndices() const
wxMenuItem * AppendSubMenu(wxMenu *submenu, const wxString &text, const wxString &help=wxEmptyString)
bool AppendUniqueVirtualFolder(const wxString &folder)
Appends a new virtual folder to the end of the list only if it doesn&#39;t exists.
Definition: cbproject.cpp:913
cbPlugin * GetIsRunning() const
Return a pointer to the plugin which is running the application.
wxColour GetColour(const wxString &id) const
size_t SetSelection(size_t new_page)
DLLIMPORT wxString cbC2U(const char *str)
Return str as a proper unicode-compatible string.
Definition: globals.cpp:733
void OnViewHideFolderName(wxCommandEvent &event)
virtual const wxString & GetTitle() const
Read the target&#39;s title.
virtual wxDragResult DoDragDrop(int flags=wxDrag_CopyOnly)
bool MatchesMask(const wxString &ext, unsigned int group) const
Return whether a file extension matches a file mask (group)
void DoOpenFile(ProjectFile *pf, const wxString &filename)
wxString Left(size_t count) const
wxArrayTreeItemIds m_DraggingSelection
void OpenFilesRecursively(wxTreeItemId &sel_id)
wxString & Last()
size_t Replace(const wxString &strOld, const wxString &strNew, bool replaceAll=true)
wxFrame * GetAppFrame() const
Definition: manager.cpp:419
virtual void SelectItem(const wxTreeItemId &item, bool select=true)
void OnViewUseFolders(wxCommandEvent &event)
const wxSize wxDefaultSize
bool SaveLayout()
cbEditor * GetBuiltinActiveEditor()
Definition: editormanager.h:95
bool CloseProject(cbProject *project, bool dontsave=false, bool refresh=true)
Close a project.
const wxPoint wxDefaultPosition
void ConfigureProjectDependencies(cbProject *base)
Displays a dialog to setup project dependencies.
void OnFindFile(wxCommandEvent &event)
A workspace class.
Definition: cbworkspace.h:26
bool IsSameAs(const wxString &s, bool caseSensitive=true) const
void CreateMenu(wxMenuBar *menuBar)
static bool IsBatchBuild()
Definition: manager.h:66
static const wxString sep
void RebuildTree()
Rebuild the project manager&#39;s tree.
wxMenuItem * AppendSeparator()
Base class that all "editors" should inherit from.
Definition: editorbase.h:30
LogManager * GetLogManager() const
Definition: manager.cpp:439
wxString GetPageToolTip(size_t pageIdx) const
static wxColour GetColour(wxSystemColour index)
void OnRenameFile(wxCommandEvent &event)
cbProject * GetActiveProject()
Retrieve the active project.
virtual wxString GetBasePath() const
Read the target&#39;s base path, e.g. if GetFilename() returns "/usr/local/bin/xxx", base path will retur...
bool IsEmpty() const
void OnProjectFileActivated(wxTreeEvent &event)
virtual void Collapse(const wxTreeItemId &item)
static wxUniChar GetPathSeparator(wxPathFormat format=wxPATH_NATIVE)
DLLIMPORT size_t GetIndexForFilterAll()
Get the filter index for the special "All files" filter.
wxString GetLongPath() const
virtual int ShowModal()
bool Save(const wxString &filename)
void OnProperties(wxCommandEvent &event)
bool operator()(ProjectFile *pf1, ProjectFile *pf2)
virtual bool IsBuiltinEditor() const
Is this a built-in editor?
Definition: editorbase.cpp:209
void CheckForExternallyModifiedProjects()
wxMenu * GetMenu(size_t menuIndex) const
wxDateTime GetLastModificationTime() const
Returns the last modification time for the file.
Definition: cbproject.h:569
wxString wxEmptyString
const wchar_t * wc_str() const
wxString Right(size_t count) const
#define wxOK
const wxString g_StartHereTitle
void OnExecParameters(wxCommandEvent &event)
cbTreeCtrl * m_pTree
void OnRenameWorkspace(wxCommandEvent &event)
int AskForBuildTargetIndex(cbProject *project)
Utility function.
void OnOpenFile(wxCommandEvent &event)
cbEditor * Open(const wxString &filename, int pos=0, ProjectFile *data=nullptr)
static bool Access(const wxString &name, wxFile::OpenMode mode)
virtual void SetItemText(const wxTreeItemId &item, const wxString &text)
bool SaveProject(cbProject *project)
Save a project to disk.
const wxString & _(const wxString &string)
int FindMenu(const wxString &title) const
EditorBase * GetEditor(int index)
void OnGotoFile(wxCommandEvent &event)
void FreezeTree()
Stop the tree control from updating.
int GetBuildTargetsCount()
Definition: cbproject.h:200
#define cbAssert(expr)
Definition: cbexception.h:48
void OnSaveAsWorkspace(wxCommandEvent &event)
wxArray< int > wxArrayInt
int First(wxUniChar ch) const
void OnOpenFolderFiles(wxCommandEvent &event)
bool IsChecked() const
ProjectBuildTarget * GetBuildTarget(int index)
Access a build target.
Definition: cbproject.cpp:1392
virtual void Expand(const wxTreeItemId &item)
PluginsArray GetMimeOffers()
static bool Exists(const wxString &filename)
cbWorkspace * GetWorkspace()
Get the current workspace filename.
bool SetPageToolTip(size_t page, const wxString &text)
DLLIMPORT wxString GetFilterString(const wxString &ext=wxEmptyString)
Generates and returns the filter string for use in file dialogs.
Definition: filefilters.cpp:60
void SetTreeItemId(wxTreeItemId id)
Sets the tree item id for the file.
Definition: projectfile.h:213
ColourManager * GetColourManager() const
Definition: manager.cpp:489
bool QueryCloseProject(cbProject *project, bool dontsavefiles)
Checks whether project is saved.
const wxArrayString & GetVirtualFolders() const
Get a list of the virtual folders.
Definition: cbproject.cpp:908
int GetPageIndex(wxWindow *page_wnd) const
void SetProject(cbProject *project, bool refresh=true)
Set the active project.
A file editor.
Definition: cbeditor.h:43
FileVisualState GetFileState() const
void ShowMenu(wxTreeItemId id, const wxPoint &pt)
bool IsEmpty() const
DLLIMPORT void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode=pdlBest, bool enforce=false)
Definition: globals.cpp:1177
cbProject * GetProject() const
Definition: cbproject.h:59
wxString GetPath(int flags=wxPATH_GET_VOLUME, wxPathFormat format=wxPATH_NATIVE) const
void Save()
Save groups/masks to config.
The entry point singleton for working with projects.
void OnAddFileToProject(wxCommandEvent &event)
cbAuiNotebook * GetNotebook()
Definition: editormanager.h:73
EVTIMPORT const wxEventType cbEVT_SHOW_DOCK_WINDOW
Definition: sdk_events.cpp:131
wxCursor wxNullCursor
void Log(const wxString &msg, int i=app_log, Logger::level lv=Logger::info)
Definition: logmanager.h:140
virtual void SetImageList(wxImageList *imageList)
wxString GetFullName() const
void AskPluginsForModuleMenu(const ModuleType type, wxMenu *menu, const FileTreeData *data=nullptr)
A notebook class This class is derived from wxAuiNotebook, to enhance its abilities.
Definition: cbauibook.h:30
FileTreeDataKind GetKind() const
Definition: cbproject.h:58
virtual void GetPaths(wxArrayString &paths) const
void OnCloseProject(wxCommandEvent &event)
void DebugLog(const wxString &msg, Logger::level lv=Logger::info)
Definition: logmanager.h:146
bool CloseWorkspace()
Close the workspace.
bool ProcessEvent(CodeBlocksEvent &event)
Definition: manager.cpp:246
bool EndsWith(const wxString &suffix, wxString *rest=NULL) const
void OnRemoveFileFromProject(wxCommandEvent &event)
virtual void Delete(const wxTreeItemId &item)
static void applyFileOptionChange(std::set< cbProject *> &modified, wxTreeCtrl &tree, Func func)
Find all selected tree items which are files and call the func on them.
void OnEndEditNode(wxTreeEvent &event)
void wxMilliSleep(unsigned long milliseconds)
wxString virtual_path
A string that represents the virtual folder this file will appear in.
Definition: projectfile.h:195
wxString GetCommonTopLevelPath() const
Definition: cbproject.cpp:423
bool GetModified() const override
Definition: cbproject.cpp:162
void OnDeleteVirtualFolder(wxCommandEvent &event)
wxString & Prepend(const wxString &str)
void OnKeyDown(wxTreeEvent &event)
virtual wxTreeItemId AddRoot(const wxString &text, int image=-1, int selImage=-1, wxTreeItemData *data=NULL)
size_t Add(const wxString &str, size_t copies=1)
static wxString ReadDataPath()
cbProject * GetParentProject()
Definition: projectfile.h:93
bool StartsWith(const wxString &prefix, wxString *rest=NULL) const
void SetKind(FileTreeDataKind kind)
Definition: cbproject.h:64
void Sort(bool reverseOrder=false)
int Add(const wxBitmap &bitmap, const wxBitmap &mask=wxNullBitmap)
virtual wxTreeItemId GetRootItem() const
size_t GetCount() const
void SetProjectFile(ProjectFile *file)
Definition: cbproject.h:68
void OnSaveWorkspace(wxCommandEvent &event)
ProjectFile * AutoGeneratedBy() const
If this is an auto-generated file, which file is generating it?
Definition: projectfile.h:201
void AddControlBelowList(wxControl *control)
Definition: goto_file.cpp:139
int Find(wxUniChar ch, bool fromEnd=false) const
wxUniChar GetChar(size_t n) const
wxUniChar Last() const
ProjectsArray * GetProjects()
Retrieve an array of all the opened projects.
void OnTreeItemRightClick(wxTreeEvent &event)
bool FileExists() const
void RemoveAt(size_t nIndex, size_t count=1)
void FinishLoadingProject(cbProject *project, bool newAddition, cb_unused FilesGroupsAndMasks *fgam)
wxString GetGroupName(unsigned int group) const
Return a specific group name.
void SetFileState(FileVisualState state)
Set the visual state (modified, read-only, etc).
int Printf(const wxString &pszFormat,...)
EditorBase * IsOpen(const wxString &filename)
bool Save()
Save the project.
Definition: cbproject.cpp:482
wxString GetFullPath(wxPathFormat format=wxPATH_NATIVE) const
virtual int OpenFile(const wxString &filename)=0
Open the file.
virtual wxColour GetItemTextColour(const wxTreeItemId &item) const
int SelectTarget(int initial=-1, bool evenIfOne=false)
Displays a target selection dialog.
Definition: cbproject.cpp:1130
void OnUpdateUI(wxUpdateUIEvent &event)
If true, use virtual folders like "Sources", "Headers", etc.
Definition: globals.h:138
virtual void SetValue(bool state)
virtual wxString GetTitle() const
Get the workspace&#39;s title.
Definition: cbworkspace.h:69
void SetFileIndex(int index)
Definition: cbproject.h:67
static int FolderIconIndex()
Definition: globals.cpp:1550
wxTreeItemId InsertItem(const wxTreeItemId &parent, const wxTreeItemId &previous, const wxString &text, int image=-1, int selImage=-1, wxTreeItemData *data=NULL)
static wxString Format(const wxString &format,...)
wxTreeItemId GetTreeSelection()
Get the selection of the project manager&#39;s tree (GUI).
wxString Mid(size_t first, size_t nCount=wxString::npos) const
bool SaveWorkspace()
Save the open workspace.
wxMenuItem * Insert(size_t pos, wxMenuItem *menuItem)
DLLIMPORT int cbMessageBox(const wxString &message, const wxString &caption=wxEmptyString, int style=wxOK, wxWindow *parent=NULL, int x=-1, int y=-1)
wxMessageBox wrapper.
Definition: globals.cpp:1395
virtual bool ItemHasChildren(const wxTreeItemId &item) const
void OnAddVirtualFolder(wxCommandEvent &event)
void OnTreeBeginDrag(wxTreeEvent &event)
wxTreeItemId AppendItem(const wxTreeItemId &parent, const wxString &text, int image=-1, int selImage=-1, wxTreeItemData *data=NULL)
virtual void UnselectAll()
wxArrayString ListNodes(wxTreeItemId node) const
wxMenuItem * AppendCheckItem(int id, const wxString &item, const wxString &help=wxEmptyString)
int GetSelection()
Definition: goto_file.cpp:134
std::map< wxString, wxString > StringToStringMap
Definition: configmanager.h:54