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