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