ide; added Debug Precompile, Debug Compile and Debug Generate Symbols support to...
[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
2187       if(text[3] == '(')
2188       {
2189          char * close = strchr(text, ')');
2190          if(close)
2191          {
2192             char name[256];
2193             strncpy(name, &text[4], close - text - 4);
2194             name[close - text - 4] = '\0';
2195             for(p : ide.workspace.projects)
2196             {
2197                if(!strcmp(p.name, name))
2198                {
2199                   path = close + 1;
2200                   prj = p;
2201                   break;
2202                }
2203             }
2204          }
2205       }
2206       if(!prj)
2207          prj = project ? project : (dir ? null : ide.project);
2208       if(colon && (colon[1] == '/' || colon[1] == '\\'))
2209       {
2210          path = (colon - 1 > path) ? colon - 1 : path;
2211          colon = strstr(colon + 1, ":");
2212       }
2213       while(isspace(*path)) path++;
2214       if(colon)
2215       {
2216          strncpy(filePath, path, colon - path);
2217          filePath[colon - path] = '\0';
2218          line = atoi(colon + 1);
2219          colon = strstr(colon + 1, ":");
2220          if(colon)
2221             col = atoi(colon + 1);
2222       }
2223       else if(path - 1 >= path && *(path - 1) == '\"')
2224       {
2225          colon = strchr(path, '\"');
2226          if(colon)
2227          {
2228             strncpy(filePath, path, colon - path);
2229             filePath[colon - path] = '\0';
2230          }
2231       }
2232
2233       if(prj)
2234          strcpy(completePath, prj.topNode.path);
2235       else if(dir && dir[0])
2236          strcpy(completePath, dir);
2237       else
2238          completePath[0] = '\0';
2239       PathCat(completePath, filePath);
2240
2241       if(FileExists(completePath).isFile)
2242       {
2243          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal);
2244          if(codeEditor && line)
2245          {
2246             EditBox editBox = codeEditor.editBox;
2247             editBox.GoToLineNum(line - 1);
2248             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2249          }
2250       }
2251    }
2252
2253    void OnRedraw(Surface surface)
2254    {
2255       Bitmap bitmap = back.bitmap;
2256       if(bitmap)
2257          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2258    }
2259
2260    void SheetSelected(SheetType sheetSelected)
2261    {
2262       if(activeChild == sheet)
2263       {
2264          if(sheetSelected == methods)
2265          {
2266             viewPropertiesItem.accelerator = f4;
2267             viewPropertiesItem.parent = viewMenu;
2268             viewMethodsItem.parent = null;
2269          }
2270          else
2271          {
2272             viewMethodsItem.accelerator = f4;
2273             viewMethodsItem.parent = viewMenu;
2274             viewPropertiesItem.parent = null;
2275          }
2276       }
2277       else
2278       {
2279          viewMethodsItem.parent = viewMenu;
2280          viewPropertiesItem.parent = viewMenu;
2281          if(sheetSelected == methods)
2282          {
2283             viewMethodsItem.accelerator = f4;
2284             viewPropertiesItem.accelerator = 0;
2285          }
2286          else
2287          {
2288             viewMethodsItem.accelerator = 0;
2289             viewPropertiesItem.accelerator = f4;
2290          }
2291       }
2292    }
2293
2294    void OnActivateClient(Window client, Window previous)
2295    {
2296       //if(!client || client != previous)
2297       {
2298          Class dataType;
2299          if(!client || client != previous)
2300          {
2301             if(previous)
2302                dataType = previous._class;
2303             if(previous && !strcmp(dataType.name, "CodeEditor"))
2304             {
2305                ((CodeEditor)previous).UpdateFormCode();
2306             }
2307             else if(previous && !strcmp(dataType.name, "Designer"))
2308             {
2309                ((Designer)previous).codeEditor.UpdateFormCode();
2310             }
2311          }
2312
2313          if(client)
2314             dataType = client._class;
2315          if(client && !strcmp(dataType.name, "CodeEditor"))
2316          {
2317             CodeEditor codeEditor = (CodeEditor)client;
2318             SetPrivateModule(codeEditor.privateModule);
2319             SetCurrentContext(codeEditor.globalContext);
2320             SetTopContext(codeEditor.globalContext);
2321             SetGlobalContext(codeEditor.globalContext);
2322             
2323             SetDefines(&codeEditor.defines);
2324             SetImports(&codeEditor.imports);
2325
2326             SetActiveDesigner(codeEditor.designer);
2327             
2328             sheet.codeEditor = codeEditor;
2329             toolBox.codeEditor = codeEditor;
2330
2331             viewDesignerItem.parent = viewMenu;
2332             if(activeChild != codeEditor)
2333             {
2334                viewCodeItem.parent = viewMenu;
2335                viewDesignerItem.accelerator = 0;
2336                viewCodeItem.accelerator = f8;
2337             }
2338             else
2339             {
2340                viewCodeItem.parent = null;
2341                viewDesignerItem.accelerator = f8;
2342             }
2343          }
2344          else if(client && !strcmp(dataType.name, "Designer"))
2345          {
2346             CodeEditor codeEditor = ((Designer)client).codeEditor;
2347             if(codeEditor)
2348             {
2349                SetPrivateModule(codeEditor.privateModule);
2350                SetCurrentContext(codeEditor.globalContext);
2351                SetTopContext(codeEditor.globalContext);
2352                SetGlobalContext(codeEditor.globalContext);
2353                SetDefines(&codeEditor.defines);
2354                SetImports(&codeEditor.imports);
2355             }
2356             else
2357             {
2358                SetPrivateModule(null);
2359                SetCurrentContext(null);
2360                SetTopContext(null);
2361                SetGlobalContext(null);
2362                SetDefines(null);
2363                SetImports(null);
2364             }
2365
2366             SetActiveDesigner((Designer)client);
2367
2368             sheet.codeEditor = codeEditor;
2369             toolBox.codeEditor = codeEditor;
2370
2371             viewCodeItem.parent = viewMenu;
2372             if(activeChild != client)
2373             {
2374                viewDesignerItem.parent = viewMenu;
2375                viewDesignerItem.accelerator = f8;
2376                viewCodeItem.accelerator = 0;
2377             }
2378             else
2379             {
2380                viewDesignerItem.parent = null;
2381                viewCodeItem.accelerator = f8;
2382             }
2383          }
2384          else
2385          {
2386             if(sheet)
2387                sheet.codeEditor = null;
2388             toolBox.codeEditor = null;
2389             SetActiveDesigner(null);
2390
2391             viewDesignerItem.parent = null;
2392             viewCodeItem.parent = null;
2393          }
2394          if(sheet)
2395             SheetSelected(sheet.sheetSelected);
2396       }
2397
2398       projectCompileItem = null;
2399
2400       if(statusBar)
2401       {
2402          statusBar.Clear();
2403          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2404          {
2405             CodeEditor codeEditor = (CodeEditor)client;
2406             EditBox editBox = codeEditor.editBox;
2407
2408             statusBar.AddField(pos);
2409
2410             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2411             statusBar.AddField(caps);
2412
2413             ovr = { width = 30, text = $"OVR", color = editBox.overwrite ? black : Color { 128, 128, 128 } };
2414             statusBar.AddField(ovr);
2415
2416             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2417             statusBar.AddField(num);
2418
2419             //statusBar.text = "Ready";
2420
2421             if(projectView && projectView.project)
2422             {
2423                ProjectNode node = projectView.GetNodeFromWindow(client, null);
2424                if(node)
2425                {
2426                   char name[1024];
2427                   sprintf(name, $"Compile %s", node.name);
2428                   projectCompileItem = 
2429                   {
2430                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2431
2432                      bool NotifySelect(MenuItem selection, Modifiers mods)
2433                      {
2434                         if(projectView)
2435                         {
2436                            bool result = false;
2437                            ProjectNode node = null;
2438                            for(p : ide.workspace.projects)
2439                            {
2440                               node = projectView.GetNodeFromWindow(activeClient, p);
2441                               //if(node && projectView.Compile(node.project, node, mods.ctrl && mods.shift))
2442                               if(node)
2443                               {
2444                                  List<ProjectNode> nodes { };
2445                                  nodes.Add(node);
2446                                  projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, normal);
2447                                  delete nodes;
2448
2449                                  result = true;
2450                                  break;
2451                               }
2452                            }
2453                            if(!result && node)
2454                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2455                         }
2456                         return true;
2457                      }
2458                   };
2459                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2460                }
2461             }
2462          }
2463          else
2464          {
2465             caps = ovr = num = null;
2466          }
2467       }
2468    }
2469
2470    bool OnClose(bool parentClosing)
2471    {
2472       //return !projectView.buildInProgress;
2473       if(projectView && projectView.buildInProgress)
2474          return false;
2475       if(DontTerminateDebugSession($"Close IDE"))
2476          return false;
2477       if(findInFilesDialog)
2478          findInFilesDialog.SearchStop();
2479       if(workspace)
2480       {
2481          workspace.timer.Stop();
2482          workspace.Save();
2483       }
2484       ideMainFrame.Destroy(0);
2485       return true;
2486    }
2487
2488    bool OnPostCreate()
2489    {
2490       int c;
2491       bool passThrough = false;
2492       bool debugStart = false;
2493       DynamicString passArgs { };
2494       for(c = 1; c<app.argc; c++)
2495       {
2496          if(!strcmp(app.argv[c], "-debug-start"))
2497             debugStart = true;
2498          else if(!passThrough && !strcmp(app.argv[c], "-@"))
2499             passThrough = true;
2500          else if(passThrough)
2501          {
2502             passArgs.concat(" ");
2503             passArgs.concat(app.argv[c]);
2504          }
2505          else
2506          {
2507             char fullPath[MAX_LOCATION];
2508             char parentPath[MAX_LOCATION];
2509             char ext[MAX_EXTENSION];
2510             bool isProject;
2511             FileAttribs dirAttribs;
2512             GetWorkingDir(fullPath, MAX_LOCATION);
2513             PathCat(fullPath, app.argv[c]);
2514             StripLastDirectory(fullPath, parentPath);
2515             GetExtension(app.argv[c], ext);
2516             isProject = !strcmpi(ext, "epj");
2517
2518             if(isProject && c > (debugStart ? 2 : 1)) continue;
2519
2520             // Create directory for projects (only)
2521             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
2522             {
2523                if(isProject && !FileExists(fullPath))
2524                {
2525                   // The NewProject will handle directory creation
2526                   /*if(!dirAttribs.isDirectory)
2527                   {
2528                      MakeDir(parentPath);
2529                      dirAttribs = FileExists(parentPath);
2530                   }
2531                   if(dirAttribs.isDirectory)*/
2532                   {
2533                      char name[MAX_LOCATION];
2534                      NewProjectDialog newProjectDialog;
2535
2536                      if(projectView)
2537                      {
2538                         projectView.visible = false;
2539                         if(!projectView.Destroy(0))
2540                            return true;
2541                      }
2542
2543                      newProjectDialog = { master = this };
2544
2545                      strcpy(name, app.argv[c]);
2546                      StripExtension(name);
2547                      GetLastDirectory(name, name);
2548                      newProjectDialog.projectName.contents = name;
2549                      newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
2550                      newProjectDialog.locationEditBox.path = parentPath;
2551                      newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
2552
2553                      newProjectDialog.Modal();
2554                      if(projectView)
2555                      {
2556                         ideSettings.AddRecentProject(projectView.fileName);
2557                         ide.UpdateRecentMenus();
2558                         settingsContainer.Save();
2559                      }
2560                   }
2561                   // Open only one project
2562                   break;
2563                }
2564                else
2565                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2566             }
2567          }
2568       }
2569       if(passThrough && projectView && projectView.project && workspace)
2570          workspace.commandLineArgs = passArgs;
2571       delete passArgs;
2572       if(debugStart)
2573          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
2574       return true;
2575    }
2576
2577    void OnDestroy()
2578    {
2579       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2580       if(projectView)
2581       {
2582          projectView.visible = false;
2583          projectView.Destroy(0);
2584          projectView = null;
2585       }
2586 #ifdef GDB_DEBUG_GUI
2587       gdbDialog.Destroy(0);
2588       delete gdbDialog;
2589 #endif
2590    }
2591
2592    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
2593    {
2594       int c, len, count;
2595       char * newList;
2596       char * oldPaths[128];
2597       String oldList = new char[maxPathLen];
2598       Array<String> newExePaths { };
2599       //Map<String, bool> exePathExists { };
2600       bool found = false;
2601 #if defined(__unix__) || defined(__APPLE__)
2602       Array<String> newLibPaths { };
2603       Map<String, bool> libPathExists { };
2604 #endif
2605
2606       if(projectsDirs)
2607       {
2608          for(prj : workspace.projects)
2609          {
2610             DirExpression targetDirExp;
2611
2612             // SKIP FIRST PROJECT...
2613             if(prj == workspace.projects.firstIterator.data) continue;
2614
2615             // NOTE: Right now the additional project config dir will be
2616             //       obtained when the debugger is started, so toggling it
2617             //       while building will change which library gets used.
2618             //       To go with the initial state, e.g. when F5 was pressed,
2619             //       we nould need to keep a list of all project's active
2620             //       config upon startup.
2621             targetDirExp = prj.GetTargetDir(compiler, prj.config);
2622
2623             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2624                cfg = prj.config;
2625             else
2626             {
2627                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2628                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2629                      break;
2630                if(!cfg)
2631                {
2632                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2633                      if(cfg.targetType == sharedLibrary && cfg.debug)
2634                         break;
2635                }
2636             }*/
2637             if(targetDirExp.dir)
2638             {
2639                char buffer[MAX_LOCATION];
2640 #if defined(__WIN32__)
2641                Array<String> paths = newExePaths;
2642 #else
2643                Array<String> paths = newLibPaths;
2644 #endif
2645                GetSystemPathBuffer(buffer, prj.topNode.path);
2646                PathCat(buffer, targetDirExp.dir);
2647                for(p : paths)
2648                {
2649                   if(!fstrcmp(p, buffer))
2650                   {
2651                      found = true;
2652                      break;
2653                   }
2654                }
2655                if(!found)
2656                   paths.Add(CopyString(buffer));
2657             }
2658             delete targetDirExp;
2659          }
2660       }
2661
2662       for(item : compiler.executableDirs)
2663       {
2664          found = false;
2665          for(p : newExePaths)
2666          {
2667             if(!fstrcmp(p, item))
2668             {
2669                found = true;
2670                break;
2671             }
2672          }
2673          if(!found)
2674             newExePaths.Add(CopySystemPath(item));
2675       }
2676
2677       GetEnvironment("PATH", oldList, maxPathLen);
2678 /*#ifdef _DEBUG
2679       printf("Old value of PATH: %s\n", oldList);
2680 #endif*/
2681       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2682       for(c = 0; c < count; c++)
2683       {
2684          found = false;
2685          for(p : newExePaths)
2686          {
2687             if(!fstrcmp(p, oldPaths[c]))
2688             {
2689                found = true;
2690                break;
2691             }
2692          }
2693          if(!found)
2694             newExePaths.Add(CopySystemPath(oldPaths[c]));
2695       }
2696
2697       len = 0;
2698       for(path : newExePaths)
2699          len += strlen(path) + 1;
2700       newList = new char[len + 1];
2701       newList[0] = '\0';
2702       for(path : newExePaths)
2703       {
2704          strcat(newList, path);
2705          strcat(newList, pathListSep);
2706       }
2707       newList[len - 1] = '\0';
2708       SetEnvironment("PATH", newList);
2709 /*#ifdef _DEBUG
2710       printf("New value of PATH: %s\n", newList);
2711 #endif*/
2712       delete newList;
2713
2714       newExePaths.Free();
2715       delete newExePaths;
2716
2717 #if defined(__unix__) || defined(__APPLE__)
2718
2719       for(item : compiler.libraryDirs)
2720       {
2721          if(!libPathExists[item])  // fstrcmp should be used
2722          {
2723             newLibPaths.Add(item);
2724             libPathExists[item] = true;
2725          }
2726       }
2727
2728 #if defined(__APPLE__)
2729       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2730 #else
2731       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2732 #endif
2733 /*#ifdef _DEBUG
2734       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
2735 #endif*/
2736       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2737       for(c = 0; c < count; c++)
2738       {
2739          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2740          {
2741             newLibPaths.Add(oldPaths[c]);
2742             libPathExists[oldPaths[c]] = true;
2743          }
2744       }
2745
2746       len = 0;
2747       for(path : newLibPaths)
2748          len += strlen(path) + 1;
2749       newList = new char[len + 1];
2750       newList[0] = '\0';
2751       for(path : newLibPaths)
2752       {
2753          strcat(newList, path);
2754          strcat(newList, pathListSep);
2755       }
2756       newList[len - 1] = '\0';
2757 #if defined(__APPLE__)
2758       SetEnvironment("DYLD_LIBRARY_PATH", newList);
2759 #else
2760       SetEnvironment("LD_LIBRARY_PATH", newList);
2761 #endif
2762 /*#ifdef _DEBUG
2763       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
2764 #endif*/
2765       delete newList;
2766
2767       delete newLibPaths;
2768       delete libPathExists;
2769 #endif
2770
2771       if(compiler.distccEnabled && compiler.distccHosts)
2772          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
2773
2774       delete oldList;
2775    }
2776
2777    void DestroyTemporaryProjectDir()
2778    {
2779       if(tmpPrjDir && tmpPrjDir[0])
2780       {
2781          if(FileExists(tmpPrjDir).isDirectory)
2782             DestroyDir(tmpPrjDir);
2783          property::tmpPrjDir = null;
2784       }
2785    }
2786
2787    IDEWorkSpace()
2788    {
2789       // Graphics Driver Menu
2790       int c;
2791
2792       /*
2793       app.currentSkin.selectionColor = selectionColor;
2794       app.currentSkin.selectionText = selectionText;
2795       */
2796
2797 /*
2798       driverItems = new MenuItem[app.numDrivers];
2799       for(c = 0; c < app.numDrivers; c++)
2800       {
2801          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
2802          driverItems[c].id = c;
2803          driverItems[c].isRadio = true;         
2804       }
2805 */
2806       driverItems = new MenuItem[2];
2807 #if defined(__unix__)
2808          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
2809          driverItems[0].id = 0;
2810          driverItems[0].isRadio = true;         
2811 #else
2812          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
2813          driverItems[0].id = 0;
2814          driverItems[0].isRadio = true;         
2815 #endif
2816          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
2817          driverItems[1].id = 1;
2818          driverItems[1].isRadio = true;         
2819
2820 /*      skinItems = new MenuItem[app.numSkins];
2821       for(c = 0; c < app.numSkins; c++)
2822       {
2823          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
2824          skinItems[c].id = c;
2825          skinItems[c].isRadio = true;         
2826       }
2827 */
2828       ideFileDialog.master = this;
2829       ideProjectFileDialog.master = this;
2830
2831       //SetDriverAndSkin();
2832       return true;
2833    }
2834
2835    void UpdateRecentMenus()
2836    {
2837       int c;
2838       Menu fileMenu = menu.FindMenu($"File");
2839       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
2840       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
2841       char itemName[MAX_LOCATION + 4];
2842       MenuItem item;
2843
2844       recentFiles.Clear();
2845       c = 0;
2846
2847       for(recent : ideSettings.recentFiles)
2848       {
2849          sprintf(itemName, "%d %s", 1 + c, recent);
2850          MakeSystemPath(itemName);
2851          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
2852          c++;
2853       }
2854
2855       recentProjects.Clear();
2856       c = 0;
2857       for(recent : ideSettings.recentProjects)
2858       {
2859          sprintf(itemName, "%d %s", 1 + c, recent);
2860          MakeSystemPath(itemName);
2861          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
2862          c++;
2863       }
2864    }
2865
2866    ~IDEWorkSpace()
2867    {
2868       delete driverItems;
2869       delete skinItems;
2870       delete ideSettings;
2871    }
2872 }
2873
2874 void DestroyDir(char * path)
2875 {
2876    RecursiveDeleteFolderFSI fsi { };
2877    fsi.Iterate(path);
2878    delete fsi;
2879 }
2880
2881 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
2882 {
2883    bool preserveRootFolder;
2884
2885    void OutFolder(char * folderPath, bool isRoot)
2886    {
2887       if(!(preserveRootFolder && isRoot))
2888          RemoveDir(folderPath);
2889    }
2890
2891    bool OnFile(char * filePath)
2892    {
2893       DeleteFile(filePath);
2894       return true;
2895    }
2896 }
2897
2898 class IDEApp : GuiApplication
2899 {
2900    //driver = "Win32Console";
2901    // driver = "OpenGL";
2902    // skin = "Aqua";
2903    //skin = "TVision";
2904    bool Init()
2905    {
2906       SetLoggingMode(stdOut, null);
2907       //SetLoggingMode(debug, null);
2908
2909       settingsContainer.Load();
2910 #if defined(__unix__) || defined(__APPLE__)
2911       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
2912 #else
2913       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
2914 #endif
2915       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
2916
2917       SetInIDE(true);
2918
2919       desktop.text = titleECEREIDE;
2920       /*
2921       int c;
2922       for(c = 1; c<app.argc; c++)
2923       {
2924          char fullPath[MAX_LOCATION];
2925          GetWorkingDir(fullPath, MAX_LOCATION);
2926          PathCat(fullPath, app.argv[c]);
2927          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2928       }
2929       */
2930       return true;
2931    }
2932 }
2933
2934 IDEMainFrame ideMainFrame { };
2935
2936 define app = ((IDEApp)__thisModule);
2937 #ifdef _DEBUG
2938 define titleECEREIDE = $"ECERE IDE (Debug)";
2939 #else
2940 define titleECEREIDE = $"ECERE IDE";
2941 #endif