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