ide: Correctly handling (once again) non existing files passed on command line, with...
[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                if(visible)
1716                   document.Activate();
1717                return document;
1718             }
1719          }
1720       }
1721
1722       if(createIfFails == whatever)
1723          ;
1724       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
1725       {
1726          if(openMethod == normal)
1727          {
1728             if(DontTerminateDebugSession($"Open Project"))
1729                return null;
1730             isProject = true;
1731             if(MenuWindowCloseAll(null, 0))
1732             {
1733                if(projectView)
1734                {
1735                   projectView.visible = false;
1736                   projectView.Destroy(0);
1737                   // Where did this come from? projectView = null;
1738                }
1739                if(!projectView)
1740                {
1741                   for(;;)
1742                   {
1743                      Project project;
1744                      Workspace workspace = null;
1745                      
1746                      if(FileExists(filePath))
1747                      {
1748                         if(!strcmp(extension, ProjectExtension))
1749                         {
1750                            char workspaceFile[MAX_LOCATION];
1751                            strcpy(workspaceFile, filePath);
1752                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
1753                            workspace = LoadWorkspace(workspaceFile, filePath);
1754                         }
1755                         else if(!strcmp(extension, WorkspaceExtension))
1756                            workspace = LoadWorkspace(filePath, null);
1757                         else
1758                            return null;
1759                         //project = LoadProject(filePath);
1760                      }
1761                      
1762                      if(workspace)
1763                      {
1764                         char absolutePath[MAX_LOCATION];
1765                         CreateProjectView(workspace, filePath);
1766                         document = projectView;
1767
1768                         workspace.DropInvalidBreakpoints();
1769                         workspace.Save();
1770
1771                         ide.projectView.ShowOutputBuildLog(true);
1772                         {
1773                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1774                            ide.projectView.DisplayCompiler(compiler, false);
1775                            delete compiler;
1776                         }
1777                         UpdateMakefiles();
1778                         {
1779                            char newWorkingDir[MAX_LOCATION];
1780                            StripLastDirectory(filePath, newWorkingDir);
1781                            ChangeFileDialogsDirectory(newWorkingDir, false);
1782                         }
1783                         if(document)
1784                            document.fileName = filePath;
1785
1786                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
1787
1788                         // this crashes on starting ide with epj file, solution please?
1789                         // app.UpdateDisplay();
1790
1791                         workspace.holdTracking = true;
1792                         for(ofi : workspace.openedFiles)
1793                         {
1794                            if(ofi.state != closed)
1795                            {
1796                               Window file = OpenFile(ofi.path, normal, true, null, no, normal);
1797                               if(file)
1798                               {
1799                                  char fileName[MAX_LOCATION];
1800                                  ProjectNode node;
1801                                  GetLastDirectory(ofi.path, fileName);
1802                                  node = projectView.project.topNode.Find(fileName, true);
1803                                  if(node)
1804                                     node.EnsureVisible();
1805                               }
1806                            }
1807                         }
1808                         workspace.holdTracking = false;
1809
1810                         workspace.timer.Start();
1811
1812                         findInFilesDialog.mode = FindInFilesMode::project;
1813                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
1814                         
1815                         {
1816                            char location[MAX_LOCATION];
1817                            StripLastDirectory(ide.project.topNode.path, location);
1818                            ChangeProjectFileDialogDirectory(location);
1819                         }
1820                         
1821                         /*
1822                         if(projectView.debugger)
1823                            projectView.debugger.EvaluateWatches();
1824                         */
1825                         
1826                         break;
1827                      }
1828                      else 
1829                      {
1830                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
1831                         {
1832                            ideProjectFileDialog.text = openProjectFileDialogTitle;
1833                            if(ideProjectFileDialog.Modal() == cancel)
1834                               return null;
1835                            filePath = ideProjectFileDialog.filePath;
1836                            GetExtension(filePath, extension);
1837                         }
1838                         else
1839                            return null;
1840                      }
1841                   }
1842                }
1843             }
1844             else
1845                return document;
1846          }
1847          else if(openMethod == add)
1848          {
1849             if(workspace)
1850             {
1851                Project prj = null;
1852                char slashFilePath[MAX_LOCATION];
1853                GetSlashPathBuffer(slashFilePath, filePath);
1854                for(p : workspace.projects)
1855                {
1856                   if(!fstrcmp(p.filePath, slashFilePath))
1857                   {
1858                      prj = p;
1859                      break;
1860                   }
1861                }
1862                if(prj)
1863                {
1864                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
1865                         contents = $"This project is already present in workspace." }.Modal();
1866                }
1867                else
1868                {
1869                   prj = LoadProject(filePath);
1870                   if(prj)
1871                   {
1872                      workspace.projects.Add(prj);
1873                      if(projectView)
1874                         projectView.AddNode(prj.topNode, null);
1875                      workspace.modified = true;
1876                      workspace.Save();
1877                      findInFilesDialog.AddProjectItem(prj);
1878                      projectView.ProjectUpdateMakefileForAllConfigs(prj, true, true);
1879
1880                      {
1881                         char location[MAX_LOCATION];
1882                         StripLastDirectory(prj.topNode.path, location);
1883                         ChangeProjectFileDialogDirectory(location);
1884                      }
1885
1886                      // projectView is associated with the main project and not with the one just added but
1887                      return projectView; // just to let the caller know something was opened
1888                   }
1889                }
1890             }
1891             else
1892                return null;
1893          }
1894       }
1895       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
1896             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
1897             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
1898       {
1899          if(FileExists(filePath))
1900             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1901                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1902                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
1903                                     };
1904          if(!document)
1905             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1906       }
1907 #ifndef NO3D
1908       else if(!strcmp(extension, "3ds"))
1909       {
1910          if(FileExists(filePath))
1911             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1912                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1913                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
1914                                     };
1915
1916          if(!document)
1917             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1918       }
1919 #endif
1920       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
1921             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
1922             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
1923             !strcmp(extension, "css") || !strcmp(extension, "php") ||
1924             !strcmp(extension, "js"))
1925       {
1926          CodeEditor editor { parent = this, state = state, visible = false };
1927          editor.updatingCode = true;
1928          if(editor.LoadFile(filePath))
1929          {
1930             document = editor;
1931             editor.visible = true;
1932          }
1933          else
1934             delete editor;
1935          needFileModified = false;
1936       }
1937       else
1938       {
1939          CodeEditor editor { parent = this, state = state, visible = false };
1940          if(editor.LoadFile(filePath))
1941          {
1942             document = editor;
1943             editor.visible = true;
1944          }
1945          else
1946             delete editor;
1947          needFileModified = false;
1948       }
1949
1950       if(document && (document._class == class(PictureEdit) ||
1951             document._class == class(ModelView)))
1952       {
1953          document.Create();
1954          if(document)
1955          {
1956             document.fileName = filePath;
1957             if(workspace && !workspace.holdTracking)
1958                workspace.UpdateOpenedFileInfo(filePath, opened);
1959          }
1960       }
1961       
1962       if(!document && createIfFails != no)
1963       {
1964          if(createIfFails != yes && !needFileModified && 
1965                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
1966             createIfFails = yes;
1967          if(createIfFails == yes || createIfFails == whatever)
1968          {
1969             document = (Window)NewCodeEditor(this, state, true);
1970             if(document)
1971                document.fileName = filePath;
1972          }
1973       }
1974
1975       if(document)
1976       {
1977          if(projectView && document._class == class(CodeEditor) && workspace)
1978          {
1979             int lineNumber, position;
1980             Point scroll;
1981             CodeEditor editor = (CodeEditor)document;
1982             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
1983             editor.openedFileInfo.holdTracking = true;
1984             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
1985             position = Max(editor.openedFileInfo.position - 1, 0);
1986             editor.editBox.GoToLineNum(lineNumber);
1987             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
1988             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
1989             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
1990             editor.editBox.scroll = scroll;
1991             editor.openedFileInfo.holdTracking = false;
1992          }
1993          
1994          if(needFileModified)
1995             document.OnFileModified = OnFileModified;
1996          document.NotifySaved = DocumentSaved;
1997          
1998          if(isProject)
1999             ideSettings.AddRecentProject(document.fileName);
2000          else
2001             ideSettings.AddRecentFile(document.fileName);
2002          ide.UpdateRecentMenus();
2003          settingsContainer.Save();
2004          
2005          return document;
2006       }
2007       else
2008          return null;
2009    }
2010
2011    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2012    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2013    {
2014       if(!parentClosing && ide.workspace)
2015          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2016       return true;
2017    }*/
2018    bool ModelView::ModelViewOnClose(bool parentClosing)
2019    {
2020       if(!parentClosing && ide.workspace)
2021          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2022       return true;
2023    }
2024    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2025    {
2026       if(!parentClosing && ide.workspace)
2027          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2028       return true;
2029    }
2030
2031    /*
2032    void OnUnloadGraphics(Window window)
2033    {
2034       display.ClearMaterials();
2035       display.ClearTextures();
2036       display.ClearMeshes();
2037    }
2038    */
2039
2040    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2041    {
2042       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2043       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2044       return true;
2045    }
2046
2047    bool OnKeyDown(Key key, unichar ch)
2048    {
2049       switch(key)
2050       {
2051          case b:
2052             projectView.Update(null);
2053             break;
2054          case capsLock:
2055             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2056             break;
2057          case numLock:
2058             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2059             break;
2060       }
2061       return true;
2062    }
2063
2064    void GoToError(const char * line)
2065    {
2066       if(projectView)
2067          projectView.GoToError(line);
2068    }
2069
2070    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2071    {
2072       char *path = text;
2073       char *colon = strchr(text, ':');
2074       char filePath[MAX_LOCATION];
2075       char completePath[MAX_LOCATION];
2076       int line = 0, col = 0;
2077       Project prj = null;
2078
2079       if(text[3] == '(')
2080       {
2081          char * close = strchr(text, ')');
2082          if(close)
2083          {
2084             char name[256];
2085             strncpy(name, &text[4], close - text - 4);
2086             name[close - text - 4] = '\0';
2087             for(p : ide.workspace.projects)
2088             {
2089                if(!strcmp(p.name, name))
2090                {
2091                   path = close + 1;
2092                   prj = p;
2093                   break;
2094                }
2095             }
2096          }
2097       }
2098       if(!prj)
2099          prj = project ? project : (dir ? null : ide.project);
2100       if(colon && (colon[1] == '/' || colon[1] == '\\'))
2101       {
2102          path = (colon - 1 > path) ? colon - 1 : path;
2103          colon = strstr(colon + 1, ":");
2104       }
2105       while(isspace(*path)) path++;
2106       if(colon)
2107       {
2108          strncpy(filePath, path, colon - path);
2109          filePath[colon - path] = '\0';
2110          line = atoi(colon + 1);
2111          colon = strstr(colon + 1, ":");
2112          if(colon)
2113             col = atoi(colon + 1);
2114       }
2115       else if(path - 1 >= path && *(path - 1) == '\"')
2116       {
2117          colon = strchr(path, '\"');
2118          if(colon)
2119          {
2120             strncpy(filePath, path, colon - path);
2121             filePath[colon - path] = '\0';
2122          }
2123       }
2124
2125       if(prj)
2126          strcpy(completePath, prj.topNode.path);
2127       else if(dir && dir[0])
2128          strcpy(completePath, dir);
2129       else
2130          completePath[0] = '\0';
2131       PathCat(completePath, filePath);
2132
2133       if(FileExists(completePath).isFile)
2134       {
2135          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal);
2136          if(codeEditor && line)
2137          {
2138             EditBox editBox = codeEditor.editBox;
2139             editBox.GoToLineNum(line - 1);
2140             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2141          }
2142       }
2143    }
2144
2145    void OnRedraw(Surface surface)
2146    {
2147       Bitmap bitmap = back.bitmap;
2148       if(bitmap)
2149          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2150    }
2151
2152    void SheetSelected(SheetType sheetSelected)
2153    {
2154       if(activeChild == sheet)
2155       {
2156          if(sheetSelected == methods)
2157          {
2158             viewPropertiesItem.accelerator = f4;
2159             viewPropertiesItem.parent = viewMenu;
2160             viewMethodsItem.parent = null;
2161          }
2162          else
2163          {
2164             viewMethodsItem.accelerator = f4;
2165             viewMethodsItem.parent = viewMenu;
2166             viewPropertiesItem.parent = null;
2167          }
2168       }
2169       else
2170       {
2171          viewMethodsItem.parent = viewMenu;
2172          viewPropertiesItem.parent = viewMenu;
2173          if(sheetSelected == methods)
2174          {
2175             viewMethodsItem.accelerator = f4;
2176             viewPropertiesItem.accelerator = 0;
2177          }
2178          else
2179          {
2180             viewMethodsItem.accelerator = 0;
2181             viewPropertiesItem.accelerator = f4;
2182          }
2183       }
2184    }
2185
2186    void OnActivateClient(Window client, Window previous)
2187    {
2188       //if(!client || client != previous)
2189       {
2190          Class dataType;
2191          if(!client || client != previous)
2192          {
2193             if(previous)
2194                dataType = previous._class;
2195             if(previous && !strcmp(dataType.name, "CodeEditor"))
2196             {
2197                ((CodeEditor)previous).UpdateFormCode();
2198             }
2199             else if(previous && !strcmp(dataType.name, "Designer"))
2200             {
2201                ((Designer)previous).codeEditor.UpdateFormCode();
2202             }
2203          }
2204
2205          if(client)
2206             dataType = client._class;
2207          if(client && !strcmp(dataType.name, "CodeEditor"))
2208          {
2209             CodeEditor codeEditor = (CodeEditor)client;
2210             SetPrivateModule(codeEditor.privateModule);
2211             SetCurrentContext(codeEditor.globalContext);
2212             SetTopContext(codeEditor.globalContext);
2213             SetGlobalContext(codeEditor.globalContext);
2214             
2215             SetDefines(&codeEditor.defines);
2216             SetImports(&codeEditor.imports);
2217
2218             SetActiveDesigner(codeEditor.designer);
2219             
2220             sheet.codeEditor = codeEditor;
2221             toolBox.codeEditor = codeEditor;
2222
2223             viewDesignerItem.parent = viewMenu;
2224             if(activeChild != codeEditor)
2225             {
2226                viewCodeItem.parent = viewMenu;
2227                viewDesignerItem.accelerator = 0;
2228                viewCodeItem.accelerator = f8;
2229             }
2230             else
2231             {
2232                viewCodeItem.parent = null;
2233                viewDesignerItem.accelerator = f8;
2234             }
2235          }
2236          else if(client && !strcmp(dataType.name, "Designer"))
2237          {
2238             CodeEditor codeEditor = ((Designer)client).codeEditor;
2239             if(codeEditor)
2240             {
2241                SetPrivateModule(codeEditor.privateModule);
2242                SetCurrentContext(codeEditor.globalContext);
2243                SetTopContext(codeEditor.globalContext);
2244                SetGlobalContext(codeEditor.globalContext);
2245                SetDefines(&codeEditor.defines);
2246                SetImports(&codeEditor.imports);
2247             }
2248             else
2249             {
2250                SetPrivateModule(null);
2251                SetCurrentContext(null);
2252                SetTopContext(null);
2253                SetGlobalContext(null);
2254                SetDefines(null);
2255                SetImports(null);
2256             }
2257
2258             SetActiveDesigner((Designer)client);
2259
2260             sheet.codeEditor = codeEditor;
2261             toolBox.codeEditor = codeEditor;
2262
2263             viewCodeItem.parent = viewMenu;
2264             if(activeChild != client)
2265             {
2266                viewDesignerItem.parent = viewMenu;
2267                viewDesignerItem.accelerator = f8;
2268                viewCodeItem.accelerator = 0;
2269             }
2270             else
2271             {
2272                viewDesignerItem.parent = null;
2273                viewCodeItem.accelerator = f8;
2274             }
2275          }
2276          else
2277          {
2278             if(sheet)
2279                sheet.codeEditor = null;
2280             toolBox.codeEditor = null;
2281             SetActiveDesigner(null);
2282
2283             viewDesignerItem.parent = null;
2284             viewCodeItem.parent = null;
2285          }
2286          if(sheet)
2287             SheetSelected(sheet.sheetSelected);
2288       }
2289
2290       projectCompileItem = null;
2291
2292       if(statusBar)
2293       {
2294          statusBar.Clear();
2295          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2296          {
2297             CodeEditor codeEditor = (CodeEditor)client;
2298             EditBox editBox = codeEditor.editBox;
2299
2300             statusBar.AddField(pos);
2301
2302             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2303             statusBar.AddField(caps);
2304
2305             ovr = { width = 30, text = $"OVR", color = editBox.overwrite ? black : Color { 128, 128, 128 } };
2306             statusBar.AddField(ovr);
2307
2308             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2309             statusBar.AddField(num);
2310
2311             //statusBar.text = "Ready";
2312
2313             if(projectView && projectView.project)
2314             {
2315                ProjectNode node = projectView.GetNodeFromWindow(client, null);
2316                if(node)
2317                {
2318                   char name[1024];
2319                   sprintf(name, $"Compile %s", node.name);
2320                   projectCompileItem = 
2321                   {
2322                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2323
2324                      bool NotifySelect(MenuItem selection, Modifiers mods)
2325                      {
2326                         if(projectView)
2327                         {
2328                            ProjectNode node = projectView.GetNodeFromWindow(activeClient, null);
2329                            if(node)
2330                               projectView.Compile(node);
2331                         }
2332                         return true;
2333                      }
2334                   };
2335                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2336                }
2337             }
2338          }
2339          else
2340          {
2341             caps = ovr = num = null;
2342          }
2343       }
2344    }
2345
2346    bool OnClose(bool parentClosing)
2347    {
2348       //return !projectView.buildInProgress;
2349       if(projectView && projectView.buildInProgress)
2350          return false;
2351       if(DontTerminateDebugSession($"Close IDE"))
2352          return false;
2353       if(findInFilesDialog)
2354          findInFilesDialog.SearchStop();
2355       if(workspace)
2356       {
2357          workspace.timer.Stop();
2358          workspace.Save();
2359       }
2360       ideMainFrame.Destroy(0);
2361       return true;
2362    }
2363
2364    bool OnPostCreate()
2365    {
2366       int c;
2367       for(c = 1; c<app.argc; c++)
2368       {
2369          char fullPath[MAX_LOCATION];
2370          char parentPath[MAX_LOCATION];
2371          char ext[MAX_EXTENSION];
2372          bool isProject;
2373          FileAttribs dirAttribs;
2374          GetWorkingDir(fullPath, MAX_LOCATION);
2375          PathCat(fullPath, app.argv[c]);
2376          StripLastDirectory(fullPath, parentPath);
2377          GetExtension(app.argv[c], ext);
2378          isProject = !strcmpi(ext, "epj");
2379
2380          if(isProject && c > 1) continue;
2381
2382          // Create directory for projects (only)
2383          if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
2384          {
2385             if(isProject && !FileExists(fullPath))
2386             {
2387                // The NewProject will handle directory creation
2388                /*if(!dirAttribs.isDirectory)
2389                {
2390                   MakeDir(parentPath);
2391                   dirAttribs = FileExists(parentPath);
2392                }
2393                if(dirAttribs.isDirectory)*/
2394                {
2395                   char name[MAX_LOCATION];
2396                   NewProjectDialog newProjectDialog;
2397
2398                   if(projectView)
2399                   {
2400                      projectView.visible = false;
2401                      if(!projectView.Destroy(0))
2402                         return true;
2403                   }
2404
2405                   newProjectDialog = { master = this };
2406
2407                   strcpy(name, app.argv[c]);
2408                   StripExtension(name);
2409                   GetLastDirectory(name, name);
2410                   newProjectDialog.projectName.contents = name;
2411                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
2412                   newProjectDialog.locationEditBox.path = parentPath;
2413                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
2414
2415                   newProjectDialog.Modal();
2416                   if(projectView)
2417                   {
2418                      ideSettings.AddRecentProject(projectView.fileName);
2419                      ide.UpdateRecentMenus();
2420                      settingsContainer.Save();
2421                   }
2422                }
2423                // Open only one project
2424                break;
2425             }
2426             else
2427                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2428          }
2429       }
2430       return true;
2431    }
2432
2433    void OnDestroy()
2434    {
2435       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2436       if(projectView)
2437       {
2438          projectView.visible = false;
2439          projectView.Destroy(0);
2440          projectView = null;
2441       }
2442 #ifdef GDB_DEBUG_GUI
2443       gdbDialog.Destroy(0);
2444       delete gdbDialog;
2445 #endif
2446    }
2447
2448    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
2449    {
2450       int c, len, count;
2451       char * newList;
2452       char * oldPaths[128];
2453       String oldList = new char[maxPathLen];
2454       Array<String> newExePaths { };
2455       //Map<String, bool> exePathExists { };
2456       bool found = false;
2457 #if defined(__unix__) || defined(__APPLE__)
2458       Array<String> newLibPaths { };
2459       Map<String, bool> libPathExists { };
2460 #endif
2461
2462       if(projectsDirs)
2463       {
2464          for(prj : workspace.projects)
2465          {
2466             DirExpression targetDirExp;
2467
2468             // SKIP FIRST PROJECT...
2469             if(prj == workspace.projects.firstIterator.data) continue;
2470
2471             // NOTE: Right now the additional project config dir will be
2472             //       obtained when the debugger is started, so toggling it
2473             //       while building will change which library gets used.
2474             //       To go with the initial state, e.g. when F5 was pressed,
2475             //       we nould need to keep a list of all project's active
2476             //       config upon startup.
2477             targetDirExp = prj.GetTargetDir(compiler, prj.config);
2478
2479             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2480                cfg = prj.config;
2481             else
2482             {
2483                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2484                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2485                      break;
2486                if(!cfg)
2487                {
2488                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2489                      if(cfg.targetType == sharedLibrary && cfg.debug)
2490                         break;
2491                }
2492             }*/
2493             if(targetDirExp.dir)
2494             {
2495                char buffer[MAX_LOCATION];
2496 #if defined(__WIN32__)
2497                Array<String> paths = newExePaths;
2498 #else
2499                Array<String> paths = newLibPaths;
2500 #endif
2501                GetSystemPathBuffer(buffer, prj.topNode.path);
2502                PathCat(buffer, targetDirExp.dir);
2503                for(p : paths)
2504                {
2505                   if(!fstrcmp(p, buffer))
2506                   {
2507                      found = true;
2508                      break;
2509                   }
2510                }
2511                if(!found)
2512                   paths.Add(CopyString(buffer));
2513             }
2514             delete targetDirExp;
2515          }
2516       }
2517
2518       for(item : compiler.executableDirs)
2519       {
2520          found = false;
2521          for(p : newExePaths)
2522          {
2523             if(!fstrcmp(p, item))
2524             {
2525                found = true;
2526                break;
2527             }
2528          }
2529          if(!found)
2530             newExePaths.Add(CopySystemPath(item));
2531       }
2532
2533       GetEnvironment("PATH", oldList, maxPathLen);
2534 /*#ifdef _DEBUG
2535       printf("Old value of PATH: %s\n", oldList);
2536 #endif*/
2537       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2538       for(c = 0; c < count; c++)
2539       {
2540          found = false;
2541          for(p : newExePaths)
2542          {
2543             if(!fstrcmp(p, oldPaths[c]))
2544             {
2545                found = true;
2546                break;
2547             }
2548          }
2549          if(!found)
2550             newExePaths.Add(CopySystemPath(oldPaths[c]));
2551       }
2552
2553       len = 0;
2554       for(path : newExePaths)
2555          len += strlen(path) + 1;
2556       newList = new char[len + 1];
2557       newList[0] = '\0';
2558       for(path : newExePaths)
2559       {
2560          strcat(newList, path);
2561          strcat(newList, pathListSep);
2562       }
2563       newList[len - 1] = '\0';
2564       SetEnvironment("PATH", newList);
2565 /*#ifdef _DEBUG
2566       printf("New value of PATH: %s\n", newList);
2567 #endif*/
2568       delete newList;
2569
2570       newExePaths.Free();
2571       delete newExePaths;
2572
2573 #if defined(__unix__) || defined(__APPLE__)
2574
2575       for(item : compiler.libraryDirs)
2576       {
2577          if(!libPathExists[item])  // fstrcmp should be used
2578          {
2579             newLibPaths.Add(item);
2580             libPathExists[item] = true;
2581          }
2582       }
2583
2584 #if defined(__APPLE__)
2585       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2586 #else
2587       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2588 #endif
2589 /*#ifdef _DEBUG
2590       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
2591 #endif*/
2592       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2593       for(c = 0; c < count; c++)
2594       {
2595          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2596          {
2597             newLibPaths.Add(oldPaths[c]);
2598             libPathExists[oldPaths[c]] = true;
2599          }
2600       }
2601
2602       len = 0;
2603       for(path : newLibPaths)
2604          len += strlen(path) + 1;
2605       newList = new char[len + 1];
2606       newList[0] = '\0';
2607       for(path : newLibPaths)
2608       {
2609          strcat(newList, path);
2610          strcat(newList, pathListSep);
2611       }
2612       newList[len - 1] = '\0';
2613 #if defined(__APPLE__)
2614       SetEnvironment("DYLD_LIBRARY_PATH", newList);
2615 #else
2616       SetEnvironment("LD_LIBRARY_PATH", newList);
2617 #endif
2618 /*#ifdef _DEBUG
2619       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
2620 #endif*/
2621       delete newList;
2622
2623       delete newLibPaths;
2624       delete libPathExists;
2625 #endif
2626
2627       if(compiler.distccEnabled && compiler.distccHosts)
2628          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
2629
2630       delete oldList;
2631    }
2632
2633    void DestroyTemporaryProjectDir()
2634    {
2635       if(tmpPrjDir && tmpPrjDir[0])
2636       {
2637          if(FileExists(tmpPrjDir).isDirectory)
2638             DestroyDir(tmpPrjDir);
2639          property::tmpPrjDir = null;
2640       }
2641    }
2642
2643    IDEWorkSpace()
2644    {
2645       // Graphics Driver Menu
2646       int c;
2647
2648       /*
2649       app.currentSkin.selectionColor = selectionColor;
2650       app.currentSkin.selectionText = selectionText;
2651       */
2652
2653 /*
2654       driverItems = new MenuItem[app.numDrivers];
2655       for(c = 0; c < app.numDrivers; c++)
2656       {
2657          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
2658          driverItems[c].id = c;
2659          driverItems[c].isRadio = true;         
2660       }
2661 */
2662       driverItems = new MenuItem[2];
2663 #if defined(__unix__)
2664          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
2665          driverItems[0].id = 0;
2666          driverItems[0].isRadio = true;         
2667 #else
2668          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
2669          driverItems[0].id = 0;
2670          driverItems[0].isRadio = true;         
2671 #endif
2672          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
2673          driverItems[1].id = 1;
2674          driverItems[1].isRadio = true;         
2675
2676 /*      skinItems = new MenuItem[app.numSkins];
2677       for(c = 0; c < app.numSkins; c++)
2678       {
2679          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
2680          skinItems[c].id = c;
2681          skinItems[c].isRadio = true;         
2682       }
2683 */
2684       ideFileDialog.master = this;
2685       ideProjectFileDialog.master = this;
2686
2687       //SetDriverAndSkin();
2688       return true;
2689    }
2690
2691    void UpdateRecentMenus()
2692    {
2693       int c;
2694       Menu fileMenu = menu.FindMenu($"File");
2695       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
2696       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
2697       char itemName[MAX_LOCATION + 4];
2698       MenuItem item;
2699
2700       recentFiles.Clear();
2701       c = 0;
2702
2703       for(recent : ideSettings.recentFiles)
2704       {
2705          sprintf(itemName, "%d %s", 1 + c, recent);
2706          MakeSystemPath(itemName);
2707          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
2708          c++;
2709       }
2710
2711       recentProjects.Clear();
2712       c = 0;
2713       for(recent : ideSettings.recentProjects)
2714       {
2715          sprintf(itemName, "%d %s", 1 + c, recent);
2716          MakeSystemPath(itemName);
2717          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
2718          c++;
2719       }
2720    }
2721
2722    ~IDEWorkSpace()
2723    {
2724       delete driverItems;
2725       delete skinItems;
2726       delete ideSettings;
2727    }
2728 }
2729
2730 void DestroyDir(char * path)
2731 {
2732    RecursiveDeleteFolderFSI fsi { };
2733    fsi.Iterate(path);
2734    delete fsi;
2735 }
2736
2737 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
2738 {
2739    bool preserveRootFolder;
2740
2741    void OutFolder(char * folderPath, bool isRoot)
2742    {
2743       if(!(preserveRootFolder && isRoot))
2744          RemoveDir(folderPath);
2745    }
2746
2747    bool OnFile(char * filePath)
2748    {
2749       DeleteFile(filePath);
2750       return true;
2751    }
2752 }
2753
2754 class IDEApp : GuiApplication
2755 {
2756    //driver = "Win32Console";
2757    // driver = "OpenGL";
2758    // skin = "Aqua";
2759    //skin = "TVision";
2760    bool Init()
2761    {
2762       SetLoggingMode(stdOut, null);
2763       //SetLoggingMode(debug, null);
2764
2765       settingsContainer.Load();
2766 #if defined(__unix__) || defined(__APPLE__)
2767       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
2768 #else
2769       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
2770 #endif
2771       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
2772
2773       SetInIDE(true);
2774
2775       desktop.text = titleECEREIDE;
2776       /*
2777       int c;
2778       for(c = 1; c<app.argc; c++)
2779       {
2780          char fullPath[MAX_LOCATION];
2781          GetWorkingDir(fullPath, MAX_LOCATION);
2782          PathCat(fullPath, app.argv[c]);
2783          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2784       }
2785       */
2786       return true;
2787    }
2788 }
2789
2790 IDEMainFrame ideMainFrame { };
2791
2792 define app = ((IDEApp)__thisModule);
2793 #ifdef _DEBUG
2794 define titleECEREIDE = $"ECERE IDE (Debug)";
2795 #else
2796 define titleECEREIDE = $"ECERE IDE";
2797 #endif