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