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