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