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