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