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