ide: added feature open recently opened files / projects in another ide by holding...
[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::FileCompile, 0);       if(menu) menu.disabled = unavailable;
1667          projectView.popupMenu.Update(null);
1668       }
1669    }
1670
1671    void AdjustDebugMenus()
1672    {
1673       bool unavailable = !project || project.GetTargetType(project.config) != executable ||
1674                projectView.buildInProgress == buildingMainProject;
1675       bool active = ide.debugger.isActive;
1676       bool executing = ide.debugger.state == running;
1677       //bool holding = ide.debugger.state == stopped;
1678
1679       debugStartResumeItem.disabled       = unavailable || executing;
1680       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
1681       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
1682       if(toolBar)
1683       {
1684          toolBar.buttonDebugStartResume.disabled      = unavailable || executing;
1685          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
1686       }
1687
1688       debugBreakItem.disabled             = unavailable || !executing;
1689       debugStopItem.disabled              = unavailable || !active;
1690       debugRestartItem.disabled           = unavailable || !active;
1691       if(toolBar)
1692       {
1693          toolBar.buttonDebugPause.disabled            = unavailable || !executing;
1694          toolBar.buttonDebugStop.disabled             = unavailable || !active;
1695          toolBar.buttonDebugRestart.disabled          = unavailable || !active;
1696       }
1697
1698       debugStepIntoItem.disabled          = unavailable || executing;
1699       debugStepOverItem.disabled          = unavailable || executing;
1700       debugStepOutItem.disabled           = unavailable || executing || !active;
1701       debugSkipStepOverItem.disabled      = unavailable || executing;
1702       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
1703       if(toolBar)
1704       {
1705          toolBar.buttonDebugStepInto.disabled         = unavailable || executing;
1706          toolBar.buttonDebugStepOver.disabled         = unavailable || executing;
1707          toolBar.buttonDebugStepOut.disabled          = unavailable || executing || !active;
1708          toolBar.buttonDebugSkipStepOver.disabled     = unavailable || executing;
1709          // toolBar.buttonDebugSkipStepOutItem.disabled  = unavailable || executing;
1710       }
1711       if((Designer)GetActiveDesigner())
1712       {
1713          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
1714          if(codeEditor)
1715          {
1716             codeEditor.debugRunToCursor.disabled      = unavailable || executing;
1717             codeEditor.debugSkipRunToCursor.disabled  = unavailable || executing;
1718          }
1719       }
1720    }
1721
1722    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
1723    {
1724       char tempString[MAX_LOCATION];
1725       strcpy(tempString, directory);
1726       if(saveSettings && !projectView)
1727       {
1728          ideSettings.ideFileDialogLocation = directory;
1729          settingsContainer.Save();
1730       }
1731
1732       ideFileDialog.currentDirectory = tempString;
1733       codeEditorFileDialog.currentDirectory = tempString;
1734       codeEditorFormFileDialog.currentDirectory = tempString;
1735    }
1736
1737    void ChangeProjectFileDialogDirectory(char * directory)
1738    {
1739       ideSettings.ideProjectFileDialogLocation = directory;
1740       settingsContainer.Save();
1741    }
1742
1743    Window FindWindow(char * filePath)
1744    {
1745       Window document = null;
1746
1747       // TOCHECK: Do we need to change slashes here?
1748       for(document = firstChild; document; document = document.next)
1749       {
1750          char * fileName = document.fileName;
1751          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
1752          {
1753             document.visible = true;
1754             document.Activate();
1755             return document;
1756          }
1757       }
1758       return null;
1759    }
1760
1761    bool DontTerminateDebugSession(char * title)
1762    {
1763       if(debugger.isActive)
1764       {
1765          if(MessageBox { type = yesNo, master = ide, 
1766                            contents = $"Do you want to terminate the debugging session in progress?", 
1767                            text = title }.Modal() == no)
1768             return true;
1769          /*
1770          MessageBox msg { type = yesNo, master = ide, 
1771                            contents = "Do you want to terminate the debugging session in progress?", 
1772                            text = title };
1773          if(msg.Modal() == no)
1774          {
1775             msg.Destroy(0);
1776             delete msg;
1777             return true;
1778          }
1779          msg.Destroy(0);
1780          delete msg;*/
1781       }
1782       return false;
1783    }
1784
1785    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod)
1786    {
1787       char extension[MAX_EXTENSION] = "";
1788       Window document = null;
1789       bool isProject = false;
1790       bool needFileModified = true;
1791       char winFilePath[MAX_LOCATION];
1792       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
1793
1794       if(!type)
1795       {
1796          GetExtension(filePath, extension);
1797          strlwr(extension);
1798       }
1799       else
1800          strcpy(extension, type);
1801
1802       if(strcmp(extension, ProjectExtension))
1803       {
1804          for(document = firstChild; document; document = document.next)
1805          {
1806             char * fileName = document.fileName;
1807             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
1808             {
1809                document.visible = true;
1810                if(visible)
1811                   document.Activate();
1812                return document;
1813             }
1814          }
1815       }
1816
1817       if(createIfFails == whatever)
1818          ;
1819       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
1820       {
1821          needFileModified = false;
1822          if(openMethod == normal)
1823          {
1824             if(DontTerminateDebugSession($"Open Project"))
1825                return null;
1826             isProject = true;
1827             if(MenuWindowCloseAll(null, 0))
1828             {
1829                if(projectView)
1830                {
1831                   projectView.visible = false;
1832                   projectView.Destroy(0);
1833                   // Where did this come from? projectView = null;
1834                }
1835                if(!projectView)
1836                {
1837                   for(;;)
1838                   {
1839                      Project project;
1840                      Workspace workspace = null;
1841                      
1842                      if(FileExists(filePath))
1843                      {
1844                         if(!strcmp(extension, ProjectExtension))
1845                         {
1846                            char workspaceFile[MAX_LOCATION];
1847                            strcpy(workspaceFile, filePath);
1848                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
1849                            workspace = LoadWorkspace(workspaceFile, filePath);
1850                         }
1851                         else if(!strcmp(extension, WorkspaceExtension))
1852                            workspace = LoadWorkspace(filePath, null);
1853                         else
1854                            return null;
1855                         //project = LoadProject(filePath);
1856                      }
1857                      
1858                      if(workspace)
1859                      {
1860                         char absolutePath[MAX_LOCATION];
1861                         CreateProjectView(workspace, filePath);
1862                         document = projectView;
1863
1864                         workspace.DropInvalidBreakpoints();
1865                         workspace.Save();
1866
1867                         ide.projectView.ShowOutputBuildLog(true);
1868                         {
1869                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1870                            ide.projectView.DisplayCompiler(compiler, false);
1871                            delete compiler;
1872                         }
1873                         UpdateCompilerConfigs();
1874                         UpdateMakefiles();
1875                         {
1876                            char newWorkingDir[MAX_LOCATION];
1877                            StripLastDirectory(filePath, newWorkingDir);
1878                            ChangeFileDialogsDirectory(newWorkingDir, false);
1879                         }
1880                         if(document)
1881                            document.fileName = filePath;
1882
1883                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
1884
1885                         // this crashes on starting ide with epj file, solution please?
1886                         // app.UpdateDisplay();
1887
1888                         workspace.holdTracking = true;
1889                         for(ofi : workspace.openedFiles)
1890                         {
1891                            if(ofi.state != closed)
1892                            {
1893                               Window file = OpenFile(ofi.path, normal, true, null, no, normal);
1894                               if(file)
1895                               {
1896                                  char fileName[MAX_LOCATION];
1897                                  ProjectNode node;
1898                                  GetLastDirectory(ofi.path, fileName);
1899                                  node = projectView.project.topNode.Find(fileName, true);
1900                                  if(node)
1901                                     node.EnsureVisible();
1902                               }
1903                            }
1904                         }
1905                         workspace.holdTracking = false;
1906
1907                         workspace.timer.Start();
1908
1909                         findInFilesDialog.mode = FindInFilesMode::project;
1910                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
1911                         
1912                         {
1913                            char location[MAX_LOCATION];
1914                            StripLastDirectory(ide.project.topNode.path, location);
1915                            ChangeProjectFileDialogDirectory(location);
1916                         }
1917                         
1918                         /*
1919                         if(projectView.debugger)
1920                            projectView.debugger.EvaluateWatches();
1921                         */
1922                         
1923                         break;
1924                      }
1925                      else 
1926                      {
1927                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
1928                         {
1929                            ideProjectFileDialog.text = openProjectFileDialogTitle;
1930                            if(ideProjectFileDialog.Modal() == cancel)
1931                               return null;
1932                            filePath = ideProjectFileDialog.filePath;
1933                            GetExtension(filePath, extension);
1934                         }
1935                         else
1936                            return null;
1937                      }
1938                   }
1939                }
1940             }
1941             else
1942                return null;
1943          }
1944          else if(openMethod == add)
1945          {
1946             if(workspace)
1947             {
1948                Project prj = null;
1949                char slashFilePath[MAX_LOCATION];
1950                GetSlashPathBuffer(slashFilePath, filePath);
1951                for(p : workspace.projects)
1952                {
1953                   if(!fstrcmp(p.filePath, slashFilePath))
1954                   {
1955                      prj = p;
1956                      break;
1957                   }
1958                }
1959                if(prj)
1960                {
1961                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
1962                         contents = $"This project is already present in workspace." }.Modal();
1963                }
1964                else
1965                {
1966                   prj = LoadProject(filePath);
1967                   if(prj)
1968                   {
1969                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1970                      prj.StartMonitoring();
1971                      workspace.projects.Add(prj);
1972                      if(projectView)
1973                         projectView.AddNode(prj.topNode, null);
1974                      workspace.modified = true;
1975                      workspace.Save();
1976                      findInFilesDialog.AddProjectItem(prj);
1977                      projectView.ShowOutputBuildLog(true);
1978                      projectView.DisplayCompiler(compiler, false);
1979                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
1980                      delete compiler;
1981
1982                      {
1983                         char location[MAX_LOCATION];
1984                         StripLastDirectory(prj.topNode.path, location);
1985                         ChangeProjectFileDialogDirectory(location);
1986                      }
1987
1988                      // projectView is associated with the main project and not with the one just added but
1989                      return projectView; // just to let the caller know something was opened
1990                   }
1991                }
1992             }
1993             else
1994                return null;
1995          }
1996       }
1997       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
1998             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
1999             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2000       {
2001          if(FileExists(filePath))
2002             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2003                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2004                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2005                                     };
2006          if(!document)
2007             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2008       }
2009 #ifndef NO3D
2010       else if(!strcmp(extension, "3ds"))
2011       {
2012          if(FileExists(filePath))
2013             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2014                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2015                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2016                                     };
2017
2018          if(!document)
2019             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2020       }
2021 #endif
2022       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2023             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2024             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2025             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2026             !strcmp(extension, "js"))
2027       {
2028          CodeEditor editor { parent = this, state = state, visible = false };
2029          editor.updatingCode = true;
2030          if(editor.LoadFile(filePath))
2031          {
2032             document = editor;
2033             editor.visible = true;
2034          }
2035          else
2036             delete editor;
2037          needFileModified = false;
2038       }
2039       else
2040       {
2041          CodeEditor editor { parent = this, state = state, visible = false };
2042          if(editor.LoadFile(filePath))
2043          {
2044             document = editor;
2045             editor.visible = true;
2046          }
2047          else
2048             delete editor;
2049          needFileModified = false;
2050       }
2051
2052       if(document && (document._class == class(PictureEdit) ||
2053             document._class == class(ModelView)))
2054       {
2055          document.Create();
2056          if(document)
2057          {
2058             document.fileName = filePath;
2059             if(workspace && !workspace.holdTracking)
2060                workspace.UpdateOpenedFileInfo(filePath, opened);
2061          }
2062       }
2063       
2064       if(!document && createIfFails != no)
2065       {
2066          if(createIfFails != yes && !needFileModified && 
2067                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2068             createIfFails = yes;
2069          if(createIfFails == yes || createIfFails == whatever)
2070          {
2071             document = (Window)NewCodeEditor(this, state, true);
2072             if(document)
2073                document.fileName = filePath;
2074          }
2075       }
2076
2077       if(document)
2078       {
2079          if(projectView && document._class == class(CodeEditor) && workspace)
2080          {
2081             int lineNumber, position;
2082             Point scroll;
2083             CodeEditor editor = (CodeEditor)document;
2084             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2085             editor.openedFileInfo.holdTracking = true;
2086             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2087             position = Max(editor.openedFileInfo.position - 1, 0);
2088             editor.editBox.GoToLineNum(lineNumber);
2089             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2090             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2091             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2092             editor.editBox.scroll = scroll;
2093             editor.openedFileInfo.holdTracking = false;
2094          }
2095          
2096          if(needFileModified)
2097             document.OnFileModified = OnFileModified;
2098          document.NotifySaved = DocumentSaved;
2099          
2100          if(isProject)
2101             ideSettings.AddRecentProject(document.fileName);
2102          else
2103             ideSettings.AddRecentFile(document.fileName);
2104          ide.UpdateRecentMenus();
2105          ide.AdjustFileMenus();
2106          settingsContainer.Save();
2107          
2108          return document;
2109       }
2110       else
2111          return null;
2112    }
2113
2114    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2115    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2116    {
2117       if(!parentClosing && ide.workspace)
2118          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2119       return true;
2120    }*/
2121    bool ModelView::ModelViewOnClose(bool parentClosing)
2122    {
2123       if(!parentClosing && ide.workspace)
2124          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2125       return true;
2126    }
2127    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2128    {
2129       if(!parentClosing && ide.workspace)
2130          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2131       return true;
2132    }
2133
2134    /*
2135    void OnUnloadGraphics(Window window)
2136    {
2137       display.ClearMaterials();
2138       display.ClearTextures();
2139       display.ClearMeshes();
2140    }
2141    */
2142
2143    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2144    {
2145       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2146       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2147       return true;
2148    }
2149
2150    bool OnKeyDown(Key key, unichar ch)
2151    {
2152       switch(key)
2153       {
2154          case b:
2155             projectView.Update(null);
2156             break;
2157          case capsLock:
2158             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2159             break;
2160          case numLock:
2161             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2162             break;
2163       }
2164       return true;
2165    }
2166
2167    void GoToError(const char * line)
2168    {
2169       if(projectView)
2170          projectView.GoToError(line);
2171    }
2172
2173    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2174    {
2175       char *path = text;
2176       char *colon = strchr(text, ':');
2177       char filePath[MAX_LOCATION];
2178       char completePath[MAX_LOCATION];
2179       int line = 0, col = 0;
2180       Project prj = null;
2181
2182       if(text[3] == '(')
2183       {
2184          char * close = strchr(text, ')');
2185          if(close)
2186          {
2187             char name[256];
2188             strncpy(name, &text[4], close - text - 4);
2189             name[close - text - 4] = '\0';
2190             for(p : ide.workspace.projects)
2191             {
2192                if(!strcmp(p.name, name))
2193                {
2194                   path = close + 1;
2195                   prj = p;
2196                   break;
2197                }
2198             }
2199          }
2200       }
2201       if(!prj)
2202          prj = project ? project : (dir ? null : ide.project);
2203       if(colon && (colon[1] == '/' || colon[1] == '\\'))
2204       {
2205          path = (colon - 1 > path) ? colon - 1 : path;
2206          colon = strstr(colon + 1, ":");
2207       }
2208       while(isspace(*path)) path++;
2209       if(colon)
2210       {
2211          strncpy(filePath, path, colon - path);
2212          filePath[colon - path] = '\0';
2213          line = atoi(colon + 1);
2214          colon = strstr(colon + 1, ":");
2215          if(colon)
2216             col = atoi(colon + 1);
2217       }
2218       else if(path - 1 >= path && *(path - 1) == '\"')
2219       {
2220          colon = strchr(path, '\"');
2221          if(colon)
2222          {
2223             strncpy(filePath, path, colon - path);
2224             filePath[colon - path] = '\0';
2225          }
2226       }
2227
2228       if(prj)
2229          strcpy(completePath, prj.topNode.path);
2230       else if(dir && dir[0])
2231          strcpy(completePath, dir);
2232       else
2233          completePath[0] = '\0';
2234       PathCat(completePath, filePath);
2235
2236       if(FileExists(completePath).isFile)
2237       {
2238          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal);
2239          if(codeEditor && line)
2240          {
2241             EditBox editBox = codeEditor.editBox;
2242             editBox.GoToLineNum(line - 1);
2243             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2244          }
2245       }
2246    }
2247
2248    void OnRedraw(Surface surface)
2249    {
2250       Bitmap bitmap = back.bitmap;
2251       if(bitmap)
2252          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2253    }
2254
2255    void SheetSelected(SheetType sheetSelected)
2256    {
2257       if(activeChild == sheet)
2258       {
2259          if(sheetSelected == methods)
2260          {
2261             viewPropertiesItem.accelerator = f4;
2262             viewPropertiesItem.parent = viewMenu;
2263             viewMethodsItem.parent = null;
2264          }
2265          else
2266          {
2267             viewMethodsItem.accelerator = f4;
2268             viewMethodsItem.parent = viewMenu;
2269             viewPropertiesItem.parent = null;
2270          }
2271       }
2272       else
2273       {
2274          viewMethodsItem.parent = viewMenu;
2275          viewPropertiesItem.parent = viewMenu;
2276          if(sheetSelected == methods)
2277          {
2278             viewMethodsItem.accelerator = f4;
2279             viewPropertiesItem.accelerator = 0;
2280          }
2281          else
2282          {
2283             viewMethodsItem.accelerator = 0;
2284             viewPropertiesItem.accelerator = f4;
2285          }
2286       }
2287    }
2288
2289    void OnActivateClient(Window client, Window previous)
2290    {
2291       //if(!client || client != previous)
2292       {
2293          Class dataType;
2294          if(!client || client != previous)
2295          {
2296             if(previous)
2297                dataType = previous._class;
2298             if(previous && !strcmp(dataType.name, "CodeEditor"))
2299             {
2300                ((CodeEditor)previous).UpdateFormCode();
2301             }
2302             else if(previous && !strcmp(dataType.name, "Designer"))
2303             {
2304                ((Designer)previous).codeEditor.UpdateFormCode();
2305             }
2306          }
2307
2308          if(client)
2309             dataType = client._class;
2310          if(client && !strcmp(dataType.name, "CodeEditor"))
2311          {
2312             CodeEditor codeEditor = (CodeEditor)client;
2313             SetPrivateModule(codeEditor.privateModule);
2314             SetCurrentContext(codeEditor.globalContext);
2315             SetTopContext(codeEditor.globalContext);
2316             SetGlobalContext(codeEditor.globalContext);
2317             
2318             SetDefines(&codeEditor.defines);
2319             SetImports(&codeEditor.imports);
2320
2321             SetActiveDesigner(codeEditor.designer);
2322             
2323             sheet.codeEditor = codeEditor;
2324             toolBox.codeEditor = codeEditor;
2325
2326             viewDesignerItem.parent = viewMenu;
2327             if(activeChild != codeEditor)
2328             {
2329                viewCodeItem.parent = viewMenu;
2330                viewDesignerItem.accelerator = 0;
2331                viewCodeItem.accelerator = f8;
2332             }
2333             else
2334             {
2335                viewCodeItem.parent = null;
2336                viewDesignerItem.accelerator = f8;
2337             }
2338          }
2339          else if(client && !strcmp(dataType.name, "Designer"))
2340          {
2341             CodeEditor codeEditor = ((Designer)client).codeEditor;
2342             if(codeEditor)
2343             {
2344                SetPrivateModule(codeEditor.privateModule);
2345                SetCurrentContext(codeEditor.globalContext);
2346                SetTopContext(codeEditor.globalContext);
2347                SetGlobalContext(codeEditor.globalContext);
2348                SetDefines(&codeEditor.defines);
2349                SetImports(&codeEditor.imports);
2350             }
2351             else
2352             {
2353                SetPrivateModule(null);
2354                SetCurrentContext(null);
2355                SetTopContext(null);
2356                SetGlobalContext(null);
2357                SetDefines(null);
2358                SetImports(null);
2359             }
2360
2361             SetActiveDesigner((Designer)client);
2362
2363             sheet.codeEditor = codeEditor;
2364             toolBox.codeEditor = codeEditor;
2365
2366             viewCodeItem.parent = viewMenu;
2367             if(activeChild != client)
2368             {
2369                viewDesignerItem.parent = viewMenu;
2370                viewDesignerItem.accelerator = f8;
2371                viewCodeItem.accelerator = 0;
2372             }
2373             else
2374             {
2375                viewDesignerItem.parent = null;
2376                viewCodeItem.accelerator = f8;
2377             }
2378          }
2379          else
2380          {
2381             if(sheet)
2382                sheet.codeEditor = null;
2383             toolBox.codeEditor = null;
2384             SetActiveDesigner(null);
2385
2386             viewDesignerItem.parent = null;
2387             viewCodeItem.parent = null;
2388          }
2389          if(sheet)
2390             SheetSelected(sheet.sheetSelected);
2391       }
2392
2393       projectCompileItem = null;
2394
2395       if(statusBar)
2396       {
2397          statusBar.Clear();
2398          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2399          {
2400             CodeEditor codeEditor = (CodeEditor)client;
2401             EditBox editBox = codeEditor.editBox;
2402
2403             statusBar.AddField(pos);
2404
2405             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2406             statusBar.AddField(caps);
2407
2408             ovr = { width = 30, text = $"OVR", color = editBox.overwrite ? black : Color { 128, 128, 128 } };
2409             statusBar.AddField(ovr);
2410
2411             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2412             statusBar.AddField(num);
2413
2414             //statusBar.text = "Ready";
2415
2416             if(projectView && projectView.project)
2417             {
2418                ProjectNode node = projectView.GetNodeFromWindow(client, null);
2419                if(node)
2420                {
2421                   char name[1024];
2422                   sprintf(name, $"Compile %s", node.name);
2423                   projectCompileItem = 
2424                   {
2425                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2426
2427                      bool NotifySelect(MenuItem selection, Modifiers mods)
2428                      {
2429                         if(projectView)
2430                         {
2431                            bool result = false;
2432                            ProjectNode node = null;
2433                            for(p : ide.workspace.projects)
2434                            {
2435                               node = projectView.GetNodeFromWindow(activeClient, p);
2436                               if(node && projectView.Compile(node))
2437                               {
2438                                  result = true;
2439                                  break;
2440                               }
2441                            }
2442                            if(!result && node)
2443                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2444                         }
2445                         return true;
2446                      }
2447                   };
2448                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2449                }
2450             }
2451          }
2452          else
2453          {
2454             caps = ovr = num = null;
2455          }
2456       }
2457    }
2458
2459    bool OnClose(bool parentClosing)
2460    {
2461       //return !projectView.buildInProgress;
2462       if(projectView && projectView.buildInProgress)
2463          return false;
2464       if(DontTerminateDebugSession($"Close IDE"))
2465          return false;
2466       if(findInFilesDialog)
2467          findInFilesDialog.SearchStop();
2468       if(workspace)
2469       {
2470          workspace.timer.Stop();
2471          workspace.Save();
2472       }
2473       ideMainFrame.Destroy(0);
2474       return true;
2475    }
2476
2477    bool OnPostCreate()
2478    {
2479       int c;
2480       for(c = 1; c<app.argc; c++)
2481       {
2482          char fullPath[MAX_LOCATION];
2483          char parentPath[MAX_LOCATION];
2484          char ext[MAX_EXTENSION];
2485          bool isProject;
2486          FileAttribs dirAttribs;
2487          GetWorkingDir(fullPath, MAX_LOCATION);
2488          PathCat(fullPath, app.argv[c]);
2489          StripLastDirectory(fullPath, parentPath);
2490          GetExtension(app.argv[c], ext);
2491          isProject = !strcmpi(ext, "epj");
2492
2493          if(isProject && c > 1) continue;
2494
2495          // Create directory for projects (only)
2496          if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
2497          {
2498             if(isProject && !FileExists(fullPath))
2499             {
2500                // The NewProject will handle directory creation
2501                /*if(!dirAttribs.isDirectory)
2502                {
2503                   MakeDir(parentPath);
2504                   dirAttribs = FileExists(parentPath);
2505                }
2506                if(dirAttribs.isDirectory)*/
2507                {
2508                   char name[MAX_LOCATION];
2509                   NewProjectDialog newProjectDialog;
2510
2511                   if(projectView)
2512                   {
2513                      projectView.visible = false;
2514                      if(!projectView.Destroy(0))
2515                         return true;
2516                   }
2517
2518                   newProjectDialog = { master = this };
2519
2520                   strcpy(name, app.argv[c]);
2521                   StripExtension(name);
2522                   GetLastDirectory(name, name);
2523                   newProjectDialog.projectName.contents = name;
2524                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
2525                   newProjectDialog.locationEditBox.path = parentPath;
2526                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
2527
2528                   newProjectDialog.Modal();
2529                   if(projectView)
2530                   {
2531                      ideSettings.AddRecentProject(projectView.fileName);
2532                      ide.UpdateRecentMenus();
2533                      settingsContainer.Save();
2534                   }
2535                }
2536                // Open only one project
2537                break;
2538             }
2539             else
2540                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2541          }
2542       }
2543       return true;
2544    }
2545
2546    void OnDestroy()
2547    {
2548       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2549       if(projectView)
2550       {
2551          projectView.visible = false;
2552          projectView.Destroy(0);
2553          projectView = null;
2554       }
2555 #ifdef GDB_DEBUG_GUI
2556       gdbDialog.Destroy(0);
2557       delete gdbDialog;
2558 #endif
2559    }
2560
2561    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
2562    {
2563       int c, len, count;
2564       char * newList;
2565       char * oldPaths[128];
2566       String oldList = new char[maxPathLen];
2567       Array<String> newExePaths { };
2568       //Map<String, bool> exePathExists { };
2569       bool found = false;
2570 #if defined(__unix__) || defined(__APPLE__)
2571       Array<String> newLibPaths { };
2572       Map<String, bool> libPathExists { };
2573 #endif
2574
2575       if(projectsDirs)
2576       {
2577          for(prj : workspace.projects)
2578          {
2579             DirExpression targetDirExp;
2580
2581             // SKIP FIRST PROJECT...
2582             if(prj == workspace.projects.firstIterator.data) continue;
2583
2584             // NOTE: Right now the additional project config dir will be
2585             //       obtained when the debugger is started, so toggling it
2586             //       while building will change which library gets used.
2587             //       To go with the initial state, e.g. when F5 was pressed,
2588             //       we nould need to keep a list of all project's active
2589             //       config upon startup.
2590             targetDirExp = prj.GetTargetDir(compiler, prj.config);
2591
2592             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2593                cfg = prj.config;
2594             else
2595             {
2596                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2597                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2598                      break;
2599                if(!cfg)
2600                {
2601                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2602                      if(cfg.targetType == sharedLibrary && cfg.debug)
2603                         break;
2604                }
2605             }*/
2606             if(targetDirExp.dir)
2607             {
2608                char buffer[MAX_LOCATION];
2609 #if defined(__WIN32__)
2610                Array<String> paths = newExePaths;
2611 #else
2612                Array<String> paths = newLibPaths;
2613 #endif
2614                GetSystemPathBuffer(buffer, prj.topNode.path);
2615                PathCat(buffer, targetDirExp.dir);
2616                for(p : paths)
2617                {
2618                   if(!fstrcmp(p, buffer))
2619                   {
2620                      found = true;
2621                      break;
2622                   }
2623                }
2624                if(!found)
2625                   paths.Add(CopyString(buffer));
2626             }
2627             delete targetDirExp;
2628          }
2629       }
2630
2631       for(item : compiler.executableDirs)
2632       {
2633          found = false;
2634          for(p : newExePaths)
2635          {
2636             if(!fstrcmp(p, item))
2637             {
2638                found = true;
2639                break;
2640             }
2641          }
2642          if(!found)
2643             newExePaths.Add(CopySystemPath(item));
2644       }
2645
2646       GetEnvironment("PATH", oldList, maxPathLen);
2647 /*#ifdef _DEBUG
2648       printf("Old value of PATH: %s\n", oldList);
2649 #endif*/
2650       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2651       for(c = 0; c < count; c++)
2652       {
2653          found = false;
2654          for(p : newExePaths)
2655          {
2656             if(!fstrcmp(p, oldPaths[c]))
2657             {
2658                found = true;
2659                break;
2660             }
2661          }
2662          if(!found)
2663             newExePaths.Add(CopySystemPath(oldPaths[c]));
2664       }
2665
2666       len = 0;
2667       for(path : newExePaths)
2668          len += strlen(path) + 1;
2669       newList = new char[len + 1];
2670       newList[0] = '\0';
2671       for(path : newExePaths)
2672       {
2673          strcat(newList, path);
2674          strcat(newList, pathListSep);
2675       }
2676       newList[len - 1] = '\0';
2677       SetEnvironment("PATH", newList);
2678 /*#ifdef _DEBUG
2679       printf("New value of PATH: %s\n", newList);
2680 #endif*/
2681       delete newList;
2682
2683       newExePaths.Free();
2684       delete newExePaths;
2685
2686 #if defined(__unix__) || defined(__APPLE__)
2687
2688       for(item : compiler.libraryDirs)
2689       {
2690          if(!libPathExists[item])  // fstrcmp should be used
2691          {
2692             newLibPaths.Add(item);
2693             libPathExists[item] = true;
2694          }
2695       }
2696
2697 #if defined(__APPLE__)
2698       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2699 #else
2700       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2701 #endif
2702 /*#ifdef _DEBUG
2703       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
2704 #endif*/
2705       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2706       for(c = 0; c < count; c++)
2707       {
2708          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2709          {
2710             newLibPaths.Add(oldPaths[c]);
2711             libPathExists[oldPaths[c]] = true;
2712          }
2713       }
2714
2715       len = 0;
2716       for(path : newLibPaths)
2717          len += strlen(path) + 1;
2718       newList = new char[len + 1];
2719       newList[0] = '\0';
2720       for(path : newLibPaths)
2721       {
2722          strcat(newList, path);
2723          strcat(newList, pathListSep);
2724       }
2725       newList[len - 1] = '\0';
2726 #if defined(__APPLE__)
2727       SetEnvironment("DYLD_LIBRARY_PATH", newList);
2728 #else
2729       SetEnvironment("LD_LIBRARY_PATH", newList);
2730 #endif
2731 /*#ifdef _DEBUG
2732       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
2733 #endif*/
2734       delete newList;
2735
2736       delete newLibPaths;
2737       delete libPathExists;
2738 #endif
2739
2740       if(compiler.distccEnabled && compiler.distccHosts)
2741          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
2742
2743       delete oldList;
2744    }
2745
2746    void DestroyTemporaryProjectDir()
2747    {
2748       if(tmpPrjDir && tmpPrjDir[0])
2749       {
2750          if(FileExists(tmpPrjDir).isDirectory)
2751             DestroyDir(tmpPrjDir);
2752          property::tmpPrjDir = null;
2753       }
2754    }
2755
2756    IDEWorkSpace()
2757    {
2758       // Graphics Driver Menu
2759       int c;
2760
2761       /*
2762       app.currentSkin.selectionColor = selectionColor;
2763       app.currentSkin.selectionText = selectionText;
2764       */
2765
2766 /*
2767       driverItems = new MenuItem[app.numDrivers];
2768       for(c = 0; c < app.numDrivers; c++)
2769       {
2770          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
2771          driverItems[c].id = c;
2772          driverItems[c].isRadio = true;         
2773       }
2774 */
2775       driverItems = new MenuItem[2];
2776 #if defined(__unix__)
2777          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
2778          driverItems[0].id = 0;
2779          driverItems[0].isRadio = true;         
2780 #else
2781          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
2782          driverItems[0].id = 0;
2783          driverItems[0].isRadio = true;         
2784 #endif
2785          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
2786          driverItems[1].id = 1;
2787          driverItems[1].isRadio = true;         
2788
2789 /*      skinItems = new MenuItem[app.numSkins];
2790       for(c = 0; c < app.numSkins; c++)
2791       {
2792          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
2793          skinItems[c].id = c;
2794          skinItems[c].isRadio = true;         
2795       }
2796 */
2797       ideFileDialog.master = this;
2798       ideProjectFileDialog.master = this;
2799
2800       //SetDriverAndSkin();
2801       return true;
2802    }
2803
2804    void UpdateRecentMenus()
2805    {
2806       int c;
2807       Menu fileMenu = menu.FindMenu($"File");
2808       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
2809       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
2810       char itemName[MAX_LOCATION + 4];
2811       MenuItem item;
2812
2813       recentFiles.Clear();
2814       c = 0;
2815
2816       for(recent : ideSettings.recentFiles)
2817       {
2818          sprintf(itemName, "%d %s", 1 + c, recent);
2819          MakeSystemPath(itemName);
2820          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
2821          c++;
2822       }
2823
2824       recentProjects.Clear();
2825       c = 0;
2826       for(recent : ideSettings.recentProjects)
2827       {
2828          sprintf(itemName, "%d %s", 1 + c, recent);
2829          MakeSystemPath(itemName);
2830          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
2831          c++;
2832       }
2833    }
2834
2835    ~IDEWorkSpace()
2836    {
2837       delete driverItems;
2838       delete skinItems;
2839       delete ideSettings;
2840    }
2841 }
2842
2843 void DestroyDir(char * path)
2844 {
2845    RecursiveDeleteFolderFSI fsi { };
2846    fsi.Iterate(path);
2847    delete fsi;
2848 }
2849
2850 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
2851 {
2852    bool preserveRootFolder;
2853
2854    void OutFolder(char * folderPath, bool isRoot)
2855    {
2856       if(!(preserveRootFolder && isRoot))
2857          RemoveDir(folderPath);
2858    }
2859
2860    bool OnFile(char * filePath)
2861    {
2862       DeleteFile(filePath);
2863       return true;
2864    }
2865 }
2866
2867 class IDEApp : GuiApplication
2868 {
2869    //driver = "Win32Console";
2870    // driver = "OpenGL";
2871    // skin = "Aqua";
2872    //skin = "TVision";
2873    bool Init()
2874    {
2875       SetLoggingMode(stdOut, null);
2876       //SetLoggingMode(debug, null);
2877
2878       settingsContainer.Load();
2879 #if defined(__unix__) || defined(__APPLE__)
2880       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
2881 #else
2882       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
2883 #endif
2884       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
2885
2886       SetInIDE(true);
2887
2888       desktop.text = titleECEREIDE;
2889       /*
2890       int c;
2891       for(c = 1; c<app.argc; c++)
2892       {
2893          char fullPath[MAX_LOCATION];
2894          GetWorkingDir(fullPath, MAX_LOCATION);
2895          PathCat(fullPath, app.argv[c]);
2896          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2897       }
2898       */
2899       return true;
2900    }
2901 }
2902
2903 IDEMainFrame ideMainFrame { };
2904
2905 define app = ((IDEApp)__thisModule);
2906 #ifdef _DEBUG
2907 define titleECEREIDE = $"ECERE IDE (Debug)";
2908 #else
2909 define titleECEREIDE = $"ECERE IDE";
2910 #endif