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