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