ide:debugger: completed gui part of breakpoint hit counter implementation. prevented...
[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          GetWorkingDir(fullPath, MAX_LOCATION);
2371          PathCat(fullPath, app.argv[c]);
2372          if(FileExists(fullPath))
2373             ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2374       }
2375       return true;
2376    }
2377
2378    void OnDestroy()
2379    {
2380       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2381       if(projectView)
2382       {
2383          projectView.visible = false;
2384          projectView.Destroy(0);
2385          projectView = null;
2386       }
2387 #ifdef GDB_DEBUG_GUI
2388       gdbDialog.Destroy(0);
2389       delete gdbDialog;
2390 #endif
2391    }
2392
2393    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
2394    {
2395       int c, len, count;
2396       char * newList;
2397       char * oldPaths[128];
2398       String oldList = new char[maxPathLen];
2399       Array<String> newExePaths { };
2400       //Map<String, bool> exePathExists { };
2401       bool found = false;
2402 #if defined(__unix__) || defined(__APPLE__)
2403       Array<String> newLibPaths { };
2404       Map<String, bool> libPathExists { };
2405 #endif
2406
2407       if(projectsDirs)
2408       {
2409          for(prj : workspace.projects)
2410          {
2411             DirExpression targetDirExp;
2412
2413             // SKIP FIRST PROJECT...
2414             if(prj == workspace.projects.firstIterator.data) continue;
2415
2416             // NOTE: Right now the additional project config dir will be
2417             //       obtained when the debugger is started, so toggling it
2418             //       while building will change which library gets used.
2419             //       To go with the initial state, e.g. when F5 was pressed,
2420             //       we nould need to keep a list of all project's active
2421             //       config upon startup.
2422             targetDirExp = prj.GetTargetDir(compiler, prj.config);
2423
2424             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2425                cfg = prj.config;
2426             else
2427             {
2428                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2429                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2430                      break;
2431                if(!cfg)
2432                {
2433                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2434                      if(cfg.targetType == sharedLibrary && cfg.debug)
2435                         break;
2436                }
2437             }*/
2438             if(targetDirExp.dir)
2439             {
2440                char buffer[MAX_LOCATION];
2441 #if defined(__WIN32__)
2442                Array<String> paths = newExePaths;
2443 #else
2444                Array<String> paths = newLibPaths;
2445 #endif
2446                GetSystemPathBuffer(buffer, prj.topNode.path);
2447                PathCat(buffer, targetDirExp.dir);
2448                for(p : paths)
2449                {
2450                   if(!fstrcmp(p, buffer))
2451                   {
2452                      found = true;
2453                      break;
2454                   }
2455                }
2456                if(!found)
2457                   paths.Add(CopyString(buffer));
2458             }
2459             delete targetDirExp;
2460          }
2461       }
2462
2463       for(item : compiler.executableDirs)
2464       {
2465          found = false;
2466          for(p : newExePaths)
2467          {
2468             if(!fstrcmp(p, item))
2469             {
2470                found = true;
2471                break;
2472             }
2473          }
2474          if(!found)
2475             newExePaths.Add(CopySystemPath(item));
2476       }
2477
2478       GetEnvironment("PATH", oldList, maxPathLen);
2479 /*#ifdef _DEBUG
2480       printf("Old value of PATH: %s\n", oldList);
2481 #endif*/
2482       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2483       for(c = 0; c < count; c++)
2484       {
2485          found = false;
2486          for(p : newExePaths)
2487          {
2488             if(!fstrcmp(p, oldPaths[c]))
2489             {
2490                found = true;
2491                break;
2492             }
2493          }
2494          if(!found)
2495             newExePaths.Add(CopySystemPath(oldPaths[c]));
2496       }
2497
2498       len = 0;
2499       for(path : newExePaths)
2500          len += strlen(path) + 1;
2501       newList = new char[len + 1];
2502       newList[0] = '\0';
2503       for(path : newExePaths)
2504       {
2505          strcat(newList, path);
2506          strcat(newList, pathListSep);
2507       }
2508       newList[len - 1] = '\0';
2509       SetEnvironment("PATH", newList);
2510 /*#ifdef _DEBUG
2511       printf("New value of PATH: %s\n", newList);
2512 #endif*/
2513       delete newList;
2514
2515       newExePaths.Free();
2516       delete newExePaths;
2517
2518 #if defined(__unix__) || defined(__APPLE__)
2519
2520       for(item : compiler.libraryDirs)
2521       {
2522          if(!libPathExists[item])  // fstrcmp should be used
2523          {
2524             newLibPaths.Add(item);
2525             libPathExists[item] = true;
2526          }
2527       }
2528
2529 #if defined(__APPLE__)
2530       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2531 #else
2532       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2533 #endif
2534 /*#ifdef _DEBUG
2535       printf("Old value of [DY]LD_LIBRARY_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          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2541          {
2542             newLibPaths.Add(oldPaths[c]);
2543             libPathExists[oldPaths[c]] = true;
2544          }
2545       }
2546
2547       len = 0;
2548       for(path : newLibPaths)
2549          len += strlen(path) + 1;
2550       newList = new char[len + 1];
2551       newList[0] = '\0';
2552       for(path : newLibPaths)
2553       {
2554          strcat(newList, path);
2555          strcat(newList, pathListSep);
2556       }
2557       newList[len - 1] = '\0';
2558 #if defined(__APPLE__)
2559       SetEnvironment("DYLD_LIBRARY_PATH", newList);
2560 #else
2561       SetEnvironment("LD_LIBRARY_PATH", newList);
2562 #endif
2563 /*#ifdef _DEBUG
2564       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
2565 #endif*/
2566       delete newList;
2567
2568       delete newLibPaths;
2569       delete libPathExists;
2570 #endif
2571
2572       if(compiler.distccEnabled && compiler.distccHosts)
2573          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
2574
2575       delete oldList;
2576    }
2577
2578    void DestroyTemporaryProjectDir()
2579    {
2580       if(tmpPrjDir && tmpPrjDir[0])
2581       {
2582          if(FileExists(tmpPrjDir).isDirectory)
2583             DestroyDir(tmpPrjDir);
2584          property::tmpPrjDir = null;
2585       }
2586    }
2587
2588    IDEWorkSpace()
2589    {
2590       // Graphics Driver Menu
2591       int c;
2592
2593       /*
2594       app.currentSkin.selectionColor = selectionColor;
2595       app.currentSkin.selectionText = selectionText;
2596       */
2597
2598 /*
2599       driverItems = new MenuItem[app.numDrivers];
2600       for(c = 0; c < app.numDrivers; c++)
2601       {
2602          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
2603          driverItems[c].id = c;
2604          driverItems[c].isRadio = true;         
2605       }
2606 */
2607       driverItems = new MenuItem[2];
2608 #if defined(__unix__)
2609          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
2610          driverItems[0].id = 0;
2611          driverItems[0].isRadio = true;         
2612 #else
2613          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
2614          driverItems[0].id = 0;
2615          driverItems[0].isRadio = true;         
2616 #endif
2617          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
2618          driverItems[1].id = 1;
2619          driverItems[1].isRadio = true;         
2620
2621 /*      skinItems = new MenuItem[app.numSkins];
2622       for(c = 0; c < app.numSkins; c++)
2623       {
2624          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
2625          skinItems[c].id = c;
2626          skinItems[c].isRadio = true;         
2627       }
2628 */
2629       ideFileDialog.master = this;
2630       ideProjectFileDialog.master = this;
2631
2632       //SetDriverAndSkin();
2633       return true;
2634    }
2635
2636    void UpdateRecentMenus()
2637    {
2638       int c;
2639       Menu fileMenu = menu.FindMenu($"File");
2640       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
2641       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
2642       char itemName[MAX_LOCATION + 4];
2643       MenuItem item;
2644
2645       recentFiles.Clear();
2646       c = 0;
2647
2648       for(recent : ideSettings.recentFiles)
2649       {
2650          sprintf(itemName, "%d %s", 1 + c, recent);
2651          MakeSystemPath(itemName);
2652          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
2653          c++;
2654       }
2655
2656       recentProjects.Clear();
2657       c = 0;
2658       for(recent : ideSettings.recentProjects)
2659       {
2660          sprintf(itemName, "%d %s", 1 + c, recent);
2661          MakeSystemPath(itemName);
2662          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
2663          c++;
2664       }
2665    }
2666
2667    ~IDEWorkSpace()
2668    {
2669       delete driverItems;
2670       delete skinItems;
2671       delete ideSettings;
2672    }
2673 }
2674
2675 void DestroyDir(char * path)
2676 {
2677    RecursiveDeleteFolderFSI fsi { };
2678    fsi.Iterate(path);
2679    delete fsi;
2680 }
2681
2682 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
2683 {
2684    bool preserveRootFolder;
2685
2686    void OutFolder(char * folderPath, bool isRoot)
2687    {
2688       if(!(preserveRootFolder && isRoot))
2689          RemoveDir(folderPath);
2690    }
2691
2692    bool OnFile(char * filePath)
2693    {
2694       DeleteFile(filePath);
2695       return true;
2696    }
2697 }
2698
2699 class IDEApp : GuiApplication
2700 {
2701    //driver = "Win32Console";
2702    // driver = "OpenGL";
2703    // skin = "Aqua";
2704    //skin = "TVision";
2705    bool Init()
2706    {
2707       SetLoggingMode(stdOut, null);
2708       //SetLoggingMode(debug, null);
2709
2710       settingsContainer.Load();
2711 #if defined(__unix__) || defined(__APPLE__)
2712       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
2713 #else
2714       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
2715 #endif
2716       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
2717
2718       SetInIDE(true);
2719
2720       desktop.text = titleECEREIDE;
2721       /*
2722       int c;
2723       for(c = 1; c<app.argc; c++)
2724       {
2725          char fullPath[MAX_LOCATION];
2726          GetWorkingDir(fullPath, MAX_LOCATION);
2727          PathCat(fullPath, app.argv[c]);
2728          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2729       }
2730       */
2731       return true;
2732    }
2733 }
2734
2735 IDEMainFrame ideMainFrame { };
2736
2737 define app = ((IDEApp)__thisModule);
2738 #ifdef _DEBUG
2739 define titleECEREIDE = $"ECERE IDE (Debug)";
2740 #else
2741 define titleECEREIDE = $"ECERE IDE";
2742 #endif