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