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