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