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