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