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