ide: Copying bold and size attributes for fonts
[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 AVLTree<const String> binaryDocExt
48 { [
49    "wav", "mp3", "flac", "ogg",
50    "mid",
51    "avi", "mkv", "mpg", "mpeg",
52    "7z", "zip", "gz", "bz2", "xz", "rar", "z", "tar", "ear",
53    "pdf", "odp", "ods", "odt", "ppt", "doc", "xls", "pptx", "docx", "xlsx"
54 ] };
55
56 #if defined(__WIN32__)
57 define pathListSep = ";";
58 #else
59 define pathListSep = ":";
60 #endif
61
62 define maxPathLen = 65 * MAX_LOCATION;
63
64 class PathBackup : struct
65 {
66    String oldLDPath;
67    String oldPath;
68
69    PathBackup()
70    {
71       oldPath = new char[maxPathLen];
72       oldLDPath = new char[maxPathLen];
73
74       GetEnvironment("PATH", oldPath, maxPathLen);
75 #if defined(__APPLE__)
76       GetEnvironment("DYLD_LIBRARY_PATH", oldLDPath, maxPathLen);
77 #else
78       GetEnvironment("LD_LIBRARY_PATH", oldLDPath, maxPathLen);
79 #endif
80    }
81
82    ~PathBackup()
83    {
84       SetEnvironment("PATH", oldPath);
85 #if defined(__APPLE__)
86       SetEnvironment("DYLD_LIBRARY_PATH", oldLDPath);
87 #else
88       SetEnvironment("LD_LIBRARY_PATH", oldLDPath);
89 #endif
90       delete oldPath;
91       delete oldLDPath;
92    }
93 };
94
95 enum OpenCreateIfFails { no, yes, something, whatever };
96 enum OpenMethod { normal, add };
97
98 static Array<FileFilter> fileFilters
99 { [
100    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
101    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
102    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
103    { $"Text files (*.txt, *.text, *.nfo, *.info)", "txt, text, nfo, info" },
104    { $"Web files (*.html, *.htm, *.xhtml, *.css, *.php, *.js, *.jsi, *.rb, *.xml)", "html, htm, xhtml, css, php, js, jsi, rb, xml" },
105    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png, *.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
106    { $"3D Studio Model Files (*.3ds)", "3ds" },
107    { $"All files", null }
108 ] };
109
110 static Array<FileType> fileTypes
111 { [
112    { $"Based on extension", null },
113    { $"Text",               "txt" },
114    { $"Image",              "jpg" },
115    { $"3D Studio Model",    "3ds" }
116 ] };
117
118 static Array<FileFilter> projectFilters
119 { [
120    { $"Project Files (*.epj)", ProjectExtension }
121 ] };
122
123 static Array<FileType> projectTypes
124 { [
125    { $"Project File", ProjectExtension }
126 ] };
127
128 static Array<FileFilter> findInFilesFileFilters
129 { [
130    { $"eC Files (*.ec, *.eh)", "ec, eh" },
131    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
132    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
133    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
134    { $"Text files (*.txt)", "txt" },
135    { $"All files", null }
136 ] };
137
138 FileDialog ideFileDialog
139 {
140    type = multiOpen, text = $"Open";
141    types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType), filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
142 };
143
144 define openProjectFileDialogTitle = $"Open Project";
145 define addProjectFileDialogTitle = $"Open Additional Project";
146 FileDialog ideProjectFileDialog
147 {
148    type = open;
149    types = projectTypes.array, sizeTypes = projectTypes.count * sizeof(FileType), filters = projectFilters.array, sizeFilters = projectFilters.count * sizeof(FileFilter);
150 };
151
152 GlobalSettingsDialog globalSettingsDialog
153 {
154    ideSettings = ideSettings;
155    settingsContainer = settingsContainer;
156
157    void OnGlobalSettingChange(GlobalSettingsChange globalSettingsChange)
158    {
159       switch(globalSettingsChange)
160       {
161          case editorSettings:
162          {
163             Window child;
164             for(child = ide.firstChild; child; child = child.next)
165             {
166                if(child._class == class(CodeEditor))
167                {
168                   CodeEditor codeEditor = (CodeEditor) child;
169                   codeEditor.editBox.freeCaret = ideSettings.useFreeCaret;
170                   // codeEditor.editBox.lineNumbers = ideSettings.showLineNumbers;
171                   codeEditor.editBox.caretFollowsScrolling = ideSettings.caretFollowsScrolling;
172                   codeEditor.OnPostCreate(); // Update editBox margin size
173                }
174             }
175             break;
176          }
177          case projectOptions:
178             break;
179          case compilerSettings:
180          {
181             ide.UpdateCompilerConfigs(true);
182             break;
183          }
184       }
185    }
186 };
187
188 void DrawLineMarginIcon(Surface surface, BitmapResource resource, int line, int lineH, int scrollY, int boxH)
189 {
190    int lineY;
191    if(line)
192    {
193       lineY = (line - 1) * lineH;
194       if(lineY + lineH > scrollY && lineY + lineH < scrollY + boxH)
195       {
196          Bitmap bitmap = resource.bitmap;
197          if(bitmap)
198             surface.Blit(bitmap, 0, lineY - scrollY + (lineH - bitmap.height) / 2 + 1, 0, 0, bitmap.width, bitmap.height);
199       }
200    }
201 }
202
203 #define IDEItem(x)   (&((IDEWorkSpace)0).x)
204
205 class IDEToolbar : ToolBar
206 {
207    // File options
208    // New
209    ToolButton buttonNewFile { this, toolTip = $"New file", menuItemPtr = IDEItem(fileNewItem) };
210    // Open
211    ToolButton buttonOpenFile { this, toolTip = $"Open file", menuItemPtr = IDEItem(fileOpenItem) };
212    // Close
213    // ToolButton buttonCloseFile { this, toolTip = $"Close file", menuItemPtr = IDEItem(fileCloseItem) };
214    // Save
215    ToolButton buttonSaveFile { this, toolTip = $"Save file", menuItemPtr = IDEItem(fileSaveItem) };
216    // Save All
217    ToolButton buttonSaveAllFile { this, toolTip = $"Save all", menuItemPtr = IDEItem(fileSaveAllItem) };
218
219    ToolSeparator separator1 { this };
220
221    // Edit options
222    // Cut
223    // Copy
224    // Paste
225    // Undo
226    // Redo
227
228    // ToolSeparator separator2 { this };
229
230    // Project options
231    // New project
232    ToolButton buttonNewProject { this, toolTip = $"New project", menuItemPtr = IDEItem(projectNewItem) };
233    // Open project
234    ToolButton buttonOpenProject { this, toolTip = $"Open project", menuItemPtr = IDEItem(projectOpenItem) };
235    // Add project to workspace
236    ToolButton buttonAddProject { this, toolTip = $"Add project to workspace", menuItemPtr = IDEItem(projectAddItem), disabled = true; };
237    // Close project
238    // ToolButton buttonCloseProject { this, toolTip = $"Close project", menuItemPtr = IDEItem(projectCloseItem), disabled = true; };
239
240    ToolSeparator separator3 { this };
241
242    // Build/Execution options
243    // Build
244    ToolButton buttonBuild { this, toolTip = $"Build project", menuItemPtr = IDEItem(projectBuildItem), disabled = true; };
245    // Re-link
246    ToolButton buttonReLink { this, toolTip = $"Relink project", menuItemPtr = IDEItem(projectLinkItem), disabled = true; };
247    // Rebuild
248    ToolButton buttonRebuild { this, toolTip = $"Rebuild project", menuItemPtr = IDEItem(projectRebuildItem), disabled = true; };
249    // Clean
250    ToolButton buttonClean { this, toolTip = $"Clean project", menuItemPtr = IDEItem(projectCleanItem), disabled = true; };
251    // Real Clean
252    // ToolButton buttonRealClean { this, toolTip = $"Real clean project", menuItemPtr = IDEItem(projectRealCleanItem), disabled = true; };
253    // Regenerate Makefile
254    ToolButton buttonRegenerateMakefile { this, toolTip = $"Regenerate Makefile", menuItemPtr = IDEItem(projectRegenerateItem), disabled = true; };
255    // Compile actual file
256    // Execute
257    ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem), disabled = true; };
258 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
259    ToolButton buttonInstall { this, toolTip = $"Install", menuItemPtr = IDEItem(projectInstallItem), disabled = true; };
260 #endif
261
262    ToolSeparator separator4 { this };
263
264    // Debug options
265    // Start/Resume
266    ToolButton buttonDebugStartResume { this, toolTip = $"Start", menuItemPtr = IDEItem(debugStartResumeItem), disabled = true; };
267    // Restart
268    ToolButton buttonDebugRestart { this, toolTip = $"Restart", menuItemPtr = IDEItem(debugRestartItem), disabled = true; };
269    // Pause
270    ToolButton buttonDebugPause { this, toolTip = $"Break", menuItemPtr = IDEItem(debugBreakItem), disabled = true; };
271    // Stop
272    ToolButton buttonDebugStop { this, toolTip = $"Stop", menuItemPtr = IDEItem(debugStopItem), disabled = true; };
273    // Breakpoints
274    //ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem) };
275    // F11
276    ToolButton buttonDebugStepInto { this, toolTip = $"Step Into", menuItemPtr = IDEItem(debugStepIntoItem), disabled = true; };
277    // F10
278    ToolButton buttonDebugStepOver { this, toolTip = $"Step Over", menuItemPtr = IDEItem(debugStepOverItem), disabled = true; };
279    // Shift+F11
280    ToolButton buttonDebugStepOut { this, toolTip = $"Step Out", menuItemPtr = IDEItem(debugStepOutItem), disabled = true; };
281    // Shift+F10
282    ToolButton buttonDebugSkipStepOver { this, toolTip = $"Step Over Skipping Breakpoints", menuItemPtr = IDEItem(debugSkipStepOverItem), disabled = true; };
283
284    ToolSeparator separator5 { this };
285
286    Window spacer5 { this, size = { 4 } };
287
288    DropBox activeConfig
289    {
290       this, toolTip = $"Active Configuration(s)", size = { 160 }, disabled = true;
291       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
292       {
293          if(row)
294             ide.workspace.SelectActiveConfig(row.string);
295          return true;
296       }
297    };
298
299    Window spacer6 { this, size = { 4 } };
300
301    DropBox activeCompiler
302    {
303       this, toolTip = $"Active Compiler", size = { 160 }, disabled = true;
304       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
305       {
306          if(ide.workspace && ide.projectView && row && strcmp(row.string, ide.workspace.compiler))
307          {
308             bool silent = ide.projectView.buildInProgress == none ? false : true;
309             CompilerConfig compiler = ideSettings.GetCompilerConfig(row.string);
310             ide.workspace.compiler = row.string;
311             ide.projectView.ShowOutputBuildLog(!silent);
312             if(!silent)
313                ide.projectView.DisplayCompiler(compiler, false);
314             for(prj : ide.workspace.projects)
315                ide.projectView.ProjectPrepareCompiler(prj, compiler, silent);
316             delete compiler;
317             ide.workspace.Save();
318          }
319          return true;
320       }
321    };
322
323    DropBox activeBitDepth
324    {
325       this, toolTip = $"Active Bit Length", size = { 60 }, disabled = true;
326       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
327       {
328          if(ide.workspace && ide.projectView && row)
329          {
330             bool silent = ide.projectView.buildInProgress == none ? false : true;
331             CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
332             ide.workspace.bitDepth = (int)row.tag;
333             ide.projectView.ShowOutputBuildLog(!silent);
334             if(!silent)
335                ide.projectView.DisplayCompiler(compiler, false);
336             for(prj : ide.workspace.projects)
337                ide.projectView.ProjectPrepareCompiler(prj, compiler, silent);
338             delete compiler;
339             ide.workspace.Save();
340          }
341          return true;
342       }
343    };
344
345    Window spacer7 { this, size = { 4 } };
346
347    void IDEToolbar()
348    {
349       DataRow row;
350       row = activeBitDepth.AddString("Auto");
351       row.tag = 0;
352       activeBitDepth.AddString("32 bit").tag = 32;
353       activeBitDepth.AddString("64 bit").tag = 64;
354       activeBitDepth.currentRow = row;
355    }
356 }
357
358 class IDEMainFrame : Window
359 {
360    background = formColor;
361    borderStyle = sizable;
362    hasMaximize = true;
363    hasMinimize = true;
364    hasClose = true;
365    minClientSize = { 600, 300 };
366    hasMenuBar = true;
367    icon = { ":icon.png" };
368    text = titleECEREIDE;
369 #if 0 //def _DEBUG
370    //stayOnTop = true;
371    size = { 800, 600 };
372    anchor = { top = 0, right = 0, bottom = 0 };
373 #else
374    state = maximized;
375    anchor = { left = 0, top = 0, right = 0, bottom = 0 };
376 #endif
377
378    Stacker stack
379    {
380       this;
381       menu = { };
382       isActiveClient = true;
383       gap = 0;
384       direction = vertical;
385       background = formColor;
386       anchor = { left = 0, top = 0, right = 0, bottom = 0 };
387    };
388    IDEToolbar toolBar
389    {
390       stack, ideWorkSpace;
391
392       void OnDestroy(void)
393       {
394          ((IDEWorkSpace)master).toolBar = null;
395       }
396    };
397    IDEWorkSpace ideWorkSpace { stack, this, toolBar = toolBar };
398 }
399
400 define ide = ideMainFrame.ideWorkSpace;
401
402 class IDEWorkSpace : Window
403 {
404    background = Color { 85, 85, 85 };
405
406    //tabCycle = true;
407    hasVertScroll = true;
408    hasHorzScroll = true;
409    hasStatusBar = true;
410    isActiveClient = true;
411    anchor = { left = 0, top = 0, right = 0, bottom = 0 };
412    menu = Menu {  };
413    IDEToolbar toolBar;
414
415    MenuItem * driverItems, * skinItems, * languageItems;
416    StatusField pos { width = 150 };
417    StatusField ovr, caps, num;
418    DualPipe documentor;
419
420    BitmapResource back                 { ":ecereBack.jpg", window = this };
421    BitmapResource bmpBp                { ":codeMarks/breakpoint.png", window = this };
422    BitmapResource bmpBpDisabled        { ":codeMarks/breakpointDisabled.png", window = this };
423    BitmapResource bmpBpHalf            { ":codeMarks/breakpointHalf.png", window = this };
424    BitmapResource bmpBpHalfDisabled    { ":codeMarks/breakpointHalfDisabled.png", window = this };
425    BitmapResource bmpCursor            { ":codeMarks/cursor.png", window = this };
426    BitmapResource bmpCursorError       { ":codeMarks/cursorError.png", window = this };
427    BitmapResource bmpTopFrame          { ":codeMarks/topFrame.png", window = this };
428    BitmapResource bmpTopFrameError     { ":codeMarks/topFrameError.png", window = this };
429    BitmapResource bmpTopFrameHalf      { ":codeMarks/topFrameHalf.png", window = this };
430    BitmapResource bmpTopFrameHalfError { ":codeMarks/topFrameHalfError.png", window = this };
431
432    Debugger debugger { };
433
434    ProjectView projectView;
435
436    OutputView outputView
437    {
438       parent = this;
439
440       void OnGotoError(const char * line, bool noParsing)
441       {
442          CompilerConfig compiler = ide.workspace ? ideSettings.GetCompilerConfig(ide.workspace.compiler) : null;
443          const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
444          ide.GoToError(line, noParsing, objectFileExt);
445       }
446
447       void OnCodeLocationParseAndGoTo(const char * line)
448       {
449          CompilerConfig compiler = ide.workspace ? ideSettings.GetCompilerConfig(ide.workspace.compiler) : null;
450          const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
451          ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir, objectFileExt);
452       }
453
454       bool OnKeyDown(Key key, unichar ch)
455       {
456          switch(key)
457          {
458             case escape:
459                if(activeBox != findBox || !ide.findInFilesDialog || !ide.findInFilesDialog.SearchAbort())
460                   ide.ShowCodeEditor();
461                break;
462             default:
463             {
464                OutputView::OnKeyDown(key, ch);
465                break;
466             }
467          }
468          return true;
469       }
470
471       bool OnClose(bool parentClosing)
472       {
473          visible = false;
474          if(!parentClosing)
475             ide.RepositionWindows(false);
476          return parentClosing;
477       }
478    };
479
480    CallStackView callStackView
481    {
482       parent = this, font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
483
484       void OnSelectFrame(int frameIndex)
485       {
486          ide.debugger.GoToStackFrameLine(frameIndex, true, true);
487          if(frameIndex >= 0)
488             ide.debugger.SelectFrame(frameIndex);
489       }
490
491       void OnToggleBreakpoint()
492       {
493          Debugger debugger = ide.debugger;
494          if(debugger.activeFrame && debugger.activeFrame.absoluteFile)
495          {
496             int line = debugger.activeFrame.line;
497             debugger.ToggleBreakpoint(debugger.activeFrame.absoluteFile, line);
498             Update(null);
499             {
500                CodeEditor codeEditor = (CodeEditor)ide.FindWindow(debugger.activeFrame.absoluteFile);
501                if(codeEditor) { codeEditor.Update(null); Activate(); }
502             }
503          }
504       }
505
506       bool OnKeyDown(Key key, unichar ch)
507       {
508          switch(key)
509          {
510             case escape: ide.ShowCodeEditor(); break;
511          }
512          return true;
513       }
514
515       bool OnClose(bool parentClosing)
516       {
517          visible = false;
518          if(!parentClosing)
519             ide.RepositionWindows(false);
520          return parentClosing;
521       }
522
523       void OnRedraw(Surface surface)
524       {
525          Debugger debugger = ide.debugger;
526          Frame activeFrame = debugger.activeFrame;
527          if(activeFrame)
528          {
529             bool error;
530             int lineCursor, lineTopFrame;
531             int lineH, scrollY, boxH;
532             BitmapResource bmp;
533             Breakpoint bp = null;
534
535             boxH = clientSize.h;
536             scrollY = editBox.scroll.y;
537             displaySystem.FontExtent(editBox.font.font, " ", 1, null, &lineH);
538             //activeThread = debugger.activeThread;
539             //hitThread = debugger.hitThread;
540             debugger.GetCallStackCursorLine(&error, &lineCursor, &lineTopFrame);
541
542             // TODO: improve bp drawing... it should be visible even if it's not on the activeFrame
543             if(activeFrame.absoluteFile)
544             {
545                for(i : ide.workspace.breakpoints; i.type == user)
546                {
547                   if(i.absoluteFilePath && i.absoluteFilePath[0] &&
548                      !fstrcmp(i.absoluteFilePath, activeFrame.absoluteFile) &&
549                      activeFrame.line == i.line)
550                   {
551                      bp = i;
552                      break;
553                   }
554                }
555             }
556             if(bp)
557                DrawLineMarginIcon(surface,
558                      /*(lineCursor == 1 || lineTopFrame == 1) ? */ide.bmpBpHalf/* : ide.bmpBp*/,
559                      lineCursor /*1*/, lineH, scrollY, boxH);
560             /*
561             if(activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
562                DrawLineMarginIcon(surface,
563                      (lineCursor == 1 || lineTopFrame == 1) ? ide.bmpBpHalf : ide.bmpBp,
564                      1, lineH, scrollY, boxH);
565             */
566             DrawLineMarginIcon(surface, error ? ide.bmpCursorError : ide.bmpCursor, lineCursor, lineH, scrollY, boxH);
567             if(bp && lineCursor == 1) //activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
568                bmp = error ? ide.bmpTopFrameHalfError : ide.bmpTopFrameHalf;
569             else
570                bmp = error ? ide.bmpTopFrameError : ide.bmpTopFrame;
571             DrawLineMarginIcon(surface, bmp, lineTopFrame, lineH, scrollY, boxH);
572          }
573          if(editBox.horzScroll && editBox.horzScroll.visible)
574          {
575             surface.SetBackground(control);
576             surface.Area(0, editBox.clientSize.h, 20 - 1, clientSize.h - 1);
577          }
578       }
579    };
580
581    WatchesView watchesView { parent = this };
582    ThreadsView threadsView
583    {
584       parent = this, font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
585
586       bool OnKeyDown(Key key, unichar ch)
587       {
588          switch(key)
589          {
590             case escape: ide.ShowCodeEditor(); break;
591          }
592          return true;
593       }
594
595       bool OnClose(bool parentClosing)
596       {
597          visible = false;
598          if(!parentClosing)
599             ide.RepositionWindows(false);
600          return parentClosing;
601       }
602
603       void OnSelectThread(int threadId)
604       {
605          if(threadId)
606             ide.debugger.SelectThread(threadId);
607       }
608
609       bool OnGetThreadsInfo(int * activeThread, int * hitThread, int * signalThread)
610       {
611          bool result = false;
612          Debugger debugger = ide.debugger;
613          *activeThread = debugger.activeThread;
614          *hitThread = debugger.hitThread;
615          *signalThread = debugger.signalThread;
616          result = true;
617          return result;
618       }
619    };
620    BreakpointsView breakpointsView { parent = this };
621
622    ToolBox toolBox { parent = this, visible = false };
623    Sheet sheet { parent = this, visible = false };
624
625    char * tmpPrjDir;
626    property char * tmpPrjDir { set { delete tmpPrjDir; if(value) tmpPrjDir = CopyString(value); } get { return tmpPrjDir; } };
627
628    Menu fileMenu { menu, $"File", f, hasMargin = true };
629       MenuItem fileNewItem
630       {
631          fileMenu, $"New", n, ctrlN;
632          bitmap = { ":actions/docNew.png" };
633          bool NotifySelect(MenuItem selection, Modifiers mods)
634          {
635             Window currentDoc = activeClient;
636             bool maximizeDoc = ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView));
637             Window document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, false);
638             RepositionWindows(false);
639             document.NotifySaved = DocumentSaved;
640             return true;
641          }
642       };
643       MenuItem fileOpenItem
644       {
645          fileMenu, $"Open...", o, ctrlO;
646          bitmap = { ":actions/docOpen.png" };
647          bool NotifySelect(MenuItem selection, Modifiers mods)
648          {
649             if(!projectView && ideSettings.ideFileDialogLocation)
650                ideFileDialog.currentDirectory = ideSettings.ideFileDialogLocation;
651             for(;;)
652             {
653                if(ideFileDialog.Modal() == ok)
654                {
655                   bool gotWhatWeWant = false;
656                   int c;
657                   int numSelections = ideFileDialog.numSelections;
658                   const char * const * multiFilePaths = ideFileDialog.multiFilePaths;
659
660                   for(c = 0; c < numSelections; c++)
661                   {
662                      if(OpenFile(multiFilePaths[c], false, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift))
663                         gotWhatWeWant = true;
664                   }
665                   if(gotWhatWeWant ||
666                      MessageBox { type = yesNo, master = this, text = $"Error opening file",
667                      contents = $"Open a different file?" }.Modal() == no)
668                   {
669                      if(!projectView && gotWhatWeWant)
670                         ChangeFileDialogsDirectory(ideFileDialog.currentDirectory, true);
671                      ide.RepositionWindows(false);
672                      break;
673                   }
674                }
675                else
676                   break;
677             }
678             return true;
679          }
680       };
681       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
682       MenuDivider { fileMenu };
683       MenuItem fileSaveItem
684       {
685          fileMenu, $"Save", s, ctrlS, bitmap = { ":actions/docSave.png" };
686
687          // For the toolbar button; clients can still override that for the menu item
688          bool Window::NotifySelect(MenuItem selection, Modifiers mods)
689          {
690             Window w = activeClient;
691             if(w)
692                w.MenuFileSave(null, 0);
693             return true;
694          }
695       };
696       MenuItem fileSaveAsItem { fileMenu, $"Save As...", a };
697       MenuItem fileSaveAllItem { fileMenu, $"Save All", l, NotifySelect = MenuFileSaveAll, bitmap = { ":actions/docSaveAll.png" } };
698       MenuDivider { fileMenu };
699       MenuItem findInFiles
700       {
701          fileMenu, $"Find In Files...", f, Key { f, ctrl = true , shift = true };
702          bool NotifySelect(MenuItem selection, Modifiers mods)
703          {
704             findInFilesDialog.replaceMode = false;
705             findInFilesDialog.Show();
706             return true;
707          }
708       };
709       MenuItem replaceInFiles
710       {
711          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
712          bool NotifySelect(MenuItem selection, Modifiers mods)
713          {
714             findInFilesDialog.replaceMode = true;
715             findInFilesDialog.Show();
716             return true;
717          }
718       };
719       MenuDivider { fileMenu };
720       MenuItem globalSettingsItem
721       {
722          fileMenu, $"Global Settings...", g;
723          bool NotifySelect(MenuItem selection, Modifiers mods)
724          {
725             globalSettingsDialog.master = this;
726             if(ide.workspace && ide.workspace.compiler)
727                globalSettingsDialog.workspaceActiveCompiler = ide.workspace.compiler;
728             else if(ideSettings.defaultCompiler)
729                globalSettingsDialog.workspaceActiveCompiler = ideSettings.defaultCompiler;
730             globalSettingsDialog.Modal();
731             return true;
732          }
733       };
734       MenuDivider { fileMenu };
735       Menu recentFiles { fileMenu, $"Recent Files", r };
736       Menu recentProjects { fileMenu, $"Recent Projects", p };
737       MenuDivider { fileMenu };
738       MenuItem exitItem
739       {
740          fileMenu, $"Exit", x, altF4;
741
742          bool NotifySelect(MenuItem selection, Modifiers mods)
743          {
744             ideMainFrame.Destroy(0);
745             return true;
746          }
747       };
748
749       bool FileRecentFile(MenuItem selection, Modifiers mods)
750       {
751          int id = 0;
752          for(file : ideSettings.recentFiles)
753          {
754             if(id == selection.id)
755             {
756                bool isProjectFile;
757                char extension[MAX_EXTENSION] = "";
758                GetExtension(file, extension);
759                isProjectFile = (!strcmpi(extension, "epj") || !strcmpi(extension, "ews"));
760                if(mods.ctrl)
761                {
762                   char * command = PrintString("ide ", isProjectFile ? "-t " : "", file);
763                   Execute(command);
764                   delete command;
765                }
766                else
767                {
768                   OpenFile(file, false, true, isProjectFile ? "txt" : null, no, normal, mods.ctrl && mods.shift);
769                   ide.RepositionWindows(false);
770                }
771                break;
772             }
773             id++;
774          }
775          return true;
776       }
777
778       bool FileRecentProject(MenuItem selection, Modifiers mods)
779       {
780          int id = 0;
781          for(file : ideSettings.recentProjects)
782          {
783             if(id == selection.id)
784             {
785                if(mods.ctrl)
786                {
787                   char * command = PrintString("ide ", file);
788                   Execute(command);
789                   delete command;
790                }
791                else
792                   OpenFile(file, false, true, null, no, normal, mods.ctrl && mods.shift);
793                break;
794             }
795             id++;
796          }
797          return true;
798       }
799
800    MenuPlacement editMenu { menu, $"Edit", e };
801
802    Menu projectMenu { menu, $"Menu"."Project", p, hasMargin = true };
803       MenuItem projectNewItem
804       {
805          projectMenu, $"New...", n, Key { n, true, true };
806          bitmap = { ":actions/projNew.png" };
807          bool NotifySelect(MenuItem selection, Modifiers mods)
808          {
809             if(!DontTerminateDebugSession($"New Project"))
810             {
811                DialogResult result;
812                NewProjectDialog newProjectDialog { master = this };
813                incref newProjectDialog;
814                result = newProjectDialog.Modal();
815                if(result == ok)
816                {
817                   if(ProjectClose())
818                   {
819                      newProjectDialog.CreateNewProject();
820                      if(projectView)
821                      {
822                         ideSettings.AddRecentProject(projectView.fileName);
823                         ide.UpdateRecentMenus();
824                         settingsContainer.Save();
825                      }
826                   }
827                }
828                delete newProjectDialog;
829             }
830             return true;
831          }
832       };
833       MenuItem projectOpenItem
834       {
835          projectMenu, $"Open...", o, Key { o, true, true };
836          bitmap = { ":actions/projOpen.png" };
837          bool NotifySelect(MenuItem selection, Modifiers mods)
838          {
839             if(ideSettings.ideProjectFileDialogLocation)
840                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
841
842             ideProjectFileDialog.text = openProjectFileDialogTitle;
843             if(ideProjectFileDialog.Modal() == ok)
844             {
845                OpenFile(ideProjectFileDialog.filePath, false, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift);
846                //ChangeProjectFileDialogDirectory(ideProjectFileDialog.currentDirectory);
847             }
848             return true;
849          }
850       };
851       MenuItem projectQuickItem
852       {
853          projectMenu, $"Quick...", q, f7, disabled = true;
854          bool NotifySelect(MenuItem selection, Modifiers mods)
855          {
856             if(!projectView)
857                QuickProjectDialog { this }.Modal();
858             return true;
859          }
860       };
861       MenuItem projectAddItem
862       {
863          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
864          bitmap = { ":actions/projAdd.png" };
865          disabled = true;
866          bool NotifySelect(MenuItem selection, Modifiers mods)
867          {
868             if(ideSettings.ideProjectFileDialogLocation)
869                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
870
871             ideProjectFileDialog.text = addProjectFileDialogTitle;
872             for(;;)
873             {
874                if(ideProjectFileDialog.Modal() == ok)
875                {
876                   if(OpenFile(ideProjectFileDialog.filePath, false, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add, mods.ctrl && mods.shift))
877                      break;
878                   if(MessageBox { type = yesNo, master = this, text = $"Error opening project file",
879                         contents = $"Add a different project?" }.Modal() == no)
880                   {
881                      break;
882                   }
883                }
884                else
885                   break;
886             }
887             return true;
888          }
889       };
890       MenuItem projectCloseItem
891       {
892          projectMenu, $"Close", c, disabled = true;
893          bool NotifySelect(MenuItem selection, Modifiers mods)
894          {
895             if(projectView)
896             {
897                if(!ide.DontTerminateDebugSession($"Project Close"))
898                   ProjectClose();
899             }
900             return true;
901          }
902       };
903       MenuDivider { projectMenu };
904       MenuItem projectSettingsItem
905       {
906          projectMenu, $"Settings...", s, altF7, disabled = true;
907          bool NotifySelect(MenuItem selection, Modifiers mods)
908          {
909             projectView.MenuSettings(projectView.active ? selection : null, mods);
910             return true;
911          }
912       };
913       MenuDivider { projectMenu };
914       MenuItem projectBrowseFolderItem
915       {
916          projectMenu, $"Browse Project Folder", p, disabled = true;
917          bool NotifySelect(MenuItem selection, Modifiers mods)
918          {
919             if(projectView)
920                projectView.MenuBrowseFolder(null, mods);
921             return true;
922          }
923       };
924       MenuDivider { projectMenu };
925       MenuItem projectRunItem
926       {
927          projectMenu, $"Run", r, ctrlF5, disabled = true;
928          bitmap = { ":actions/run.png" };
929          bool NotifySelect(MenuItem selection, Modifiers mods)
930          {
931             if(projectView)
932                projectView.Run(null, mods);
933             return true;
934          }
935       };
936       MenuItem projectBuildItem
937       {
938          projectMenu, $"Build", b, f7, disabled = true;
939          bitmap = { ":actions/build.png" };
940          bool NotifySelect(MenuItem selection, Modifiers mods)
941          {
942             if(projectView)
943             {
944                if(projectView.buildInProgress == none)
945                   projectView.ProjectBuild(projectView.active ? selection : null, mods);
946                else
947                   projectView.stopBuild = true;
948             }
949             return true;
950          }
951       };
952       MenuItem projectLinkItem
953       {
954          projectMenu, $"Relink", l, disabled = true;
955          bitmap = { ":actions/relink.png" };
956          bool NotifySelect(MenuItem selection, Modifiers mods)
957          {
958             if(projectView)
959                projectView.ProjectLink(projectView.active ? selection : null, mods);
960             return true;
961          }
962       };
963       MenuItem projectRebuildItem
964       {
965          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
966          bitmap = { ":actions/rebuild.png" };
967          bool NotifySelect(MenuItem selection, Modifiers mods)
968          {
969             if(projectView)
970                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
971             return true;
972          }
973       };
974       MenuItem projectCleanTargetItem
975       {
976          projectMenu, $"Clean Target", g, disabled = true;
977          bitmap = { ":actions/clean.png" };
978          bool NotifySelect(MenuItem selection, Modifiers mods)
979          {
980             if(projectView)
981             {
982                debugger.Stop();
983                projectView.ProjectCleanTarget(projectView.active ? selection : null, mods);
984             }
985             return true;
986          }
987       };
988       MenuItem projectCleanItem
989       {
990          projectMenu, $"Clean", e, disabled = true;
991          bitmap = { ":actions/clean.png" };
992          bool NotifySelect(MenuItem selection, Modifiers mods)
993          {
994             if(projectView)
995             {
996                debugger.Stop();
997                projectView.ProjectClean(projectView.active ? selection : null, mods);
998             }
999             return true;
1000          }
1001       };
1002       MenuItem projectRealCleanItem
1003       {
1004          projectMenu, $"Real Clean", disabled = true;
1005          bitmap = { ":actions/clean.png" };
1006          bool NotifySelect(MenuItem selection, Modifiers mods)
1007          {
1008             if(projectView)
1009             {
1010                debugger.Stop();
1011                projectView.ProjectRealClean(projectView.active ? selection : null, mods);
1012             }
1013             return true;
1014          }
1015       };
1016       MenuItem projectRegenerateItem
1017       {
1018          projectMenu, $"Regenerate Makefile", m, disabled = true;
1019          bitmap = { ":actions/regMakefile.png" };
1020          bool NotifySelect(MenuItem selection, Modifiers mods)
1021          {
1022             if(projectView)
1023                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
1024             return true;
1025          }
1026       };
1027       MenuItem projectInstallItem
1028       {
1029 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
1030          projectMenu, $"Install", t, disabled = true;
1031 #endif
1032          bitmap = { ":status/software-update-available.png" };
1033          bool NotifySelect(MenuItem selection, Modifiers mods)
1034          {
1035             if(projectView)
1036                projectView.ProjectInstall(projectView.active ? selection : null, mods);
1037             return true;
1038          }
1039       };
1040       MenuItem projectCompileItem;
1041    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
1042       MenuItem debugStartResumeItem
1043       {
1044          debugMenu, $"Start", s, f5, disabled = true;
1045          bitmap = { ":actions/debug.png" };
1046          NotifySelect = MenuDebugStart;
1047       };
1048       bool MenuDebugStart(MenuItem selection, Modifiers mods)
1049       {
1050          if(projectView)
1051          {
1052             debugStartResumeItem.disabled = true; // a very rare exception to calling AdjustDebugMenus
1053             if(!projectView.DebugStart())
1054                debugStartResumeItem.disabled = false; // same exception
1055          }
1056          return true;
1057       }
1058       bool MenuDebugResume(MenuItem selection, Modifiers mods)
1059       {
1060          if(projectView)
1061             projectView.DebugResume();
1062          return true;
1063       }
1064       MenuItem debugRestartItem
1065       {
1066          debugMenu, $"Restart", r, Key { f5, ctrl = true, shift = true }, disabled = true;
1067          bitmap = { ":actions/restart.png" };
1068          bool NotifySelect(MenuItem selection, Modifiers mods)
1069          {
1070             if(projectView)
1071                projectView.DebugRestart();
1072             return true;
1073          }
1074       };
1075       MenuItem debugBreakItem
1076       {
1077          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
1078          bitmap = { ":actions/pause.png" };
1079          bool NotifySelect(MenuItem selection, Modifiers mods)
1080          {
1081             if(projectView && projectView.buildInProgress != none)
1082                return true;
1083             if(projectView)
1084                projectView.DebugBreak();
1085             return true;
1086          }
1087       };
1088       MenuItem debugStopItem
1089       {
1090          debugMenu, $"Stop", p, shiftF5, disabled = true;
1091          bitmap = { ":actions/stopDebug.png" };
1092          bool NotifySelect(MenuItem selection, Modifiers mods)
1093          {
1094             if(projectView)
1095                projectView.DebugStop();
1096             return true;
1097          }
1098       };
1099       MenuDivider { debugMenu };
1100       ModelView duck
1101       {
1102          this,
1103          // nonClient = true,
1104          autoCreate = false,
1105          alphaBlend = true,
1106          opacity = 0,
1107          isRemote = true,
1108          borderStyle = 0,
1109          moveable = true,
1110          anchor = { right = 0, bottom = 0 },
1111          inactive = true,
1112          isActiveClient = false,
1113          stayOnTop = true,
1114          clickThrough = true,
1115          size = { 500, 500 };
1116
1117          bool OnLoadGraphics()
1118          {
1119             ModelView::OnLoadGraphics();
1120             camera.position.z /= 1.3;
1121             camera.orientation = Euler { yaw = 280, pitch = 20 };
1122             camera.Update();
1123             Update(null);
1124             return true;
1125          }
1126
1127          bool OnRightButtonDown(int x, int y, Modifiers mods)
1128          {
1129             if(!displaySystem.flags.flipping) return true;
1130             MenuWindowMove(null, 0);
1131             return false;
1132          }
1133
1134          bool OnRightButtonUp(int x, int y, Modifiers mods)
1135          {
1136             position = position;
1137             state = normal;
1138             return true;
1139          }
1140       };
1141       MenuItem debugRubberDuck
1142       {
1143          debugMenu, $"Rubber Duck", checkable = true, disabled = true;
1144          bool NotifySelect(MenuItem selection, Modifiers mods)
1145          {
1146             if(selection.checked)
1147                duck.Create();
1148             else
1149                duck.Destroy(0);
1150             return true;
1151          }
1152       };
1153 #ifndef __WIN32__
1154       MenuDivider { debugMenu };
1155       MenuItem debugUseValgrindItem
1156       {
1157          debugMenu, $"Use Valgrind", d, disabled = true, checkable = true;
1158          bool NotifySelect(MenuItem selection, Modifiers mods)
1159          {
1160             if(ide.workspace)
1161             {
1162                ide.workspace.useValgrind = selection.checked;
1163                ide.workspace.Save();
1164             }
1165             ide.AdjustValgrindMenus();
1166             return true;
1167          }
1168       };
1169       Menu debugValgrindLeakCheckItem { debugMenu, $"Valgrind Leak Check", h };
1170          MenuItem debugValgrindNoLeakCheckItem      { debugValgrindLeakCheckItem, $"No"     , f, id = ValgrindLeakCheck::no     , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; };
1171          MenuItem debugValgrindSummaryLeakCheckItem { debugValgrindLeakCheckItem, $"Summary", f, id = ValgrindLeakCheck::summary, checkable = true, disabled = true; NotifySelect = ValgrindLCSelect, checked = true; };
1172          MenuItem debugValgrindYesLeakCheckItem     { debugValgrindLeakCheckItem, $"Yes"    , f, id = ValgrindLeakCheck::yes    , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; };
1173          MenuItem debugValgrindFullLeakCheckItem    { debugValgrindLeakCheckItem, $"Full"   , f, id = ValgrindLeakCheck::full   , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; };
1174          bool ValgrindLCSelect(MenuItem selection, Modifiers mods)
1175          {
1176             if(ide.workspace)
1177             {
1178                if(selection.checked)
1179                {
1180                   ValgrindLeakCheck vgLeakCheck = (ValgrindLeakCheck)selection.id;
1181
1182                   debugValgrindNoLeakCheckItem.checked      = debugValgrindNoLeakCheckItem.id      == vgLeakCheck;
1183                   debugValgrindSummaryLeakCheckItem.checked = debugValgrindSummaryLeakCheckItem.id == vgLeakCheck;
1184                   debugValgrindYesLeakCheckItem.checked     = debugValgrindYesLeakCheckItem.id     == vgLeakCheck;
1185                   debugValgrindFullLeakCheckItem.checked    = debugValgrindFullLeakCheckItem.id    == vgLeakCheck;
1186
1187                   ide.workspace.vgLeakCheck = vgLeakCheck;
1188                   ide.workspace.Save();
1189                }
1190                else
1191                   selection.checked = true;
1192             }
1193             return true;
1194          }
1195       Menu debugValgrindRedzoneSizeItem { debugMenu, $"Valgrind Redzone Size", z };
1196          MenuItem debugValgrindRSDefaultItem { debugValgrindRedzoneSizeItem, $"Default", f, id =  -1, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect, checked = true; };
1197          MenuItem debugValgrindRS0Item       { debugValgrindRedzoneSizeItem, "0"      , f, id =   0, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
1198          MenuItem debugValgrindRS16Item      { debugValgrindRedzoneSizeItem, "16"     , f, id =  16, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
1199          MenuItem debugValgrindRS32Item      { debugValgrindRedzoneSizeItem, "32"     , f, id =  32, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
1200          MenuItem debugValgrindRS64Item      { debugValgrindRedzoneSizeItem, "64"     , f, id =  64, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
1201          MenuItem debugValgrindRS128Item     { debugValgrindRedzoneSizeItem, "128"    , f, id = 128, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
1202          MenuItem debugValgrindRS256Item     { debugValgrindRedzoneSizeItem, "256"    , f, id = 256, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
1203          MenuItem debugValgrindRS512Item     { debugValgrindRedzoneSizeItem, "512"    , f, id = 512, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
1204          bool ValgrindRSSelect(MenuItem selection, Modifiers mods)
1205          {
1206             if(ide.workspace)
1207             {
1208                if(selection.checked)
1209                {
1210                   int vgRedzoneSize = (int)selection.id;
1211
1212                   debugValgrindRSDefaultItem.checked = debugValgrindRSDefaultItem.id == vgRedzoneSize;
1213                   debugValgrindRS0Item.checked       = debugValgrindRS0Item.id       == vgRedzoneSize;
1214                   debugValgrindRS16Item.checked      = debugValgrindRS16Item.id      == vgRedzoneSize;
1215                   debugValgrindRS32Item.checked      = debugValgrindRS32Item.id      == vgRedzoneSize;
1216                   debugValgrindRS64Item.checked      = debugValgrindRS64Item.id      == vgRedzoneSize;
1217                   debugValgrindRS128Item.checked     = debugValgrindRS128Item.id     == vgRedzoneSize;
1218                   debugValgrindRS256Item.checked     = debugValgrindRS256Item.id     == vgRedzoneSize;
1219                   debugValgrindRS512Item.checked     = debugValgrindRS512Item.id     == vgRedzoneSize;
1220
1221                   ide.workspace.vgRedzoneSize = vgRedzoneSize;
1222                   ide.workspace.Save();
1223                }
1224                else
1225                   selection.checked = true;
1226             }
1227             return true;
1228          }
1229       MenuItem debugValgrindTrackOriginsItem
1230       {
1231          debugMenu, $"Valgrind Track Origins", k, checkable = true, disabled = true;
1232          bool NotifySelect(MenuItem selection, Modifiers mods)
1233          {
1234             if(ide.workspace)
1235             {
1236                ide.workspace.vgTrackOrigins = selection.checked;
1237                ide.workspace.Save();
1238             }
1239             return true;
1240          }
1241       };
1242 #endif
1243       MenuDivider { debugMenu };
1244       MenuItem debugStepIntoItem
1245       {
1246          debugMenu, $"Step Into", i, f11, disabled = true;
1247          bitmap = { ":actions/stepInto.png" };
1248          bool NotifySelect(MenuItem selection, Modifiers mods)
1249          {
1250             if(projectView) projectView.DebugStepInto();
1251             return true;
1252          }
1253       };
1254       MenuItem debugStepOverItem
1255       {
1256          debugMenu, $"Step Over", v, f10, disabled = true;
1257          bitmap = { ":actions/stepOver.png" };
1258          bool NotifySelect(MenuItem selection, Modifiers mods)
1259          {
1260             if(projectView) projectView.DebugStepOver(false);
1261             return true;
1262          }
1263       };
1264       MenuItem debugSkipStepOverItem
1265       {
1266          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
1267          bitmap = { ":actions/stepOverSkipBreak.png" };
1268          bool NotifySelect(MenuItem selection, Modifiers mods)
1269          {
1270             if(projectView) projectView.DebugStepOver(true);
1271             return true;
1272          }
1273       };
1274       MenuItem debugStepOutItem
1275       {
1276          debugMenu, $"Step Out", o, shiftF11, disabled = true;
1277          bitmap = { ":actions/stepOut.png" };
1278          bool NotifySelect(MenuItem selection, Modifiers mods)
1279          {
1280             if(projectView) projectView.DebugStepOut(false);
1281             return true;
1282          }
1283       };
1284       MenuItem debugSkipStepOutItem
1285       {
1286          debugMenu, $"Step Out Skipping Breakpoints", n, Key { f11, ctrl = true, shift = true }, disabled = true;
1287          bitmap = { ":actions/skipBreaks.png" };
1288          bool NotifySelect(MenuItem selection, Modifiers mods)
1289          {
1290             if(projectView) projectView.DebugStepOut(true);
1291             return true;
1292          }
1293       };
1294 #if 0
1295       MenuItem debugStepUntilItem
1296       {
1297          debugMenu, $"Step Over Until Next Line", x, disabled = true;
1298          bool NotifySelect(MenuItem selection, Modifiers mods)
1299          {
1300             if(projectView) projectView.DebugStepUntil(false);
1301             return true;
1302          }
1303       };
1304       MenuItem debugSkipStepUntilItem
1305       {
1306          debugMenu, $"Step Over Until Next Line Skipping Breakpoints", e, Key { f10, shift = true, alt = true }, disabled = true;
1307          bool NotifySelect(MenuItem selection, Modifiers mods)
1308          {
1309             if(projectView) projectView.DebugStepUntil(true);
1310             return true;
1311          }
1312       };
1313 #endif
1314       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
1315       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
1316       MenuPlacement debugRunToCursorAtSameLevelItem { debugMenu, $"Run To Cursor At Same Level", l };
1317       MenuPlacement debugSkipRunToCursorAtSameLevelItem { debugMenu, $"Run To Cursor At Same Level Skipping Breakpoints", g };
1318 #if 0
1319       MenuPlacement debugBpRunToCursorItem { debugMenu, $"BP Run To Cursor" };
1320       MenuPlacement debugBpSkipRunToCursorItem { debugMenu, $"BP Run To Cursor Skipping Breakpoints" };
1321       MenuPlacement debugBpRunToCursorAtSameLevelItem { debugMenu, $"BP Run To Cursor At Same Level" };
1322       MenuPlacement debugBpSkipRunToCursorAtSameLevelItem { debugMenu, $"BP Run To Cursor At Same Level Skipping Breakpoints" };
1323 #endif
1324       //MenuDivider { debugMenu };
1325       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
1326    MenuPlacement imageMenu { menu, $"Image", i };
1327    Menu viewMenu { menu, $"View", v };
1328       MenuItem viewProjectItem
1329       {
1330          viewMenu, $"Project View", j, alt0, disabled = true;
1331          bool NotifySelect(MenuItem selection, Modifiers mods)
1332          {
1333             if(projectView)
1334             {
1335                projectView.visible = true;
1336                projectView.Activate();
1337             }
1338             return true;
1339          }
1340       };
1341       MenuPlacement { viewMenu, $"View Designer" };
1342       MenuPlacement { viewMenu, $"View Code" };
1343       MenuPlacement { viewMenu, $"View Properties" };
1344       MenuPlacement { viewMenu, $"View Methods" };
1345       MenuItem viewDesignerItem
1346       {
1347          viewMenu, $"View Designer", d, f8;
1348          bool NotifySelect(MenuItem selection, Modifiers mods)
1349          {
1350             Window client = activeClient;
1351             Class dataType = client._class;
1352             if(!strcmp(dataType.name, "Designer"))
1353             {
1354                client.visible = true;
1355                client.Activate();
1356             }
1357             else
1358                ((CodeEditor)client).ViewDesigner();
1359             return true;
1360          }
1361       };
1362       MenuItem viewCodeItem
1363       {
1364          viewMenu, $"View Code", c, f8;
1365          bool NotifySelect(MenuItem selection, Modifiers mods)
1366          {
1367             Window client = activeClient;
1368             Class dataType = client._class;
1369             if(!strcmp(dataType.name, "Designer"))
1370                client = ((Designer)client).codeEditor;
1371
1372             client.Activate();
1373             // Do this after so the caret isn't moved yet...
1374             client.visible = true;
1375             return true;
1376          }
1377       };
1378       MenuItem viewPropertiesItem
1379       {
1380          viewMenu, $"View Properties", p, f4;
1381          bool NotifySelect(MenuItem selection, Modifiers mods)
1382          {
1383             sheet.visible = true;
1384             sheet.sheetSelected = properties;
1385             sheet.Activate();
1386             return true;
1387          }
1388       };
1389       MenuItem viewMethodsItem
1390       {
1391          viewMenu, $"View Methods", m, f4;
1392          bool NotifySelect(MenuItem selection, Modifiers mods)
1393          {
1394             sheet.visible = true;
1395             sheet.sheetSelected = methods;
1396             sheet.Activate();
1397             return true;
1398          }
1399       };
1400       MenuItem viewToolBoxItem
1401       {
1402          viewMenu, $"View Toolbox", x, f12;
1403          bool NotifySelect(MenuItem selection, Modifiers mods)
1404          {
1405             toolBox.visible = true;
1406             toolBox.Activate();
1407             return true;
1408          }
1409       };
1410       MenuItem viewOutputItem
1411       {
1412          viewMenu, $"Output", o, alt2;
1413          bool NotifySelect(MenuItem selection, Modifiers mods)
1414          {
1415             outputView.Show();
1416             return true;
1417          }
1418       };
1419       MenuItem viewWatchesItem
1420       {
1421          viewMenu, $"Watches", w, alt3;
1422          bool NotifySelect(MenuItem selection, Modifiers mods)
1423          {
1424             watchesView.Show();
1425             return true;
1426          }
1427       };
1428       MenuItem viewThreadsItem
1429       {
1430          viewMenu, $"Threads", t, alt4;
1431          bool NotifySelect(MenuItem selection, Modifiers mods)
1432          {
1433             threadsView.Show();
1434             return true;
1435          }
1436       };
1437       MenuItem viewBreakpointsItem
1438       {
1439          viewMenu, $"Breakpoints", b, alt5;
1440          bool NotifySelect(MenuItem selection, Modifiers mods)
1441          {
1442             breakpointsView.Show();
1443             return true;
1444          }
1445       };
1446       MenuItem viewCallStackItem
1447       {
1448          viewMenu, $"Call Stack", s, alt7;
1449          bool NotifySelect(MenuItem selection, Modifiers mods)
1450          {
1451             callStackView.Show();
1452             return true;
1453          }
1454       };
1455       MenuItem viewAllDebugViews
1456       {
1457          viewMenu, $"All Debug Views", a, alt9;
1458          bool NotifySelect(MenuItem selection, Modifiers mods)
1459          {
1460             outputView.Show();
1461             watchesView.Show();
1462             threadsView.Show();
1463             callStackView.Show();
1464             breakpointsView.Show();
1465             return true;
1466          }
1467       };
1468 #ifdef GDB_DEBUG_GUI
1469       MenuDivider { viewMenu };
1470       MenuItem viewGDBItem
1471       {
1472          viewMenu, $"GDB Dialog", g, Key { f9, shift = true, ctrl = true };
1473          bool NotifySelect(MenuItem selection, Modifiers mods)
1474          {
1475             gdbDialog.Show();
1476             return true;
1477          }
1478       };
1479 #endif
1480       MenuDivider { viewMenu };
1481       MenuItem viewColorPicker
1482       {
1483          viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
1484          bool NotifySelect(MenuItem selection, Modifiers mods)
1485          {
1486             ColorPicker colorPicker { master = this };
1487             colorPicker.Modal();
1488             return true;
1489          }
1490       };
1491       MenuDivider { viewMenu };
1492       /*
1493       MenuItem
1494       {
1495          viewMenu, "Full Screen", f, checkable = true;
1496
1497          bool NotifySelect(MenuItem selection, Modifiers mods)
1498          {
1499             app.fullScreen ^= true;
1500             SetDriverAndSkin();
1501             anchor = { 0, 0, 0, 0 };
1502             return true;
1503          }
1504       };
1505       */
1506       Menu driversMenu { viewMenu, $"Graphics Driver", v };
1507
1508       MenuDivider { viewMenu };
1509
1510       Menu languageMenu { viewMenu, "Language", l };
1511
1512       //Menu skinsMenu { viewMenu, "GUI Skins", k };
1513    Menu windowMenu { menu, $"Window", w };
1514       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
1515       MenuDivider { windowMenu };
1516       MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext };
1517       MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious };
1518       MenuDivider { windowMenu };
1519       MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade };
1520       MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz };
1521       MenuItem { windowMenu, $"Tile Vertically", v, NotifySelect = MenuWindowTileVert };
1522       MenuItem { windowMenu, $"Arrange Icons", a, NotifySelect = MenuWindowArrangeIcons };
1523       MenuDivider { windowMenu };
1524       MenuItem { windowMenu, $"Windows...", w, NotifySelect = MenuWindowWindows };
1525    Menu helpMenu { menu, $"Help", h };
1526       MenuItem
1527       {
1528          helpMenu, $"API Reference", r, f1;
1529          bool NotifySelect(MenuItem selection, Modifiers mods)
1530          {
1531             if(!documentor)
1532             {
1533                char * p = new char[MAX_LOCATION];
1534                p[0] = '\0';
1535                strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
1536                PathCat(p, "documentor");
1537    #if defined(__WIN32__)
1538                ChangeExtension(p, "exe", p);
1539    #endif
1540                if(!FileExists(p).isFile)
1541                   strcpy(p, "documentor");
1542
1543                documentor = DualPipeOpen({ input = true, output = true, showWindow = true }, p);
1544                delete p;
1545             }
1546             else
1547             {
1548                Process_ShowWindows(documentor.GetProcessID());
1549                // documentor.Puts("Activate\n");
1550             }
1551             return true;
1552          }
1553       };
1554       MenuDivider { helpMenu };
1555       MenuItem
1556       {
1557          helpMenu, $"Ecere Tao of Programming [work in progress]", t;
1558          bool NotifySelect(MenuItem selection, Modifiers mods)
1559          {
1560             FindAndShellOpenInstalledFile("doc", "Ecere Tao of Programming [work in progress].pdf");
1561             return true;
1562          }
1563       };
1564       MenuDivider { helpMenu };
1565       MenuItem
1566       {
1567          helpMenu, $"Documentation Folder", d;
1568          bool NotifySelect(MenuItem selection, Modifiers mods)
1569          {
1570             FindAndShellOpenInstalledFolder("doc");
1571             return true;
1572          }
1573       };
1574       MenuItem
1575       {
1576          helpMenu, $"Samples Folder", s;
1577          bool NotifySelect(MenuItem selection, Modifiers mods)
1578          {
1579             FindAndShellOpenInstalledFolder("samples");
1580             return true;
1581          }
1582       };
1583       MenuItem
1584       {
1585          helpMenu, $"Extras Folder", x;
1586          bool NotifySelect(MenuItem selection, Modifiers mods)
1587          {
1588             FindAndShellOpenInstalledFolder("extras");
1589             return true;
1590          }
1591       };
1592       MenuDivider { helpMenu };
1593       MenuItem
1594       {
1595          helpMenu, $"Community Forums", f;
1596          bool NotifySelect(MenuItem selection, Modifiers mods)
1597          {
1598             ShellOpen("http://ecere.com/forums");
1599             return true;
1600          }
1601       };
1602       MenuDivider { helpMenu };
1603       MenuItem
1604       {
1605          helpMenu, $"About...", a;
1606          bool NotifySelect(MenuItem selection, Modifiers mods)
1607          {
1608             AboutIDE { master = this }.Modal();
1609             return true;
1610          }
1611       };
1612
1613    property ToolBox toolBox
1614    {
1615       get { return toolBox; }
1616    }
1617
1618    property Sheet sheet
1619    {
1620       get { return sheet; }
1621    }
1622
1623    property Project project
1624    {
1625       get { return projectView ? projectView.project : null; }
1626    }
1627
1628    property Workspace workspace
1629    {
1630       get { return projectView ? projectView.workspace : null; }
1631    }
1632
1633    FindInFilesDialog findInFilesDialog
1634    {
1635       master = this,
1636       filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter);
1637       filter = 1;
1638    };
1639
1640    bool noParsing;
1641    bool debugStart;
1642
1643 #ifdef GDB_DEBUG_GUI
1644    GDBDialog gdbDialog
1645    {
1646       master = this, parent = this;
1647       //anchor = { left = 100, top = 100, right = 100, bottom = 100 };
1648
1649       void OnCommand(const char * string)
1650       {
1651          if(ide)
1652             ide.debugger.SendGDBCommand(string);
1653       }
1654    };
1655 #endif
1656
1657    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
1658    {
1659       //app.driver = app.drivers[selection.id];
1660 #if defined(__unix__) || defined(__APPLE__)
1661       app.driver = selection.id ? "OpenGL" : "X";
1662 #else
1663       app.driver = selection.id ? "OpenGL" : "GDI";
1664 #endif
1665       delete ideSettings.displayDriver;
1666       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
1667
1668       ide.debugRubberDuck.disabled = !ide.duck.modelFile || strcmpi(app.driver, "OpenGL");
1669
1670       settingsContainer.Save();
1671       //SetDriverAndSkin();
1672       return true;
1673    }
1674
1675    bool NotifySelectDisplaySkin(MenuItem selection, Modifiers mods)
1676    {
1677       app.skin = app.skins[selection.id];
1678       SetDriverAndSkin();
1679       return true;
1680    }
1681
1682    void SetDriverAndSkin()
1683    {
1684       int c;
1685       for(c = 0; c < app.numSkins; c++)
1686          if(!strcmp(app.skins[c], app.skin))
1687          {
1688             skinItems[c].checked = true;
1689             break;
1690          }
1691       for(c = 0; c < app.numDrivers; c++)
1692          if(!strcmp(app.drivers[c], app.driver))
1693          {
1694             driverItems[c].checked = true;
1695             break;
1696          }
1697    }
1698
1699    ProjectView CreateProjectView(Workspace workspace, const char * fileName)
1700    {
1701       Project project = workspace.projects.firstIterator.data;
1702       projectView = ProjectView
1703       {
1704          this;
1705          fileName = fileName;
1706
1707          void NotifyDestroyed(Window window, DialogResult result)
1708          {
1709             projectView = null;
1710             text = titleECEREIDE;
1711
1712             AdjustMenus();
1713          }
1714       };
1715       projectView.Create();
1716       RepositionWindows(false);
1717
1718       // Leave it after Create to avoid flicker due to seeing IDE without a project view
1719       projectView.workspace = workspace;
1720       projectView.project = project;
1721       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
1722
1723       AdjustMenus();
1724
1725       ide.breakpointsView.LoadFromWorkspace();
1726       ide.watchesView.LoadFromWorkspace();
1727
1728       findInFilesDialog.projectNodeField.userData = projectView;
1729
1730       {
1731          char fileName[MAX_LOCATION];
1732          strcpy(fileName, project.topNode.path);
1733          PathCat(fileName, project.topNode.name);
1734       }
1735       return projectView;
1736    }
1737
1738    bool ProjectClose()
1739    {
1740       projectView.visible = false;
1741       if((!projectView || projectView.created == false || projectView.Destroy(0)) && MenuWindowCloseAll(null, 0))
1742       {
1743          if(findInFilesDialog)
1744          {
1745             char workingDir[MAX_LOCATION];
1746             GetWorkingDir(workingDir, MAX_LOCATION);
1747             findInFilesDialog.SearchStop();
1748             findInFilesDialog.currentDirectory = workingDir;
1749          }
1750          sheet.visible = false;
1751          toolBox.visible = false;
1752          outputView.visible = false;
1753          ideMainFrame.text = titleECEREIDE;
1754          ide.AdjustMenus();
1755          return true;
1756       }
1757       return false;
1758    }
1759
1760    void RepositionWindows(bool expand)
1761    {
1762       if(this)
1763       {
1764          Window child;
1765          bool callStackVisible = expand ? false : callStackView.visible;
1766          bool threadsVisible = expand ? false : threadsView.visible;
1767          bool watchesVisible = expand ? false : watchesView.visible;
1768          bool breakpointsVisible = expand ? false : breakpointsView.visible;
1769          bool toolBoxVisible = toolBox.visible;
1770          bool outputVisible = expand ? false : outputView.visible;
1771          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
1772          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
1773
1774          for(child = firstChild; child; child = child.next)
1775          {
1776             if(child._class == class(CodeEditor) || child._class == class(Designer) ||
1777                child._class == class(Sheet) || child._class == class(ProjectView))
1778             {
1779                Anchor anchor = child.anchor;
1780                anchor.top = topDistance;
1781                anchor.bottom = bottomDistance;
1782                if(child._class == class(CodeEditor) || child._class == class(Designer))
1783                {
1784                   anchor.left = (sheet.visible || (projectView && projectView.visible)) ? 300 : 0;
1785                   anchor.right = toolBoxVisible ? 150 : 0;
1786                }
1787                if(ide.projectView)
1788                   child.anchor = anchor;
1789             }
1790             else if(expand)
1791             {
1792                if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) ||
1793                   child._class == class(BreakpointsView))
1794                   child.visible = false;
1795             }
1796          }
1797          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
1798          Update(null);
1799          if(duck.visible) duck.Update(null);   // TOFIX: If this is not here, the duck disappears -- Why?
1800       }
1801    }
1802
1803    bool ShowCodeEditor()
1804    {
1805       if(activeClient)
1806          activeClient.Activate();
1807       else if(projectView)
1808       {
1809          projectView.visible = true;
1810          projectView.Activate();
1811       }
1812       else if(sheet.visible)
1813          sheet.Activate();
1814       else
1815          outputView.visible = false;
1816       return false;
1817    }
1818
1819    void DocumentSaved(Window document, const char * fileName)
1820    {
1821       ideSettings.AddRecentFile(fileName);
1822       ide.UpdateRecentMenus();
1823       ide.AdjustFileMenus();
1824       settingsContainer.Save();
1825    }
1826
1827    bool Window::OnFileModified(FileChange fileChange, const char * param)
1828    {
1829       char temp[4096];
1830       sprintf(temp, $"The document %s was modified by another application.\n"
1831             "Would you like to reload it and lose your changes?", this.fileName);
1832       if(MessageBox { type = yesNo, master = this/*.parent*/,
1833             text = $"Document has been modified", contents = temp }.Modal() == yes)
1834       {
1835          bool noParsing = (this._class == class(CodeEditor) && ((CodeEditor)this).noParsing) ? true : false;
1836          char * fileName = CopyString(this.fileName);
1837          WindowState state = this.state;
1838          Anchor anchor = this.anchor;
1839          Size size = this.size;
1840
1841          this.modifiedDocument = false;
1842          this.Destroy(0);
1843          this = ide.OpenFile(fileName, false, true, null, no, normal, noParsing);
1844          if(this)
1845          {
1846             this.anchor = anchor;
1847             this.size = size;
1848             this.SetState(state, true, 0);
1849          }
1850          delete fileName;
1851          return true;
1852       }
1853       return true;
1854    }
1855
1856    void UpdateMakefiles()
1857    {
1858       if(workspace)
1859       {
1860          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1861          for(prj : workspace.projects)
1862             projectView.ProjectUpdateMakefileForAllConfigs(prj);
1863          delete compiler;
1864       }
1865    }
1866
1867    void UpdateCompilerConfigs(bool mute)
1868    {
1869       UpdateToolBarActiveCompilers();
1870       if(workspace)
1871       {
1872          bool silent = mute || (ide.projectView.buildInProgress == none ? false : true);
1873          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1874          if(!silent)
1875          {
1876             projectView.ShowOutputBuildLog(true);
1877             projectView.DisplayCompiler(compiler, false);
1878          }
1879          for(prj : workspace.projects)
1880             projectView.ProjectPrepareCompiler(prj, compiler, silent);
1881          delete compiler;
1882       }
1883    }
1884
1885    void UpdateToolBarActiveCompilers()
1886    {
1887       toolBar.activeCompiler.Clear();
1888       for(compiler : ideSettings.compilerConfigs)
1889       {
1890          DataRow row = toolBar.activeCompiler.AddString(compiler.name);
1891          if(workspace && workspace.compiler && !strcmp(compiler.name, workspace.compiler))
1892             toolBar.activeCompiler.currentRow = row;
1893       }
1894       if(!toolBar.activeCompiler.currentRow && toolBar.activeCompiler.firstRow)
1895          toolBar.activeCompiler.SelectRow(toolBar.activeCompiler.firstRow);
1896       toolBar.activeBitDepth.SelectRow(toolBar.activeBitDepth.FindRow(workspace ? workspace.bitDepth : 0));
1897    }
1898
1899    void UpdateToolBarActiveConfigs(bool selectionOnly)
1900    {
1901       bool commonSelected = false;
1902       DataRow row = toolBar.activeConfig.currentRow;
1903       if(selectionOnly)
1904          row = toolBar.activeConfig.FindRow(1);
1905       else
1906       {
1907          toolBar.activeConfig.Clear();
1908          row = toolBar.activeConfig.AddString($"(Mixed)");
1909          row.tag = 1;
1910       }
1911       if(workspace)
1912       {
1913          char * configName = null;
1914          if(!selectionOnly)
1915          {
1916             Map<String, int> configs { }; // TOIMP: just need sort but using map until containers have sort
1917             for(prj : workspace.projects)
1918             {
1919                for(cfg : prj.configurations)
1920                {
1921                   if(cfg.name)
1922                      configs[cfg.name] = 1;
1923                }
1924             }
1925             for(name : configs)
1926             {
1927                toolBar.activeConfig.AddString(&name);
1928             }
1929             delete configs;
1930          }
1931          if(projectView && projectView.project)
1932          {
1933             for(prj : workspace.projects)
1934             {
1935                if(prj.config && prj.config.name)
1936                {
1937                   configName = prj.config.name;
1938                   break;
1939                }
1940             }
1941             if(configName)
1942             {
1943                commonSelected = true;
1944                for(prj : workspace.projects)
1945                {
1946                   if(prj.config && (!prj.config.name || strcmp(prj.config.name, configName)))
1947                   {
1948                      commonSelected = false;
1949                      break;
1950                   }
1951                }
1952             }
1953          }
1954          if(commonSelected)
1955          {
1956             commonSelected = false;
1957             for(row = toolBar.activeConfig.firstRow; row; row = row.next)
1958             {
1959                if(!strcmp(row.string, configName))
1960                {
1961                   toolBar.activeConfig.currentRow = row;
1962                   commonSelected = true;
1963                   break;
1964                }
1965             }
1966          }
1967       }
1968       if(!selectionOnly)
1969          toolBar.activeConfig.Sort(null, 0);
1970       if(!commonSelected)
1971          toolBar.activeConfig.currentRow = row;
1972    }
1973
1974    void AdjustMenus()
1975    {
1976       bool unavailable = !project;
1977
1978       projectAddItem.disabled             = unavailable;
1979       toolBar.buttonAddProject.disabled   = unavailable;
1980
1981       projectSettingsItem.disabled        = unavailable;
1982
1983       projectBrowseFolderItem.disabled    = unavailable;
1984
1985       viewProjectItem.disabled            = unavailable;
1986
1987       toolBar.activeConfig.disabled       = unavailable;
1988       toolBar.activeCompiler.disabled     = unavailable;
1989       toolBar.activeBitDepth.disabled     = unavailable;
1990
1991 #ifndef __WIN32__
1992       debugUseValgrindItem.disabled       = unavailable;
1993       AdjustValgrindMenus();
1994 #endif
1995
1996       AdjustFileMenus();
1997       AdjustBuildMenus();
1998       AdjustDebugMenus();
1999    }
2000
2001 #ifndef __WIN32__
2002    void AdjustValgrindMenus()
2003    {
2004       bool unavailable = !project || !debugUseValgrindItem.checked;
2005       debugValgrindNoLeakCheckItem.disabled        = unavailable;
2006       debugValgrindSummaryLeakCheckItem.disabled   = unavailable;
2007       debugValgrindYesLeakCheckItem.disabled       = unavailable;
2008       debugValgrindFullLeakCheckItem.disabled      = unavailable;
2009
2010       debugValgrindTrackOriginsItem.disabled       = unavailable;
2011
2012       debugValgrindRSDefaultItem.disabled          = unavailable;
2013       debugValgrindRS0Item.disabled                = unavailable;
2014       debugValgrindRS16Item.disabled               = unavailable;
2015       debugValgrindRS32Item.disabled               = unavailable;
2016       debugValgrindRS64Item.disabled               = unavailable;
2017       debugValgrindRS128Item.disabled              = unavailable;
2018       debugValgrindRS256Item.disabled              = unavailable;
2019       debugValgrindRS512Item.disabled              = unavailable;
2020    }
2021 #endif
2022
2023    property bool hasOpenedCodeEditors
2024    {
2025       get
2026       {
2027          Window w;
2028          for(w = firstChild; w; w = w.next)
2029             if(w._class == class(CodeEditor) &&
2030                   w.isDocument && !w.closing && w.visible && w.created &&
2031                   w.fileName && w.fileName[0])
2032                return true;
2033          return false;
2034       }
2035    }
2036
2037    void AdjustFileMenus()
2038    {
2039       bool unavailable = project != null || !hasOpenedCodeEditors; // are they supported source code (ec, c, cpp, etc) ?
2040
2041       projectQuickItem.disabled           = unavailable;
2042    }
2043
2044    void AdjustBuildMenus()
2045    {
2046       bool unavailable = project && projectView.buildInProgress;
2047       bool naForRun = unavailable || !project || project.GetTargetType(project.config) != executable;
2048
2049       projectNewItem.disabled             = unavailable;
2050       toolBar.buttonNewProject.disabled   = unavailable;
2051       projectOpenItem.disabled            = unavailable;
2052       toolBar.buttonOpenProject.disabled  = unavailable;
2053
2054       unavailable = !project || projectView.buildInProgress;
2055
2056       projectCloseItem.disabled           = unavailable;
2057       // toolBar.buttonCloseProject.disabled = unavailable;
2058
2059       projectRunItem.disabled    = naForRun;
2060       toolBar.buttonRun.disabled = naForRun;
2061
2062       projectBuildItem.disabled = false;
2063       projectBuildItem.text     = unavailable ? $"Stop Build" : $"Build";
2064       projectBuildItem.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
2065
2066       projectLinkItem.disabled                  = unavailable;
2067       toolBar.buttonReLink.disabled             = unavailable;
2068       projectRebuildItem.disabled               = unavailable;
2069       toolBar.buttonRebuild.disabled            = unavailable;
2070       projectCleanItem.disabled                 = unavailable;
2071       toolBar.buttonClean.disabled              = unavailable;
2072       projectCleanTargetItem.disabled           = unavailable;
2073       projectRealCleanItem.disabled             = unavailable;
2074       // toolBar.buttonRealClean.disabled          = unavailable;
2075       projectRegenerateItem.disabled            = unavailable;
2076       toolBar.buttonRegenerateMakefile.disabled = unavailable;
2077 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
2078       projectInstallItem.disabled               = unavailable;
2079       toolBar.buttonInstall.disabled            = unavailable;
2080 #endif
2081       projectCompileItem.disabled               = unavailable;
2082
2083       AdjustPopupBuildMenus();
2084    }
2085
2086    void AdjustPopupBuildMenus()
2087    {
2088       bool unavailable = !project || projectView.buildInProgress;
2089
2090       if(projectView && projectView.popupMenu && projectView.popupMenu.menu && projectView.popupMenu.created)
2091       {
2092          MenuItem menu;
2093          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectBuild, 0);
2094          if(menu)
2095          {
2096             menu.disabled = false;
2097             menu.text   = unavailable ? $"Stop Build" : $"Build";
2098             menu.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
2099          }
2100
2101          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectLink, 0);              if(menu) menu.disabled = unavailable;
2102          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRebuild, 0);           if(menu) menu.disabled = unavailable;
2103          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectCleanTarget, 0);       if(menu) menu.disabled = unavailable;
2104          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, 0);             if(menu) menu.disabled = unavailable;
2105          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, 0);         if(menu) menu.disabled = unavailable;
2106          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, 0);        if(menu) menu.disabled = unavailable;
2107          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectInstall, 0);           if(menu) menu.disabled = unavailable;
2108          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRemove, 0);            if(menu) menu.disabled = unavailable;
2109          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileClean, 0);                if(menu) menu.disabled = unavailable;
2110          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileCompile, 0);              if(menu) menu.disabled = unavailable;
2111          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugPrecompile, 0);      if(menu) menu.disabled = unavailable;
2112          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugCompile, 0);         if(menu) menu.disabled = unavailable;
2113          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugGenerateSymbols, 0); if(menu) menu.disabled = unavailable;
2114          projectView.popupMenu.Update(null);
2115       }
2116    }
2117
2118    property bool areDebugMenusUnavailable { get {
2119       return !project ||
2120             project.GetTargetType(project.config) != executable ||
2121             projectView.buildInProgress == buildingMainProject;
2122    } }
2123
2124    property bool isBreakpointTogglingUnavailable { get { return !project; } }
2125    property bool isDebuggerRunning { get { if(ide.debugger) return ide.debugger.state == running; return false; } }
2126    property bool isDebuggerStopped { get { if(ide.debugger) return ide.debugger.state == stopped; return false; } }
2127
2128    void AdjustDebugMenus()
2129    {
2130       bool unavailable = areDebugMenusUnavailable;
2131       bool running = isDebuggerRunning;
2132       bool stopped = isDebuggerStopped;
2133       bool active = debugger.isActive;
2134
2135       bool isNotRunning    = unavailable || !running;
2136       bool isNotNotRunning = unavailable || running;
2137       bool isNotStopped    = unavailable || !stopped;
2138       bool isNotActive     = unavailable || !active;
2139
2140       debugStartResumeItem.disabled       = isNotNotRunning;
2141       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
2142       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
2143       if(toolBar)
2144       {
2145          toolBar.buttonDebugStartResume.disabled      = isNotNotRunning;
2146          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
2147       }
2148
2149       debugBreakItem.disabled             = isNotRunning;
2150       debugStopItem.disabled              = isNotActive;
2151       debugRestartItem.disabled           = isNotActive;
2152       if(toolBar)
2153       {
2154          toolBar.buttonDebugPause.disabled            = isNotRunning;
2155          toolBar.buttonDebugStop.disabled             = isNotActive;
2156          toolBar.buttonDebugRestart.disabled          = isNotActive;
2157       }
2158
2159       debugStepIntoItem.disabled          = isNotNotRunning;
2160       debugStepOverItem.disabled          = isNotNotRunning;
2161       debugSkipStepOverItem.disabled      = isNotNotRunning;
2162       debugStepOutItem.disabled           = isNotStopped;
2163       debugSkipStepOutItem.disabled       = isNotStopped;
2164 #if 0
2165       debugStepUntilItem.disabled         = isNotStopped;
2166       debugSkipStepUntilItem.disabled     = isNotStopped;
2167 #endif
2168       if(toolBar)
2169       {
2170          toolBar.buttonDebugStepInto.disabled         = isNotNotRunning;
2171          toolBar.buttonDebugStepOver.disabled         = isNotNotRunning;
2172          toolBar.buttonDebugSkipStepOver.disabled     = isNotNotRunning;
2173          toolBar.buttonDebugStepOut.disabled          = isNotStopped;
2174          //toolBar.buttonDebugSkipStepOutItem.disabled  = isNotNotRunning;
2175       }
2176       if((Designer)GetActiveDesigner())
2177       {
2178          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
2179          if(codeEditor)
2180             codeEditor.AdjustDebugMenus();
2181       }
2182    }
2183
2184    void ChangeFileDialogsDirectory(const char * directory, bool saveSettings)
2185    {
2186       char tempString[MAX_LOCATION];
2187       strcpy(tempString, directory);
2188       if(saveSettings && !projectView)
2189       {
2190          ideSettings.ideFileDialogLocation = directory;
2191          settingsContainer.Save();
2192       }
2193
2194       ideFileDialog.currentDirectory = tempString;
2195       codeEditorFileDialog.currentDirectory = tempString;
2196       codeEditorFormFileDialog.currentDirectory = tempString;
2197    }
2198
2199    void ChangeProjectFileDialogDirectory(char * directory)
2200    {
2201       ideSettings.ideProjectFileDialogLocation = directory;
2202       settingsContainer.Save();
2203    }
2204
2205    Window FindWindow(const char * filePath)
2206    {
2207       Window document = null;
2208
2209       // TOCHECK: Do we need to change slashes here?
2210       for(document = firstChild; document; document = document.next)
2211       {
2212          const char * fileName = document.fileName;
2213          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
2214          {
2215             document.visible = true;
2216             document.Activate();
2217             return document;
2218          }
2219       }
2220       return null;
2221    }
2222
2223    bool DontTerminateDebugSession(const char * title)
2224    {
2225       if(debugger.isActive)
2226       {
2227          if(MessageBox { type = yesNo, master = ide,
2228                            contents = $"Do you want to terminate the debugging session in progress?",
2229                            text = title }.Modal() == no)
2230             return true;
2231          /*
2232          MessageBox msg { type = yesNo, master = ide,
2233                            contents = "Do you want to terminate the debugging session in progress?",
2234                            text = title };
2235          if(msg.Modal() == no)
2236          {
2237             msg.Destroy(0);
2238             delete msg;
2239             return true;
2240          }
2241          msg.Destroy(0);
2242          delete msg;*/
2243       }
2244       return false;
2245    }
2246
2247    Window OpenFile(const char * origFilePath, bool dontMaximize, bool visible, const char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
2248    {
2249       char extension[MAX_EXTENSION] = "";
2250       Window document = null;
2251       bool isProject = false;
2252       bool needFileModified = true;
2253       char winFilePath[MAX_LOCATION];
2254       const char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
2255       Window currentDoc = activeClient;
2256       bool maximizeDoc = !dontMaximize && ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView));
2257       if(!type)
2258       {
2259          GetExtension(filePath, extension);
2260          strlwr(extension);
2261       }
2262       else
2263          strcpy(extension, type);
2264
2265       if(strcmp(extension, ProjectExtension))
2266       {
2267          for(document = firstChild; document; document = document.next)
2268          {
2269             const char * fileName = document.fileName;
2270             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
2271             {
2272                document.visible = true;
2273                if(visible)
2274                   document.Activate();
2275                return document;
2276             }
2277          }
2278       }
2279
2280       if(createIfFails == whatever)
2281          ;
2282       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
2283       {
2284          needFileModified = false;
2285          if(openMethod == normal)
2286          {
2287             if(DontTerminateDebugSession($"Open Project"))
2288                return null;
2289             isProject = true;
2290             if(ProjectClose())
2291             {
2292                if(!projectView)
2293                {
2294                   for(;;)
2295                   {
2296                      Workspace workspace = null;
2297
2298                      if(FileExists(filePath))
2299                      {
2300                         if(!strcmp(extension, ProjectExtension))
2301                         {
2302                            char workspaceFile[MAX_LOCATION];
2303                            strcpy(workspaceFile, filePath);
2304                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
2305                            workspace = LoadWorkspace(workspaceFile, filePath);
2306                         }
2307                         else if(!strcmp(extension, WorkspaceExtension))
2308                            workspace = LoadWorkspace(filePath, null);
2309                         else
2310                            return null;
2311                      }
2312
2313                      if(workspace)
2314                      {
2315                         CreateProjectView(workspace, filePath);
2316                         document = projectView;
2317
2318                         toolBox.visible = true;
2319                         sheet.visible = true;
2320                         projectView.MakeActive();
2321
2322                         workspace.ParseLoadedBreakpoints();
2323                         workspace.DropInvalidBreakpoints(null);
2324                         workspace.Save();
2325
2326                         ide.projectView.ShowOutputBuildLog(true);
2327                         {
2328                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
2329                            ide.projectView.DisplayCompiler(compiler, false);
2330                            delete compiler;
2331                         }
2332                         UpdateCompilerConfigs(false);
2333                         UpdateMakefiles();
2334                         {
2335                            char newWorkingDir[MAX_LOCATION];
2336                            StripLastDirectory(filePath, newWorkingDir);
2337                            ChangeFileDialogsDirectory(newWorkingDir, false);
2338                         }
2339                         if(document)
2340                            document.fileName = filePath;
2341
2342                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
2343
2344                         // this crashes on starting ide with epj file, solution please?
2345                         // app.UpdateDisplay();
2346
2347                         workspace.holdTracking = true;
2348                         for(ofi : workspace.openedFiles)
2349                         {
2350                            if(ofi.state != closed)
2351                            {
2352                               Window file = OpenFile(ofi.path, false, true, null, no, normal, noParsing);
2353                               if(file)
2354                               {
2355                                  char fileName[MAX_LOCATION];
2356                                  ProjectNode node;
2357                                  GetLastDirectory(ofi.path, fileName);
2358                                  node = projectView.project.topNode.Find(fileName, true);
2359                                  if(node)
2360                                     node.EnsureVisible();
2361                               }
2362                            }
2363                         }
2364                         ide.RepositionWindows(false);
2365                         workspace.holdTracking = false;
2366
2367                         workspace.timer.Start();
2368
2369 #if !defined(__WIN32__)
2370                         // Valgrind Debug menu updates
2371                         debugUseValgrindItem.checked = workspace.useValgrind;
2372
2373                         debugValgrindNoLeakCheckItem.checked      = workspace.vgLeakCheck == no;
2374                         debugValgrindSummaryLeakCheckItem.checked = workspace.vgLeakCheck == summary;
2375                         debugValgrindYesLeakCheckItem.checked     = workspace.vgLeakCheck == yes;
2376                         debugValgrindFullLeakCheckItem.checked    = workspace.vgLeakCheck == full;
2377
2378                         debugValgrindRSDefaultItem.checked = workspace.vgRedzoneSize == -1;
2379                         debugValgrindRS0Item.checked       = workspace.vgRedzoneSize == 0;
2380                         debugValgrindRS16Item.checked      = workspace.vgRedzoneSize == 16;
2381                         debugValgrindRS32Item.checked      = workspace.vgRedzoneSize == 32;
2382                         debugValgrindRS64Item.checked      = workspace.vgRedzoneSize == 64;
2383                         debugValgrindRS128Item.checked     = workspace.vgRedzoneSize == 128;
2384                         debugValgrindRS256Item.checked     = workspace.vgRedzoneSize == 256;
2385                         debugValgrindRS512Item.checked     = workspace.vgRedzoneSize == 512;
2386
2387                         debugValgrindTrackOriginsItem.checked = workspace.vgTrackOrigins;
2388 #endif
2389
2390                         findInFilesDialog.mode = FindInFilesMode::project;
2391                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
2392
2393                         {
2394                            char location[MAX_LOCATION];
2395                            StripLastDirectory(ide.project.topNode.path, location);
2396                            ChangeProjectFileDialogDirectory(location);
2397                         }
2398
2399                         break;
2400                      }
2401                      else
2402                      {
2403                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
2404                         {
2405                            ideProjectFileDialog.text = openProjectFileDialogTitle;
2406                            if(ideProjectFileDialog.Modal() == cancel)
2407                               return null;
2408                            filePath = ideProjectFileDialog.filePath;
2409                            GetExtension(filePath, extension);
2410                         }
2411                         else
2412                            return null;
2413                      }
2414                   }
2415                }
2416             }
2417             else
2418                return null;
2419          }
2420          else if(openMethod == add)
2421          {
2422             if(workspace)
2423             {
2424                Project prj = null;
2425                char slashFilePath[MAX_LOCATION];
2426                GetSlashPathBuffer(slashFilePath, filePath);
2427                for(p : workspace.projects)
2428                {
2429                   if(!fstrcmp(p.filePath, slashFilePath))
2430                   {
2431                      prj = p;
2432                      break;
2433                   }
2434                }
2435                if(prj)
2436                {
2437                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project",
2438                         contents = $"This project is already present in workspace." }.Modal();
2439                }
2440                else
2441                {
2442                   prj = LoadProject(filePath, null);
2443                   if(prj)
2444                   {
2445                      const char * activeConfigName = null;
2446                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
2447                      prj.StartMonitoring();
2448                      workspace.projects.Add(prj);
2449                      if(toolBar.activeConfig.currentRow && toolBar.activeConfig.currentRow != toolBar.activeConfig.firstRow &&
2450                            toolBar.activeConfig.currentRow.string && toolBar.activeConfig.currentRow.string[0])
2451                         activeConfigName = toolBar.activeConfig.currentRow.string;
2452                      if(activeConfigName)
2453                      {
2454                         for(cfg : prj.configurations)
2455                         {
2456                            if(cfg.name && !strcmp(cfg.name, activeConfigName))
2457                            {
2458                               prj.config = cfg;
2459                               break;
2460                            }
2461                         }
2462                      }
2463                      if(projectView)
2464                         projectView.AddNode(prj.topNode, null);
2465                      workspace.modified = true;
2466                      workspace.Save();
2467                      findInFilesDialog.AddProjectItem(prj);
2468                      projectView.ShowOutputBuildLog(true);
2469                      projectView.DisplayCompiler(compiler, false);
2470                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
2471                      delete compiler;
2472
2473                      {
2474                         char location[MAX_LOCATION];
2475                         StripLastDirectory(prj.topNode.path, location);
2476                         ChangeProjectFileDialogDirectory(location);
2477                      }
2478
2479                      // projectView is associated with the main project and not with the one just added but
2480                      return projectView; // just to let the caller know something was opened
2481                   }
2482                }
2483             }
2484             else
2485                return null;
2486          }
2487       }
2488       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
2489             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
2490             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2491       {
2492          if(FileExists(filePath))
2493             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable,
2494                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state,
2495                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2496                                     };
2497          if(!document)
2498             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2499       }
2500 #ifndef NO3D
2501       else if(!strcmp(extension, "3ds"))
2502       {
2503          if(FileExists(filePath))
2504             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable,
2505                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state,
2506                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2507                                     };
2508
2509          if(!document)
2510             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2511       }
2512 #endif
2513       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2514             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2515             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2516             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2517             !strcmp(extension, "js"))
2518       {
2519          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2520          editor.updatingCode = true;
2521          if(editor.LoadFile(filePath))
2522          {
2523             document = editor;
2524             editor.visible = true;
2525          }
2526          else
2527             delete editor;
2528          needFileModified = false;
2529       }
2530       else
2531       {
2532          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2533          if(editor.LoadFile(filePath))
2534          {
2535             document = editor;
2536             editor.visible = true;
2537          }
2538          else
2539             delete editor;
2540          needFileModified = false;
2541       }
2542
2543       if(document && (document._class == class(PictureEdit) ||
2544             document._class == class(ModelView)))
2545       {
2546          document.Create();
2547          if(document)
2548          {
2549             document.fileName = filePath;
2550             if(workspace && !workspace.holdTracking)
2551                workspace.UpdateOpenedFileInfo(filePath, opened);
2552          }
2553       }
2554
2555       if(!document && createIfFails != no)
2556       {
2557          if(createIfFails != yes && !needFileModified &&
2558                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2559             createIfFails = yes;
2560          if(createIfFails == yes || createIfFails == whatever)
2561          {
2562             document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, true);
2563             if(document)
2564                document.fileName = filePath;
2565          }
2566       }
2567
2568       if(document)
2569       {
2570          if(projectView && document._class == class(CodeEditor) && workspace)
2571          {
2572             int lineNumber, position;
2573             Point scroll;
2574             CodeEditor editor = (CodeEditor)document;
2575             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2576             editor.openedFileInfo.holdTracking = true;
2577             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2578             position = Max(editor.openedFileInfo.position - 1, 0);
2579             if(editor.editBox.GoToLineNum(lineNumber))
2580                editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2581             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2582             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2583             editor.editBox.scroll = scroll;
2584             editor.openedFileInfo.holdTracking = false;
2585          }
2586
2587          if(needFileModified)
2588             document.OnFileModified = OnFileModified;
2589          document.NotifySaved = DocumentSaved;
2590          if(maximizeDoc && document.hasMaximize)
2591             document.state = maximized;
2592
2593          if(isProject)
2594             ideSettings.AddRecentProject(document.fileName);
2595          else
2596             ideSettings.AddRecentFile(document.fileName);
2597          ide.UpdateRecentMenus();
2598          ide.AdjustFileMenus();
2599          settingsContainer.Save();
2600
2601          return document;
2602       }
2603       else
2604          return null;
2605    }
2606
2607    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2608    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2609    {
2610       if(!parentClosing && ide.workspace)
2611          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2612       return true;
2613    }*/
2614    bool ModelView::ModelViewOnClose(bool parentClosing)
2615    {
2616       if(!parentClosing && ide.workspace)
2617          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2618       return true;
2619    }
2620    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2621    {
2622       if(!parentClosing && ide.workspace)
2623          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2624       return true;
2625    }
2626
2627    /*
2628    void OnUnloadGraphics(Window window)
2629    {
2630       display.ClearMaterials();
2631       display.ClearTextures();
2632       display.ClearMeshes();
2633    }
2634    */
2635
2636    void UpdateStateLight(StatusField fld, bool on)
2637    {
2638       fld.color = on ? lime : Color { 128,128,128 };
2639       fld.backColor = on ? dimGray : 0;
2640       fld.bold = on;
2641    }
2642
2643    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2644    {
2645       UpdateStateLight(caps, app.GetKeyState(capsState));
2646       UpdateStateLight(num, app.GetKeyState(numState));
2647       return true;
2648    }
2649
2650    bool OnKeyDown(Key key, unichar ch)
2651    {
2652       switch(key)
2653       {
2654          case b: projectView.Update(null); break;
2655          case capsLock: UpdateStateLight(caps, app.GetKeyState(capsState)); break;
2656          case numLock:  UpdateStateLight(num, app.GetKeyState(numState)); break;
2657       }
2658       return true;
2659    }
2660
2661    bool OnKeyUp(Key key, unichar ch)
2662    {
2663       switch(key)
2664       {
2665          case capsLock: UpdateStateLight(caps, app.GetKeyState(capsState)); break;
2666          case numLock:  UpdateStateLight(num, app.GetKeyState(numState)); break;
2667       }
2668       return true;
2669    }
2670
2671    void GoToError(const char * line, bool noParsing, const char * objectFileExt)
2672    {
2673       if(projectView)
2674          projectView.GoToError(line, noParsing, objectFileExt);
2675    }
2676
2677    FileAttribs GoToCodeSelectFile(const char * filePath, const char * dir, Project prj, ProjectNode * node, char * selectedPath, const char * objectFileExt)
2678    {
2679       FileAttribs result { };
2680       FileAttribs fileAttribs;
2681       if(filePath[0])
2682       {
2683          if(prj)
2684             strcpy(selectedPath, prj.topNode.path);
2685          else if(dir && dir[0])
2686             strcpy(selectedPath, dir);
2687          else
2688             selectedPath[0] = '\0';
2689          PathCat(selectedPath, filePath);
2690
2691          if((fileAttribs = FileExists(selectedPath)).isFile)
2692             result = fileAttribs;
2693          else if(workspace)
2694          {
2695             bool done = false;
2696             for(p : workspace.projects)
2697             {
2698                strcpy(selectedPath, p.topNode.path);
2699                PathCat(selectedPath, filePath);
2700                if((fileAttribs = FileExists(selectedPath)).isFile)
2701                {
2702                   done = true;
2703                   result = fileAttribs;
2704                   break;
2705                }
2706             }
2707             if(!done)
2708             {
2709                Project project;
2710                ProjectNode n = null;
2711                for(p : workspace.projects)
2712                {
2713                   if((n = p.topNode.Find(filePath, false)))
2714                   {
2715                      n.GetFullFilePath(selectedPath, true);
2716                      if((fileAttribs = FileExists(selectedPath)).isFile)
2717                      {
2718                         if(node) *node = n;
2719                         result = fileAttribs;
2720                         break;
2721                      }
2722                   }
2723                }
2724                if(!n && (n = workspace.GetObjectFileNode(filePath, &project, selectedPath, objectFileExt)) && project &&
2725                      (fileAttribs = FileExists(selectedPath)).isFile)
2726                {
2727                   if(node) *node = n;
2728                   result = fileAttribs;
2729                }
2730             }
2731          }
2732       }
2733       return result;
2734    }
2735
2736    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir, const char * objectFileExt)
2737    {
2738       char *s = null;
2739       const char *path = text;
2740       char *colon = strchr(text, ':');
2741       char filePath[MAX_LOCATION] = "";
2742       char completePath[MAX_LOCATION];
2743       int line = 0, col = 0;
2744       int len = strlen(text);
2745       Project prj = null;
2746       FileAttribs fileAttribs;
2747
2748       // support for valgrind output
2749       if((s = strstr(text, "==")) && s == text && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
2750       {
2751          colon = s;
2752          for(; s>text; s--)
2753          {
2754             if(*s == '(')
2755             {
2756                path = s+1;
2757                break;
2758             }
2759          }
2760          /*for(s=colon; *s; s++)
2761          {
2762             if(*s == ')')
2763             {
2764                *s = '\0';;
2765                break;
2766             }
2767          }*/
2768          //*colon = '\0';
2769          //line = atoi(colon+1);
2770       }
2771       // support for "Found n match(es) in "file/path";
2772       else if(path[len-1] == '\"' && strstr(path, $"Found %d match%s in \"%s\"%s\n\n"."Found") && strstr(path, $"match") && strstr(path, $"in") && (s = strstr(path, "\"")) && s != path+len-1)
2773       {
2774          path = s+1;
2775       }
2776       else
2777       {
2778          if(colon && (colon[1] == '/' || colon[1] == '\\'))
2779          {
2780             path = (colon - 1 > path) ? colon - 1 : path;
2781             colon = strstr(colon + 1, ":");
2782          }
2783          if(*path == '*' && (s = strchr(path+1, '*')))
2784             path = s+1;
2785          while(isspace(*path)) path++;
2786       }
2787       if(*path == '(')
2788       {
2789          char * close = strchr(path, ')');
2790          if(close)
2791          {
2792             char name[256];
2793             strncpy(name, path+1, close - path - 1);
2794             name[close - path - 1] = '\0';
2795             for(p : ide.workspace.projects)
2796             {
2797                if(!strcmp(p.name, name))
2798                {
2799                   path = close + 1;
2800                   prj = p;
2801                   break;
2802                }
2803             }
2804          }
2805       }
2806       if(!prj)
2807          prj = project ? project : (dir ? null : ide.project);
2808       if(colon)
2809       {
2810          strncpy(filePath, path, colon - path);
2811          filePath[colon - path] = '\0';
2812          line = atoi(colon + 1);
2813          colon = strstr(colon + 1, ":");
2814          if(colon)
2815             col = atoi(colon + 1);
2816       }
2817       else if(path - 1 >= text && *(path - 1) == '\"')
2818       {
2819          colon = strchr(path, '\"');
2820          if(colon)
2821          {
2822             strncpy(filePath, path, colon - path);
2823             filePath[colon - path] = '\0';
2824          }
2825       }
2826       else if(path && !colon)
2827       {
2828          strcpy(filePath, path);
2829       }
2830
2831       if((fileAttribs = GoToCodeSelectFile(filePath, dir, prj, null, completePath, objectFileExt)))
2832          CodeLocationGoTo(completePath, fileAttribs, line, col);
2833    }
2834
2835    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
2836    {
2837       if(fileAttribs.isFile)
2838       {
2839          char ext[MAX_EXTENSION];
2840          GetExtension(path, ext);
2841          strlwr(ext);
2842          if(binaryDocExt.Find(ext))
2843             ShellOpen(path);
2844          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "bc") ||
2845                !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
2846          {
2847             char dirPath[MAX_LOCATION];
2848             StripLastDirectory(path, dirPath);
2849             ShellOpen(dirPath);
2850          }
2851          else
2852          {
2853             CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, !strcmpi(ext, "epj") ? "txt" : ext, no, normal, false);
2854             if(codeEditor && codeEditor._class == class(CodeEditor) && line)
2855             {
2856                EditBox editBox = codeEditor.editBox;
2857                editBox.GoToLineNum(line - 1);
2858                editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2859             }
2860          }
2861       }
2862       else if(fileAttribs.isDirectory)
2863          ShellOpen(path);
2864    }
2865
2866    void OnRedraw(Surface surface)
2867    {
2868       Bitmap bitmap = back.bitmap;
2869       if(bitmap)
2870          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2871    }
2872
2873    void SheetSelected(SheetType sheetSelected)
2874    {
2875       if(activeChild == sheet)
2876       {
2877          if(sheetSelected == methods)
2878          {
2879             viewPropertiesItem.accelerator = f4;
2880             viewPropertiesItem.parent = viewMenu;
2881             viewMethodsItem.parent = null;
2882          }
2883          else
2884          {
2885             viewMethodsItem.accelerator = f4;
2886             viewMethodsItem.parent = viewMenu;
2887             viewPropertiesItem.parent = null;
2888          }
2889       }
2890       else
2891       {
2892          viewMethodsItem.parent = viewMenu;
2893          viewPropertiesItem.parent = viewMenu;
2894          if(sheetSelected == methods)
2895          {
2896             viewMethodsItem.accelerator = f4;
2897             viewPropertiesItem.accelerator = 0;
2898          }
2899          else
2900          {
2901             viewMethodsItem.accelerator = 0;
2902             viewPropertiesItem.accelerator = f4;
2903          }
2904       }
2905    }
2906
2907    void OnActivateClient(Window client, Window previous)
2908    {
2909       //if(!client || client != previous)
2910       {
2911          Class dataType;
2912          if(!client || client != previous)
2913          {
2914             if(previous)
2915                dataType = previous._class;
2916             if(previous && !strcmp(dataType.name, "CodeEditor"))
2917             {
2918                ((CodeEditor)previous).UpdateFormCode();
2919             }
2920             else if(previous && !strcmp(dataType.name, "Designer"))
2921             {
2922                ((Designer)previous).codeEditor.UpdateFormCode();
2923             }
2924          }
2925
2926          if(client)
2927             dataType = client._class;
2928          if(client && !strcmp(dataType.name, "CodeEditor"))
2929          {
2930             CodeEditor codeEditor = (CodeEditor)client;
2931             SetPrivateModule(codeEditor.privateModule);
2932             SetCurrentContext(codeEditor.globalContext);
2933             SetTopContext(codeEditor.globalContext);
2934             SetGlobalContext(codeEditor.globalContext);
2935
2936             SetDefines(&codeEditor.defines);
2937             SetImports(&codeEditor.imports);
2938
2939             SetActiveDesigner(codeEditor.designer);
2940
2941             sheet.codeEditor = codeEditor;
2942             toolBox.codeEditor = codeEditor;
2943
2944             viewDesignerItem.parent = viewMenu;
2945             if(activeChild != codeEditor)
2946             {
2947                viewCodeItem.parent = viewMenu;
2948                viewDesignerItem.accelerator = 0;
2949                viewCodeItem.accelerator = f8;
2950             }
2951             else
2952             {
2953                viewCodeItem.parent = null;
2954                viewDesignerItem.accelerator = f8;
2955             }
2956          }
2957          else if(client && !strcmp(dataType.name, "Designer"))
2958          {
2959             CodeEditor codeEditor = ((Designer)client).codeEditor;
2960             if(codeEditor)
2961             {
2962                SetPrivateModule(codeEditor.privateModule);
2963                SetCurrentContext(codeEditor.globalContext);
2964                SetTopContext(codeEditor.globalContext);
2965                SetGlobalContext(codeEditor.globalContext);
2966                SetDefines(&codeEditor.defines);
2967                SetImports(&codeEditor.imports);
2968             }
2969             else
2970             {
2971                SetPrivateModule(null);
2972                SetCurrentContext(null);
2973                SetTopContext(null);
2974                SetGlobalContext(null);
2975                SetDefines(null);
2976                SetImports(null);
2977             }
2978
2979             SetActiveDesigner((Designer)client);
2980
2981             sheet.codeEditor = codeEditor;
2982             toolBox.codeEditor = codeEditor;
2983
2984             viewCodeItem.parent = viewMenu;
2985             if(activeChild != client)
2986             {
2987                viewDesignerItem.parent = viewMenu;
2988                viewDesignerItem.accelerator = f8;
2989                viewCodeItem.accelerator = 0;
2990             }
2991             else
2992             {
2993                viewDesignerItem.parent = null;
2994                viewCodeItem.accelerator = f8;
2995             }
2996          }
2997          else
2998          {
2999             if(!client && !projectView && sheet.visible)
3000             {
3001                if(sheet)
3002                   sheet.visible = false;
3003                toolBox.visible = false;
3004             }
3005             if(sheet)
3006                sheet.codeEditor = null;
3007             toolBox.codeEditor = null;
3008             SetActiveDesigner(null);
3009
3010             viewDesignerItem.parent = null;
3011             viewCodeItem.parent = null;
3012          }
3013          if(sheet)
3014             SheetSelected(sheet.sheetSelected);
3015       }
3016
3017       projectCompileItem = null;
3018
3019       if(statusBar)
3020       {
3021          statusBar.Clear();
3022          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
3023          {
3024             CodeEditor codeEditor = (CodeEditor)client;
3025             EditBox editBox = codeEditor.editBox;
3026
3027             statusBar.AddField(pos);
3028
3029             caps = { width = 40, text = $"CAPS" };
3030             statusBar.AddField(caps);
3031             UpdateStateLight(caps, app.GetKeyState(capsState));
3032
3033             ovr = { width = 36, text = $"OVR" };
3034             statusBar.AddField(ovr);
3035             UpdateStateLight(ovr, (editBox && editBox.overwrite));
3036
3037             num = { width = 36, text = $"NUM" };
3038             statusBar.AddField(num);
3039             UpdateStateLight(num, app.GetKeyState(numState));
3040
3041             //statusBar.text = "Ready";
3042
3043             if(projectView && projectView.project)
3044             {
3045                bool isCObject = false;
3046                ProjectNode node = projectView.GetNodeFromWindow(client, null, true, false, null);
3047                if(!node && (node = projectView.GetNodeFromWindow(client, null, true, true, null)))
3048                   isCObject = true;
3049                if(node)
3050                {
3051                   char nodeName[MAX_FILENAME];
3052                   char name[MAX_FILENAME+96];
3053                   if(isCObject)
3054                      ChangeExtension(node.name, "c", nodeName);
3055                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
3056                   projectCompileItem =
3057                   {
3058                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
3059
3060                      bool NotifySelect(MenuItem selection, Modifiers mods)
3061                      {
3062                         if(projectView)
3063                         {
3064                            bool isCObject = false;
3065                            bool isExcluded = false;
3066                            ProjectNode node = projectView.GetNodeForCompilationFromWindow(activeClient, true, &isExcluded, &isCObject);
3067                            if(node)
3068                            {
3069                               if(isExcluded)
3070                                  ide.outputView.buildBox.Logf($"%s %s is excluded from current build configuration.\n", isCObject ? "Object file" : "File", node.name);
3071                               else
3072                               {
3073                                  List<ProjectNode> nodes { };
3074                                  nodes.Add(node);
3075                                  projectView.Compile(node.project, nodes, false, false, isCObject ? cObject : normal);
3076                                  delete nodes;
3077                               }
3078                            }
3079                         }
3080                         return true;
3081                      }
3082                   };
3083                   projectMenu.AddDynamic(projectCompileItem, ide, false);
3084                }
3085             }
3086          }
3087          else
3088          {
3089             caps = ovr = num = null;
3090          }
3091       }
3092    }
3093
3094    bool OnClose(bool parentClosing)
3095    {
3096       //return !projectView.buildInProgress;
3097       if(projectView && projectView.buildInProgress)
3098          return false;
3099       if(DontTerminateDebugSession($"Close IDE"))
3100          return false;
3101       if(findInFilesDialog)
3102          findInFilesDialog.SearchStop();
3103       if(workspace)
3104       {
3105          workspace.timer.Stop();
3106          workspace.Save();
3107       }
3108       ideMainFrame.Destroy(0);
3109       return true;
3110    }
3111
3112    bool OnPostCreate()
3113    {
3114       int c;
3115       bool passThrough = false;
3116       bool debugWorkDir = false;
3117       char * passDebugWorkDir = null;
3118       bool openAsText = false;
3119       DynamicString passArgs { };
3120       int ptArg = 0;
3121
3122       for(c = 1; c<app.argc; c++)
3123       {
3124          if(passThrough)
3125          {
3126             const char * arg = app.argv[c];
3127             char * buf = new char[strlen(arg)*2+1];
3128             if(ptArg++ > 0)
3129                passArgs.concat(" ");
3130             PassArg(buf, arg);
3131             passArgs.concat(buf);
3132             delete buf;
3133          }
3134          else if(debugWorkDir)
3135          {
3136             passDebugWorkDir = CopyString(app.argv[c]);
3137             StripQuotes(passDebugWorkDir, passDebugWorkDir);
3138             debugWorkDir = false;
3139          }
3140          else if(!strcmp(app.argv[c], "-t"))
3141             openAsText = true;
3142          else if(!strcmp(app.argv[c], "-no-parsing"))
3143             ide.noParsing = true;
3144          else if(!strcmp(app.argv[c], "-debug-start"))
3145             ide.debugStart = true;
3146          else if(!strcmp(app.argv[c], "-debug-work-dir"))
3147             debugWorkDir = true;
3148          else if(!strcmp(app.argv[c], "-@"))
3149             passThrough = true;
3150          else
3151          {
3152             char fullPath[MAX_LOCATION];
3153             char parentPath[MAX_LOCATION];
3154             char ext[MAX_EXTENSION];
3155             bool isProject;
3156             FileAttribs dirAttribs;
3157             GetWorkingDir(fullPath, MAX_LOCATION);
3158             PathCat(fullPath, app.argv[c]);
3159             StripLastDirectory(fullPath, parentPath);
3160             GetExtension(app.argv[c], ext);
3161             isProject = !openAsText && !strcmpi(ext, "epj");
3162
3163             if(isProject && c > 1 + (ide.debugStart ? 1 : 0)) continue;
3164
3165             // Create directory for projects (only)
3166             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
3167             {
3168                if(isProject && !FileExists(fullPath))
3169                {
3170                   char name[MAX_LOCATION];
3171                   NewProjectDialog newProjectDialog;
3172
3173                   if(projectView)
3174                   {
3175                      projectView.visible = false;
3176                      if(!projectView.Destroy(0))
3177                         return true;
3178                   }
3179
3180                   newProjectDialog = { master = this };
3181
3182                   strcpy(name, app.argv[c]);
3183                   StripExtension(name);
3184                   GetLastDirectory(name, name);
3185                   newProjectDialog.projectName.contents = name;
3186                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
3187                   newProjectDialog.locationEditBox.path = parentPath;
3188                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
3189
3190                   incref newProjectDialog;
3191                   newProjectDialog.Modal();
3192                   if(projectView)
3193                   {
3194                      ideSettings.AddRecentProject(projectView.fileName);
3195                      ide.UpdateRecentMenus();
3196                      settingsContainer.Save();
3197                   }
3198                   delete newProjectDialog;
3199                   // Open only one project
3200                   break;
3201                }
3202                else
3203                   ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
3204             }
3205             else if(strstr(fullPath, "http://") == fullPath)
3206                ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
3207          }
3208       }
3209       if(passThrough && projectView && projectView.project && workspace)
3210          workspace.commandLineArgs = passArgs;
3211       if(passDebugWorkDir && projectView && projectView.project && workspace)
3212       {
3213          workspace.debugDir = passDebugWorkDir;
3214          delete passDebugWorkDir;
3215       }
3216
3217       UpdateToolBarActiveConfigs(false);
3218       UpdateToolBarActiveCompilers();
3219       delete passArgs;
3220       return true;
3221    }
3222
3223    void OnDestroy()
3224    {
3225       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
3226       if(projectView)
3227       {
3228          projectView.visible = false;
3229          projectView.Destroy(0);
3230          projectView = null;
3231       }
3232 #ifdef GDB_DEBUG_GUI
3233       gdbDialog.Destroy(0);
3234       delete gdbDialog;
3235 #endif
3236    }
3237
3238    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
3239    {
3240       int c, len, count;
3241       char * newList;
3242       char * oldPaths[128];
3243       String oldList = new char[maxPathLen];
3244       Array<String> newExePaths { };
3245       //Map<String, bool> exePathExists { };
3246       bool found = false;
3247 #if defined(__unix__) || defined(__APPLE__)
3248       Array<String> newLibPaths { };
3249       Map<String, bool> libPathExists { };
3250 #endif
3251
3252       if(projectsDirs)
3253       {
3254          for(prj : workspace.projects)
3255          {
3256             DirExpression targetDirExp;
3257
3258             // SKIP FIRST PROJECT...
3259             if(prj == workspace.projects.firstIterator.data) continue;
3260
3261             // NOTE: Right now the additional project config dir will be
3262             //       obtained when the debugger is started, so toggling it
3263             //       while building will change which library gets used.
3264             //       To go with the initial state, e.g. when F5 was pressed,
3265             //       we nould need to keep a list of all project's active
3266             //       config upon startup.
3267             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
3268
3269             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
3270                cfg = prj.config;
3271             else
3272             {
3273                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3274                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
3275                      break;
3276                if(!cfg)
3277                {
3278                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3279                      if(cfg.targetType == sharedLibrary && cfg.debug)
3280                         break;
3281                }
3282             }*/
3283             if(targetDirExp.dir)
3284             {
3285                char buffer[MAX_LOCATION];
3286 #if defined(__WIN32__)
3287                Array<String> paths = newExePaths;
3288 #else
3289                Array<String> paths = newLibPaths;
3290 #endif
3291                GetSystemPathBuffer(buffer, prj.topNode.path);
3292                PathCat(buffer, targetDirExp.dir);
3293                for(p : paths)
3294                {
3295                   if(!fstrcmp(p, buffer))
3296                   {
3297                      found = true;
3298                      break;
3299                   }
3300                }
3301                if(!found)
3302                   paths.Add(CopyString(buffer));
3303             }
3304             delete targetDirExp;
3305          }
3306       }
3307
3308       for(item : compiler.executableDirs)
3309       {
3310          DirExpression dirExpr { };
3311          dirExpr.Evaluate(item, null, compiler, null, 0);
3312          found = false;
3313
3314          for(p : newExePaths)
3315          {
3316             if(!fstrcmp(p, dirExpr.dir))
3317             {
3318                found = true;
3319                break;
3320             }
3321          }
3322          if(!found)
3323             newExePaths.Add(CopySystemPath(dirExpr.dir));
3324          delete dirExpr;
3325       }
3326
3327       GetEnvironment("PATH", oldList, maxPathLen);
3328 /*#ifdef _DEBUG
3329       printf("Old value of PATH: %s\n", oldList);
3330 #endif*/
3331       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3332       for(c = 0; c < count; c++)
3333       {
3334          found = false;
3335          for(p : newExePaths)
3336          {
3337             if(!fstrcmp(p, oldPaths[c]))
3338             {
3339                found = true;
3340                break;
3341             }
3342          }
3343          if(!found)
3344             newExePaths.Add(CopySystemPath(oldPaths[c]));
3345       }
3346
3347       len = 0;
3348       for(path : newExePaths)
3349          len += strlen(path) + 1;
3350       newList = new char[len + 1];
3351       newList[0] = '\0';
3352       for(path : newExePaths)
3353       {
3354          strcat(newList, path);
3355          strcat(newList, pathListSep);
3356       }
3357       newList[len - 1] = '\0';
3358       SetEnvironment("PATH", newList);
3359 /*#ifdef _DEBUG
3360       printf("New value of PATH: %s\n", newList);
3361 #endif*/
3362       delete newList;
3363
3364       newExePaths.Free();
3365       delete newExePaths;
3366
3367 #if defined(__unix__) || defined(__APPLE__)
3368
3369       for(item : compiler.libraryDirs)
3370       {
3371          if(!libPathExists[item])  // fstrcmp should be used
3372          {
3373             String s = CopyString(item);
3374             newLibPaths.Add(s);
3375             libPathExists[s] = true;
3376          }
3377       }
3378
3379 #if defined(__APPLE__)
3380       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3381 #else
3382       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3383 #endif
3384 /*#ifdef _DEBUG
3385       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3386 #endif*/
3387       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3388       for(c = 0; c < count; c++)
3389       {
3390          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3391          {
3392             String s = CopyString(oldPaths[c]);
3393             newLibPaths.Add(s);
3394             libPathExists[s] = true;
3395          }
3396       }
3397
3398       len = 0;
3399       for(path : newLibPaths)
3400          len += strlen(path) + 1;
3401       newList = new char[len + 1];
3402       newList[0] = '\0';
3403       for(path : newLibPaths)
3404       {
3405          strcat(newList, path);
3406          strcat(newList, pathListSep);
3407       }
3408       newList[len - 1] = '\0';
3409 #if defined(__APPLE__)
3410       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3411 #else
3412       SetEnvironment("LD_LIBRARY_PATH", newList);
3413 #endif
3414 /*#ifdef _DEBUG
3415       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3416 #endif*/
3417       delete newList;
3418
3419       newLibPaths.Free();
3420       delete newLibPaths;
3421       delete libPathExists;
3422 #endif
3423
3424       if(compiler.distccEnabled && compiler.distccHosts)
3425          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3426
3427       delete oldList;
3428    }
3429
3430    void DestroyTemporaryProjectDir()
3431    {
3432       if(tmpPrjDir && tmpPrjDir[0])
3433       {
3434          if(FileExists(tmpPrjDir).isDirectory)
3435             DestroyDir(tmpPrjDir);
3436          property::tmpPrjDir = null;
3437       }
3438    }
3439
3440    IDEWorkSpace()
3441    {
3442       // Graphics Driver Menu
3443
3444       /*
3445       app.currentSkin.selectionColor = selectionColor;
3446       app.currentSkin.selectionText = selectionText;
3447       */
3448
3449 /*
3450       driverItems = new MenuItem[app.numDrivers];
3451       for(c = 0; c < app.numDrivers; c++)
3452       {
3453          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3454          driverItems[c].id = c;
3455          driverItems[c].isRadio = true;
3456       }
3457 */
3458       driverItems = new MenuItem[2];
3459 #if defined(__unix__)
3460          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3461          driverItems[0].id = 0;
3462          driverItems[0].isRadio = true;
3463 #else
3464          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3465          driverItems[0].id = 0;
3466          driverItems[0].isRadio = true;
3467 #endif
3468          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3469          driverItems[1].id = 1;
3470          driverItems[1].isRadio = true;
3471
3472 /*      skinItems = new MenuItem[app.numSkins];
3473       for(c = 0; c < app.numSkins; c++)
3474       {
3475          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3476          skinItems[c].id = c;
3477          skinItems[c].isRadio = true;
3478       }
3479 */
3480       ideFileDialog.master = this;
3481       ideProjectFileDialog.master = this;
3482
3483       //SetDriverAndSkin();
3484       return true;
3485    }
3486
3487    void UpdateRecentMenus()
3488    {
3489       int c;
3490       Menu fileMenu = menu.FindMenu($"File");
3491       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3492       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3493       char * itemPath = new char[MAX_LOCATION];
3494       char * itemName = new char[MAX_LOCATION+4];
3495
3496       recentFiles.Clear();
3497       c = 0;
3498
3499       for(recent : ideSettings.recentFiles)
3500       {
3501          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3502          MakeSystemPath(itemPath);
3503          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3504          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3505          c++;
3506       }
3507
3508       recentProjects.Clear();
3509       c = 0;
3510       for(recent : ideSettings.recentProjects)
3511       {
3512          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3513          MakeSystemPath(itemPath);
3514          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3515          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3516          c++;
3517       }
3518
3519       delete itemPath;
3520       delete itemName;
3521    }
3522
3523    ~IDEWorkSpace()
3524    {
3525       delete driverItems;
3526       delete skinItems;
3527       delete languageItems;
3528       delete ideSettings;
3529       if(documentor)
3530       {
3531          documentor.Puts("Quit\n");
3532          documentor.Wait();
3533          delete documentor;
3534       }
3535    }
3536 }
3537
3538 void DestroyDir(char * path)
3539 {
3540    RecursiveDeleteFolderFSI fsi { };
3541    fsi.Iterate(path);
3542    delete fsi;
3543 }
3544
3545 #if defined(__WIN32__)
3546 define sdkDirName = "Ecere SDK";
3547 #else
3548 define sdkDirName = "ecere";
3549 #endif
3550
3551 bool GetInstalledFileOrFolder(const char * subDir, const char * name, char * path, FileAttribs attribs)
3552 {
3553    bool found = false;
3554    char * v = new char[maxPathLen];
3555    v[0] = '\0';
3556    if(!found)
3557    {
3558       strncpy(path, settingsContainer.moduleLocation, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
3559       StripLastDirectory(path, path);
3560       PathCat(path, subDir);
3561       if(name) PathCat(path, name);
3562       if(FileExists(path) & attribs) found = true;
3563    }
3564 #if defined(__WIN32__)
3565    if(!found)
3566    {
3567       for(s : [ "ECERE_SDK_SRC", "AppData", "ALLUSERSPROFILE", "USERPROFILE", "HOMEPATH", "ProgramData", "ProgramFiles", "ProgramFiles(x86)", "SystemDrive" ])
3568       {
3569          GetEnvironment(s, v, maxPathLen);
3570          if(v[0])
3571          {
3572             strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
3573             if(!strcmp(s, "SystemDrive"))
3574                PathCat(path, "Program Files");
3575             if(strcmp(s, "ECERE_SDK_SRC"))
3576                PathCat(path, sdkDirName);
3577             PathCat(path, subDir);
3578             if(name) PathCat(path, name);
3579             if(FileExists(path) & attribs)
3580             {
3581                found = true;
3582                break;
3583             }
3584          }
3585       }
3586    }
3587 #else
3588    if(!found)
3589    {
3590       char * p = null;
3591       char * tokens[256];
3592       int c, numTokens;
3593
3594       GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3595       numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3596       if(!numTokens)
3597       {
3598          p = new char[MAX_LOCATION];
3599          p[0] = '\0';
3600          strcat(p, "/usr/share");
3601          tokens[0] = p;
3602          numTokens = 1;
3603       }
3604       for(c=0; c<numTokens; c++)
3605       {
3606          strncpy(path, tokens[c], MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
3607          PathCat(path, sdkDirName);
3608          PathCat(path, subDir);
3609          if(name)
3610             PathCat(path, name);
3611          if(FileExists(path) & attribs)
3612          {
3613             found = true;
3614             break;
3615          }
3616       }
3617       delete p;
3618    }
3619 #endif
3620    delete v;
3621    return found;
3622 }
3623
3624 void FindAndShellOpenInstalledFolder(const char * name)
3625 {
3626    char path[MAX_LOCATION];
3627    if(GetInstalledFileOrFolder(name, null, path, { isDirectory = true }))
3628       ShellOpen(path);
3629 }
3630
3631 void FindAndShellOpenInstalledFile(const char * subdir, const char * name)
3632 {
3633    char path[MAX_LOCATION];
3634    if(GetInstalledFileOrFolder(subdir, name, path, { isFile = true }))
3635       ShellOpen(path);
3636 }
3637
3638 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3639 {
3640    bool preserveRootFolder;
3641
3642    void OutFolder(const char * folderPath, bool isRoot)
3643    {
3644       if(!(preserveRootFolder && isRoot))
3645          RemoveDir(folderPath);
3646    }
3647
3648    bool OnFile(const char * filePath)
3649    {
3650       DeleteFile(filePath);
3651       return true;
3652    }
3653 }
3654
3655 class IDEApp : GuiApplication
3656 {
3657    //driver = "Win32Console";
3658    // driver = "OpenGL";
3659    // skin = "Aqua";
3660    //skin = "TVision";
3661
3662    TempFile includeFile { };
3663    int argFilesCount;
3664
3665    bool Init()
3666    {
3667       char ext[MAX_EXTENSION];
3668       SetLoggingMode(stdOut, null);
3669       //SetLoggingMode(debug, null);
3670
3671       settingsContainer.Load();
3672
3673       if(ideSettings.language)
3674       {
3675          const String language = GetLanguageString();
3676          if(ideSettings.language.OnCompare(language))
3677          {
3678             LanguageRestart(ideSettings.language, app, null, null, null, null, true);
3679             return false;
3680          }
3681       }
3682
3683       // First count files arg to decide whether to maximize
3684       {
3685          bool passThrough = false, debugWorkDir = false;
3686          int c;
3687          argFilesCount = 0;
3688          for(c = 1; c<app.argc; c++)
3689          {
3690             if(passThrough);
3691             else if(debugWorkDir)
3692                debugWorkDir = false;
3693             else if(!strcmp(app.argv[c], "-t"));
3694             else if(!strcmp(app.argv[c], "-no-parsing"));
3695             else if(!strcmp(app.argv[c], "-debug-start"));
3696             else if(!strcmp(app.argv[c], "-debug-work-dir"))
3697                debugWorkDir = true;
3698             else if(!strcmp(app.argv[c], "-@"))
3699                passThrough = true;
3700             else
3701                argFilesCount++;
3702          }
3703       }
3704
3705       if(app.argFilesCount > 0 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
3706       {
3707          app.driver = "OpenGL";
3708          ide.driverItems[1].checked = true;
3709       }
3710       else
3711       {
3712 #if defined(__unix__) || defined(__APPLE__)
3713          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3714 #else
3715          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3716 #endif
3717          ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3718       }
3719
3720       {
3721          char model[MAX_LOCATION];
3722          if(GetInstalledFileOrFolder("samples", "3D/ModelViewer/models/duck/duck.3DS", model, { isFile = true }))
3723          {
3724             ide.duck.modelFile = model;
3725             ide.duck.parent = ideMainFrame;
3726          }
3727       }
3728       if(ide.duck.modelFile && !strcmpi(app.driver, "OpenGL"))
3729          ide.debugRubberDuck.disabled = false;
3730
3731       SetInIDE(true);
3732
3733       desktop.caption = titleECEREIDE;
3734       /*
3735       int c;
3736       for(c = 1; c<app.argc; c++)
3737       {
3738          char fullPath[MAX_LOCATION];
3739          GetWorkingDir(fullPath, MAX_LOCATION);
3740          PathCat(fullPath, app.argv[c]);
3741          ide.OpenFile(fullPath, app.argFilesCount > 1, true, null, yes, normal, false);
3742       }
3743       */
3744
3745       // Default to language specified by environment if no language selected
3746       if(!ideSettings.language)
3747       {
3748          ideSettings.language = GetLanguageString();
3749          settingsContainer.Save();
3750       }
3751
3752       // Default to home directory if no directory yet set up
3753       if(!ideSettings.ideProjectFileDialogLocation[0])
3754       {
3755          bool found = false;
3756          char location[MAX_LOCATION];
3757          char * home = getenv("HOME");
3758          char * homeDrive = getenv("HOMEDRIVE");
3759          char * homePath = getenv("HOMEPATH");
3760          char * userProfile = getenv("USERPROFILE");
3761          char * systemDrive = getenv("SystemDrive");
3762          if(home && FileExists(home).isDirectory)
3763          {
3764             strcpy(location, home);
3765             found = true;
3766          }
3767          if(!found && homeDrive && homePath)
3768          {
3769             strcpy(location, homeDrive);
3770             PathCat(location, homePath);
3771             if(FileExists(location).isDirectory)
3772                found = true;
3773          }
3774          if(!found && FileExists(userProfile).isDirectory)
3775          {
3776             strcpy(location, userProfile);
3777             found = true;
3778          }
3779          if(!found && FileExists(systemDrive).isDirectory)
3780          {
3781             strcpy(location, systemDrive);
3782             found = true;
3783          }
3784          if(found)
3785          {
3786             ideSettings.ideProjectFileDialogLocation = location;
3787             if(!ideSettings.ideFileDialogLocation[0])
3788                ideSettings.ideFileDialogLocation = location;
3789          }
3790       }
3791
3792       if(!LoadIncludeFile())
3793          PrintLn($"error: unable to load :crossplatform.mk file inside ide binary.");
3794
3795       // Create language menu
3796       {
3797          String language = ideSettings.language;
3798          int i = 0;
3799          bool found = false;
3800
3801          ide.languageItems = new MenuItem[languages.count];
3802          for(l : languages)
3803          {
3804             ide.languageItems[i] =
3805             {
3806                ide.languageMenu, l.name;
3807                bitmap = { l.bitmap };
3808                id = i;
3809                isRadio = true;
3810
3811                bool Window::NotifySelect(MenuItem selection, Modifiers mods)
3812                {
3813                   if(!LanguageRestart(languages[(int)selection.id].code, app, ideSettings, settingsContainer, ide, ide.projectView, false))
3814                   {
3815                      // Re-select previous selected language if aborted
3816                      String language = ideSettings.language;
3817                      int i = 0;
3818                      for(l : languages)
3819                      {
3820                         if(((!language || !language[0]) && i == 0) ||
3821                            (language && !strcmpi(l.code, language)))
3822                         {
3823                            ide.languageItems[i].checked = true;
3824                            break;
3825                         }
3826                         i++;
3827                      }
3828                   }
3829                   return true;
3830                }
3831             };
3832             i++;
3833          }
3834
3835          // Try to find country-specific language first
3836          if(language)
3837          {
3838             i = 0;
3839             for(l : languages)
3840             {
3841                if(!strcmpi(l.code, language) || (i == 0 && !strcmpi("en", language)))
3842                {
3843                   ide.languageItems[i].checked = true;
3844                   found = true;
3845                   break;
3846                }
3847                i++;
3848             }
3849          }
3850
3851          // Try generalizing locale
3852          if(!found && language)
3853          {
3854             char * under;
3855             char genericLocale[256];
3856             i = 0;
3857             strncpy(genericLocale, language, sizeof(genericLocale));
3858             genericLocale[sizeof(genericLocale)-1] = 0;
3859
3860             under = strchr(genericLocale, '_');
3861             if(under)
3862                *under = 0;
3863             if(!strcmpi(genericLocale, "zh"))
3864                strcpy(genericLocale, "zh_CN");
3865             if(strcmp(genericLocale, language))
3866             {
3867                for(l : languages)
3868                {
3869                   if(!strcmpi(l.code, genericLocale) || (i == 0 && !strcmpi("en", genericLocale)))
3870                   {
3871                      ide.languageItems[i].checked = true;
3872                      found = true;
3873                      break;
3874                   }
3875                   i++;
3876                }
3877             }
3878          }
3879
3880          if(!found)
3881             ide.languageItems[0].checked = true;
3882
3883          MenuDivider { ide.languageMenu };
3884          MenuItem
3885          {
3886             ide.languageMenu, "Help Translate";
3887
3888             bool Window::NotifySelect(MenuItem selection, Modifiers mods)
3889             {
3890                ShellOpen("http://translations.launchpad.net/ecere");
3891                return true;
3892             }
3893          };
3894       }
3895
3896       ideMainFrame.Create();
3897       if(app.argFilesCount > 1)
3898          ide.MenuWindowTileVert(null, 0);
3899       return true;
3900    }
3901
3902    bool Cycle(bool idle)
3903    {
3904       if(ide.documentor)
3905       {
3906          if(ide.documentor.Peek())
3907          {
3908             char line[1024];
3909             ide.documentor.GetLine(line, sizeof(line));
3910             if(!strcmpi(line, "Exited"))
3911             {
3912                ide.documentor.CloseInput();
3913                ide.documentor.CloseOutput();
3914                ide.documentor.Wait();
3915                delete ide.documentor;
3916             }
3917          }
3918          if(ide.documentor && ide.documentor.eof)
3919          {
3920             ide.documentor.CloseInput();
3921             ide.documentor.CloseOutput();
3922             ide.documentor.Wait();
3923             delete ide.documentor;
3924          }
3925       }
3926       return true;
3927    }
3928
3929    bool LoadIncludeFile()
3930    {
3931       bool result = false;
3932       File include = FileOpen(":crossplatform.mk", read);
3933       if(include)
3934       {
3935          File f = includeFile;
3936          if(f)
3937          {
3938             for(; !include.Eof(); )
3939             {
3940                char buffer[4096];
3941                int count = include.Read(buffer, 1, 4096);
3942                f.Write(buffer, 1, count);
3943             }
3944             result = true;
3945          }
3946          delete include;
3947       }
3948       return result;
3949    }
3950 }
3951
3952 IDEMainFrame ideMainFrame { };
3953
3954 define app = ((IDEApp)__thisModule);
3955 #ifdef _DEBUG
3956 define titleECEREIDE = $"Ecere IDE (Debug)";
3957 #else
3958 define titleECEREIDE = $"Ecere IDE";
3959 #endif