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