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