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