ecere/gui: Fixes to make MDI work with Stacker containing Toolbar & Workspace in IDE
[sdk] / ide / src / ide.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 public import static "ec"
4 #else
5 public import "ecere"
6 public import "ec"
7 #endif
8
9 import "GlobalSettingsDialog"
10 import "NewProjectDialog"
11 import "FindInFilesDialog"
12 import "ActiveCompilerDialog"
13
14 #ifdef GDB_DEBUG_GUI
15 import "GDBDialog"
16 #endif
17
18 import "Project"
19 import "ProjectActiveConfig"
20 import "ProjectConfig"
21 import "ProjectNode"
22 import "NodeProperties"
23 import "ProjectSettings"
24 import "ProjectView"
25 import "Workspace"
26
27 import "CodeEditor"
28 import "Designer"
29 import "ToolBox"
30 import "Sheet"
31
32 import "Debugger"
33
34 import "OutputView"
35 import "BreakpointsView"
36 import "CallStackView"
37 import "ThreadsView"
38 import "WatchesView"
39
40 #ifndef NO3D
41 import "ModelView"
42 #endif
43 import "PictureEdit"
44
45 import "about"
46
47 import "FileSystemIterator"
48
49 #if defined(__WIN32__)
50 define pathListSep = ";";
51 #else
52 define pathListSep = ":";
53 #endif
54
55 define maxPathLen = 65 * MAX_LOCATION;
56
57 class PathBackup : struct
58 {
59    String oldLDPath;
60    String oldPath;
61
62    PathBackup()
63    {
64       oldPath = new char[maxPathLen];
65       oldLDPath = new char[maxPathLen];
66
67       GetEnvironment("PATH", oldPath, maxPathLen);
68 #if defined(__APPLE__)
69       GetEnvironment("DYLD_LIBRARY_PATH", oldLDPath, maxPathLen);
70 #else
71       GetEnvironment("LD_LIBRARY_PATH", oldLDPath, maxPathLen);
72 #endif
73    }
74
75    ~PathBackup()
76    {
77       SetEnvironment("PATH", oldPath);
78 #if defined(__APPLE__)
79       SetEnvironment("DYLD_LIBRARY_PATH", oldLDPath);
80 #else
81       SetEnvironment("LD_LIBRARY_PATH", oldLDPath);
82 #endif
83       delete oldPath;
84       delete oldLDPath;
85    }
86 };
87
88 enum OpenCreateIfFails { no, yes, something, whatever };
89 enum OpenMethod { normal, add };
90
91 static Array<FileFilter> fileFilters
92 { [
93    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
94    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
95    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
96    { $"Text files (*.txt, *.text, *.nfo, *.info)", "txt, text, nfo, info" },
97    { $"Web files (*.html, *.htm, *.xhtml, *.css, *.php, *.js, *.jsi, *.rb, *.xml)", "html, htm, xhtml, css, php, js, jsi, rb, xml" },
98    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png, *.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
99    { $"3D Studio Model Files (*.3ds)", "3ds" },
100    { $"All files", null }
101 ] };
102
103 static Array<FileType> fileTypes
104 { [
105    { $"Based on extension", null },
106    { $"Text",               "txt" },
107    { $"Image",              "jpg" },
108    { $"3D Studio Model",    "3ds" }
109 ] };
110
111 static Array<FileFilter> projectFilters
112 { [
113    { $"Project Files (*.epj)", ProjectExtension }
114 ] };
115
116 static Array<FileType> projectTypes
117 { [
118    { $"Project File", ProjectExtension }
119 ] };
120
121 static Array<FileFilter> findInFilesFileFilters
122 { [
123    { $"eC Files (*.ec, *.eh)", "ec, eh" },
124    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
125    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
126    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
127    { $"Text files (*.txt)", "txt" },
128    { $"All files", null }
129 ] };
130
131 FileDialog ideFileDialog
132 {
133    type = multiOpen, text = $"Open";
134    types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType), filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
135 };
136
137 define openProjectFileDialogTitle = $"Open Project";
138 define addProjectFileDialogTitle = $"Open Additional Project";
139 FileDialog ideProjectFileDialog
140 {
141    type = open;
142    types = projectTypes.array, sizeTypes = projectTypes.count * sizeof(FileType), filters = projectFilters.array, sizeFilters = projectFilters.count * sizeof(FileFilter);
143 };
144
145 GlobalSettingsDialog globalSettingsDialog
146 {
147    ideSettings = ideSettings;
148    settingsContainer = settingsContainer;
149
150    void OnGlobalSettingChange(GlobalSettingsChange globalSettingsChange)
151    {
152       switch(globalSettingsChange)
153       {
154          case editorSettings:
155          {
156             Window child;
157             for(child = ide.firstChild; child; child = child.next)
158             {
159                if(child._class == class(CodeEditor))
160                {
161                   CodeEditor codeEditor = (CodeEditor) child;
162                   codeEditor.editBox.freeCaret = ideSettings.useFreeCaret;
163                   // codeEditor.editBox.lineNumbers = ideSettings.showLineNumbers;
164                   codeEditor.editBox.caretFollowsScrolling = ideSettings.caretFollowsScrolling;
165                   codeEditor.OnPostCreate(); // Update editBox margin size
166                }
167             }
168             break;
169          }
170          case projectOptions:
171             break;
172          case compilerSettings:
173          {
174             ide.UpdateMakefiles();
175             break;
176          }
177       }
178    }
179 };
180
181 void DrawLineMarginIcon(Surface surface, BitmapResource resource, int line, int lineH, int scrollY, int boxH)
182 {
183    int lineY;
184    if(line)
185    {
186       lineY = (line - 1) * lineH;
187       if(lineY + lineH > scrollY && lineY + lineH < scrollY + boxH)
188       {
189          Bitmap bitmap = resource.bitmap;
190          if(bitmap)
191             surface.Blit(bitmap, 0, lineY - scrollY + (lineH - bitmap.height) / 2 + 1, 0, 0, bitmap.width, bitmap.height);
192       }
193    }
194 }
195
196 public class ToolBar : public Stacker
197 {
198    direction = horizontal;
199    background = activeBorder;
200    opacity = 1.0f;
201    gap = 0;
202
203    anchor = Anchor { left = 0, right = 0 };
204    clientSize = { h = 32 };
205    borderStyle = bevel;
206 }
207
208 class IDEMainFrame : Window
209 {
210    background = activeBorder;
211    borderStyle = sizable;
212    hasMaximize = true;
213    hasMinimize = true;
214    hasClose = true;
215    size = { 840, 480 };
216    minClientSize = { 600, 300 };
217    nativeDecorations = true; 
218    borderStyle = sizable;
219    hasMaximize = true;
220    hasMinimize = true;
221    hasClose = true;
222    hasMenuBar = true;
223    icon = { ":icon.png" };
224    text = titleECEREIDE;
225
226    Stacker stack
227    {
228       this;
229       menu = { };
230       isActiveClient = true;
231       gap = 0;
232       direction = vertical;
233       background = activeBorder;
234       anchor = { left = 0, top = 0, right = 0, bottom = 0 };
235    };
236
237    ToolBar toolBar
238    {
239       stack, this;
240       size = { h = 32 };
241    };
242
243    IDEWorkSpace ideWorkSpace { stack, this };
244 }
245
246 define ide = ideMainFrame.ideWorkSpace;
247
248 class IDEWorkSpace : Window
249 {
250    background = Color { 85, 85, 85 };
251
252    //tabCycle = true;
253    hasVertScroll = true;
254    hasHorzScroll = true;
255    hasStatusBar = true;
256    isActiveClient = true;
257 #if 0 //def _DEBUG
258    //stayOnTop = true;
259    size = { 800, 600 };
260    anchor = { top = 0, right = 0, bottom = 0 };
261 #else
262    //state = maximized;
263    anchor = { left = 0, top = 0, right = 0, bottom = 0 };
264 #endif
265    menu = Menu {  };
266
267    MenuItem * driverItems, * skinItems;
268    StatusField pos { width = 150 };
269    StatusField ovr, caps, num;
270
271    BitmapResource back                 { ":ecereBack.jpg", window = this };
272    BitmapResource bmpBp                { ":codeMarks/breakpoint.png", window = this };
273    BitmapResource bmpBpDisabled        { ":codeMarks/breakpointDisabled.png", window = this };
274    BitmapResource bmpBpHalf            { ":codeMarks/breakpointHalf.png", window = this };
275    BitmapResource bmpBpHalfDisabled    { ":codeMarks/breakpointHalfDisabled.png", window = this };
276    BitmapResource bmpCursor            { ":codeMarks/cursor.png", window = this };
277    BitmapResource bmpCursorError       { ":codeMarks/cursorError.png", window = this };
278    BitmapResource bmpTopFrame          { ":codeMarks/topFrame.png", window = this };
279    BitmapResource bmpTopFrameError     { ":codeMarks/topFrameError.png", window = this };
280    BitmapResource bmpTopFrameHalf      { ":codeMarks/topFrameHalf.png", window = this };
281    BitmapResource bmpTopFrameHalfError { ":codeMarks/topFrameHalfError.png", window = this };
282    
283    Debugger debugger { };
284
285    ProjectView projectView;
286
287    OutputView outputView
288    {
289       parent = this;
290
291       void OnGotoError(char * line)
292       {
293          ide.GoToError(line);
294       }
295
296       void OnCodeLocationParseAndGoTo(char * line)
297       {
298          ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir);
299       }
300
301       bool OnKeyDown(Key key, unichar ch)
302       {
303          switch(key)
304          {
305             case escape: 
306                if(!ide.findInFilesDialog || !ide.findInFilesDialog.SearchAbort())
307                   ide.ShowCodeEditor(); 
308                break;
309             case ctrlS:
310                ide.projectView.stopBuild = true;
311                break;
312             default:
313             {
314                OutputView::OnKeyDown(key, ch);
315                break;
316             }
317          }
318          return true;
319       }
320
321       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
322       {
323          if(active)
324             ide.RepositionWindows(false);
325          return true;
326       }
327
328       bool OnClose(bool parentClosing)
329       {
330          visible = false;
331          if(!parentClosing)
332             ide.RepositionWindows(false);
333          return parentClosing;
334       }
335    };
336
337    CallStackView callStackView
338    {
339       parent = this, font = { panelFont.faceName, panelFont.size };
340
341       void OnGotoLine(char * line)
342       {
343          int stackLvl;
344          stackLvl = atoi(line);
345          ide.debugger.GoToStackFrameLine(stackLvl, true);
346       }
347
348       void OnSelectFrame(int lineNumber)
349       {
350          ide.debugger.SelectFrame(lineNumber);
351       }
352
353       void OnToggleBreakpoint()
354       {
355          Debugger debugger = ide.debugger;
356          if(debugger.activeFrame && debugger.activeFrame.absoluteFile)
357          {
358             int line = debugger.activeFrame.line;
359             char name[MAX_LOCATION];
360             Project prj = null;
361             // TOFIX: Improve on this, don't use only filename, make a function
362             GetLastDirectory(debugger.activeFrame.absoluteFile, name);
363             if(ide && ide.workspace)
364             {
365                for(p : ide.workspace.projects)
366                {
367                   if(p.topNode.Find(name, false))
368                   {
369                      prj = p;
370                      break;
371                   }
372                }
373                if(!prj)
374                {
375                   for(p : ide.workspace.projects)
376                   {
377                      if(eString_PathInsideOf(debugger.activeFrame.absoluteFile, p.topNode.path))
378                      {
379                         prj = p;
380                         break;
381                      }
382                   }
383                }
384             }
385             debugger.ToggleBreakpoint(debugger.activeFrame.absoluteFile, line, prj);
386             Update(null);
387             {
388                CodeEditor codeEditor = (CodeEditor)ide.FindWindow(debugger.activeFrame.absoluteFile);
389                if(codeEditor) { codeEditor.Update(null); Activate(); }
390             }
391          }
392       }
393
394       bool OnKeyDown(Key key, unichar ch)
395       {
396          switch(key)
397          {
398             case escape: ide.ShowCodeEditor(); break;
399          }
400          return true;
401       }
402
403       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
404       {
405          if(active)
406             ide.RepositionWindows(false);
407          return true;
408       }
409
410       bool OnClose(bool parentClosing)
411       {
412          visible = false;
413          if(!parentClosing)
414             ide.RepositionWindows(false);
415          return parentClosing;
416       }
417
418       void OnRedraw(Surface surface)
419       {
420          bool error;
421          int lineCursor, lineTopFrame, activeThread, hitThread;
422          int lineH, scrollY, boxH;
423          BitmapResource bmp;
424          Breakpoint bp = null;
425          Debugger debugger = ide.debugger;
426          Frame activeFrame = debugger.activeFrame;
427
428          boxH = clientSize.h;
429          scrollY = editBox.scroll.y;
430          displaySystem.FontExtent(editBox.font.font, " ", 1, null, &lineH);
431
432          activeThread = debugger.activeThread;
433          hitThread = debugger.hitThread;
434          debugger.GetCallStackCursorLine(&error, &lineCursor, &lineTopFrame);
435
436          if(activeFrame && activeFrame.absoluteFile)
437          {
438             for(i : ide.workspace.breakpoints; i.type == user)
439             {
440                if(i.absoluteFilePath && i.absoluteFilePath[0] && 
441                   !fstrcmp(i.absoluteFilePath, activeFrame.absoluteFile) &&
442                   activeFrame.line == i.line)
443                {
444                   bp = i;
445                   break;
446                }
447             }
448          }
449
450          if(bp)
451             DrawLineMarginIcon(surface,
452                   /*(lineCursor == 1 || lineTopFrame == 1) ? */ide.bmpBpHalf/* : ide.bmpBp*/,
453                   lineCursor /*1*/, lineH, scrollY, boxH);
454
455          /*
456          if(activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
457             DrawLineMarginIcon(surface,
458                   (lineCursor == 1 || lineTopFrame == 1) ? ide.bmpBpHalf : ide.bmpBp,
459                   1, lineH, scrollY, boxH);
460          */
461          DrawLineMarginIcon(surface, error ? ide.bmpCursorError : ide.bmpCursor, lineCursor, lineH, scrollY, boxH);
462          if(bp && lineCursor == 1) //activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
463             bmp = error ? ide.bmpTopFrameHalfError : ide.bmpTopFrameHalf;
464          else
465             bmp = error ? ide.bmpTopFrameError : ide.bmpTopFrame;
466          DrawLineMarginIcon(surface, bmp, lineTopFrame, lineH, scrollY, boxH);
467          if(editBox.horzScroll && editBox.horzScroll.visible)
468          {
469             surface.SetBackground(control);
470             surface.Area(0, editBox.clientSize.h, 20 - 1, clientSize.h - 1);
471          }
472       }
473    };
474    
475    WatchesView watchesView { parent = this };
476    ThreadsView threadsView
477    {
478       parent = this, font = { panelFont.faceName, panelFont.size };
479
480       bool OnKeyDown(Key key, unichar ch)
481       {
482          switch(key)
483          {
484             case escape: ide.ShowCodeEditor(); break;
485          }
486          return true;
487       }
488
489       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
490       {
491          if(active)
492             ide.RepositionWindows(false);
493          return true;
494       }
495
496       bool OnClose(bool parentClosing)
497       {
498          visible = false;
499          if(!parentClosing)
500             ide.RepositionWindows(false);
501          return parentClosing;
502       }
503
504       void OnSelectThread(int threadId)
505       {
506          if(threadId)
507             ide.debugger.SelectThread(threadId);
508       }
509
510       bool OnGetThreadsInfo(int * activeThread, int * hitThread, int * signalThread)
511       {
512          bool result = false;
513          Debugger debugger = ide.debugger;
514          *activeThread = debugger.activeThread;
515          *hitThread = debugger.hitThread;
516          *signalThread = debugger.signalThread;
517          result = true;
518          return result;
519       }
520    };
521    BreakpointsView breakpointsView { parent = this };
522
523    ToolBox toolBox { parent = this };
524    Sheet sheet { parent = this };
525
526    char * tmpPrjDir;
527    property char * tmpPrjDir { set { delete tmpPrjDir; if(value) tmpPrjDir = CopyString(value); } get { return tmpPrjDir; } };
528
529    Menu fileMenu { menu, $"File", f };
530       MenuItem fileNewItem
531       {
532          fileMenu, $"New", n, ctrlN;
533          bool NotifySelect(MenuItem selection, Modifiers mods)
534          {
535             Window document = (Window)NewCodeEditor(this, normal, false);
536             document.NotifySaved = DocumentSaved;
537             return true;
538          }
539       }
540       MenuItem fileOpenItem
541       {
542          fileMenu, $"Open...", o, ctrlO;
543          bool NotifySelect(MenuItem selection, Modifiers mods)
544          {
545             if(!projectView && ideSettings.ideFileDialogLocation)
546                ideFileDialog.currentDirectory = ideSettings.ideFileDialogLocation;
547             for(;;)
548             {
549                if(ideFileDialog.Modal() == ok)
550                {
551                   bool gotWhatWeWant = false;
552                   int c;
553                   int numSelections = ideFileDialog.numSelections;
554                   char ** multiFilePaths = ideFileDialog.multiFilePaths;
555
556                   for(c = 0; c < numSelections; c++)
557                   {
558                      if(OpenFile(multiFilePaths[c], normal, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal))
559                         gotWhatWeWant = true;
560                   }
561                   if(gotWhatWeWant ||
562                      MessageBox { type = yesNo, master = this, text = $"Error opening file", 
563                      contents = $"Open a different file?" }.Modal() == no)
564                   {
565                      if(!projectView && gotWhatWeWant)
566                         ChangeFileDialogsDirectory(ideFileDialog.currentDirectory, true);
567                      break;
568                   }
569                }
570                else
571                   break;
572             }
573             return true;
574          }
575       }
576       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
577       MenuDivider { fileMenu };
578       MenuItem fileSaveItem { fileMenu, $"Save", s, ctrlS };
579       MenuItem fileSaveAsItem { fileMenu, $"Save As...", a };
580       MenuItem fileSaveAllItem { fileMenu, $"Save All", l, NotifySelect = MenuFileSaveAll };
581       MenuDivider { fileMenu };
582       MenuItem findInFiles
583       {
584          fileMenu, $"Find In Files...", f, Key { f, ctrl = true , shift = true };
585          bool NotifySelect(MenuItem selection, Modifiers mods)
586          {
587             findInFilesDialog.replaceMode = false;
588             findInFilesDialog.Show();
589             return true;
590          }
591       }
592       MenuItem replaceInFiles
593       {
594          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
595          bool NotifySelect(MenuItem selection, Modifiers mods)
596          {
597             findInFilesDialog.replaceMode = true;
598             findInFilesDialog.Show();
599             return true;
600          }
601       }
602       MenuDivider { fileMenu };
603       MenuItem globalSettingsItem
604       {
605          fileMenu, $"Global Settings...", g;
606          bool NotifySelect(MenuItem selection, Modifiers mods)
607          {
608             globalSettingsDialog.master = this;
609             if(ide.workspace && ide.workspace.compiler)
610                globalSettingsDialog.workspaceActiveCompiler = ide.workspace.compiler;
611             else if(ideSettings.defaultCompiler)
612                globalSettingsDialog.workspaceActiveCompiler = ideSettings.defaultCompiler;
613             globalSettingsDialog.Modal();
614             return true;
615          }
616       }
617       MenuDivider { fileMenu };
618       Menu recentFiles { fileMenu, $"Recent Files", r };
619       Menu recentProjects { fileMenu, $"Recent Projects", p };
620       MenuDivider { fileMenu };
621       MenuItem exitItem
622       {
623          fileMenu, $"Exit", x, altF4, NotifySelect = MenuFileExit;
624
625          bool NotifySelect(MenuItem selection, Modifiers mods)
626          {
627             ideMainFrame.Destroy(0);
628             return true;
629          }
630       };
631
632       bool FileRecentFile(MenuItem selection, Modifiers mods)
633       {
634          int id = 0;
635          for(file : ideSettings.recentFiles)
636          {
637             if(id == selection.id)
638             {
639                OpenFile(file, normal, true, null, no, normal);
640                break;
641             }
642             id++;
643          }
644          return true;
645       }
646
647       bool FileRecentProject(MenuItem selection, Modifiers mods)
648       {
649          int id = 0;
650          for(file : ideSettings.recentProjects)
651          {
652             if(id == selection.id)
653             {
654                OpenFile(file, normal, true, null, no, normal);
655                break;
656             }
657             id++;
658          }
659          return true;
660       }
661
662    MenuPlacement editMenu { menu, $"Edit", e };
663    
664    Menu projectMenu { menu, $"Menu"."Project", p };
665       MenuItem projectNewItem
666       {
667          projectMenu, $"New...", n, Key { n, true, true };
668          bool NotifySelect(MenuItem selection, Modifiers mods)
669          {
670             if(!DontTerminateDebugSession($"New Project"))
671                if(MenuWindowCloseAll(null, 0))
672                {
673                   NewProjectDialog newProjectDialog;
674
675                   if(projectView)
676                   {
677                      projectView.visible = false;
678                      if(!projectView.Destroy(0))
679                         return true;
680                   }
681                   
682                   newProjectDialog = { master = this };
683                   newProjectDialog.Modal();
684                   if(projectView)
685                   {
686                      ideSettings.AddRecentProject(projectView.fileName);
687                      ide.UpdateRecentMenus();
688                      settingsContainer.Save();
689                   }
690                }
691             return true;
692          }
693       }
694       MenuItem projectOpenItem
695       {
696          projectMenu, $"Open...", o, Key { o, true, true };
697          bool NotifySelect(MenuItem selection, Modifiers mods)
698          {
699             if(ideSettings.ideProjectFileDialogLocation)
700                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
701
702             ideProjectFileDialog.text = openProjectFileDialogTitle;
703             if(ideProjectFileDialog.Modal() == ok)
704             {
705                OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal);
706                //ChangeProjectFileDialogDirectory(ideProjectFileDialog.currentDirectory);
707             }
708             return true;
709          }
710       }
711       MenuItem projectQuickItem
712       {
713          projectMenu, $"Quick...", q, f7;
714          bool NotifySelect(MenuItem selection, Modifiers mods)
715          {
716             if(!projectView)
717                QuickProjectDialog{ this }.Modal();
718             return true;
719          }
720       }
721       MenuItem projectAddItem
722       {
723          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
724          disabled = true;
725          bool NotifySelect(MenuItem selection, Modifiers mods)
726          {
727             if(ideSettings.ideProjectFileDialogLocation)
728                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
729
730             ideProjectFileDialog.text = addProjectFileDialogTitle;
731             for(;;)
732             {
733                if(ideProjectFileDialog.Modal() == ok)
734                {
735                   if(OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add))
736                      break;
737                   if(MessageBox { type = yesNo, master = this, text = $"Error opening project file", 
738                         contents = $"Add a different project?" }.Modal() == no)
739                   {
740                      break;
741                   }
742                }
743                else
744                   break;
745             }
746             return true;
747          }
748       }
749       MenuItem projectCloseItem
750       {
751          projectMenu, $"Close", c, disabled = true;
752          bool NotifySelect(MenuItem selection, Modifiers mods)
753          {
754             if(projectView)
755             {
756                if(!ide.DontTerminateDebugSession($"Project Close"))
757                {
758                   if(findInFilesDialog)
759                      findInFilesDialog.SearchStop();
760                   projectView.visible = false;
761                   if(projectView.Destroy(0))
762                      MenuWindowCloseAll(null, 0);
763                   {
764                      char workingDir[MAX_LOCATION];
765                      GetWorkingDir(workingDir, MAX_LOCATION);
766                      findInFilesDialog.currentDirectory = workingDir;
767                   }
768                }
769             }
770             return true;
771          }
772       }
773       MenuDivider { projectMenu };
774       MenuItem activeCompilerItem
775       {
776          projectMenu, $"Active Compiler...", g, /*altF5, */disabled = true;
777          bool NotifySelect(MenuItem selection, Modifiers mods)
778          {
779             projectView.MenuCompiler(null, mods);
780             return true;
781          }
782       }
783       MenuItem projectActiveConfigItem
784       {
785          projectMenu, $"Active Configuration...", g, altF5, disabled = true;
786          bool NotifySelect(MenuItem selection, Modifiers mods)
787          {
788             projectView.MenuConfig(projectView.active ? selection : null, mods);
789             return true;
790          }
791       }
792       MenuItem projectSettingsItem
793       {
794          projectMenu, $"Settings...", s, altF7, disabled = true;
795          bool NotifySelect(MenuItem selection, Modifiers mods)
796          {
797             projectView.MenuSettings(projectView.active ? selection : null, mods);
798             return true;
799          }
800       }
801       MenuDivider { projectMenu };
802       MenuItem projectBrowseFolderItem
803       {
804          projectMenu, $"Browse Project Folder", p, disabled = true;
805          bool NotifySelect(MenuItem selection, Modifiers mods)
806          {
807             if(projectView)
808                projectView.MenuBrowseFolder(null, mods);
809             return true;
810          }
811       }
812       MenuDivider { projectMenu };
813       MenuItem projectRunItem
814       {
815          projectMenu, $"Run", r, ctrlF5, disabled = true;
816          bool NotifySelect(MenuItem selection, Modifiers mods)
817          {
818             if(projectView)
819                projectView.Run(null, mods);
820             return true;
821          }
822       }
823       MenuItem projectBuildItem
824       {
825          projectMenu, $"Build", b, f7, disabled = true;
826          bool NotifySelect(MenuItem selection, Modifiers mods)
827          {
828             if(projectView)
829                projectView.ProjectBuild(projectView.active ? selection : null, mods);
830             return true;
831          }
832       }
833       MenuItem projectLinkItem
834       {
835          projectMenu, $"Relink", l, disabled = true;
836          bool NotifySelect(MenuItem selection, Modifiers mods)
837          {
838             if(projectView)
839                projectView.ProjectLink(projectView.active ? selection : null, mods);
840             return true;
841          }
842       }
843       MenuItem projectRebuildItem
844       {
845          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
846          bool NotifySelect(MenuItem selection, Modifiers mods)
847          {
848             if(projectView)
849                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
850             return true;
851          }
852       }
853       MenuItem projectCleanItem
854       {
855          projectMenu, $"Clean", e, disabled = true;
856          bool NotifySelect(MenuItem selection, Modifiers mods)
857          {
858             if(projectView)
859             {
860                debugger.Stop();
861                projectView.ProjectClean(projectView.active ? selection : null, mods);
862             }
863             return true;
864          }
865       }
866       MenuItem projectRegenerateItem
867       {
868          projectMenu, $"Regenerate Makefile", m, disabled = true;
869          bool NotifySelect(MenuItem selection, Modifiers mods)
870          {
871             if(projectView)
872                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
873             return true;
874          }
875       }
876       MenuItem projectCompileItem;
877    Menu debugMenu { menu, $"Debug", d };
878       MenuItem debugStartResumeItem
879       {
880          debugMenu, $"Start", s, f5, disabled = true;
881          NotifySelect = MenuDebugStart;
882       }
883       bool MenuDebugStart(MenuItem selection, Modifiers mods)
884       {
885          if(projectView)
886          {
887             debugStartResumeItem.disabled = true; // a very rare exception to calling AdjustDebugMenus
888             if(!projectView.DebugStart())
889                debugStartResumeItem.disabled = false; // same exception
890          }
891          return true;
892       }
893       bool MenuDebugResume(MenuItem selection, Modifiers mods)
894       {
895          if(projectView)
896             projectView.DebugResume();
897          return true;
898       }
899       MenuItem debugRestartItem
900       {
901          debugMenu, $"Restart", r, Key { f5, ctrl = true, shift = true }, disabled = true;
902          bool NotifySelect(MenuItem selection, Modifiers mods)
903          {
904             if(projectView)
905                projectView.DebugRestart();
906             return true;
907          }
908       }
909       MenuItem debugBreakItem
910       {
911          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
912          bool NotifySelect(MenuItem selection, Modifiers mods)
913          {
914             if(projectView)
915                projectView.DebugBreak();
916             return true;
917          }
918       }
919       MenuItem debugStopItem
920       {
921          debugMenu, $"Stop", p, shiftF5, disabled = true;
922          bool NotifySelect(MenuItem selection, Modifiers mods)
923          {
924             if(projectView)
925                projectView.DebugStop();
926             return true;
927          }
928       }
929       MenuDivider { debugMenu };
930       MenuItem debugStepIntoItem
931       {
932          debugMenu, $"Step Into", i, f11, disabled = true;
933          bool NotifySelect(MenuItem selection, Modifiers mods)
934          {
935             if(projectView)
936                projectView.DebugStepInto();
937             return true;
938          }
939       }
940       MenuItem debugStepOverItem
941       {
942          debugMenu, $"Step Over", v, f10, disabled = true;
943          bool NotifySelect(MenuItem selection, Modifiers mods)
944          {
945             if(projectView)
946                projectView.DebugStepOver(false);
947             return true;
948          }
949       }
950       MenuItem debugStepOutItem
951       {
952          debugMenu, $"Step Out", o, shiftF11, disabled = true;
953          bool NotifySelect(MenuItem selection, Modifiers mods)
954          {
955             if(projectView)
956                projectView.DebugStepOut(false);
957             return true;
958          }
959       }
960       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
961       MenuItem debugSkipStepOverItem
962       {
963          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
964          bool NotifySelect(MenuItem selection, Modifiers mods)
965          {
966             if(projectView)
967                projectView.DebugStepOver(true);
968             return true;
969          }
970       }
971       MenuItem debugSkipStepOutItem
972       {
973          debugMenu, $"Step Out Skipping Breakpoints", t, Key { f11, ctrl = true, shift = true }, disabled = true;
974          bool NotifySelect(MenuItem selection, Modifiers mods)
975          {
976             if(projectView)
977                projectView.DebugStepOut(true);
978             return true;
979          }
980       }
981       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
982       //MenuDivider { debugMenu };
983       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
984    MenuPlacement imageMenu { menu, $"Image", i };
985    Menu viewMenu { menu, $"View", v };
986       MenuItem viewProjectItem
987       {
988          viewMenu, $"Project View", j, alt0, disabled = true;
989          bool NotifySelect(MenuItem selection, Modifiers mods)
990          {
991             if(projectView)
992             {
993                projectView.visible = true;
994                projectView.Activate();
995             }
996             return true;
997          }
998       }
999       MenuPlacement { viewMenu, $"View Designer" };
1000       MenuPlacement { viewMenu, $"View Code" };
1001       MenuPlacement { viewMenu, $"View Properties" };
1002       MenuPlacement { viewMenu, $"View Methods" };
1003       MenuItem viewDesignerItem
1004       {
1005          viewMenu, $"View Designer", d, f8;
1006          bool NotifySelect(MenuItem selection, Modifiers mods)
1007          {
1008             Window client = activeClient;
1009             Class dataType = client._class;
1010             if(!strcmp(dataType.name, "Designer"))
1011             {
1012                client.visible = true;
1013                client.Activate();
1014             }
1015             else
1016                ((CodeEditor)client).ViewDesigner();
1017             return true;
1018          }
1019       }
1020       MenuItem viewCodeItem
1021       {
1022          viewMenu, $"View Code", c, f8;
1023          bool NotifySelect(MenuItem selection, Modifiers mods)
1024          {
1025             Window client = activeClient;
1026             Class dataType = client._class;
1027             if(!strcmp(dataType.name, "Designer"))
1028                client = ((Designer)client).codeEditor;
1029
1030             client.Activate();
1031             // Do this after so the caret isn't moved yet...
1032             client.visible = true;
1033             return true;
1034          }
1035       }
1036       MenuItem viewPropertiesItem
1037       {
1038          viewMenu, $"View Properties", p, f4;
1039          bool NotifySelect(MenuItem selection, Modifiers mods)
1040          {
1041             sheet.visible = true;
1042             sheet.sheetSelected = properties;
1043             sheet.Activate();
1044             return true;
1045          }
1046       }
1047       MenuItem viewMethodsItem
1048       {
1049          viewMenu, $"View Methods", m, f4;
1050          bool NotifySelect(MenuItem selection, Modifiers mods)
1051          {
1052             sheet.visible = true;
1053             sheet.sheetSelected = methods;
1054             sheet.Activate();
1055             return true;
1056          }
1057       }
1058       MenuItem viewToolBoxItem
1059       {
1060          viewMenu, $"View Toolbox", x, f12;
1061          bool NotifySelect(MenuItem selection, Modifiers mods)
1062          {
1063             toolBox.visible = true;
1064             toolBox.Activate();
1065             return true;
1066          }
1067       }
1068       MenuItem viewOutputItem
1069       {
1070          viewMenu, $"Output", o, alt2;
1071          bool NotifySelect(MenuItem selection, Modifiers mods)
1072          {
1073             outputView.Show();
1074             return true;
1075          }
1076       }
1077       MenuItem viewWatchesItem
1078       {
1079          viewMenu, $"Watches", w, alt3;
1080          bool NotifySelect(MenuItem selection, Modifiers mods)
1081          {
1082             watchesView.Show();
1083             return true;
1084          }
1085       }
1086       MenuItem viewThreadsItem
1087       {
1088          viewMenu, $"Threads", t, alt4;
1089          bool NotifySelect(MenuItem selection, Modifiers mods)
1090          {
1091             threadsView.Show();
1092             return true;
1093          }
1094       }
1095       MenuItem viewBreakpointsItem
1096       {
1097          viewMenu, $"Breakpoints", b, alt5;
1098          bool NotifySelect(MenuItem selection, Modifiers mods)
1099          {
1100             breakpointsView.Show();
1101             return true;
1102          }
1103       }
1104       MenuItem viewCallStackItem
1105       {
1106          viewMenu, $"Call Stack", s, alt7;
1107          bool NotifySelect(MenuItem selection, Modifiers mods)
1108          {
1109             callStackView.Show();
1110             return true;
1111          }
1112       }
1113       MenuItem viewAllDebugViews
1114       {
1115          viewMenu, $"All Debug Views", a, alt9;
1116          bool NotifySelect(MenuItem selection, Modifiers mods)
1117          {
1118             outputView.Show();
1119             watchesView.Show();
1120             threadsView.Show();
1121             callStackView.Show();
1122             breakpointsView.Show();
1123             return true;
1124          }
1125       }
1126 #ifdef GDB_DEBUG_GUI
1127       MenuDivider { viewMenu };
1128       MenuItem viewGDBItem
1129       {
1130          viewMenu, $"GDB Dialog", g, Key { f9, shift = true, ctrl = true };
1131          bool NotifySelect(MenuItem selection, Modifiers mods)
1132          {
1133             gdbDialog.Show();
1134             return true;
1135          }
1136       }
1137 #endif
1138       MenuDivider { viewMenu };
1139       MenuItem viewColorPicker
1140       {
1141          viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
1142          bool NotifySelect(MenuItem selection, Modifiers mods)
1143          {
1144             ColorPicker colorPicker { master = this, parent = this, stayOnTop = true };
1145             colorPicker.Create();
1146             return true;
1147          }
1148       }
1149       MenuDivider { viewMenu };
1150       /*
1151       MenuItem
1152       {
1153          viewMenu, "Full Screen", f, checkable = true;
1154
1155          bool NotifySelect(MenuItem selection, Modifiers mods)
1156          {
1157             app.fullScreen ^= true;
1158             SetDriverAndSkin();
1159             anchor = { 0, 0, 0, 0 };
1160             return true;
1161          }
1162       };
1163       */
1164       Menu driversMenu { viewMenu, $"Graphics Driver", v };
1165       //Menu skinsMenu { viewMenu, "GUI Skins", k };
1166    Menu windowMenu { menu, $"Window", w };
1167       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
1168       MenuDivider { windowMenu };
1169       MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext };
1170       MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious };
1171       MenuDivider { windowMenu };
1172       MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade };
1173       MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz };
1174       MenuItem { windowMenu, $"Tile Vertically", v, NotifySelect = MenuWindowTileVert };
1175       MenuItem { windowMenu, $"Arrange Icons", a, NotifySelect = MenuWindowArrangeIcons };
1176       MenuDivider { windowMenu };
1177       MenuItem { windowMenu, $"Windows...", w, NotifySelect = MenuWindowWindows };
1178    Menu helpMenu { menu, $"Help", h };
1179       MenuItem
1180       {
1181          helpMenu, $"API Reference", r, f1;
1182          bool NotifySelect(MenuItem selection, Modifiers mods)
1183          {
1184             Execute("documentor");
1185             return true;
1186          }
1187       }
1188       MenuDivider { helpMenu };
1189       MenuItem
1190       {
1191          helpMenu, $"About...", a;
1192          bool NotifySelect(MenuItem selection, Modifiers mods)
1193          {
1194             AboutIDE { master = this }.Modal();
1195             return true;
1196          }
1197       }
1198
1199    property ToolBox toolBox
1200    {
1201       get { return toolBox; }
1202    }
1203
1204    property Sheet sheet
1205    {
1206       get { return sheet; }
1207    }
1208
1209    property Project project
1210    {
1211       get { return projectView ? projectView.project : null; }
1212    }
1213
1214    property Workspace workspace
1215    {
1216       get { return projectView ? projectView.workspace : null; }
1217    }
1218
1219    FindInFilesDialog findInFilesDialog
1220    {
1221       master = this, parent = this;
1222       filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter);
1223       filter = 1;
1224    };
1225
1226 #ifdef GDB_DEBUG_GUI
1227    GDBDialog gdbDialog
1228    {
1229       master = this, parent = this;
1230       anchor = { left = 100, top = 100, right = 100, bottom = 100 };
1231
1232       void OnCommand(char * string)
1233       {
1234          if(ide)
1235             ide.debugger.SendGDBCommand(string);
1236       }
1237    };
1238 #endif
1239    
1240    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
1241    {
1242       //app.driver = app.drivers[selection.id];
1243 #ifdef __unix__
1244       app.driver = selection.id ? "OpenGL" : "X";
1245 #else
1246       app.driver = selection.id ? "OpenGL" : "GDI";
1247 #endif
1248       delete ideSettings.displayDriver;
1249       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
1250
1251       settingsContainer.Save();
1252       //SetDriverAndSkin();
1253       return true;
1254    }
1255
1256    bool NotifySelectDisplaySkin(MenuItem selection, Modifiers mods)
1257    {
1258       app.skin = app.skins[selection.id];
1259       SetDriverAndSkin();
1260       return true;
1261    }
1262
1263    void SetDriverAndSkin()
1264    {
1265       int c;
1266       for(c = 0; c < app.numSkins; c++)
1267          if(!strcmp(app.skins[c], app.skin))
1268          {
1269             skinItems[c].checked = true;
1270             break;
1271          }
1272       for(c = 0; c < app.numDrivers; c++)
1273          if(!strcmp(app.drivers[c], app.driver))
1274          {
1275             driverItems[c].checked = true;
1276             break;
1277          }
1278    }
1279
1280    ProjectView CreateProjectView(Workspace workspace, char * fileName)
1281    {
1282       Project project = workspace.projects.firstIterator.data;
1283       projectView = ProjectView
1284       {
1285          this;
1286          fileName = fileName;
1287          
1288          void NotifyDestroyed(Window window, DialogResult result)
1289          {
1290             projectView = null;
1291             text = titleECEREIDE;
1292             
1293             AdjustMenus();
1294          }
1295       };
1296       projectView.Create();
1297       RepositionWindows(false);
1298
1299       // Leave it after Create to avoid flicker due to seeing IDE without a project view
1300       projectView.workspace = workspace;
1301       projectView.project = project;
1302       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
1303
1304       AdjustMenus();
1305
1306       ide.breakpointsView.LoadFromWorkspace();
1307       ide.watchesView.LoadFromWorkspace();
1308
1309       findInFilesDialog.projectNodeField.userData = projectView;
1310
1311       {
1312          char fileName[MAX_LOCATION];
1313          strcpy(fileName, project.topNode.path);
1314          PathCat(fileName, project.topNode.name);
1315       }
1316       return projectView;
1317    }
1318
1319    bool GetDebugMenusDisabled()
1320    {
1321       if(projectView)
1322       {
1323          Project project = projectView.project;
1324          if(project)
1325             if(project.GetTargetType(project.config) == executable)
1326                return false;
1327            
1328       }
1329       return true;
1330    }
1331
1332    void RepositionWindows(bool expand)
1333    {
1334       if(this)
1335       {
1336          Window child;
1337          bool inDebugMode = debugger.isActive;
1338          bool callStackVisible = expand ? false : callStackView.visible;
1339          bool threadsVisible = expand ? false : threadsView.visible;
1340          bool watchesVisible = expand ? false : watchesView.visible;
1341          bool breakpointsVisible = expand ? false : breakpointsView.visible;
1342          bool toolBoxVisible = toolBox.visible;
1343          bool outputVisible = expand ? false : outputView.visible;
1344          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
1345          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
1346          
1347          for(child = firstChild; child; child = child.next)
1348          {
1349             if(child._class == class(CodeEditor) || child._class == class(Designer) || 
1350                child._class == class(Sheet) || child._class == class(ProjectView))
1351             {
1352                Anchor anchor = child.anchor;
1353                anchor.top = topDistance;
1354                anchor.bottom = bottomDistance;
1355                if(child._class == class(CodeEditor) || child._class == class(Designer))
1356                {
1357                   anchor.right = toolBoxVisible ? 150 : 0;
1358                }
1359                child.anchor = anchor;
1360             }
1361             else if(expand)
1362             {
1363                if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) || 
1364                   child._class == class(BreakpointsView))
1365                   child.visible = false;
1366             }
1367          }
1368          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
1369          Update(null);
1370       }
1371    }
1372
1373    bool ShowCodeEditor()
1374    {
1375       if(activeClient)
1376          activeClient.Activate();
1377       else if(projectView)
1378       { 
1379          projectView.visible = true;
1380          projectView.Activate();
1381       }
1382       else
1383       {
1384          sheet.visible = true;
1385          sheet.Activate();
1386       }
1387       return false;
1388    }
1389
1390    bool ShouldStopBuild()
1391    {
1392       return projectView.stopBuild;
1393    }
1394
1395    void DocumentSaved(Window document, char * fileName)
1396    {
1397       ideSettings.AddRecentFile(fileName);
1398       ide.UpdateRecentMenus();
1399       settingsContainer.Save();
1400    }
1401
1402    bool Window::OnFileModified(FileChange fileChange, char * param)
1403    {
1404       char temp[4096];
1405       sprintf(temp, $"The document %s was modified by another application.\n"
1406             "Would you like to reload it and lose your changes?", this.fileName);
1407       if(MessageBox { type = yesNo, master = this/*.parent*/,
1408             text = $"Document has been modified", contents = temp }.Modal() == yes)
1409       {
1410          char * fileName = CopyString(this.fileName);
1411          WindowState state = this.state;
1412          Anchor anchor = this.anchor;
1413          Size size = this.size;
1414
1415          this.modifiedDocument = false;
1416          this.Destroy(0);
1417          this = ide.OpenFile(fileName, normal, true, null, no, normal);
1418          if(this)
1419          {
1420             this.anchor = anchor;
1421             this.size = size;
1422             this.SetState(state, true, 0);
1423          }
1424          delete fileName;
1425          return true;
1426       }
1427       return true;
1428    }
1429
1430    void UpdateMakefiles()
1431    {
1432       if(workspace)
1433       {
1434          for(prj : workspace.projects)
1435          {
1436             bool first = prj == workspace.projects.firstIterator.data;
1437             projectView.ProjectUpdateMakefileForAllConfigs(prj, first, first);
1438          }
1439       }
1440    }
1441
1442    void AdjustMenus()
1443    {
1444       bool unavailable = !project;
1445
1446       projectQuickItem.disabled           = !unavailable;
1447
1448       projectAddItem.disabled             = unavailable;
1449
1450       activeCompilerItem.disabled         = unavailable;
1451       projectActiveConfigItem.disabled    = unavailable;
1452       projectSettingsItem.disabled        = unavailable;
1453
1454       projectBrowseFolderItem.disabled    = unavailable;
1455
1456       viewProjectItem.disabled            = unavailable;
1457
1458       AdjustBuildMenus();
1459       AdjustDebugMenus();
1460    }
1461
1462    void AdjustBuildMenus()
1463    {
1464       bool unavailable = project && projectView.buildInProgress;
1465
1466       projectNewItem.disabled          = unavailable;
1467       projectOpenItem.disabled         = unavailable;
1468
1469       unavailable = !project || projectView.buildInProgress;
1470
1471       projectCloseItem.disabled        = unavailable;
1472
1473       projectRunItem.disabled          = unavailable || project.GetTargetType(project.config) != executable;
1474       projectBuildItem.disabled        = unavailable;
1475       projectLinkItem.disabled         = unavailable;
1476       projectRebuildItem.disabled      = unavailable;
1477       projectCleanItem.disabled        = unavailable;
1478       projectRegenerateItem.disabled   = unavailable;
1479       projectCompileItem.disabled      = unavailable;
1480    }
1481
1482    void AdjustDebugMenus()
1483    {
1484       bool unavailable = !project || project.GetTargetType(project.config) != executable ||
1485                projectView.buildInProgress == buildingMainProject;
1486       bool active = ide.debugger.isActive;
1487       bool executing = ide.debugger.state == running;
1488       //bool holding = ide.debugger.state == stopped;
1489
1490       debugStartResumeItem.disabled       = unavailable || executing;
1491
1492       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
1493       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
1494
1495       debugBreakItem.disabled             = unavailable || !executing;
1496       debugStopItem.disabled              = unavailable || !active;
1497       debugRestartItem.disabled           = unavailable || !active;
1498
1499       debugStepIntoItem.disabled          = unavailable || executing;
1500       debugStepOverItem.disabled          = unavailable || executing;
1501       debugStepOutItem.disabled           = unavailable || executing || !active;
1502       debugSkipStepOverItem.disabled      = unavailable || executing;
1503       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
1504
1505       if((Designer)GetActiveDesigner())
1506       {
1507          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
1508          if(codeEditor)
1509          {
1510             codeEditor.debugRunToCursor.disabled      = unavailable || executing;
1511             codeEditor.debugSkipRunToCursor.disabled  = unavailable || executing;
1512          }
1513       }
1514    }
1515
1516    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
1517    {
1518       char tempString[MAX_LOCATION];
1519       strcpy(tempString, directory);
1520       if(saveSettings && !projectView)
1521       {
1522          ideSettings.ideFileDialogLocation = directory;
1523          settingsContainer.Save();
1524       }
1525
1526       ideFileDialog.currentDirectory = tempString;
1527       codeEditorFileDialog.currentDirectory = tempString;
1528       codeEditorFormFileDialog.currentDirectory = tempString;
1529    }
1530
1531    void ChangeProjectFileDialogDirectory(char * directory)
1532    {
1533       ideSettings.ideProjectFileDialogLocation = directory;
1534       settingsContainer.Save();
1535    }
1536
1537    Window FindWindow(char * filePath)
1538    {
1539       Window document = null;
1540
1541       // TOCHECK: Do we need to change slashes here?
1542       for(document = firstChild; document; document = document.next)
1543       {
1544          char * fileName = document.fileName;
1545          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
1546          {
1547             document.visible = true;
1548             document.Activate();
1549             return document;
1550          }
1551       }
1552       return null;
1553    }
1554
1555    bool DontTerminateDebugSession(char * title)
1556    {
1557       if(debugger.isActive)
1558       {
1559          if(MessageBox { type = yesNo, master = ide, 
1560                            contents = $"Do you want to terminate the debugging session in progress?", 
1561                            text = title }.Modal() == no)
1562             return true;
1563          /*
1564          MessageBox msg { type = yesNo, master = ide, 
1565                            contents = "Do you want to terminate the debugging session in progress?", 
1566                            text = title };
1567          if(msg.Modal() == no)
1568          {
1569             msg.Destroy(0);
1570             delete msg;
1571             return true;
1572          }
1573          msg.Destroy(0);
1574          delete msg;*/
1575       }
1576       return false;
1577    }
1578
1579    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod)
1580    {
1581       char extension[MAX_EXTENSION] = "";
1582       Window document = null;
1583       bool isProject = false;
1584       bool needFileModified = true;
1585       char winFilePath[MAX_LOCATION];
1586       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
1587
1588       if(!type)
1589       {
1590          GetExtension(filePath, extension);
1591          strlwr(extension);
1592       }
1593       else
1594          strcpy(extension, type);
1595
1596       if(strcmp(extension, ProjectExtension))
1597       {
1598          for(document = firstChild; document; document = document.next)
1599          {
1600             char * fileName = document.fileName;
1601             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
1602             {
1603                document.visible = true;
1604                document.Activate();
1605                return document;
1606             }
1607          }
1608       }
1609
1610       if(createIfFails == whatever)
1611          ;
1612       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
1613       {
1614          if(openMethod == normal)
1615          {
1616             if(DontTerminateDebugSession($"Open Project"))
1617                return null;
1618             isProject = true;
1619             if(MenuWindowCloseAll(null, 0))
1620             {
1621                if(projectView)
1622                {
1623                   projectView.visible = false;
1624                   projectView.Destroy(0);
1625                   // Where did this come from? projectView = null;
1626                }
1627                if(!projectView)
1628                {
1629                   for(;;)
1630                   {
1631                      Project project;
1632                      Workspace workspace = null;
1633                      
1634                      if(FileExists(filePath))
1635                      {
1636                         if(!strcmp(extension, ProjectExtension))
1637                         {
1638                            char workspaceFile[MAX_LOCATION];
1639                            strcpy(workspaceFile, filePath);
1640                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
1641                            workspace = LoadWorkspace(workspaceFile, filePath);
1642                         }
1643                         else if(!strcmp(extension, WorkspaceExtension))
1644                            workspace = LoadWorkspace(filePath, null);
1645                         else
1646                            return null;
1647                         //project = LoadProject(filePath);
1648                      }
1649                      
1650                      if(workspace)
1651                      {
1652                         char absolutePath[MAX_LOCATION];
1653                         CreateProjectView(workspace, filePath);
1654                         document = projectView;
1655
1656                         workspace.DropInvalidBreakpoints();
1657                         workspace.Save();
1658
1659                         ide.projectView.ShowOutputBuildLog(true);
1660                         {
1661                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1662                            ide.projectView.DisplayCompiler(compiler, false);
1663                            delete compiler;
1664                         }
1665                         UpdateMakefiles();
1666                         {
1667                            char newWorkingDir[MAX_LOCATION];
1668                            StripLastDirectory(filePath, newWorkingDir);
1669                            ChangeFileDialogsDirectory(newWorkingDir, false);
1670                         }
1671                         if(document)
1672                            document.fileName = filePath;
1673
1674                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
1675
1676                         // this crashes on starting ide with epj file, solution please?
1677                         // app.UpdateDisplay();
1678
1679                         workspace.holdTracking = true;
1680                         for(ofi : workspace.openedFiles)
1681                         {
1682                            if(ofi.state != closed)
1683                            {
1684                               Window file = OpenFile(ofi.path, normal, true, null, no, normal);
1685                               if(file)
1686                               {
1687                                  char fileName[MAX_LOCATION];
1688                                  ProjectNode node;
1689                                  GetLastDirectory(ofi.path, fileName);
1690                                  node = projectView.project.topNode.Find(fileName, true);
1691                                  if(node)
1692                                     node.EnsureVisible();
1693                               }
1694                            }
1695                         }
1696                         workspace.holdTracking = false;
1697
1698                         workspace.timer.Start();
1699
1700                         findInFilesDialog.mode = FindInFilesMode::project;
1701                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
1702                         
1703                         {
1704                            char location[MAX_LOCATION];
1705                            StripLastDirectory(ide.project.topNode.path, location);
1706                            ChangeProjectFileDialogDirectory(location);
1707                         }
1708                         
1709                         /*
1710                         if(projectView.debugger)
1711                            projectView.debugger.EvaluateWatches();
1712                         */
1713                         
1714                         break;
1715                      }
1716                      else 
1717                      {
1718                         if(MessageBox { type = yesNo, parent = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
1719                         {
1720                            ideProjectFileDialog.text = openProjectFileDialogTitle;
1721                            if(ideProjectFileDialog.Modal() == cancel)
1722                               return null;
1723                            filePath = ideProjectFileDialog.filePath;
1724                            GetExtension(filePath, extension);
1725                         }
1726                         else
1727                            return null;
1728                      }
1729                   }
1730                }
1731             }
1732             else
1733                return document;
1734          }
1735          else if(openMethod == add)
1736          {
1737             if(workspace)
1738             {
1739                Project prj = null;
1740                char slashFilePath[MAX_LOCATION];
1741                GetSlashPathBuffer(slashFilePath, filePath);
1742                for(p : workspace.projects)
1743                {
1744                   if(!fstrcmp(p.filePath, slashFilePath))
1745                   {
1746                      prj = p;
1747                      break;
1748                   }
1749                }
1750                if(prj)
1751                {
1752                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
1753                         contents = $"This project is already present in workspace." }.Modal();
1754                }
1755                else
1756                {
1757                   prj = LoadProject(filePath);
1758                   if(prj)
1759                   {
1760                      workspace.projects.Add(prj);
1761                      if(projectView)
1762                         projectView.AddNode(prj.topNode, null);
1763                      workspace.modified = true;
1764                      workspace.Save();
1765                      findInFilesDialog.AddProjectItem(prj);
1766                      projectView.ProjectUpdateMakefileForAllConfigs(prj, true, true);
1767
1768                      {
1769                         char location[MAX_LOCATION];
1770                         StripLastDirectory(prj.topNode.path, location);
1771                         ChangeProjectFileDialogDirectory(location);
1772                      }
1773
1774                      // projectView is associated with the main project and not with the one just added but
1775                      return projectView; // just to let the caller know something was opened
1776                   }
1777                }
1778             }
1779             else
1780                return null;
1781          }
1782       }
1783       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
1784             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
1785             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
1786       {
1787          if(FileExists(filePath))
1788             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1789                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1790                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
1791                                     };
1792          if(!document)
1793             MessageBox { type = ok, parent = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1794       }
1795 #ifndef NO3D
1796       else if(!strcmp(extension, "3ds"))
1797       {
1798          if(FileExists(filePath))
1799             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1800                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1801                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
1802                                     };
1803
1804          if(!document)
1805             MessageBox { type = ok, parent = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1806       }
1807 #endif
1808       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
1809             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
1810             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
1811             !strcmp(extension, "css") || !strcmp(extension, "php") ||
1812             !strcmp(extension, "js"))
1813       {
1814          CodeEditor editor { parent = this, state = state, visible = false };
1815          editor.updatingCode = true;
1816          if(editor.LoadFile(filePath))
1817          {
1818             document = editor;
1819             editor.visible = true;
1820          }
1821          else
1822             delete editor;
1823          needFileModified = false;
1824       }
1825       else
1826       {
1827          CodeEditor editor { parent = this, state = state, visible = false };
1828          if(editor.LoadFile(filePath))
1829          {
1830             document = editor;
1831             editor.visible = true;
1832          }
1833          else
1834             delete editor;
1835          needFileModified = false;
1836       }
1837
1838       if(document && (document._class == class(PictureEdit) ||
1839             document._class == class(ModelView)))
1840       {
1841          document.Create();
1842          if(document)
1843          {
1844             document.fileName = filePath;
1845             if(workspace && !workspace.holdTracking)
1846                workspace.UpdateOpenedFileInfo(filePath, opened);
1847          }
1848       }
1849       
1850       if(!document && createIfFails != no)
1851       {
1852          if(createIfFails != yes && !needFileModified && 
1853                MessageBox { type = yesNo, parent = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
1854             createIfFails = yes;
1855          if(createIfFails == yes || createIfFails == whatever)
1856          {
1857             document = (Window)NewCodeEditor(this, state, true);
1858             if(document)
1859                document.fileName = filePath;
1860          }
1861       }
1862
1863       if(document)
1864       {
1865          if(projectView && document._class == class(CodeEditor) && workspace)
1866          {
1867             int lineNumber, position;
1868             Point scroll;
1869             CodeEditor editor = (CodeEditor)document;
1870             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
1871             editor.openedFileInfo.holdTracking = true;
1872             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
1873             position = Max(editor.openedFileInfo.position - 1, 0);
1874             editor.editBox.GoToLineNum(lineNumber);
1875             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
1876             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
1877             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
1878             editor.editBox.scroll = scroll;
1879             editor.openedFileInfo.holdTracking = false;
1880          }
1881          
1882          if(needFileModified)
1883             document.OnFileModified = OnFileModified;
1884          document.NotifySaved = DocumentSaved;
1885          
1886          if(isProject)
1887             ideSettings.AddRecentProject(document.fileName);
1888          else
1889             ideSettings.AddRecentFile(document.fileName);
1890          ide.UpdateRecentMenus();
1891          settingsContainer.Save();
1892          
1893          return document;
1894       }
1895       else
1896          return null;
1897    }
1898
1899    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
1900    /*bool Window::GenericDocumentOnClose(bool parentClosing)
1901    {
1902       if(!parentClosing && ide.workspace)
1903          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
1904       return true;
1905    }*/
1906    bool ModelView::ModelViewOnClose(bool parentClosing)
1907    {
1908       if(!parentClosing && ide.workspace)
1909          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
1910       return true;
1911    }
1912    bool PictureEdit::PictureEditOnClose(bool parentClosing)
1913    {
1914       if(!parentClosing && ide.workspace)
1915          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
1916       return true;
1917    }
1918
1919    /*
1920    void OnUnloadGraphics(Window window)
1921    {
1922       display.ClearMaterials();
1923       display.ClearTextures();
1924       display.ClearMeshes();
1925    }
1926    */
1927
1928    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
1929    {
1930       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
1931       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
1932       return true;
1933    }
1934
1935    bool OnKeyDown(Key key, unichar ch)
1936    {
1937       switch(key)
1938       {
1939          case b:
1940             projectView.Update(null);
1941             break;
1942          case capsLock:
1943             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
1944             break;
1945          case numLock:
1946             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
1947             break;
1948       }
1949       return true;
1950    }
1951
1952    void GoToError(const char * line)
1953    {
1954       if(projectView)
1955          projectView.GoToError(line);
1956    }
1957
1958    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
1959    {
1960       char *path = text;
1961       char *colon = strchr(text, ':');
1962       char filePath[MAX_LOCATION];
1963       char completePath[MAX_LOCATION];
1964       int line = 0, col = 0;
1965       Project prj = null;
1966
1967       if(text[3] == '(')
1968       {
1969          char * close = strchr(text, ')');
1970          if(close)
1971          {
1972             char name[256];
1973             strncpy(name, &text[4], close - text - 4);
1974             name[close - text - 4] = '\0';
1975             for(p : ide.workspace.projects)
1976             {
1977                if(!strcmp(p.name, name))
1978                {
1979                   path = close + 1;
1980                   prj = p;
1981                   break;
1982                }
1983             }
1984          }
1985       }
1986       if(!prj)
1987          prj = project ? project : (dir ? null : ide.project);
1988       if(colon && (colon[1] == '/' || colon[1] == '\\'))
1989       {
1990          path = (colon - 1 > path) ? colon - 1 : path;
1991          colon = strstr(colon + 1, ":");
1992       }
1993       while(isspace(*path)) path++;
1994       if(colon)
1995       {
1996          strncpy(filePath, path, colon - path);
1997          filePath[colon - path] = '\0';
1998          line = atoi(colon + 1);
1999          colon = strstr(colon + 1, ":");
2000          if(colon)
2001             col = atoi(colon + 1);
2002       }
2003       else if(path - 1 >= path && *(path - 1) == '\"')
2004       {
2005          colon = strchr(path, '\"');
2006          if(colon)
2007          {
2008             strncpy(filePath, path, colon - path);
2009             filePath[colon - path] = '\0';
2010          }
2011       }
2012
2013       if(prj)
2014          strcpy(completePath, prj.topNode.path);
2015       else if(dir && dir[0])
2016          strcpy(completePath, dir);
2017       else
2018          completePath[0] = '\0';
2019       PathCat(completePath, filePath);
2020
2021       if(FileExists(completePath).isFile)
2022       {
2023          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal);
2024          if(codeEditor && line)
2025          {
2026             EditBox editBox = codeEditor.editBox;
2027             editBox.GoToLineNum(line - 1);
2028             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2029          }
2030       }
2031    }
2032
2033    void OnRedraw(Surface surface)
2034    {
2035       Bitmap bitmap = back.bitmap;
2036       if(bitmap)
2037          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2038    }
2039
2040    void SheetSelected(SheetType sheetSelected)
2041    {
2042       if(activeChild == sheet)
2043       {
2044          if(sheetSelected == methods)
2045          {
2046             viewPropertiesItem.accelerator = f4;
2047             viewPropertiesItem.parent = viewMenu;
2048             viewMethodsItem.parent = null;
2049          }
2050          else
2051          {
2052             viewMethodsItem.accelerator = f4;
2053             viewMethodsItem.parent = viewMenu;
2054             viewPropertiesItem.parent = null;
2055          }
2056       }
2057       else
2058       {
2059          viewMethodsItem.parent = viewMenu;
2060          viewPropertiesItem.parent = viewMenu;
2061          if(sheetSelected == methods)
2062          {
2063             viewMethodsItem.accelerator = f4;
2064             viewPropertiesItem.accelerator = 0;
2065          }
2066          else
2067          {
2068             viewMethodsItem.accelerator = 0;
2069             viewPropertiesItem.accelerator = f4;
2070          }
2071       }
2072    }
2073
2074    void OnActivateClient(Window client, Window previous)
2075    {
2076       //if(!client || client != previous)
2077       {
2078          Class dataType;
2079          if(!client || client != previous)
2080          {
2081             if(previous)
2082                dataType = previous._class;
2083             if(previous && !strcmp(dataType.name, "CodeEditor"))
2084             {
2085                ((CodeEditor)previous).UpdateFormCode();
2086             }
2087             else if(previous && !strcmp(dataType.name, "Designer"))
2088             {
2089                ((Designer)previous).codeEditor.UpdateFormCode();
2090             }
2091          }
2092
2093          if(client)
2094             dataType = client._class;
2095          if(client && !strcmp(dataType.name, "CodeEditor"))
2096          {
2097             CodeEditor codeEditor = (CodeEditor)client;
2098             SetPrivateModule(codeEditor.privateModule);
2099             SetCurrentContext(codeEditor.globalContext);
2100             SetTopContext(codeEditor.globalContext);
2101             SetGlobalContext(codeEditor.globalContext);
2102             
2103             SetDefines(&codeEditor.defines);
2104             SetImports(&codeEditor.imports);
2105
2106             SetActiveDesigner(codeEditor.designer);
2107             
2108             sheet.codeEditor = codeEditor;
2109             toolBox.codeEditor = codeEditor;
2110
2111             viewDesignerItem.parent = viewMenu;
2112             if(activeChild != codeEditor)
2113             {
2114                viewCodeItem.parent = viewMenu;
2115                viewDesignerItem.accelerator = 0;
2116                viewCodeItem.accelerator = f8;
2117             }
2118             else
2119             {
2120                viewCodeItem.parent = null;
2121                viewDesignerItem.accelerator = f8;
2122             }
2123          }
2124          else if(client && !strcmp(dataType.name, "Designer"))
2125          {
2126             CodeEditor codeEditor = ((Designer)client).codeEditor;
2127             if(codeEditor)
2128             {
2129                SetPrivateModule(codeEditor.privateModule);
2130                SetCurrentContext(codeEditor.globalContext);
2131                SetTopContext(codeEditor.globalContext);
2132                SetGlobalContext(codeEditor.globalContext);
2133                SetDefines(&codeEditor.defines);
2134                SetImports(&codeEditor.imports);
2135             }
2136             else
2137             {
2138                SetPrivateModule(null);
2139                SetCurrentContext(null);
2140                SetTopContext(null);
2141                SetGlobalContext(null);
2142                SetDefines(null);
2143                SetImports(null);
2144             }
2145
2146             SetActiveDesigner((Designer)client);
2147
2148             sheet.codeEditor = codeEditor;
2149             toolBox.codeEditor = codeEditor;
2150
2151             viewCodeItem.parent = viewMenu;
2152             if(activeChild != client)
2153             {
2154                viewDesignerItem.parent = viewMenu;
2155                viewDesignerItem.accelerator = f8;
2156                viewCodeItem.accelerator = 0;
2157             }
2158             else
2159             {
2160                viewDesignerItem.parent = null;
2161                viewCodeItem.accelerator = f8;
2162             }
2163          }
2164          else
2165          {
2166             if(sheet)
2167                sheet.codeEditor = null;
2168             toolBox.codeEditor = null;
2169             SetActiveDesigner(null);
2170
2171             viewDesignerItem.parent = null;
2172             viewCodeItem.parent = null;
2173          }
2174          if(sheet)
2175             SheetSelected(sheet.sheetSelected);
2176       }
2177
2178       projectCompileItem = null;
2179
2180       if(statusBar)
2181       {
2182          statusBar.Clear();
2183          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2184          {
2185             CodeEditor codeEditor = (CodeEditor)client;
2186             EditBox editBox = codeEditor.editBox;
2187
2188             statusBar.AddField(pos);
2189
2190             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2191             statusBar.AddField(caps);
2192
2193             ovr = { width = 30, text = $"OVR", color = editBox.overwrite ? black : Color { 128, 128, 128 } };
2194             statusBar.AddField(ovr);
2195
2196             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2197             statusBar.AddField(num);
2198
2199             //statusBar.text = "Ready";
2200
2201             if(projectView && projectView.project)
2202             {
2203                ProjectNode node = projectView.GetNodeFromWindow(client, null);
2204                if(node)
2205                {
2206                   char name[1024];
2207                   sprintf(name, $"Compile %s", node.name);
2208                   projectCompileItem = 
2209                   {
2210                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2211
2212                      bool NotifySelect(MenuItem selection, Modifiers mods)
2213                      {
2214                         if(projectView)
2215                         {
2216                            ProjectNode node = projectView.GetNodeFromWindow(activeClient, null);
2217                            if(node)
2218                               projectView.Compile(node);
2219                         }
2220                         return true;
2221                      }
2222                   };
2223                   projectMenu.AddDynamic(projectCompileItem, null, false);
2224                }
2225             }
2226          }
2227          else
2228          {
2229             caps = ovr = num = null;
2230          }
2231       }
2232    }
2233
2234    bool OnClose(bool parentClosing)
2235    {
2236       //return !projectView.buildInProgress;
2237       if(projectView && projectView.buildInProgress)
2238          return false;
2239       if(DontTerminateDebugSession($"Close IDE"))
2240          return false;
2241       if(findInFilesDialog)
2242          findInFilesDialog.SearchStop();
2243       if(workspace)
2244       {
2245          workspace.timer.Stop();
2246          workspace.Save();
2247       }
2248       ideMainFrame.Destroy(0);
2249       return true;
2250    }
2251
2252    bool OnPostCreate()
2253    {
2254       int c;
2255       for(c = 1; c<app.argc; c++)
2256       {
2257          char fullPath[MAX_LOCATION];
2258          GetWorkingDir(fullPath, MAX_LOCATION);
2259          PathCat(fullPath, app.argv[c]);
2260          if(FileExists(fullPath))
2261             ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2262       }
2263       return true;
2264    }
2265
2266    void OnDestroy()
2267    {
2268       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2269       if(projectView)
2270       {
2271          projectView.visible = false;
2272          projectView.Destroy(0);
2273          projectView = null;
2274       }
2275 #ifdef GDB_DEBUG_GUI
2276       gdbDialog.Destroy(0);
2277       delete gdbDialog;
2278 #endif
2279    }
2280
2281    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
2282    {
2283       int c, len, count;
2284       char * newList;
2285       char * oldPaths[128];
2286       String oldList = new char[maxPathLen];
2287       Array<String> newExePaths { };
2288       //Map<String, bool> exePathExists { };
2289       bool found = false;
2290 #if defined(__unix__) || defined(__APPLE__)
2291       Array<String> newLibPaths { };
2292       Map<String, bool> libPathExists { };
2293 #endif
2294
2295       if(projectsDirs)
2296       {
2297          for(prj : workspace.projects)
2298          {
2299             DirExpression targetDirExp;
2300
2301             // SKIP FIRST PROJECT...
2302             if(prj == workspace.projects.firstIterator.data) continue;
2303
2304             // NOTE: Right now the additional project config dir will be
2305             //       obtained when the debugger is started, so toggling it
2306             //       while building will change which library gets used.
2307             //       To go with the initial state, e.g. when F5 was pressed,
2308             //       we nould need to keep a list of all project's active
2309             //       config upon startup.
2310             targetDirExp = prj.GetTargetDir(compiler, prj.config);
2311
2312             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2313                cfg = prj.config;
2314             else
2315             {
2316                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2317                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2318                      break;
2319                if(!cfg)
2320                {
2321                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2322                      if(cfg.targetType == sharedLibrary && cfg.debug)
2323                         break;
2324                }
2325             }*/
2326             if(targetDirExp.dir)
2327             {
2328                char buffer[MAX_LOCATION];
2329 #if defined(__WIN32__)
2330                Array<String> paths = newExePaths;
2331 #else
2332                Array<String> paths = newLibPaths;
2333 #endif
2334                GetSystemPathBuffer(buffer, prj.topNode.path);
2335                PathCat(buffer, targetDirExp.dir);
2336                for(p : paths)
2337                {
2338                   if(!fstrcmp(p, buffer))
2339                   {
2340                      found = true;
2341                      break;
2342                   }
2343                }
2344                if(!found)
2345                   paths.Add(CopyString(buffer));
2346             }
2347             delete targetDirExp;
2348          }
2349       }
2350
2351       for(item : compiler.executableDirs)
2352       {
2353          found = false;
2354          for(p : newExePaths)
2355          {
2356             if(!fstrcmp(p, item))
2357             {
2358                found = true;
2359                break;
2360             }
2361          }
2362          if(!found)
2363             newExePaths.Add(CopySystemPath(item));
2364       }
2365
2366       GetEnvironment("PATH", oldList, maxPathLen);
2367 /*#ifdef _DEBUG
2368       printf("Old value of PATH: %s\n", oldList);
2369 #endif*/
2370       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2371       for(c = 0; c < count; c++)
2372       {
2373          found = false;
2374          for(p : newExePaths)
2375          {
2376             if(!fstrcmp(p, oldPaths[c]))
2377             {
2378                found = true;
2379                break;
2380             }
2381          }
2382          if(!found)
2383             newExePaths.Add(CopySystemPath(oldPaths[c]));
2384       }
2385
2386       len = 0;
2387       for(path : newExePaths)
2388          len += strlen(path) + 1;
2389       newList = new char[len + 1];
2390       newList[0] = '\0';
2391       for(path : newExePaths)
2392       {
2393          strcat(newList, path);
2394          strcat(newList, pathListSep);
2395       }
2396       newList[len - 1] = '\0';
2397       SetEnvironment("PATH", newList);
2398 /*#ifdef _DEBUG
2399       printf("New value of PATH: %s\n", newList);
2400 #endif*/
2401       delete newList;
2402
2403       newExePaths.Free();
2404       delete newExePaths;
2405
2406 #if defined(__unix__) || defined(__APPLE__)
2407
2408       for(item : compiler.libraryDirs)
2409       {
2410          if(!libPathExists[item])  // fstrcmp should be used
2411          {
2412             newLibPaths.Add(item);
2413             libPathExists[item] = true;
2414          }
2415       }
2416
2417 #if defined(__APPLE__)
2418       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2419 #else
2420       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2421 #endif
2422 /*#ifdef _DEBUG
2423       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
2424 #endif*/
2425       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2426       for(c = 0; c < count; c++)
2427       {
2428          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2429          {
2430             newLibPaths.Add(oldPaths[c]);
2431             libPathExists[oldPaths[c]] = true;
2432          }
2433       }
2434
2435       len = 0;
2436       for(path : newLibPaths)
2437          len += strlen(path) + 1;
2438       newList = new char[len + 1];
2439       newList[0] = '\0';
2440       for(path : newLibPaths)
2441       {
2442          strcat(newList, path);
2443          strcat(newList, pathListSep);
2444       }
2445       newList[len - 1] = '\0';
2446 #if defined(__APPLE__)
2447       SetEnvironment("DYLD_LIBRARY_PATH", newList);
2448 #else
2449       SetEnvironment("LD_LIBRARY_PATH", newList);
2450 #endif
2451 /*#ifdef _DEBUG
2452       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
2453 #endif*/
2454       delete newList;
2455
2456       delete newLibPaths;
2457       delete libPathExists;
2458 #endif
2459
2460       if(compiler.distccEnabled && compiler.distccHosts)
2461          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
2462
2463       delete oldList;
2464    }
2465
2466    void DestroyTemporaryProjectDir()
2467    {
2468       if(tmpPrjDir && tmpPrjDir[0])
2469       {
2470          if(FileExists(tmpPrjDir).isDirectory)
2471             DestroyDir(tmpPrjDir);
2472          property::tmpPrjDir = null;
2473       }
2474    }
2475
2476    IDEWorkSpace()
2477    {
2478       // Graphics Driver Menu
2479       int c;
2480
2481       /*
2482       app.currentSkin.selectionColor = selectionColor;
2483       app.currentSkin.selectionText = selectionText;
2484       */
2485
2486 /*
2487       driverItems = new MenuItem[app.numDrivers];
2488       for(c = 0; c < app.numDrivers; c++)
2489       {
2490          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
2491          driverItems[c].id = c;
2492          driverItems[c].isRadio = true;         
2493       }
2494 */
2495       driverItems = new MenuItem[2];
2496 #if defined(__unix__)
2497          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
2498          driverItems[0].id = 0;
2499          driverItems[0].isRadio = true;         
2500 #else
2501          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
2502          driverItems[0].id = 0;
2503          driverItems[0].isRadio = true;         
2504 #endif
2505          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
2506          driverItems[1].id = 1;
2507          driverItems[1].isRadio = true;         
2508
2509 /*      skinItems = new MenuItem[app.numSkins];
2510       for(c = 0; c < app.numSkins; c++)
2511       {
2512          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
2513          skinItems[c].id = c;
2514          skinItems[c].isRadio = true;         
2515       }
2516 */
2517       ideFileDialog.master = this;
2518       ideProjectFileDialog.master = this;
2519
2520       //SetDriverAndSkin();
2521       return true;
2522    }
2523
2524    void UpdateRecentMenus()
2525    {
2526       int c;
2527       Menu fileMenu = menu.FindMenu($"File");
2528       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
2529       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
2530       char itemName[MAX_LOCATION + 4];
2531       MenuItem item;
2532
2533       recentFiles.Clear();
2534       c = 0;
2535
2536       for(recent : ideSettings.recentFiles)
2537       {
2538          sprintf(itemName, "%d %s", 1 + c, recent);
2539          MakeSystemPath(itemName);
2540          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, null, true);
2541          c++;
2542       }
2543
2544       recentProjects.Clear();
2545       c = 0;
2546       for(recent : ideSettings.recentProjects)
2547       {
2548          sprintf(itemName, "%d %s", 1 + c, recent);
2549          MakeSystemPath(itemName);
2550          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, null, true);
2551          c++;
2552       }
2553    }
2554
2555    ~IDEWorkSpace()
2556    {
2557       delete driverItems;
2558       delete skinItems;
2559       delete ideSettings;
2560    }
2561 }
2562
2563 void DestroyDir(char * path)
2564 {
2565    RecursiveDeleteFolderFSI fsi { };
2566    fsi.Iterate(path);
2567    delete fsi;
2568 }
2569
2570 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
2571 {
2572    bool preserveRootFolder;
2573
2574    void OutFolder(char * folderPath, bool isRoot)
2575    {
2576       if(!(preserveRootFolder && isRoot))
2577          RemoveDir(folderPath);
2578    }
2579
2580    bool OnFile(char * filePath)
2581    {
2582       DeleteFile(filePath);
2583       return true;
2584    }
2585 }
2586
2587 class IDEApp : GuiApplication
2588 {
2589    //driver = "Win32Console";
2590    // driver = "OpenGL";
2591    // skin = "Aqua";
2592    //skin = "TVision";
2593    bool Init()
2594    {
2595       SetLoggingMode(stdOut, null);
2596       //SetLoggingMode(debug, null);
2597
2598       settingsContainer.Load();
2599 #if defined(__unix__) || defined(__APPLE__)
2600       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
2601 #else
2602       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
2603 #endif
2604       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
2605
2606       SetInIDE(true);
2607
2608       desktop.text = titleECEREIDE;
2609       /*
2610       int c;
2611       for(c = 1; c<app.argc; c++)
2612       {
2613          char fullPath[MAX_LOCATION];
2614          GetWorkingDir(fullPath, MAX_LOCATION);
2615          PathCat(fullPath, app.argv[c]);
2616          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2617       }
2618       */
2619       return true;
2620    }
2621 }
2622
2623 IDEMainFrame ideMainFrame { };
2624
2625 define app = ((IDEApp)__thisModule);
2626 #ifdef _DEBUG
2627 define titleECEREIDE = $"ECERE IDE (Debug)";
2628 #else
2629 define titleECEREIDE = $"ECERE IDE";
2630 #endif