ecere/ide: Moved IDE's eString_PathInsideOf to ecere::com::IsPathInsideOf
[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                OpenFile(file, normal, true, null, no, normal);
719                break;
720             }
721             id++;
722          }
723          return true;
724       }
725
726       bool FileRecentProject(MenuItem selection, Modifiers mods)
727       {
728          int id = 0;
729          for(file : ideSettings.recentProjects)
730          {
731             if(id == selection.id)
732             {
733                OpenFile(file, normal, true, null, no, normal);
734                break;
735             }
736             id++;
737          }
738          return true;
739       }
740
741    MenuPlacement editMenu { menu, $"Edit", e };
742    
743    Menu projectMenu { menu, $"Menu"."Project", p, hasMargin = true };
744       MenuItem projectNewItem
745       {
746          projectMenu, $"New...", n, Key { n, true, true };
747          bitmap = { ":actions/projNew.png" };
748          bool NotifySelect(MenuItem selection, Modifiers mods)
749          {
750             if(!DontTerminateDebugSession($"New Project"))
751                if(MenuWindowCloseAll(null, 0))
752                {
753                   NewProjectDialog newProjectDialog;
754
755                   if(projectView)
756                   {
757                      projectView.visible = false;
758                      if(!projectView.Destroy(0))
759                         return true;
760                   }
761                   
762                   newProjectDialog = { master = this };
763                   newProjectDialog.Modal();
764                   if(projectView)
765                   {
766                      ideSettings.AddRecentProject(projectView.fileName);
767                      ide.UpdateRecentMenus();
768                      settingsContainer.Save();
769                   }
770                }
771             return true;
772          }
773       }
774       MenuItem projectOpenItem
775       {
776          projectMenu, $"Open...", o, Key { o, true, true };
777          bitmap = { ":actions/projOpen.png" };
778          bool NotifySelect(MenuItem selection, Modifiers mods)
779          {
780             if(ideSettings.ideProjectFileDialogLocation)
781                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
782
783             ideProjectFileDialog.text = openProjectFileDialogTitle;
784             if(ideProjectFileDialog.Modal() == ok)
785             {
786                OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal);
787                //ChangeProjectFileDialogDirectory(ideProjectFileDialog.currentDirectory);
788             }
789             return true;
790          }
791       }
792       MenuItem projectQuickItem
793       {
794          projectMenu, $"Quick...", q, f7, disabled = true;
795          bool NotifySelect(MenuItem selection, Modifiers mods)
796          {
797             if(!projectView)
798                QuickProjectDialog{ this }.Modal();
799             return true;
800          }
801       }
802       MenuItem projectAddItem
803       {
804          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
805          bitmap = { ":actions/projAdd.png" };
806          disabled = true;
807          bool NotifySelect(MenuItem selection, Modifiers mods)
808          {
809             if(ideSettings.ideProjectFileDialogLocation)
810                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
811
812             ideProjectFileDialog.text = addProjectFileDialogTitle;
813             for(;;)
814             {
815                if(ideProjectFileDialog.Modal() == ok)
816                {
817                   if(OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add))
818                      break;
819                   if(MessageBox { type = yesNo, master = this, text = $"Error opening project file", 
820                         contents = $"Add a different project?" }.Modal() == no)
821                   {
822                      break;
823                   }
824                }
825                else
826                   break;
827             }
828             return true;
829          }
830       }
831       MenuItem projectCloseItem
832       {
833          projectMenu, $"Close", c, disabled = true;
834          bool NotifySelect(MenuItem selection, Modifiers mods)
835          {
836             if(projectView)
837             {
838                if(!ide.DontTerminateDebugSession($"Project Close"))
839                {
840                   if(findInFilesDialog)
841                      findInFilesDialog.SearchStop();
842                   projectView.visible = false;
843                   if(projectView.Destroy(0))
844                      MenuWindowCloseAll(null, 0);
845                   {
846                      char workingDir[MAX_LOCATION];
847                      GetWorkingDir(workingDir, MAX_LOCATION);
848                      findInFilesDialog.currentDirectory = workingDir;
849                      ideMainFrame.text = titleECEREIDE;
850                   }
851                }
852             }
853             return true;
854          }
855       }
856       MenuDivider { projectMenu };
857       MenuItem activeCompilerItem
858       {
859          projectMenu, $"Active Compiler...", g, /*altF5, */disabled = true;
860          bool NotifySelect(MenuItem selection, Modifiers mods)
861          {
862             projectView.MenuCompiler(null, mods);
863             return true;
864          }
865       }
866       MenuItem projectActiveConfigItem
867       {
868          projectMenu, $"Active Configuration...", g, altF5, disabled = true;
869          bool NotifySelect(MenuItem selection, Modifiers mods)
870          {
871             projectView.MenuConfig(projectView.active ? selection : null, mods);
872             return true;
873          }
874       }
875       MenuItem projectSettingsItem
876       {
877          projectMenu, $"Settings...", s, altF7, disabled = true;
878          bool NotifySelect(MenuItem selection, Modifiers mods)
879          {
880             projectView.MenuSettings(projectView.active ? selection : null, mods);
881             return true;
882          }
883       }
884       MenuDivider { projectMenu };
885       MenuItem projectBrowseFolderItem
886       {
887          projectMenu, $"Browse Project Folder", p, disabled = true;
888          bool NotifySelect(MenuItem selection, Modifiers mods)
889          {
890             if(projectView)
891                projectView.MenuBrowseFolder(null, mods);
892             return true;
893          }
894       }
895       MenuDivider { projectMenu };
896       MenuItem projectRunItem
897       {
898          projectMenu, $"Run", r, ctrlF5, disabled = true;
899          bitmap = { ":actions/run.png" };
900          bool NotifySelect(MenuItem selection, Modifiers mods)
901          {
902             if(projectView)
903                projectView.Run(null, mods);
904             return true;
905          }
906       }
907       MenuItem projectBuildItem
908       {
909          projectMenu, $"Build", b, f7, disabled = true;
910          bitmap = { ":actions/build.png" };
911          bool NotifySelect(MenuItem selection, Modifiers mods)
912          {
913             if(projectView)
914                projectView.ProjectBuild(projectView.active ? selection : null, mods);
915             return true;
916          }
917       }
918       MenuItem projectLinkItem
919       {
920          projectMenu, $"Relink", l, disabled = true;
921          bitmap = { ":actions/relink.png" };
922          bool NotifySelect(MenuItem selection, Modifiers mods)
923          {
924             if(projectView)
925                projectView.ProjectLink(projectView.active ? selection : null, mods);
926             return true;
927          }
928       }
929       MenuItem projectRebuildItem
930       {
931          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
932          bitmap = { ":actions/rebuild.png" };
933          bool NotifySelect(MenuItem selection, Modifiers mods)
934          {
935             if(projectView)
936                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
937             return true;
938          }
939       }
940       MenuItem projectCleanItem
941       {
942          projectMenu, $"Clean", e, disabled = true;
943          bitmap = { ":actions/clean.png" };
944          bool NotifySelect(MenuItem selection, Modifiers mods)
945          {
946             if(projectView)
947             {
948                debugger.Stop();
949                projectView.ProjectClean(projectView.active ? selection : null, mods);
950             }
951             return true;
952          }
953       }
954       MenuItem projectRealCleanItem
955       {
956          projectMenu, $"Real Clean", disabled = true;
957          bitmap = { ":actions/clean.png" };
958          bool NotifySelect(MenuItem selection, Modifiers mods)
959          {
960             if(projectView)
961             {
962                debugger.Stop();
963                projectView.ProjectRealClean(projectView.active ? selection : null, mods);
964             }
965             return true;
966          }
967       }
968       MenuItem projectRegenerateItem
969       {
970          projectMenu, $"Regenerate Makefile", m, disabled = true;
971          bitmap = { ":actions/regMakefile.png" };
972          bool NotifySelect(MenuItem selection, Modifiers mods)
973          {
974             if(projectView)
975                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
976             return true;
977          }
978       }
979       MenuItem projectCompileItem;
980    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
981       MenuItem debugStartResumeItem
982       {
983          debugMenu, $"Start", s, f5, disabled = true;
984          bitmap = { ":actions/debug.png" };
985          NotifySelect = MenuDebugStart;
986       }
987       bool MenuDebugStart(MenuItem selection, Modifiers mods)
988       {
989          if(projectView)
990          {
991             debugStartResumeItem.disabled = true; // a very rare exception to calling AdjustDebugMenus
992             if(!projectView.DebugStart())
993                debugStartResumeItem.disabled = false; // same exception
994          }
995          return true;
996       }
997       bool MenuDebugResume(MenuItem selection, Modifiers mods)
998       {
999          if(projectView)
1000             projectView.DebugResume();
1001          return true;
1002       }
1003       MenuItem debugRestartItem
1004       {
1005          debugMenu, $"Restart", r, Key { f5, ctrl = true, shift = true }, disabled = true;
1006          bitmap = { ":actions/restart.png" };
1007          bool NotifySelect(MenuItem selection, Modifiers mods)
1008          {
1009             if(projectView)
1010                projectView.DebugRestart();
1011             return true;
1012          }
1013       }
1014       MenuItem debugBreakItem
1015       {
1016          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
1017          bitmap = { ":actions/pause.png" };
1018          bool NotifySelect(MenuItem selection, Modifiers mods)
1019          {
1020             if(projectView)
1021                projectView.DebugBreak();
1022             return true;
1023          }
1024       }
1025       MenuItem debugStopItem
1026       {
1027          debugMenu, $"Stop", p, shiftF5, disabled = true;
1028          bitmap = { ":actions/stopDebug.png" };
1029          bool NotifySelect(MenuItem selection, Modifiers mods)
1030          {
1031             if(projectView)
1032                projectView.DebugStop();
1033             return true;
1034          }
1035       }
1036       MenuDivider { debugMenu };
1037       MenuItem debugStepIntoItem
1038       {
1039          debugMenu, $"Step Into", i, f11, disabled = true;
1040          bitmap = { ":actions/stepInto.png" };
1041          bool NotifySelect(MenuItem selection, Modifiers mods)
1042          {
1043             if(projectView)
1044                projectView.DebugStepInto();
1045             return true;
1046          }
1047       }
1048       MenuItem debugStepOverItem
1049       {
1050          debugMenu, $"Step Over", v, f10, disabled = true;
1051          bitmap = { ":actions/stepOver.png" };
1052          bool NotifySelect(MenuItem selection, Modifiers mods)
1053          {
1054             if(projectView)
1055                projectView.DebugStepOver(false);
1056             return true;
1057          }
1058       }
1059       MenuItem debugStepOutItem
1060       {
1061          debugMenu, $"Step Out", o, shiftF11, disabled = true;
1062          bitmap = { ":actions/stepOut.png" };
1063          bool NotifySelect(MenuItem selection, Modifiers mods)
1064          {
1065             if(projectView)
1066                projectView.DebugStepOut(false);
1067             return true;
1068          }
1069       }
1070       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
1071       MenuItem debugSkipStepOverItem
1072       {
1073          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
1074          bool NotifySelect(MenuItem selection, Modifiers mods)
1075          {
1076             if(projectView)
1077                projectView.DebugStepOver(true);
1078             return true;
1079          }
1080       }
1081       MenuItem debugSkipStepOutItem
1082       {
1083          debugMenu, $"Step Out Skipping Breakpoints", t, Key { f11, ctrl = true, shift = true }, disabled = true;
1084          bitmap = { ":actions/skipBreaks.png" };
1085          bool NotifySelect(MenuItem selection, Modifiers mods)
1086          {
1087             if(projectView)
1088                projectView.DebugStepOut(true);
1089             return true;
1090          }
1091       }
1092       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
1093       //MenuDivider { debugMenu };
1094       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
1095    MenuPlacement imageMenu { menu, $"Image", i };
1096    Menu viewMenu { menu, $"View", v };
1097       MenuItem viewProjectItem
1098       {
1099          viewMenu, $"Project View", j, alt0, disabled = true;
1100          bool NotifySelect(MenuItem selection, Modifiers mods)
1101          {
1102             if(projectView)
1103             {
1104                projectView.visible = true;
1105                projectView.Activate();
1106             }
1107             return true;
1108          }
1109       }
1110       MenuPlacement { viewMenu, $"View Designer" };
1111       MenuPlacement { viewMenu, $"View Code" };
1112       MenuPlacement { viewMenu, $"View Properties" };
1113       MenuPlacement { viewMenu, $"View Methods" };
1114       MenuItem viewDesignerItem
1115       {
1116          viewMenu, $"View Designer", d, f8;
1117          bool NotifySelect(MenuItem selection, Modifiers mods)
1118          {
1119             Window client = activeClient;
1120             Class dataType = client._class;
1121             if(!strcmp(dataType.name, "Designer"))
1122             {
1123                client.visible = true;
1124                client.Activate();
1125             }
1126             else
1127                ((CodeEditor)client).ViewDesigner();
1128             return true;
1129          }
1130       }
1131       MenuItem viewCodeItem
1132       {
1133          viewMenu, $"View Code", c, f8;
1134          bool NotifySelect(MenuItem selection, Modifiers mods)
1135          {
1136             Window client = activeClient;
1137             Class dataType = client._class;
1138             if(!strcmp(dataType.name, "Designer"))
1139                client = ((Designer)client).codeEditor;
1140
1141             client.Activate();
1142             // Do this after so the caret isn't moved yet...
1143             client.visible = true;
1144             return true;
1145          }
1146       }
1147       MenuItem viewPropertiesItem
1148       {
1149          viewMenu, $"View Properties", p, f4;
1150          bool NotifySelect(MenuItem selection, Modifiers mods)
1151          {
1152             sheet.visible = true;
1153             sheet.sheetSelected = properties;
1154             sheet.Activate();
1155             return true;
1156          }
1157       }
1158       MenuItem viewMethodsItem
1159       {
1160          viewMenu, $"View Methods", m, f4;
1161          bool NotifySelect(MenuItem selection, Modifiers mods)
1162          {
1163             sheet.visible = true;
1164             sheet.sheetSelected = methods;
1165             sheet.Activate();
1166             return true;
1167          }
1168       }
1169       MenuItem viewToolBoxItem
1170       {
1171          viewMenu, $"View Toolbox", x, f12;
1172          bool NotifySelect(MenuItem selection, Modifiers mods)
1173          {
1174             toolBox.visible = true;
1175             toolBox.Activate();
1176             return true;
1177          }
1178       }
1179       MenuItem viewOutputItem
1180       {
1181          viewMenu, $"Output", o, alt2;
1182          bool NotifySelect(MenuItem selection, Modifiers mods)
1183          {
1184             outputView.Show();
1185             return true;
1186          }
1187       }
1188       MenuItem viewWatchesItem
1189       {
1190          viewMenu, $"Watches", w, alt3;
1191          bool NotifySelect(MenuItem selection, Modifiers mods)
1192          {
1193             watchesView.Show();
1194             return true;
1195          }
1196       }
1197       MenuItem viewThreadsItem
1198       {
1199          viewMenu, $"Threads", t, alt4;
1200          bool NotifySelect(MenuItem selection, Modifiers mods)
1201          {
1202             threadsView.Show();
1203             return true;
1204          }
1205       }
1206       MenuItem viewBreakpointsItem
1207       {
1208          viewMenu, $"Breakpoints", b, alt5;
1209          bool NotifySelect(MenuItem selection, Modifiers mods)
1210          {
1211             breakpointsView.Show();
1212             return true;
1213          }
1214       }
1215       MenuItem viewCallStackItem
1216       {
1217          viewMenu, $"Call Stack", s, alt7;
1218          bool NotifySelect(MenuItem selection, Modifiers mods)
1219          {
1220             callStackView.Show();
1221             return true;
1222          }
1223       }
1224       MenuItem viewAllDebugViews
1225       {
1226          viewMenu, $"All Debug Views", a, alt9;
1227          bool NotifySelect(MenuItem selection, Modifiers mods)
1228          {
1229             outputView.Show();
1230             watchesView.Show();
1231             threadsView.Show();
1232             callStackView.Show();
1233             breakpointsView.Show();
1234             return true;
1235          }
1236       }
1237 #ifdef GDB_DEBUG_GUI
1238       MenuDivider { viewMenu };
1239       MenuItem viewGDBItem
1240       {
1241          viewMenu, $"GDB Dialog", g, Key { f9, shift = true, ctrl = true };
1242          bool NotifySelect(MenuItem selection, Modifiers mods)
1243          {
1244             gdbDialog.Show();
1245             return true;
1246          }
1247       }
1248 #endif
1249       MenuDivider { viewMenu };
1250       MenuItem viewColorPicker
1251       {
1252          viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
1253          bool NotifySelect(MenuItem selection, Modifiers mods)
1254          {
1255             ColorPicker colorPicker { master = this, parent = this, stayOnTop = true };
1256             colorPicker.Create();
1257             return true;
1258          }
1259       }
1260       MenuDivider { viewMenu };
1261       /*
1262       MenuItem
1263       {
1264          viewMenu, "Full Screen", f, checkable = true;
1265
1266          bool NotifySelect(MenuItem selection, Modifiers mods)
1267          {
1268             app.fullScreen ^= true;
1269             SetDriverAndSkin();
1270             anchor = { 0, 0, 0, 0 };
1271             return true;
1272          }
1273       };
1274       */
1275       Menu driversMenu { viewMenu, $"Graphics Driver", v };
1276       //Menu skinsMenu { viewMenu, "GUI Skins", k };
1277    Menu windowMenu { menu, $"Window", w };
1278       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
1279       MenuDivider { windowMenu };
1280       MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext };
1281       MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious };
1282       MenuDivider { windowMenu };
1283       MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade };
1284       MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz };
1285       MenuItem { windowMenu, $"Tile Vertically", v, NotifySelect = MenuWindowTileVert };
1286       MenuItem { windowMenu, $"Arrange Icons", a, NotifySelect = MenuWindowArrangeIcons };
1287       MenuDivider { windowMenu };
1288       MenuItem { windowMenu, $"Windows...", w, NotifySelect = MenuWindowWindows };
1289    Menu helpMenu { menu, $"Help", h };
1290       MenuItem
1291       {
1292          helpMenu, $"API Reference", r, f1;
1293          bool NotifySelect(MenuItem selection, Modifiers mods)
1294          {
1295             Execute("documentor");
1296             return true;
1297          }
1298       }
1299       MenuDivider { helpMenu };
1300       MenuItem
1301       {
1302          helpMenu, $"About...", a;
1303          bool NotifySelect(MenuItem selection, Modifiers mods)
1304          {
1305             AboutIDE { master = this }.Modal();
1306             return true;
1307          }
1308       }
1309
1310    property ToolBox toolBox
1311    {
1312       get { return toolBox; }
1313    }
1314
1315    property Sheet sheet
1316    {
1317       get { return sheet; }
1318    }
1319
1320    property Project project
1321    {
1322       get { return projectView ? projectView.project : null; }
1323    }
1324
1325    property Workspace workspace
1326    {
1327       get { return projectView ? projectView.workspace : null; }
1328    }
1329
1330    FindInFilesDialog findInFilesDialog
1331    {
1332       master = this, parent = this;
1333       filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter);
1334       filter = 1;
1335    };
1336
1337 #ifdef GDB_DEBUG_GUI
1338    GDBDialog gdbDialog
1339    {
1340       master = this, parent = this;
1341       anchor = { left = 100, top = 100, right = 100, bottom = 100 };
1342
1343       void OnCommand(char * string)
1344       {
1345          if(ide)
1346             ide.debugger.SendGDBCommand(string);
1347       }
1348    };
1349 #endif
1350    
1351    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
1352    {
1353       //app.driver = app.drivers[selection.id];
1354 #ifdef __unix__
1355       app.driver = selection.id ? "OpenGL" : "X";
1356 #else
1357       app.driver = selection.id ? "OpenGL" : "GDI";
1358 #endif
1359       delete ideSettings.displayDriver;
1360       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
1361
1362       settingsContainer.Save();
1363       //SetDriverAndSkin();
1364       return true;
1365    }
1366
1367    bool NotifySelectDisplaySkin(MenuItem selection, Modifiers mods)
1368    {
1369       app.skin = app.skins[selection.id];
1370       SetDriverAndSkin();
1371       return true;
1372    }
1373
1374    void SetDriverAndSkin()
1375    {
1376       int c;
1377       for(c = 0; c < app.numSkins; c++)
1378          if(!strcmp(app.skins[c], app.skin))
1379          {
1380             skinItems[c].checked = true;
1381             break;
1382          }
1383       for(c = 0; c < app.numDrivers; c++)
1384          if(!strcmp(app.drivers[c], app.driver))
1385          {
1386             driverItems[c].checked = true;
1387             break;
1388          }
1389    }
1390
1391    ProjectView CreateProjectView(Workspace workspace, char * fileName)
1392    {
1393       Project project = workspace.projects.firstIterator.data;
1394       projectView = ProjectView
1395       {
1396          this;
1397          fileName = fileName;
1398          
1399          void NotifyDestroyed(Window window, DialogResult result)
1400          {
1401             projectView = null;
1402             text = titleECEREIDE;
1403             
1404             AdjustMenus();
1405          }
1406       };
1407       projectView.Create();
1408       RepositionWindows(false);
1409
1410       // Leave it after Create to avoid flicker due to seeing IDE without a project view
1411       projectView.workspace = workspace;
1412       projectView.project = project;
1413       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
1414
1415       AdjustMenus();
1416
1417       ide.breakpointsView.LoadFromWorkspace();
1418       ide.watchesView.LoadFromWorkspace();
1419
1420       findInFilesDialog.projectNodeField.userData = projectView;
1421
1422       {
1423          char fileName[MAX_LOCATION];
1424          strcpy(fileName, project.topNode.path);
1425          PathCat(fileName, project.topNode.name);
1426       }
1427       return projectView;
1428    }
1429
1430    bool GetDebugMenusDisabled()
1431    {
1432       if(projectView)
1433       {
1434          Project project = projectView.project;
1435          if(project)
1436             if(project.GetTargetType(project.config) == executable)
1437                return false;
1438            
1439       }
1440       return true;
1441    }
1442
1443    void RepositionWindows(bool expand)
1444    {
1445       if(this)
1446       {
1447          Window child;
1448          bool inDebugMode = debugger.isActive;
1449          bool callStackVisible = expand ? false : callStackView.visible;
1450          bool threadsVisible = expand ? false : threadsView.visible;
1451          bool watchesVisible = expand ? false : watchesView.visible;
1452          bool breakpointsVisible = expand ? false : breakpointsView.visible;
1453          bool toolBoxVisible = toolBox.visible;
1454          bool outputVisible = expand ? false : outputView.visible;
1455          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
1456          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
1457          
1458          for(child = firstChild; child; child = child.next)
1459          {
1460             if(child._class == class(CodeEditor) || child._class == class(Designer) || 
1461                child._class == class(Sheet) || child._class == class(ProjectView))
1462             {
1463                Anchor anchor = child.anchor;
1464                anchor.top = topDistance;
1465                anchor.bottom = bottomDistance;
1466                if(child._class == class(CodeEditor) || child._class == class(Designer))
1467                {
1468                   anchor.right = toolBoxVisible ? 150 : 0;
1469                }
1470                child.anchor = anchor;
1471             }
1472             else if(expand)
1473             {
1474                if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) || 
1475                   child._class == class(BreakpointsView))
1476                   child.visible = false;
1477             }
1478          }
1479          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
1480          Update(null);
1481       }
1482    }
1483
1484    bool ShowCodeEditor()
1485    {
1486       if(activeClient)
1487          activeClient.Activate();
1488       else if(projectView)
1489       { 
1490          projectView.visible = true;
1491          projectView.Activate();
1492       }
1493       else
1494       {
1495          sheet.visible = true;
1496          sheet.Activate();
1497       }
1498       return false;
1499    }
1500
1501    bool ShouldStopBuild()
1502    {
1503       return projectView.stopBuild;
1504    }
1505
1506    void DocumentSaved(Window document, char * fileName)
1507    {
1508       ideSettings.AddRecentFile(fileName);
1509       ide.UpdateRecentMenus();
1510       ide.AdjustFileMenus();
1511       settingsContainer.Save();
1512    }
1513
1514    bool Window::OnFileModified(FileChange fileChange, char * param)
1515    {
1516       char temp[4096];
1517       sprintf(temp, $"The document %s was modified by another application.\n"
1518             "Would you like to reload it and lose your changes?", this.fileName);
1519       if(MessageBox { type = yesNo, master = this/*.parent*/,
1520             text = $"Document has been modified", contents = temp }.Modal() == yes)
1521       {
1522          char * fileName = CopyString(this.fileName);
1523          WindowState state = this.state;
1524          Anchor anchor = this.anchor;
1525          Size size = this.size;
1526
1527          this.modifiedDocument = false;
1528          this.Destroy(0);
1529          this = ide.OpenFile(fileName, normal, true, null, no, normal);
1530          if(this)
1531          {
1532             this.anchor = anchor;
1533             this.size = size;
1534             this.SetState(state, true, 0);
1535          }
1536          delete fileName;
1537          return true;
1538       }
1539       return true;
1540    }
1541
1542    void UpdateMakefiles()
1543    {
1544       if(workspace)
1545       {
1546          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1547          for(prj : workspace.projects)
1548             projectView.ProjectUpdateMakefileForAllConfigs(prj);
1549          delete compiler;
1550       }
1551    }
1552
1553    void UpdateCompilerConfigs()
1554    {
1555       if(workspace)
1556       {
1557          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1558          projectView.ShowOutputBuildLog(true);
1559          projectView.DisplayCompiler(compiler, false);
1560          for(prj : workspace.projects)
1561             projectView.ProjectPrepareCompiler(prj, compiler);
1562          delete compiler;
1563       }
1564    }
1565
1566    void AdjustMenus()
1567    {
1568       bool unavailable = !project;
1569
1570       projectAddItem.disabled             = unavailable;
1571       toolBar.buttonAddProject.disabled   = unavailable;
1572
1573       activeCompilerItem.disabled         = unavailable;
1574       projectActiveConfigItem.disabled    = unavailable;
1575       projectSettingsItem.disabled        = unavailable;
1576
1577       projectBrowseFolderItem.disabled    = unavailable;
1578
1579       viewProjectItem.disabled            = unavailable;
1580
1581       AdjustFileMenus();
1582       AdjustBuildMenus();
1583       AdjustDebugMenus();
1584    }
1585
1586    property bool hasOpenedCodeEditors
1587    {
1588       get
1589       {
1590          Window w;
1591          for(w = firstChild; w; w = w.next)
1592             if(w._class == class(CodeEditor) &&
1593                   w.isDocument && !w.closing && w.visible && w.created &&
1594                   w.fileName && w.fileName[0])
1595                return true;
1596          return false;
1597       }
1598    }
1599
1600    void AdjustFileMenus()
1601    {
1602       bool unavailable = project != null || !hasOpenedCodeEditors; // are they supported source code (ec, c, cpp, etc) ?
1603
1604       projectQuickItem.disabled           = unavailable;
1605    }
1606
1607    void AdjustBuildMenus()
1608    {
1609       bool unavailable = project && projectView.buildInProgress;
1610
1611       projectNewItem.disabled             = unavailable;
1612       toolBar.buttonNewProject.disabled   = unavailable;
1613       projectOpenItem.disabled            = unavailable;
1614       toolBar.buttonOpenProject.disabled  = unavailable;
1615
1616       unavailable = !project || projectView.buildInProgress;
1617
1618       projectCloseItem.disabled           = unavailable;
1619       // toolBar.buttonCloseProject.disabled = unavailable;
1620
1621       projectRunItem.disabled    = unavailable || project.GetTargetType(project.config) != executable;
1622       toolBar.buttonRun.disabled = unavailable || project.GetTargetType(project.config) != executable;
1623       projectBuildItem.disabled                 = unavailable;
1624       toolBar.buttonBuild.disabled              = unavailable;
1625       projectLinkItem.disabled                  = unavailable;
1626       toolBar.buttonReLink.disabled             = unavailable;
1627       projectRebuildItem.disabled               = unavailable;
1628       toolBar.buttonRebuild.disabled            = unavailable;
1629       projectCleanItem.disabled                 = unavailable;
1630       toolBar.buttonClean.disabled              = unavailable;
1631       projectRealCleanItem.disabled             = unavailable;
1632       // toolBar.buttonRealClean.disabled          = unavailable;
1633       projectRegenerateItem.disabled            = unavailable;
1634       toolBar.buttonRegenerateMakefile.disabled = unavailable;
1635       projectCompileItem.disabled               = unavailable;
1636
1637       if(projectView && projectView.popupMenu && projectView.popupMenu.menu && projectView.popupMenu.created)
1638       {
1639          MenuItem menu;
1640          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectBuild, 0);      if(menu) menu.disabled = unavailable;
1641          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectLink, 0);       if(menu) menu.disabled = unavailable;
1642          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRebuild, 0);    if(menu) menu.disabled = unavailable;
1643          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, 0);      if(menu) menu.disabled = unavailable;
1644          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, 0);  if(menu) menu.disabled = unavailable;
1645          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, 0); if(menu) menu.disabled = unavailable;
1646          projectView.popupMenu.Update(null);
1647       }
1648    }
1649
1650    void AdjustDebugMenus()
1651    {
1652       bool unavailable = !project || project.GetTargetType(project.config) != executable ||
1653                projectView.buildInProgress == buildingMainProject;
1654       bool active = ide.debugger.isActive;
1655       bool executing = ide.debugger.state == running;
1656       //bool holding = ide.debugger.state == stopped;
1657
1658       debugStartResumeItem.disabled       = unavailable || executing;
1659       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
1660       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
1661       if(toolBar)
1662       {
1663          toolBar.buttonDebugStartResume.disabled      = unavailable || executing;
1664          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
1665       }
1666
1667       debugBreakItem.disabled             = unavailable || !executing;
1668       debugStopItem.disabled              = unavailable || !active;
1669       debugRestartItem.disabled           = unavailable || !active;
1670       if(toolBar)
1671       {
1672          toolBar.buttonDebugPause.disabled            = unavailable || !executing;
1673          toolBar.buttonDebugStop.disabled             = unavailable || !active;
1674          toolBar.buttonDebugRestart.disabled          = unavailable || !active;
1675       }
1676
1677       debugStepIntoItem.disabled          = unavailable || executing;
1678       debugStepOverItem.disabled          = unavailable || executing;
1679       debugStepOutItem.disabled           = unavailable || executing || !active;
1680       debugSkipStepOverItem.disabled      = unavailable || executing;
1681       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
1682       if(toolBar)
1683       {
1684          toolBar.buttonDebugStepInto.disabled         = unavailable || executing;
1685          toolBar.buttonDebugStepOver.disabled         = unavailable || executing;
1686          toolBar.buttonDebugStepOut.disabled          = unavailable || executing || !active;
1687          toolBar.buttonDebugSkipStepOver.disabled     = unavailable || executing;
1688          // toolBar.buttonDebugSkipStepOutItem.disabled  = unavailable || executing;
1689       }
1690       if((Designer)GetActiveDesigner())
1691       {
1692          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
1693          if(codeEditor)
1694          {
1695             codeEditor.debugRunToCursor.disabled      = unavailable || executing;
1696             codeEditor.debugSkipRunToCursor.disabled  = unavailable || executing;
1697          }
1698       }
1699    }
1700
1701    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
1702    {
1703       char tempString[MAX_LOCATION];
1704       strcpy(tempString, directory);
1705       if(saveSettings && !projectView)
1706       {
1707          ideSettings.ideFileDialogLocation = directory;
1708          settingsContainer.Save();
1709       }
1710
1711       ideFileDialog.currentDirectory = tempString;
1712       codeEditorFileDialog.currentDirectory = tempString;
1713       codeEditorFormFileDialog.currentDirectory = tempString;
1714    }
1715
1716    void ChangeProjectFileDialogDirectory(char * directory)
1717    {
1718       ideSettings.ideProjectFileDialogLocation = directory;
1719       settingsContainer.Save();
1720    }
1721
1722    Window FindWindow(char * filePath)
1723    {
1724       Window document = null;
1725
1726       // TOCHECK: Do we need to change slashes here?
1727       for(document = firstChild; document; document = document.next)
1728       {
1729          char * fileName = document.fileName;
1730          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
1731          {
1732             document.visible = true;
1733             document.Activate();
1734             return document;
1735          }
1736       }
1737       return null;
1738    }
1739
1740    bool DontTerminateDebugSession(char * title)
1741    {
1742       if(debugger.isActive)
1743       {
1744          if(MessageBox { type = yesNo, master = ide, 
1745                            contents = $"Do you want to terminate the debugging session in progress?", 
1746                            text = title }.Modal() == no)
1747             return true;
1748          /*
1749          MessageBox msg { type = yesNo, master = ide, 
1750                            contents = "Do you want to terminate the debugging session in progress?", 
1751                            text = title };
1752          if(msg.Modal() == no)
1753          {
1754             msg.Destroy(0);
1755             delete msg;
1756             return true;
1757          }
1758          msg.Destroy(0);
1759          delete msg;*/
1760       }
1761       return false;
1762    }
1763
1764    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod)
1765    {
1766       char extension[MAX_EXTENSION] = "";
1767       Window document = null;
1768       bool isProject = false;
1769       bool needFileModified = true;
1770       char winFilePath[MAX_LOCATION];
1771       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
1772
1773       if(!type)
1774       {
1775          GetExtension(filePath, extension);
1776          strlwr(extension);
1777       }
1778       else
1779          strcpy(extension, type);
1780
1781       if(strcmp(extension, ProjectExtension))
1782       {
1783          for(document = firstChild; document; document = document.next)
1784          {
1785             char * fileName = document.fileName;
1786             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
1787             {
1788                document.visible = true;
1789                if(visible)
1790                   document.Activate();
1791                return document;
1792             }
1793          }
1794       }
1795
1796       if(createIfFails == whatever)
1797          ;
1798       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
1799       {
1800          needFileModified = false;
1801          if(openMethod == normal)
1802          {
1803             if(DontTerminateDebugSession($"Open Project"))
1804                return null;
1805             isProject = true;
1806             if(MenuWindowCloseAll(null, 0))
1807             {
1808                if(projectView)
1809                {
1810                   projectView.visible = false;
1811                   projectView.Destroy(0);
1812                   // Where did this come from? projectView = null;
1813                }
1814                if(!projectView)
1815                {
1816                   for(;;)
1817                   {
1818                      Project project;
1819                      Workspace workspace = null;
1820                      
1821                      if(FileExists(filePath))
1822                      {
1823                         if(!strcmp(extension, ProjectExtension))
1824                         {
1825                            char workspaceFile[MAX_LOCATION];
1826                            strcpy(workspaceFile, filePath);
1827                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
1828                            workspace = LoadWorkspace(workspaceFile, filePath);
1829                         }
1830                         else if(!strcmp(extension, WorkspaceExtension))
1831                            workspace = LoadWorkspace(filePath, null);
1832                         else
1833                            return null;
1834                         //project = LoadProject(filePath);
1835                      }
1836                      
1837                      if(workspace)
1838                      {
1839                         char absolutePath[MAX_LOCATION];
1840                         CreateProjectView(workspace, filePath);
1841                         document = projectView;
1842
1843                         workspace.DropInvalidBreakpoints();
1844                         workspace.Save();
1845
1846                         ide.projectView.ShowOutputBuildLog(true);
1847                         {
1848                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1849                            ide.projectView.DisplayCompiler(compiler, false);
1850                            delete compiler;
1851                         }
1852                         UpdateCompilerConfigs();
1853                         UpdateMakefiles();
1854                         {
1855                            char newWorkingDir[MAX_LOCATION];
1856                            StripLastDirectory(filePath, newWorkingDir);
1857                            ChangeFileDialogsDirectory(newWorkingDir, false);
1858                         }
1859                         if(document)
1860                            document.fileName = filePath;
1861
1862                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
1863
1864                         // this crashes on starting ide with epj file, solution please?
1865                         // app.UpdateDisplay();
1866
1867                         workspace.holdTracking = true;
1868                         for(ofi : workspace.openedFiles)
1869                         {
1870                            if(ofi.state != closed)
1871                            {
1872                               Window file = OpenFile(ofi.path, normal, true, null, no, normal);
1873                               if(file)
1874                               {
1875                                  char fileName[MAX_LOCATION];
1876                                  ProjectNode node;
1877                                  GetLastDirectory(ofi.path, fileName);
1878                                  node = projectView.project.topNode.Find(fileName, true);
1879                                  if(node)
1880                                     node.EnsureVisible();
1881                               }
1882                            }
1883                         }
1884                         workspace.holdTracking = false;
1885
1886                         workspace.timer.Start();
1887
1888                         findInFilesDialog.mode = FindInFilesMode::project;
1889                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
1890                         
1891                         {
1892                            char location[MAX_LOCATION];
1893                            StripLastDirectory(ide.project.topNode.path, location);
1894                            ChangeProjectFileDialogDirectory(location);
1895                         }
1896                         
1897                         /*
1898                         if(projectView.debugger)
1899                            projectView.debugger.EvaluateWatches();
1900                         */
1901                         
1902                         break;
1903                      }
1904                      else 
1905                      {
1906                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
1907                         {
1908                            ideProjectFileDialog.text = openProjectFileDialogTitle;
1909                            if(ideProjectFileDialog.Modal() == cancel)
1910                               return null;
1911                            filePath = ideProjectFileDialog.filePath;
1912                            GetExtension(filePath, extension);
1913                         }
1914                         else
1915                            return null;
1916                      }
1917                   }
1918                }
1919             }
1920             else
1921                return null;
1922          }
1923          else if(openMethod == add)
1924          {
1925             if(workspace)
1926             {
1927                Project prj = null;
1928                char slashFilePath[MAX_LOCATION];
1929                GetSlashPathBuffer(slashFilePath, filePath);
1930                for(p : workspace.projects)
1931                {
1932                   if(!fstrcmp(p.filePath, slashFilePath))
1933                   {
1934                      prj = p;
1935                      break;
1936                   }
1937                }
1938                if(prj)
1939                {
1940                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
1941                         contents = $"This project is already present in workspace." }.Modal();
1942                }
1943                else
1944                {
1945                   prj = LoadProject(filePath);
1946                   if(prj)
1947                   {
1948                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1949                      prj.StartMonitoring();
1950                      workspace.projects.Add(prj);
1951                      if(projectView)
1952                         projectView.AddNode(prj.topNode, null);
1953                      workspace.modified = true;
1954                      workspace.Save();
1955                      findInFilesDialog.AddProjectItem(prj);
1956                      projectView.ShowOutputBuildLog(true);
1957                      projectView.DisplayCompiler(compiler, false);
1958                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
1959                      delete compiler;
1960
1961                      {
1962                         char location[MAX_LOCATION];
1963                         StripLastDirectory(prj.topNode.path, location);
1964                         ChangeProjectFileDialogDirectory(location);
1965                      }
1966
1967                      // projectView is associated with the main project and not with the one just added but
1968                      return projectView; // just to let the caller know something was opened
1969                   }
1970                }
1971             }
1972             else
1973                return null;
1974          }
1975       }
1976       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
1977             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
1978             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
1979       {
1980          if(FileExists(filePath))
1981             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1982                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1983                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
1984                                     };
1985          if(!document)
1986             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1987       }
1988 #ifndef NO3D
1989       else if(!strcmp(extension, "3ds"))
1990       {
1991          if(FileExists(filePath))
1992             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1993                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1994                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
1995                                     };
1996
1997          if(!document)
1998             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1999       }
2000 #endif
2001       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2002             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2003             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2004             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2005             !strcmp(extension, "js"))
2006       {
2007          CodeEditor editor { parent = this, state = state, visible = false };
2008          editor.updatingCode = true;
2009          if(editor.LoadFile(filePath))
2010          {
2011             document = editor;
2012             editor.visible = true;
2013          }
2014          else
2015             delete editor;
2016          needFileModified = false;
2017       }
2018       else
2019       {
2020          CodeEditor editor { parent = this, state = state, visible = false };
2021          if(editor.LoadFile(filePath))
2022          {
2023             document = editor;
2024             editor.visible = true;
2025          }
2026          else
2027             delete editor;
2028          needFileModified = false;
2029       }
2030
2031       if(document && (document._class == class(PictureEdit) ||
2032             document._class == class(ModelView)))
2033       {
2034          document.Create();
2035          if(document)
2036          {
2037             document.fileName = filePath;
2038             if(workspace && !workspace.holdTracking)
2039                workspace.UpdateOpenedFileInfo(filePath, opened);
2040          }
2041       }
2042       
2043       if(!document && createIfFails != no)
2044       {
2045          if(createIfFails != yes && !needFileModified && 
2046                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2047             createIfFails = yes;
2048          if(createIfFails == yes || createIfFails == whatever)
2049          {
2050             document = (Window)NewCodeEditor(this, state, true);
2051             if(document)
2052                document.fileName = filePath;
2053          }
2054       }
2055
2056       if(document)
2057       {
2058          if(projectView && document._class == class(CodeEditor) && workspace)
2059          {
2060             int lineNumber, position;
2061             Point scroll;
2062             CodeEditor editor = (CodeEditor)document;
2063             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2064             editor.openedFileInfo.holdTracking = true;
2065             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2066             position = Max(editor.openedFileInfo.position - 1, 0);
2067             editor.editBox.GoToLineNum(lineNumber);
2068             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2069             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2070             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2071             editor.editBox.scroll = scroll;
2072             editor.openedFileInfo.holdTracking = false;
2073          }
2074          
2075          if(needFileModified)
2076             document.OnFileModified = OnFileModified;
2077          document.NotifySaved = DocumentSaved;
2078          
2079          if(isProject)
2080             ideSettings.AddRecentProject(document.fileName);
2081          else
2082             ideSettings.AddRecentFile(document.fileName);
2083          ide.UpdateRecentMenus();
2084          ide.AdjustFileMenus();
2085          settingsContainer.Save();
2086          
2087          return document;
2088       }
2089       else
2090          return null;
2091    }
2092
2093    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2094    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2095    {
2096       if(!parentClosing && ide.workspace)
2097          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2098       return true;
2099    }*/
2100    bool ModelView::ModelViewOnClose(bool parentClosing)
2101    {
2102       if(!parentClosing && ide.workspace)
2103          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2104       return true;
2105    }
2106    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2107    {
2108       if(!parentClosing && ide.workspace)
2109          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2110       return true;
2111    }
2112
2113    /*
2114    void OnUnloadGraphics(Window window)
2115    {
2116       display.ClearMaterials();
2117       display.ClearTextures();
2118       display.ClearMeshes();
2119    }
2120    */
2121
2122    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2123    {
2124       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2125       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2126       return true;
2127    }
2128
2129    bool OnKeyDown(Key key, unichar ch)
2130    {
2131       switch(key)
2132       {
2133          case b:
2134             projectView.Update(null);
2135             break;
2136          case capsLock:
2137             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2138             break;
2139          case numLock:
2140             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2141             break;
2142       }
2143       return true;
2144    }
2145
2146    void GoToError(const char * line)
2147    {
2148       if(projectView)
2149          projectView.GoToError(line);
2150    }
2151
2152    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2153    {
2154       char *path = text;
2155       char *colon = strchr(text, ':');
2156       char filePath[MAX_LOCATION];
2157       char completePath[MAX_LOCATION];
2158       int line = 0, col = 0;
2159       Project prj = null;
2160
2161       if(text[3] == '(')
2162       {
2163          char * close = strchr(text, ')');
2164          if(close)
2165          {
2166             char name[256];
2167             strncpy(name, &text[4], close - text - 4);
2168             name[close - text - 4] = '\0';
2169             for(p : ide.workspace.projects)
2170             {
2171                if(!strcmp(p.name, name))
2172                {
2173                   path = close + 1;
2174                   prj = p;
2175                   break;
2176                }
2177             }
2178          }
2179       }
2180       if(!prj)
2181          prj = project ? project : (dir ? null : ide.project);
2182       if(colon && (colon[1] == '/' || colon[1] == '\\'))
2183       {
2184          path = (colon - 1 > path) ? colon - 1 : path;
2185          colon = strstr(colon + 1, ":");
2186       }
2187       while(isspace(*path)) path++;
2188       if(colon)
2189       {
2190          strncpy(filePath, path, colon - path);
2191          filePath[colon - path] = '\0';
2192          line = atoi(colon + 1);
2193          colon = strstr(colon + 1, ":");
2194          if(colon)
2195             col = atoi(colon + 1);
2196       }
2197       else if(path - 1 >= path && *(path - 1) == '\"')
2198       {
2199          colon = strchr(path, '\"');
2200          if(colon)
2201          {
2202             strncpy(filePath, path, colon - path);
2203             filePath[colon - path] = '\0';
2204          }
2205       }
2206
2207       if(prj)
2208          strcpy(completePath, prj.topNode.path);
2209       else if(dir && dir[0])
2210          strcpy(completePath, dir);
2211       else
2212          completePath[0] = '\0';
2213       PathCat(completePath, filePath);
2214
2215       if(FileExists(completePath).isFile)
2216       {
2217          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal);
2218          if(codeEditor && line)
2219          {
2220             EditBox editBox = codeEditor.editBox;
2221             editBox.GoToLineNum(line - 1);
2222             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2223          }
2224       }
2225    }
2226
2227    void OnRedraw(Surface surface)
2228    {
2229       Bitmap bitmap = back.bitmap;
2230       if(bitmap)
2231          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2232    }
2233
2234    void SheetSelected(SheetType sheetSelected)
2235    {
2236       if(activeChild == sheet)
2237       {
2238          if(sheetSelected == methods)
2239          {
2240             viewPropertiesItem.accelerator = f4;
2241             viewPropertiesItem.parent = viewMenu;
2242             viewMethodsItem.parent = null;
2243          }
2244          else
2245          {
2246             viewMethodsItem.accelerator = f4;
2247             viewMethodsItem.parent = viewMenu;
2248             viewPropertiesItem.parent = null;
2249          }
2250       }
2251       else
2252       {
2253          viewMethodsItem.parent = viewMenu;
2254          viewPropertiesItem.parent = viewMenu;
2255          if(sheetSelected == methods)
2256          {
2257             viewMethodsItem.accelerator = f4;
2258             viewPropertiesItem.accelerator = 0;
2259          }
2260          else
2261          {
2262             viewMethodsItem.accelerator = 0;
2263             viewPropertiesItem.accelerator = f4;
2264          }
2265       }
2266    }
2267
2268    void OnActivateClient(Window client, Window previous)
2269    {
2270       //if(!client || client != previous)
2271       {
2272          Class dataType;
2273          if(!client || client != previous)
2274          {
2275             if(previous)
2276                dataType = previous._class;
2277             if(previous && !strcmp(dataType.name, "CodeEditor"))
2278             {
2279                ((CodeEditor)previous).UpdateFormCode();
2280             }
2281             else if(previous && !strcmp(dataType.name, "Designer"))
2282             {
2283                ((Designer)previous).codeEditor.UpdateFormCode();
2284             }
2285          }
2286
2287          if(client)
2288             dataType = client._class;
2289          if(client && !strcmp(dataType.name, "CodeEditor"))
2290          {
2291             CodeEditor codeEditor = (CodeEditor)client;
2292             SetPrivateModule(codeEditor.privateModule);
2293             SetCurrentContext(codeEditor.globalContext);
2294             SetTopContext(codeEditor.globalContext);
2295             SetGlobalContext(codeEditor.globalContext);
2296             
2297             SetDefines(&codeEditor.defines);
2298             SetImports(&codeEditor.imports);
2299
2300             SetActiveDesigner(codeEditor.designer);
2301             
2302             sheet.codeEditor = codeEditor;
2303             toolBox.codeEditor = codeEditor;
2304
2305             viewDesignerItem.parent = viewMenu;
2306             if(activeChild != codeEditor)
2307             {
2308                viewCodeItem.parent = viewMenu;
2309                viewDesignerItem.accelerator = 0;
2310                viewCodeItem.accelerator = f8;
2311             }
2312             else
2313             {
2314                viewCodeItem.parent = null;
2315                viewDesignerItem.accelerator = f8;
2316             }
2317          }
2318          else if(client && !strcmp(dataType.name, "Designer"))
2319          {
2320             CodeEditor codeEditor = ((Designer)client).codeEditor;
2321             if(codeEditor)
2322             {
2323                SetPrivateModule(codeEditor.privateModule);
2324                SetCurrentContext(codeEditor.globalContext);
2325                SetTopContext(codeEditor.globalContext);
2326                SetGlobalContext(codeEditor.globalContext);
2327                SetDefines(&codeEditor.defines);
2328                SetImports(&codeEditor.imports);
2329             }
2330             else
2331             {
2332                SetPrivateModule(null);
2333                SetCurrentContext(null);
2334                SetTopContext(null);
2335                SetGlobalContext(null);
2336                SetDefines(null);
2337                SetImports(null);
2338             }
2339
2340             SetActiveDesigner((Designer)client);
2341
2342             sheet.codeEditor = codeEditor;
2343             toolBox.codeEditor = codeEditor;
2344
2345             viewCodeItem.parent = viewMenu;
2346             if(activeChild != client)
2347             {
2348                viewDesignerItem.parent = viewMenu;
2349                viewDesignerItem.accelerator = f8;
2350                viewCodeItem.accelerator = 0;
2351             }
2352             else
2353             {
2354                viewDesignerItem.parent = null;
2355                viewCodeItem.accelerator = f8;
2356             }
2357          }
2358          else
2359          {
2360             if(sheet)
2361                sheet.codeEditor = null;
2362             toolBox.codeEditor = null;
2363             SetActiveDesigner(null);
2364
2365             viewDesignerItem.parent = null;
2366             viewCodeItem.parent = null;
2367          }
2368          if(sheet)
2369             SheetSelected(sheet.sheetSelected);
2370       }
2371
2372       projectCompileItem = null;
2373
2374       if(statusBar)
2375       {
2376          statusBar.Clear();
2377          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2378          {
2379             CodeEditor codeEditor = (CodeEditor)client;
2380             EditBox editBox = codeEditor.editBox;
2381
2382             statusBar.AddField(pos);
2383
2384             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2385             statusBar.AddField(caps);
2386
2387             ovr = { width = 30, text = $"OVR", color = editBox.overwrite ? black : Color { 128, 128, 128 } };
2388             statusBar.AddField(ovr);
2389
2390             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2391             statusBar.AddField(num);
2392
2393             //statusBar.text = "Ready";
2394
2395             if(projectView && projectView.project)
2396             {
2397                ProjectNode node = projectView.GetNodeFromWindow(client, null);
2398                if(node)
2399                {
2400                   char name[1024];
2401                   sprintf(name, $"Compile %s", node.name);
2402                   projectCompileItem = 
2403                   {
2404                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2405
2406                      bool NotifySelect(MenuItem selection, Modifiers mods)
2407                      {
2408                         if(projectView)
2409                         {
2410                            bool result = false;
2411                            ProjectNode node = null;
2412                            for(p : ide.workspace.projects)
2413                            {
2414                               node = projectView.GetNodeFromWindow(activeClient, p);
2415                               if(node && projectView.Compile(node))
2416                               {
2417                                  result = true;
2418                                  break;
2419                               }
2420                            }
2421                            if(!result && node)
2422                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2423                         }
2424                         return true;
2425                      }
2426                   };
2427                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2428                }
2429             }
2430          }
2431          else
2432          {
2433             caps = ovr = num = null;
2434          }
2435       }
2436    }
2437
2438    bool OnClose(bool parentClosing)
2439    {
2440       //return !projectView.buildInProgress;
2441       if(projectView && projectView.buildInProgress)
2442          return false;
2443       if(DontTerminateDebugSession($"Close IDE"))
2444          return false;
2445       if(findInFilesDialog)
2446          findInFilesDialog.SearchStop();
2447       if(workspace)
2448       {
2449          workspace.timer.Stop();
2450          workspace.Save();
2451       }
2452       ideMainFrame.Destroy(0);
2453       return true;
2454    }
2455
2456    bool OnPostCreate()
2457    {
2458       int c;
2459       for(c = 1; c<app.argc; c++)
2460       {
2461          char fullPath[MAX_LOCATION];
2462          char parentPath[MAX_LOCATION];
2463          char ext[MAX_EXTENSION];
2464          bool isProject;
2465          FileAttribs dirAttribs;
2466          GetWorkingDir(fullPath, MAX_LOCATION);
2467          PathCat(fullPath, app.argv[c]);
2468          StripLastDirectory(fullPath, parentPath);
2469          GetExtension(app.argv[c], ext);
2470          isProject = !strcmpi(ext, "epj");
2471
2472          if(isProject && c > 1) continue;
2473
2474          // Create directory for projects (only)
2475          if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
2476          {
2477             if(isProject && !FileExists(fullPath))
2478             {
2479                // The NewProject will handle directory creation
2480                /*if(!dirAttribs.isDirectory)
2481                {
2482                   MakeDir(parentPath);
2483                   dirAttribs = FileExists(parentPath);
2484                }
2485                if(dirAttribs.isDirectory)*/
2486                {
2487                   char name[MAX_LOCATION];
2488                   NewProjectDialog newProjectDialog;
2489
2490                   if(projectView)
2491                   {
2492                      projectView.visible = false;
2493                      if(!projectView.Destroy(0))
2494                         return true;
2495                   }
2496
2497                   newProjectDialog = { master = this };
2498
2499                   strcpy(name, app.argv[c]);
2500                   StripExtension(name);
2501                   GetLastDirectory(name, name);
2502                   newProjectDialog.projectName.contents = name;
2503                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
2504                   newProjectDialog.locationEditBox.path = parentPath;
2505                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
2506
2507                   newProjectDialog.Modal();
2508                   if(projectView)
2509                   {
2510                      ideSettings.AddRecentProject(projectView.fileName);
2511                      ide.UpdateRecentMenus();
2512                      settingsContainer.Save();
2513                   }
2514                }
2515                // Open only one project
2516                break;
2517             }
2518             else
2519                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2520          }
2521       }
2522       return true;
2523    }
2524
2525    void OnDestroy()
2526    {
2527       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2528       if(projectView)
2529       {
2530          projectView.visible = false;
2531          projectView.Destroy(0);
2532          projectView = null;
2533       }
2534 #ifdef GDB_DEBUG_GUI
2535       gdbDialog.Destroy(0);
2536       delete gdbDialog;
2537 #endif
2538    }
2539
2540    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
2541    {
2542       int c, len, count;
2543       char * newList;
2544       char * oldPaths[128];
2545       String oldList = new char[maxPathLen];
2546       Array<String> newExePaths { };
2547       //Map<String, bool> exePathExists { };
2548       bool found = false;
2549 #if defined(__unix__) || defined(__APPLE__)
2550       Array<String> newLibPaths { };
2551       Map<String, bool> libPathExists { };
2552 #endif
2553
2554       if(projectsDirs)
2555       {
2556          for(prj : workspace.projects)
2557          {
2558             DirExpression targetDirExp;
2559
2560             // SKIP FIRST PROJECT...
2561             if(prj == workspace.projects.firstIterator.data) continue;
2562
2563             // NOTE: Right now the additional project config dir will be
2564             //       obtained when the debugger is started, so toggling it
2565             //       while building will change which library gets used.
2566             //       To go with the initial state, e.g. when F5 was pressed,
2567             //       we nould need to keep a list of all project's active
2568             //       config upon startup.
2569             targetDirExp = prj.GetTargetDir(compiler, prj.config);
2570
2571             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2572                cfg = prj.config;
2573             else
2574             {
2575                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2576                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2577                      break;
2578                if(!cfg)
2579                {
2580                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2581                      if(cfg.targetType == sharedLibrary && cfg.debug)
2582                         break;
2583                }
2584             }*/
2585             if(targetDirExp.dir)
2586             {
2587                char buffer[MAX_LOCATION];
2588 #if defined(__WIN32__)
2589                Array<String> paths = newExePaths;
2590 #else
2591                Array<String> paths = newLibPaths;
2592 #endif
2593                GetSystemPathBuffer(buffer, prj.topNode.path);
2594                PathCat(buffer, targetDirExp.dir);
2595                for(p : paths)
2596                {
2597                   if(!fstrcmp(p, buffer))
2598                   {
2599                      found = true;
2600                      break;
2601                   }
2602                }
2603                if(!found)
2604                   paths.Add(CopyString(buffer));
2605             }
2606             delete targetDirExp;
2607          }
2608       }
2609
2610       for(item : compiler.executableDirs)
2611       {
2612          found = false;
2613          for(p : newExePaths)
2614          {
2615             if(!fstrcmp(p, item))
2616             {
2617                found = true;
2618                break;
2619             }
2620          }
2621          if(!found)
2622             newExePaths.Add(CopySystemPath(item));
2623       }
2624
2625       GetEnvironment("PATH", oldList, maxPathLen);
2626 /*#ifdef _DEBUG
2627       printf("Old value of PATH: %s\n", oldList);
2628 #endif*/
2629       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2630       for(c = 0; c < count; c++)
2631       {
2632          found = false;
2633          for(p : newExePaths)
2634          {
2635             if(!fstrcmp(p, oldPaths[c]))
2636             {
2637                found = true;
2638                break;
2639             }
2640          }
2641          if(!found)
2642             newExePaths.Add(CopySystemPath(oldPaths[c]));
2643       }
2644
2645       len = 0;
2646       for(path : newExePaths)
2647          len += strlen(path) + 1;
2648       newList = new char[len + 1];
2649       newList[0] = '\0';
2650       for(path : newExePaths)
2651       {
2652          strcat(newList, path);
2653          strcat(newList, pathListSep);
2654       }
2655       newList[len - 1] = '\0';
2656       SetEnvironment("PATH", newList);
2657 /*#ifdef _DEBUG
2658       printf("New value of PATH: %s\n", newList);
2659 #endif*/
2660       delete newList;
2661
2662       newExePaths.Free();
2663       delete newExePaths;
2664
2665 #if defined(__unix__) || defined(__APPLE__)
2666
2667       for(item : compiler.libraryDirs)
2668       {
2669          if(!libPathExists[item])  // fstrcmp should be used
2670          {
2671             newLibPaths.Add(item);
2672             libPathExists[item] = true;
2673          }
2674       }
2675
2676 #if defined(__APPLE__)
2677       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2678 #else
2679       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2680 #endif
2681 /*#ifdef _DEBUG
2682       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
2683 #endif*/
2684       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2685       for(c = 0; c < count; c++)
2686       {
2687          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2688          {
2689             newLibPaths.Add(oldPaths[c]);
2690             libPathExists[oldPaths[c]] = true;
2691          }
2692       }
2693
2694       len = 0;
2695       for(path : newLibPaths)
2696          len += strlen(path) + 1;
2697       newList = new char[len + 1];
2698       newList[0] = '\0';
2699       for(path : newLibPaths)
2700       {
2701          strcat(newList, path);
2702          strcat(newList, pathListSep);
2703       }
2704       newList[len - 1] = '\0';
2705 #if defined(__APPLE__)
2706       SetEnvironment("DYLD_LIBRARY_PATH", newList);
2707 #else
2708       SetEnvironment("LD_LIBRARY_PATH", newList);
2709 #endif
2710 /*#ifdef _DEBUG
2711       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
2712 #endif*/
2713       delete newList;
2714
2715       delete newLibPaths;
2716       delete libPathExists;
2717 #endif
2718
2719       if(compiler.distccEnabled && compiler.distccHosts)
2720          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
2721
2722       delete oldList;
2723    }
2724
2725    void DestroyTemporaryProjectDir()
2726    {
2727       if(tmpPrjDir && tmpPrjDir[0])
2728       {
2729          if(FileExists(tmpPrjDir).isDirectory)
2730             DestroyDir(tmpPrjDir);
2731          property::tmpPrjDir = null;
2732       }
2733    }
2734
2735    IDEWorkSpace()
2736    {
2737       // Graphics Driver Menu
2738       int c;
2739
2740       /*
2741       app.currentSkin.selectionColor = selectionColor;
2742       app.currentSkin.selectionText = selectionText;
2743       */
2744
2745 /*
2746       driverItems = new MenuItem[app.numDrivers];
2747       for(c = 0; c < app.numDrivers; c++)
2748       {
2749          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
2750          driverItems[c].id = c;
2751          driverItems[c].isRadio = true;         
2752       }
2753 */
2754       driverItems = new MenuItem[2];
2755 #if defined(__unix__)
2756          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
2757          driverItems[0].id = 0;
2758          driverItems[0].isRadio = true;         
2759 #else
2760          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
2761          driverItems[0].id = 0;
2762          driverItems[0].isRadio = true;         
2763 #endif
2764          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
2765          driverItems[1].id = 1;
2766          driverItems[1].isRadio = true;         
2767
2768 /*      skinItems = new MenuItem[app.numSkins];
2769       for(c = 0; c < app.numSkins; c++)
2770       {
2771          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
2772          skinItems[c].id = c;
2773          skinItems[c].isRadio = true;         
2774       }
2775 */
2776       ideFileDialog.master = this;
2777       ideProjectFileDialog.master = this;
2778
2779       //SetDriverAndSkin();
2780       return true;
2781    }
2782
2783    void UpdateRecentMenus()
2784    {
2785       int c;
2786       Menu fileMenu = menu.FindMenu($"File");
2787       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
2788       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
2789       char itemName[MAX_LOCATION + 4];
2790       MenuItem item;
2791
2792       recentFiles.Clear();
2793       c = 0;
2794
2795       for(recent : ideSettings.recentFiles)
2796       {
2797          sprintf(itemName, "%d %s", 1 + c, recent);
2798          MakeSystemPath(itemName);
2799          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
2800          c++;
2801       }
2802
2803       recentProjects.Clear();
2804       c = 0;
2805       for(recent : ideSettings.recentProjects)
2806       {
2807          sprintf(itemName, "%d %s", 1 + c, recent);
2808          MakeSystemPath(itemName);
2809          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
2810          c++;
2811       }
2812    }
2813
2814    ~IDEWorkSpace()
2815    {
2816       delete driverItems;
2817       delete skinItems;
2818       delete ideSettings;
2819    }
2820 }
2821
2822 void DestroyDir(char * path)
2823 {
2824    RecursiveDeleteFolderFSI fsi { };
2825    fsi.Iterate(path);
2826    delete fsi;
2827 }
2828
2829 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
2830 {
2831    bool preserveRootFolder;
2832
2833    void OutFolder(char * folderPath, bool isRoot)
2834    {
2835       if(!(preserveRootFolder && isRoot))
2836          RemoveDir(folderPath);
2837    }
2838
2839    bool OnFile(char * filePath)
2840    {
2841       DeleteFile(filePath);
2842       return true;
2843    }
2844 }
2845
2846 class IDEApp : GuiApplication
2847 {
2848    //driver = "Win32Console";
2849    // driver = "OpenGL";
2850    // skin = "Aqua";
2851    //skin = "TVision";
2852    bool Init()
2853    {
2854       SetLoggingMode(stdOut, null);
2855       //SetLoggingMode(debug, null);
2856
2857       settingsContainer.Load();
2858 #if defined(__unix__) || defined(__APPLE__)
2859       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
2860 #else
2861       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
2862 #endif
2863       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
2864
2865       SetInIDE(true);
2866
2867       desktop.text = titleECEREIDE;
2868       /*
2869       int c;
2870       for(c = 1; c<app.argc; c++)
2871       {
2872          char fullPath[MAX_LOCATION];
2873          GetWorkingDir(fullPath, MAX_LOCATION);
2874          PathCat(fullPath, app.argv[c]);
2875          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2876       }
2877       */
2878       return true;
2879    }
2880 }
2881
2882 IDEMainFrame ideMainFrame { };
2883
2884 define app = ((IDEApp)__thisModule);
2885 #ifdef _DEBUG
2886 define titleECEREIDE = $"ECERE IDE (Debug)";
2887 #else
2888 define titleECEREIDE = $"ECERE IDE";
2889 #endif