ide: MessageBox parent/master fixes to be root windows with native decorations
[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                   }
836                }
837             }
838             return true;
839          }
840       }
841       MenuDivider { projectMenu };
842       MenuItem activeCompilerItem
843       {
844          projectMenu, $"Active Compiler...", g, /*altF5, */disabled = true;
845          bool NotifySelect(MenuItem selection, Modifiers mods)
846          {
847             projectView.MenuCompiler(null, mods);
848             return true;
849          }
850       }
851       MenuItem projectActiveConfigItem
852       {
853          projectMenu, $"Active Configuration...", g, altF5, disabled = true;
854          bool NotifySelect(MenuItem selection, Modifiers mods)
855          {
856             projectView.MenuConfig(projectView.active ? selection : null, mods);
857             return true;
858          }
859       }
860       MenuItem projectSettingsItem
861       {
862          projectMenu, $"Settings...", s, altF7, disabled = true;
863          bool NotifySelect(MenuItem selection, Modifiers mods)
864          {
865             projectView.MenuSettings(projectView.active ? selection : null, mods);
866             return true;
867          }
868       }
869       MenuDivider { projectMenu };
870       MenuItem projectBrowseFolderItem
871       {
872          projectMenu, $"Browse Project Folder", p, disabled = true;
873          bool NotifySelect(MenuItem selection, Modifiers mods)
874          {
875             if(projectView)
876                projectView.MenuBrowseFolder(null, mods);
877             return true;
878          }
879       }
880       MenuDivider { projectMenu };
881       MenuItem projectRunItem
882       {
883          projectMenu, $"Run", r, ctrlF5, disabled = true;
884          bitmap = { ":actions/run.png" };
885          bool NotifySelect(MenuItem selection, Modifiers mods)
886          {
887             if(projectView)
888                projectView.Run(null, mods);
889             return true;
890          }
891       }
892       MenuItem projectBuildItem
893       {
894          projectMenu, $"Build", b, f7, disabled = true;
895          bitmap = { ":actions/build.png" };
896          bool NotifySelect(MenuItem selection, Modifiers mods)
897          {
898             if(projectView)
899                projectView.ProjectBuild(projectView.active ? selection : null, mods);
900             return true;
901          }
902       }
903       MenuItem projectLinkItem
904       {
905          projectMenu, $"Relink", l, disabled = true;
906          bitmap = { ":actions/relink.png" };
907          bool NotifySelect(MenuItem selection, Modifiers mods)
908          {
909             if(projectView)
910                projectView.ProjectLink(projectView.active ? selection : null, mods);
911             return true;
912          }
913       }
914       MenuItem projectRebuildItem
915       {
916          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
917          bitmap = { ":actions/rebuild.png" };
918          bool NotifySelect(MenuItem selection, Modifiers mods)
919          {
920             if(projectView)
921                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
922             return true;
923          }
924       }
925       MenuItem projectCleanItem
926       {
927          projectMenu, $"Clean", e, disabled = true;
928          bitmap = { ":actions/clean.png" };
929          bool NotifySelect(MenuItem selection, Modifiers mods)
930          {
931             if(projectView)
932             {
933                debugger.Stop();
934                projectView.ProjectClean(projectView.active ? selection : null, mods);
935             }
936             return true;
937          }
938       }
939       MenuItem projectRegenerateItem
940       {
941          projectMenu, $"Regenerate Makefile", m, disabled = true;
942          bitmap = { ":actions/regMakefile.png" };
943          bool NotifySelect(MenuItem selection, Modifiers mods)
944          {
945             if(projectView)
946                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
947             return true;
948          }
949       }
950       MenuItem projectCompileItem;
951    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
952       MenuItem debugStartResumeItem
953       {
954          debugMenu, $"Start", s, f5, disabled = true;
955          bitmap = { ":actions/debug.png" };
956          NotifySelect = MenuDebugStart;
957       }
958       bool MenuDebugStart(MenuItem selection, Modifiers mods)
959       {
960          if(projectView)
961          {
962             debugStartResumeItem.disabled = true; // a very rare exception to calling AdjustDebugMenus
963             if(!projectView.DebugStart())
964                debugStartResumeItem.disabled = false; // same exception
965          }
966          return true;
967       }
968       bool MenuDebugResume(MenuItem selection, Modifiers mods)
969       {
970          if(projectView)
971             projectView.DebugResume();
972          return true;
973       }
974       MenuItem debugRestartItem
975       {
976          debugMenu, $"Restart", r, Key { f5, ctrl = true, shift = true }, disabled = true;
977          bitmap = { ":actions/restart.png" };
978          bool NotifySelect(MenuItem selection, Modifiers mods)
979          {
980             if(projectView)
981                projectView.DebugRestart();
982             return true;
983          }
984       }
985       MenuItem debugBreakItem
986       {
987          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
988          bitmap = { ":actions/pause.png" };
989          bool NotifySelect(MenuItem selection, Modifiers mods)
990          {
991             if(projectView)
992                projectView.DebugBreak();
993             return true;
994          }
995       }
996       MenuItem debugStopItem
997       {
998          debugMenu, $"Stop", p, shiftF5, disabled = true;
999          bitmap = { ":actions/stopDebug.png" };
1000          bool NotifySelect(MenuItem selection, Modifiers mods)
1001          {
1002             if(projectView)
1003                projectView.DebugStop();
1004             return true;
1005          }
1006       }
1007       MenuDivider { debugMenu };
1008       MenuItem debugStepIntoItem
1009       {
1010          debugMenu, $"Step Into", i, f11, disabled = true;
1011          bitmap = { ":actions/stepInto.png" };
1012          bool NotifySelect(MenuItem selection, Modifiers mods)
1013          {
1014             if(projectView)
1015                projectView.DebugStepInto();
1016             return true;
1017          }
1018       }
1019       MenuItem debugStepOverItem
1020       {
1021          debugMenu, $"Step Over", v, f10, disabled = true;
1022          bitmap = { ":actions/stepOver.png" };
1023          bool NotifySelect(MenuItem selection, Modifiers mods)
1024          {
1025             if(projectView)
1026                projectView.DebugStepOver(false);
1027             return true;
1028          }
1029       }
1030       MenuItem debugStepOutItem
1031       {
1032          debugMenu, $"Step Out", o, shiftF11, disabled = true;
1033          bitmap = { ":actions/stepOut.png" };
1034          bool NotifySelect(MenuItem selection, Modifiers mods)
1035          {
1036             if(projectView)
1037                projectView.DebugStepOut(false);
1038             return true;
1039          }
1040       }
1041       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
1042       MenuItem debugSkipStepOverItem
1043       {
1044          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
1045          bool NotifySelect(MenuItem selection, Modifiers mods)
1046          {
1047             if(projectView)
1048                projectView.DebugStepOver(true);
1049             return true;
1050          }
1051       }
1052       MenuItem debugSkipStepOutItem
1053       {
1054          debugMenu, $"Step Out Skipping Breakpoints", t, Key { f11, ctrl = true, shift = true }, disabled = true;
1055          bitmap = { ":actions/skipBreaks.png" };
1056          bool NotifySelect(MenuItem selection, Modifiers mods)
1057          {
1058             if(projectView)
1059                projectView.DebugStepOut(true);
1060             return true;
1061          }
1062       }
1063       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
1064       //MenuDivider { debugMenu };
1065       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
1066    MenuPlacement imageMenu { menu, $"Image", i };
1067    Menu viewMenu { menu, $"View", v };
1068       MenuItem viewProjectItem
1069       {
1070          viewMenu, $"Project View", j, alt0, disabled = true;
1071          bool NotifySelect(MenuItem selection, Modifiers mods)
1072          {
1073             if(projectView)
1074             {
1075                projectView.visible = true;
1076                projectView.Activate();
1077             }
1078             return true;
1079          }
1080       }
1081       MenuPlacement { viewMenu, $"View Designer" };
1082       MenuPlacement { viewMenu, $"View Code" };
1083       MenuPlacement { viewMenu, $"View Properties" };
1084       MenuPlacement { viewMenu, $"View Methods" };
1085       MenuItem viewDesignerItem
1086       {
1087          viewMenu, $"View Designer", d, f8;
1088          bool NotifySelect(MenuItem selection, Modifiers mods)
1089          {
1090             Window client = activeClient;
1091             Class dataType = client._class;
1092             if(!strcmp(dataType.name, "Designer"))
1093             {
1094                client.visible = true;
1095                client.Activate();
1096             }
1097             else
1098                ((CodeEditor)client).ViewDesigner();
1099             return true;
1100          }
1101       }
1102       MenuItem viewCodeItem
1103       {
1104          viewMenu, $"View Code", c, f8;
1105          bool NotifySelect(MenuItem selection, Modifiers mods)
1106          {
1107             Window client = activeClient;
1108             Class dataType = client._class;
1109             if(!strcmp(dataType.name, "Designer"))
1110                client = ((Designer)client).codeEditor;
1111
1112             client.Activate();
1113             // Do this after so the caret isn't moved yet...
1114             client.visible = true;
1115             return true;
1116          }
1117       }
1118       MenuItem viewPropertiesItem
1119       {
1120          viewMenu, $"View Properties", p, f4;
1121          bool NotifySelect(MenuItem selection, Modifiers mods)
1122          {
1123             sheet.visible = true;
1124             sheet.sheetSelected = properties;
1125             sheet.Activate();
1126             return true;
1127          }
1128       }
1129       MenuItem viewMethodsItem
1130       {
1131          viewMenu, $"View Methods", m, f4;
1132          bool NotifySelect(MenuItem selection, Modifiers mods)
1133          {
1134             sheet.visible = true;
1135             sheet.sheetSelected = methods;
1136             sheet.Activate();
1137             return true;
1138          }
1139       }
1140       MenuItem viewToolBoxItem
1141       {
1142          viewMenu, $"View Toolbox", x, f12;
1143          bool NotifySelect(MenuItem selection, Modifiers mods)
1144          {
1145             toolBox.visible = true;
1146             toolBox.Activate();
1147             return true;
1148          }
1149       }
1150       MenuItem viewOutputItem
1151       {
1152          viewMenu, $"Output", o, alt2;
1153          bool NotifySelect(MenuItem selection, Modifiers mods)
1154          {
1155             outputView.Show();
1156             return true;
1157          }
1158       }
1159       MenuItem viewWatchesItem
1160       {
1161          viewMenu, $"Watches", w, alt3;
1162          bool NotifySelect(MenuItem selection, Modifiers mods)
1163          {
1164             watchesView.Show();
1165             return true;
1166          }
1167       }
1168       MenuItem viewThreadsItem
1169       {
1170          viewMenu, $"Threads", t, alt4;
1171          bool NotifySelect(MenuItem selection, Modifiers mods)
1172          {
1173             threadsView.Show();
1174             return true;
1175          }
1176       }
1177       MenuItem viewBreakpointsItem
1178       {
1179          viewMenu, $"Breakpoints", b, alt5;
1180          bool NotifySelect(MenuItem selection, Modifiers mods)
1181          {
1182             breakpointsView.Show();
1183             return true;
1184          }
1185       }
1186       MenuItem viewCallStackItem
1187       {
1188          viewMenu, $"Call Stack", s, alt7;
1189          bool NotifySelect(MenuItem selection, Modifiers mods)
1190          {
1191             callStackView.Show();
1192             return true;
1193          }
1194       }
1195       MenuItem viewAllDebugViews
1196       {
1197          viewMenu, $"All Debug Views", a, alt9;
1198          bool NotifySelect(MenuItem selection, Modifiers mods)
1199          {
1200             outputView.Show();
1201             watchesView.Show();
1202             threadsView.Show();
1203             callStackView.Show();
1204             breakpointsView.Show();
1205             return true;
1206          }
1207       }
1208 #ifdef GDB_DEBUG_GUI
1209       MenuDivider { viewMenu };
1210       MenuItem viewGDBItem
1211       {
1212          viewMenu, $"GDB Dialog", g, Key { f9, shift = true, ctrl = true };
1213          bool NotifySelect(MenuItem selection, Modifiers mods)
1214          {
1215             gdbDialog.Show();
1216             return true;
1217          }
1218       }
1219 #endif
1220       MenuDivider { viewMenu };
1221       MenuItem viewColorPicker
1222       {
1223          viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
1224          bool NotifySelect(MenuItem selection, Modifiers mods)
1225          {
1226             ColorPicker colorPicker { master = this, parent = this, stayOnTop = true };
1227             colorPicker.Create();
1228             return true;
1229          }
1230       }
1231       MenuDivider { viewMenu };
1232       /*
1233       MenuItem
1234       {
1235          viewMenu, "Full Screen", f, checkable = true;
1236
1237          bool NotifySelect(MenuItem selection, Modifiers mods)
1238          {
1239             app.fullScreen ^= true;
1240             SetDriverAndSkin();
1241             anchor = { 0, 0, 0, 0 };
1242             return true;
1243          }
1244       };
1245       */
1246       Menu driversMenu { viewMenu, $"Graphics Driver", v };
1247       //Menu skinsMenu { viewMenu, "GUI Skins", k };
1248    Menu windowMenu { menu, $"Window", w };
1249       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
1250       MenuDivider { windowMenu };
1251       MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext };
1252       MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious };
1253       MenuDivider { windowMenu };
1254       MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade };
1255       MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz };
1256       MenuItem { windowMenu, $"Tile Vertically", v, NotifySelect = MenuWindowTileVert };
1257       MenuItem { windowMenu, $"Arrange Icons", a, NotifySelect = MenuWindowArrangeIcons };
1258       MenuDivider { windowMenu };
1259       MenuItem { windowMenu, $"Windows...", w, NotifySelect = MenuWindowWindows };
1260    Menu helpMenu { menu, $"Help", h };
1261       MenuItem
1262       {
1263          helpMenu, $"API Reference", r, f1;
1264          bool NotifySelect(MenuItem selection, Modifiers mods)
1265          {
1266             Execute("documentor");
1267             return true;
1268          }
1269       }
1270       MenuDivider { helpMenu };
1271       MenuItem
1272       {
1273          helpMenu, $"About...", a;
1274          bool NotifySelect(MenuItem selection, Modifiers mods)
1275          {
1276             AboutIDE { master = this }.Modal();
1277             return true;
1278          }
1279       }
1280
1281    property ToolBox toolBox
1282    {
1283       get { return toolBox; }
1284    }
1285
1286    property Sheet sheet
1287    {
1288       get { return sheet; }
1289    }
1290
1291    property Project project
1292    {
1293       get { return projectView ? projectView.project : null; }
1294    }
1295
1296    property Workspace workspace
1297    {
1298       get { return projectView ? projectView.workspace : null; }
1299    }
1300
1301    FindInFilesDialog findInFilesDialog
1302    {
1303       master = this, parent = this;
1304       filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter);
1305       filter = 1;
1306    };
1307
1308 #ifdef GDB_DEBUG_GUI
1309    GDBDialog gdbDialog
1310    {
1311       master = this, parent = this;
1312       anchor = { left = 100, top = 100, right = 100, bottom = 100 };
1313
1314       void OnCommand(char * string)
1315       {
1316          if(ide)
1317             ide.debugger.SendGDBCommand(string);
1318       }
1319    };
1320 #endif
1321    
1322    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
1323    {
1324       //app.driver = app.drivers[selection.id];
1325 #ifdef __unix__
1326       app.driver = selection.id ? "OpenGL" : "X";
1327 #else
1328       app.driver = selection.id ? "OpenGL" : "GDI";
1329 #endif
1330       delete ideSettings.displayDriver;
1331       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
1332
1333       settingsContainer.Save();
1334       //SetDriverAndSkin();
1335       return true;
1336    }
1337
1338    bool NotifySelectDisplaySkin(MenuItem selection, Modifiers mods)
1339    {
1340       app.skin = app.skins[selection.id];
1341       SetDriverAndSkin();
1342       return true;
1343    }
1344
1345    void SetDriverAndSkin()
1346    {
1347       int c;
1348       for(c = 0; c < app.numSkins; c++)
1349          if(!strcmp(app.skins[c], app.skin))
1350          {
1351             skinItems[c].checked = true;
1352             break;
1353          }
1354       for(c = 0; c < app.numDrivers; c++)
1355          if(!strcmp(app.drivers[c], app.driver))
1356          {
1357             driverItems[c].checked = true;
1358             break;
1359          }
1360    }
1361
1362    ProjectView CreateProjectView(Workspace workspace, char * fileName)
1363    {
1364       Project project = workspace.projects.firstIterator.data;
1365       projectView = ProjectView
1366       {
1367          this;
1368          fileName = fileName;
1369          
1370          void NotifyDestroyed(Window window, DialogResult result)
1371          {
1372             projectView = null;
1373             text = titleECEREIDE;
1374             
1375             AdjustMenus();
1376          }
1377       };
1378       projectView.Create();
1379       RepositionWindows(false);
1380
1381       // Leave it after Create to avoid flicker due to seeing IDE without a project view
1382       projectView.workspace = workspace;
1383       projectView.project = project;
1384       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
1385
1386       AdjustMenus();
1387
1388       ide.breakpointsView.LoadFromWorkspace();
1389       ide.watchesView.LoadFromWorkspace();
1390
1391       findInFilesDialog.projectNodeField.userData = projectView;
1392
1393       {
1394          char fileName[MAX_LOCATION];
1395          strcpy(fileName, project.topNode.path);
1396          PathCat(fileName, project.topNode.name);
1397       }
1398       return projectView;
1399    }
1400
1401    bool GetDebugMenusDisabled()
1402    {
1403       if(projectView)
1404       {
1405          Project project = projectView.project;
1406          if(project)
1407             if(project.GetTargetType(project.config) == executable)
1408                return false;
1409            
1410       }
1411       return true;
1412    }
1413
1414    void RepositionWindows(bool expand)
1415    {
1416       if(this)
1417       {
1418          Window child;
1419          bool inDebugMode = debugger.isActive;
1420          bool callStackVisible = expand ? false : callStackView.visible;
1421          bool threadsVisible = expand ? false : threadsView.visible;
1422          bool watchesVisible = expand ? false : watchesView.visible;
1423          bool breakpointsVisible = expand ? false : breakpointsView.visible;
1424          bool toolBoxVisible = toolBox.visible;
1425          bool outputVisible = expand ? false : outputView.visible;
1426          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
1427          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
1428          
1429          for(child = firstChild; child; child = child.next)
1430          {
1431             if(child._class == class(CodeEditor) || child._class == class(Designer) || 
1432                child._class == class(Sheet) || child._class == class(ProjectView))
1433             {
1434                Anchor anchor = child.anchor;
1435                anchor.top = topDistance;
1436                anchor.bottom = bottomDistance;
1437                if(child._class == class(CodeEditor) || child._class == class(Designer))
1438                {
1439                   anchor.right = toolBoxVisible ? 150 : 0;
1440                }
1441                child.anchor = anchor;
1442             }
1443             else if(expand)
1444             {
1445                if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) || 
1446                   child._class == class(BreakpointsView))
1447                   child.visible = false;
1448             }
1449          }
1450          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
1451          Update(null);
1452       }
1453    }
1454
1455    bool ShowCodeEditor()
1456    {
1457       if(activeClient)
1458          activeClient.Activate();
1459       else if(projectView)
1460       { 
1461          projectView.visible = true;
1462          projectView.Activate();
1463       }
1464       else
1465       {
1466          sheet.visible = true;
1467          sheet.Activate();
1468       }
1469       return false;
1470    }
1471
1472    bool ShouldStopBuild()
1473    {
1474       return projectView.stopBuild;
1475    }
1476
1477    void DocumentSaved(Window document, char * fileName)
1478    {
1479       ideSettings.AddRecentFile(fileName);
1480       ide.UpdateRecentMenus();
1481       settingsContainer.Save();
1482    }
1483
1484    bool Window::OnFileModified(FileChange fileChange, char * param)
1485    {
1486       char temp[4096];
1487       sprintf(temp, $"The document %s was modified by another application.\n"
1488             "Would you like to reload it and lose your changes?", this.fileName);
1489       if(MessageBox { type = yesNo, master = this/*.parent*/,
1490             text = $"Document has been modified", contents = temp }.Modal() == yes)
1491       {
1492          char * fileName = CopyString(this.fileName);
1493          WindowState state = this.state;
1494          Anchor anchor = this.anchor;
1495          Size size = this.size;
1496
1497          this.modifiedDocument = false;
1498          this.Destroy(0);
1499          this = ide.OpenFile(fileName, normal, true, null, no, normal);
1500          if(this)
1501          {
1502             this.anchor = anchor;
1503             this.size = size;
1504             this.SetState(state, true, 0);
1505          }
1506          delete fileName;
1507          return true;
1508       }
1509       return true;
1510    }
1511
1512    void UpdateMakefiles()
1513    {
1514       if(workspace)
1515       {
1516          for(prj : workspace.projects)
1517          {
1518             bool first = prj == workspace.projects.firstIterator.data;
1519             projectView.ProjectUpdateMakefileForAllConfigs(prj, first, first);
1520          }
1521       }
1522    }
1523
1524    void AdjustMenus()
1525    {
1526       bool unavailable = !project;
1527
1528       projectQuickItem.disabled           = !unavailable;
1529
1530       projectAddItem.disabled             = unavailable;
1531       ((IDEMainFrame)master).toolBar.buttonAddProject.disabled = unavailable;
1532
1533       activeCompilerItem.disabled         = unavailable;
1534       projectActiveConfigItem.disabled    = unavailable;
1535       projectSettingsItem.disabled        = unavailable;
1536
1537       projectBrowseFolderItem.disabled    = unavailable;
1538
1539       viewProjectItem.disabled            = unavailable;
1540
1541       AdjustBuildMenus();
1542       AdjustDebugMenus();
1543    }
1544
1545    void AdjustBuildMenus()
1546    {
1547       bool unavailable = project && projectView.buildInProgress;
1548
1549       projectNewItem.disabled          = unavailable;
1550       toolBar.buttonNewProject.disabled = unavailable;
1551       projectOpenItem.disabled         = unavailable;
1552       toolBar.buttonOpenProject.disabled = unavailable;
1553
1554       unavailable = !project || projectView.buildInProgress;
1555
1556       projectCloseItem.disabled        = unavailable;
1557       // toolBar.buttonCloseProject.disabled = unavailable;
1558
1559       projectRunItem.disabled          = unavailable || project.GetTargetType(project.config) != executable;
1560       toolBar.buttonRun.disabled = unavailable || project.GetTargetType(project.config) != executable;
1561       projectBuildItem.disabled        = unavailable;
1562       toolBar.buttonBuild.disabled = unavailable;
1563       projectLinkItem.disabled         = unavailable;
1564       toolBar.buttonReLink.disabled = unavailable;
1565       projectRebuildItem.disabled      = unavailable;
1566       toolBar.buttonRebuild.disabled = unavailable;
1567       projectCleanItem.disabled        = unavailable;
1568       toolBar.buttonClean.disabled = unavailable;
1569       projectRegenerateItem.disabled   = unavailable;
1570       toolBar.buttonRegenerateMakefile.disabled = unavailable;
1571       projectCompileItem.disabled      = unavailable;
1572    }
1573
1574    void AdjustDebugMenus()
1575    {
1576       bool unavailable = !project || project.GetTargetType(project.config) != executable ||
1577                projectView.buildInProgress == buildingMainProject;
1578       bool active = ide.debugger.isActive;
1579       bool executing = ide.debugger.state == running;
1580       //bool holding = ide.debugger.state == stopped;
1581
1582       debugStartResumeItem.disabled       = unavailable || executing;
1583       if (toolBar)
1584          toolBar.buttonDebugStartResume.disabled = unavailable || executing;
1585
1586       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
1587       if (toolBar)
1588          toolBar.buttonDebugStartResume.toolTip = active ? $"Resume" : $"Start";
1589       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
1590
1591       debugBreakItem.disabled             = unavailable || !executing;
1592       if (toolBar)
1593          toolBar.buttonDebugPause.disabled = unavailable || !executing;
1594       debugStopItem.disabled              = unavailable || !active;
1595       if (toolBar)
1596          toolBar.buttonDebugStop.disabled = unavailable || !active;
1597       debugRestartItem.disabled           = unavailable || !active;
1598       if (toolBar)
1599          toolBar.buttonDebugRestart.disabled =unavailable || !active;
1600
1601       debugStepIntoItem.disabled          = unavailable || executing;
1602       if (toolBar)
1603          toolBar.buttonDebugStepInto.disabled = unavailable || executing;
1604       debugStepOverItem.disabled          = unavailable || executing;
1605       if (toolBar)
1606          toolBar.buttonDebugStepOver.disabled = unavailable || executing;
1607       debugStepOutItem.disabled           = unavailable || executing || !active;
1608       if (toolBar)
1609          toolBar.buttonDebugStepOut.disabled = unavailable || executing || !active;
1610       debugSkipStepOverItem.disabled      = unavailable || executing;
1611       if (toolBar)
1612          toolBar.buttonDebugSkipStepOver.disabled = unavailable || executing;
1613       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
1614
1615       if((Designer)GetActiveDesigner())
1616       {
1617          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
1618          if(codeEditor)
1619          {
1620             codeEditor.debugRunToCursor.disabled      = unavailable || executing;
1621             codeEditor.debugSkipRunToCursor.disabled  = unavailable || executing;
1622          }
1623       }
1624    }
1625
1626    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
1627    {
1628       char tempString[MAX_LOCATION];
1629       strcpy(tempString, directory);
1630       if(saveSettings && !projectView)
1631       {
1632          ideSettings.ideFileDialogLocation = directory;
1633          settingsContainer.Save();
1634       }
1635
1636       ideFileDialog.currentDirectory = tempString;
1637       codeEditorFileDialog.currentDirectory = tempString;
1638       codeEditorFormFileDialog.currentDirectory = tempString;
1639    }
1640
1641    void ChangeProjectFileDialogDirectory(char * directory)
1642    {
1643       ideSettings.ideProjectFileDialogLocation = directory;
1644       settingsContainer.Save();
1645    }
1646
1647    Window FindWindow(char * filePath)
1648    {
1649       Window document = null;
1650
1651       // TOCHECK: Do we need to change slashes here?
1652       for(document = firstChild; document; document = document.next)
1653       {
1654          char * fileName = document.fileName;
1655          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
1656          {
1657             document.visible = true;
1658             document.Activate();
1659             return document;
1660          }
1661       }
1662       return null;
1663    }
1664
1665    bool DontTerminateDebugSession(char * title)
1666    {
1667       if(debugger.isActive)
1668       {
1669          if(MessageBox { type = yesNo, master = ide, 
1670                            contents = $"Do you want to terminate the debugging session in progress?", 
1671                            text = title }.Modal() == no)
1672             return true;
1673          /*
1674          MessageBox msg { type = yesNo, master = ide, 
1675                            contents = "Do you want to terminate the debugging session in progress?", 
1676                            text = title };
1677          if(msg.Modal() == no)
1678          {
1679             msg.Destroy(0);
1680             delete msg;
1681             return true;
1682          }
1683          msg.Destroy(0);
1684          delete msg;*/
1685       }
1686       return false;
1687    }
1688
1689    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod)
1690    {
1691       char extension[MAX_EXTENSION] = "";
1692       Window document = null;
1693       bool isProject = false;
1694       bool needFileModified = true;
1695       char winFilePath[MAX_LOCATION];
1696       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
1697
1698       if(!type)
1699       {
1700          GetExtension(filePath, extension);
1701          strlwr(extension);
1702       }
1703       else
1704          strcpy(extension, type);
1705
1706       if(strcmp(extension, ProjectExtension))
1707       {
1708          for(document = firstChild; document; document = document.next)
1709          {
1710             char * fileName = document.fileName;
1711             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
1712             {
1713                document.visible = true;
1714                document.Activate();
1715                return document;
1716             }
1717          }
1718       }
1719
1720       if(createIfFails == whatever)
1721          ;
1722       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
1723       {
1724          if(openMethod == normal)
1725          {
1726             if(DontTerminateDebugSession($"Open Project"))
1727                return null;
1728             isProject = true;
1729             if(MenuWindowCloseAll(null, 0))
1730             {
1731                if(projectView)
1732                {
1733                   projectView.visible = false;
1734                   projectView.Destroy(0);
1735                   // Where did this come from? projectView = null;
1736                }
1737                if(!projectView)
1738                {
1739                   for(;;)
1740                   {
1741                      Project project;
1742                      Workspace workspace = null;
1743                      
1744                      if(FileExists(filePath))
1745                      {
1746                         if(!strcmp(extension, ProjectExtension))
1747                         {
1748                            char workspaceFile[MAX_LOCATION];
1749                            strcpy(workspaceFile, filePath);
1750                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
1751                            workspace = LoadWorkspace(workspaceFile, filePath);
1752                         }
1753                         else if(!strcmp(extension, WorkspaceExtension))
1754                            workspace = LoadWorkspace(filePath, null);
1755                         else
1756                            return null;
1757                         //project = LoadProject(filePath);
1758                      }
1759                      
1760                      if(workspace)
1761                      {
1762                         char absolutePath[MAX_LOCATION];
1763                         CreateProjectView(workspace, filePath);
1764                         document = projectView;
1765
1766                         workspace.DropInvalidBreakpoints();
1767                         workspace.Save();
1768
1769                         ide.projectView.ShowOutputBuildLog(true);
1770                         {
1771                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1772                            ide.projectView.DisplayCompiler(compiler, false);
1773                            delete compiler;
1774                         }
1775                         UpdateMakefiles();
1776                         {
1777                            char newWorkingDir[MAX_LOCATION];
1778                            StripLastDirectory(filePath, newWorkingDir);
1779                            ChangeFileDialogsDirectory(newWorkingDir, false);
1780                         }
1781                         if(document)
1782                            document.fileName = filePath;
1783
1784                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
1785
1786                         // this crashes on starting ide with epj file, solution please?
1787                         // app.UpdateDisplay();
1788
1789                         workspace.holdTracking = true;
1790                         for(ofi : workspace.openedFiles)
1791                         {
1792                            if(ofi.state != closed)
1793                            {
1794                               Window file = OpenFile(ofi.path, normal, true, null, no, normal);
1795                               if(file)
1796                               {
1797                                  char fileName[MAX_LOCATION];
1798                                  ProjectNode node;
1799                                  GetLastDirectory(ofi.path, fileName);
1800                                  node = projectView.project.topNode.Find(fileName, true);
1801                                  if(node)
1802                                     node.EnsureVisible();
1803                               }
1804                            }
1805                         }
1806                         workspace.holdTracking = false;
1807
1808                         workspace.timer.Start();
1809
1810                         findInFilesDialog.mode = FindInFilesMode::project;
1811                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
1812                         
1813                         {
1814                            char location[MAX_LOCATION];
1815                            StripLastDirectory(ide.project.topNode.path, location);
1816                            ChangeProjectFileDialogDirectory(location);
1817                         }
1818                         
1819                         /*
1820                         if(projectView.debugger)
1821                            projectView.debugger.EvaluateWatches();
1822                         */
1823                         
1824                         break;
1825                      }
1826                      else 
1827                      {
1828                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
1829                         {
1830                            ideProjectFileDialog.text = openProjectFileDialogTitle;
1831                            if(ideProjectFileDialog.Modal() == cancel)
1832                               return null;
1833                            filePath = ideProjectFileDialog.filePath;
1834                            GetExtension(filePath, extension);
1835                         }
1836                         else
1837                            return null;
1838                      }
1839                   }
1840                }
1841             }
1842             else
1843                return document;
1844          }
1845          else if(openMethod == add)
1846          {
1847             if(workspace)
1848             {
1849                Project prj = null;
1850                char slashFilePath[MAX_LOCATION];
1851                GetSlashPathBuffer(slashFilePath, filePath);
1852                for(p : workspace.projects)
1853                {
1854                   if(!fstrcmp(p.filePath, slashFilePath))
1855                   {
1856                      prj = p;
1857                      break;
1858                   }
1859                }
1860                if(prj)
1861                {
1862                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
1863                         contents = $"This project is already present in workspace." }.Modal();
1864                }
1865                else
1866                {
1867                   prj = LoadProject(filePath);
1868                   if(prj)
1869                   {
1870                      workspace.projects.Add(prj);
1871                      if(projectView)
1872                         projectView.AddNode(prj.topNode, null);
1873                      workspace.modified = true;
1874                      workspace.Save();
1875                      findInFilesDialog.AddProjectItem(prj);
1876                      projectView.ProjectUpdateMakefileForAllConfigs(prj, true, true);
1877
1878                      {
1879                         char location[MAX_LOCATION];
1880                         StripLastDirectory(prj.topNode.path, location);
1881                         ChangeProjectFileDialogDirectory(location);
1882                      }
1883
1884                      // projectView is associated with the main project and not with the one just added but
1885                      return projectView; // just to let the caller know something was opened
1886                   }
1887                }
1888             }
1889             else
1890                return null;
1891          }
1892       }
1893       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
1894             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
1895             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
1896       {
1897          if(FileExists(filePath))
1898             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1899                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1900                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
1901                                     };
1902          if(!document)
1903             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1904       }
1905 #ifndef NO3D
1906       else if(!strcmp(extension, "3ds"))
1907       {
1908          if(FileExists(filePath))
1909             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
1910                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
1911                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
1912                                     };
1913
1914          if(!document)
1915             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
1916       }
1917 #endif
1918       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
1919             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
1920             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
1921             !strcmp(extension, "css") || !strcmp(extension, "php") ||
1922             !strcmp(extension, "js"))
1923       {
1924          CodeEditor editor { parent = this, state = state, visible = false };
1925          editor.updatingCode = true;
1926          if(editor.LoadFile(filePath))
1927          {
1928             document = editor;
1929             editor.visible = true;
1930          }
1931          else
1932             delete editor;
1933          needFileModified = false;
1934       }
1935       else
1936       {
1937          CodeEditor editor { parent = this, state = state, visible = false };
1938          if(editor.LoadFile(filePath))
1939          {
1940             document = editor;
1941             editor.visible = true;
1942          }
1943          else
1944             delete editor;
1945          needFileModified = false;
1946       }
1947
1948       if(document && (document._class == class(PictureEdit) ||
1949             document._class == class(ModelView)))
1950       {
1951          document.Create();
1952          if(document)
1953          {
1954             document.fileName = filePath;
1955             if(workspace && !workspace.holdTracking)
1956                workspace.UpdateOpenedFileInfo(filePath, opened);
1957          }
1958       }
1959       
1960       if(!document && createIfFails != no)
1961       {
1962          if(createIfFails != yes && !needFileModified && 
1963                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
1964             createIfFails = yes;
1965          if(createIfFails == yes || createIfFails == whatever)
1966          {
1967             document = (Window)NewCodeEditor(this, state, true);
1968             if(document)
1969                document.fileName = filePath;
1970          }
1971       }
1972
1973       if(document)
1974       {
1975          if(projectView && document._class == class(CodeEditor) && workspace)
1976          {
1977             int lineNumber, position;
1978             Point scroll;
1979             CodeEditor editor = (CodeEditor)document;
1980             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
1981             editor.openedFileInfo.holdTracking = true;
1982             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
1983             position = Max(editor.openedFileInfo.position - 1, 0);
1984             editor.editBox.GoToLineNum(lineNumber);
1985             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
1986             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
1987             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
1988             editor.editBox.scroll = scroll;
1989             editor.openedFileInfo.holdTracking = false;
1990          }
1991          
1992          if(needFileModified)
1993             document.OnFileModified = OnFileModified;
1994          document.NotifySaved = DocumentSaved;
1995          
1996          if(isProject)
1997             ideSettings.AddRecentProject(document.fileName);
1998          else
1999             ideSettings.AddRecentFile(document.fileName);
2000          ide.UpdateRecentMenus();
2001          settingsContainer.Save();
2002          
2003          return document;
2004       }
2005       else
2006          return null;
2007    }
2008
2009    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2010    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2011    {
2012       if(!parentClosing && ide.workspace)
2013          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2014       return true;
2015    }*/
2016    bool ModelView::ModelViewOnClose(bool parentClosing)
2017    {
2018       if(!parentClosing && ide.workspace)
2019          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2020       return true;
2021    }
2022    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2023    {
2024       if(!parentClosing && ide.workspace)
2025          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2026       return true;
2027    }
2028
2029    /*
2030    void OnUnloadGraphics(Window window)
2031    {
2032       display.ClearMaterials();
2033       display.ClearTextures();
2034       display.ClearMeshes();
2035    }
2036    */
2037
2038    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2039    {
2040       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2041       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2042       return true;
2043    }
2044
2045    bool OnKeyDown(Key key, unichar ch)
2046    {
2047       switch(key)
2048       {
2049          case b:
2050             projectView.Update(null);
2051             break;
2052          case capsLock:
2053             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2054             break;
2055          case numLock:
2056             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2057             break;
2058       }
2059       return true;
2060    }
2061
2062    void GoToError(const char * line)
2063    {
2064       if(projectView)
2065          projectView.GoToError(line);
2066    }
2067
2068    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2069    {
2070       char *path = text;
2071       char *colon = strchr(text, ':');
2072       char filePath[MAX_LOCATION];
2073       char completePath[MAX_LOCATION];
2074       int line = 0, col = 0;
2075       Project prj = null;
2076
2077       if(text[3] == '(')
2078       {
2079          char * close = strchr(text, ')');
2080          if(close)
2081          {
2082             char name[256];
2083             strncpy(name, &text[4], close - text - 4);
2084             name[close - text - 4] = '\0';
2085             for(p : ide.workspace.projects)
2086             {
2087                if(!strcmp(p.name, name))
2088                {
2089                   path = close + 1;
2090                   prj = p;
2091                   break;
2092                }
2093             }
2094          }
2095       }
2096       if(!prj)
2097          prj = project ? project : (dir ? null : ide.project);
2098       if(colon && (colon[1] == '/' || colon[1] == '\\'))
2099       {
2100          path = (colon - 1 > path) ? colon - 1 : path;
2101          colon = strstr(colon + 1, ":");
2102       }
2103       while(isspace(*path)) path++;
2104       if(colon)
2105       {
2106          strncpy(filePath, path, colon - path);
2107          filePath[colon - path] = '\0';
2108          line = atoi(colon + 1);
2109          colon = strstr(colon + 1, ":");
2110          if(colon)
2111             col = atoi(colon + 1);
2112       }
2113       else if(path - 1 >= path && *(path - 1) == '\"')
2114       {
2115          colon = strchr(path, '\"');
2116          if(colon)
2117          {
2118             strncpy(filePath, path, colon - path);
2119             filePath[colon - path] = '\0';
2120          }
2121       }
2122
2123       if(prj)
2124          strcpy(completePath, prj.topNode.path);
2125       else if(dir && dir[0])
2126          strcpy(completePath, dir);
2127       else
2128          completePath[0] = '\0';
2129       PathCat(completePath, filePath);
2130
2131       if(FileExists(completePath).isFile)
2132       {
2133          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal);
2134          if(codeEditor && line)
2135          {
2136             EditBox editBox = codeEditor.editBox;
2137             editBox.GoToLineNum(line - 1);
2138             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2139          }
2140       }
2141    }
2142
2143    void OnRedraw(Surface surface)
2144    {
2145       Bitmap bitmap = back.bitmap;
2146       if(bitmap)
2147          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2148    }
2149
2150    void SheetSelected(SheetType sheetSelected)
2151    {
2152       if(activeChild == sheet)
2153       {
2154          if(sheetSelected == methods)
2155          {
2156             viewPropertiesItem.accelerator = f4;
2157             viewPropertiesItem.parent = viewMenu;
2158             viewMethodsItem.parent = null;
2159          }
2160          else
2161          {
2162             viewMethodsItem.accelerator = f4;
2163             viewMethodsItem.parent = viewMenu;
2164             viewPropertiesItem.parent = null;
2165          }
2166       }
2167       else
2168       {
2169          viewMethodsItem.parent = viewMenu;
2170          viewPropertiesItem.parent = viewMenu;
2171          if(sheetSelected == methods)
2172          {
2173             viewMethodsItem.accelerator = f4;
2174             viewPropertiesItem.accelerator = 0;
2175          }
2176          else
2177          {
2178             viewMethodsItem.accelerator = 0;
2179             viewPropertiesItem.accelerator = f4;
2180          }
2181       }
2182    }
2183
2184    void OnActivateClient(Window client, Window previous)
2185    {
2186       //if(!client || client != previous)
2187       {
2188          Class dataType;
2189          if(!client || client != previous)
2190          {
2191             if(previous)
2192                dataType = previous._class;
2193             if(previous && !strcmp(dataType.name, "CodeEditor"))
2194             {
2195                ((CodeEditor)previous).UpdateFormCode();
2196             }
2197             else if(previous && !strcmp(dataType.name, "Designer"))
2198             {
2199                ((Designer)previous).codeEditor.UpdateFormCode();
2200             }
2201          }
2202
2203          if(client)
2204             dataType = client._class;
2205          if(client && !strcmp(dataType.name, "CodeEditor"))
2206          {
2207             CodeEditor codeEditor = (CodeEditor)client;
2208             SetPrivateModule(codeEditor.privateModule);
2209             SetCurrentContext(codeEditor.globalContext);
2210             SetTopContext(codeEditor.globalContext);
2211             SetGlobalContext(codeEditor.globalContext);
2212             
2213             SetDefines(&codeEditor.defines);
2214             SetImports(&codeEditor.imports);
2215
2216             SetActiveDesigner(codeEditor.designer);
2217             
2218             sheet.codeEditor = codeEditor;
2219             toolBox.codeEditor = codeEditor;
2220
2221             viewDesignerItem.parent = viewMenu;
2222             if(activeChild != codeEditor)
2223             {
2224                viewCodeItem.parent = viewMenu;
2225                viewDesignerItem.accelerator = 0;
2226                viewCodeItem.accelerator = f8;
2227             }
2228             else
2229             {
2230                viewCodeItem.parent = null;
2231                viewDesignerItem.accelerator = f8;
2232             }
2233          }
2234          else if(client && !strcmp(dataType.name, "Designer"))
2235          {
2236             CodeEditor codeEditor = ((Designer)client).codeEditor;
2237             if(codeEditor)
2238             {
2239                SetPrivateModule(codeEditor.privateModule);
2240                SetCurrentContext(codeEditor.globalContext);
2241                SetTopContext(codeEditor.globalContext);
2242                SetGlobalContext(codeEditor.globalContext);
2243                SetDefines(&codeEditor.defines);
2244                SetImports(&codeEditor.imports);
2245             }
2246             else
2247             {
2248                SetPrivateModule(null);
2249                SetCurrentContext(null);
2250                SetTopContext(null);
2251                SetGlobalContext(null);
2252                SetDefines(null);
2253                SetImports(null);
2254             }
2255
2256             SetActiveDesigner((Designer)client);
2257
2258             sheet.codeEditor = codeEditor;
2259             toolBox.codeEditor = codeEditor;
2260
2261             viewCodeItem.parent = viewMenu;
2262             if(activeChild != client)
2263             {
2264                viewDesignerItem.parent = viewMenu;
2265                viewDesignerItem.accelerator = f8;
2266                viewCodeItem.accelerator = 0;
2267             }
2268             else
2269             {
2270                viewDesignerItem.parent = null;
2271                viewCodeItem.accelerator = f8;
2272             }
2273          }
2274          else
2275          {
2276             if(sheet)
2277                sheet.codeEditor = null;
2278             toolBox.codeEditor = null;
2279             SetActiveDesigner(null);
2280
2281             viewDesignerItem.parent = null;
2282             viewCodeItem.parent = null;
2283          }
2284          if(sheet)
2285             SheetSelected(sheet.sheetSelected);
2286       }
2287
2288       projectCompileItem = null;
2289
2290       if(statusBar)
2291       {
2292          statusBar.Clear();
2293          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2294          {
2295             CodeEditor codeEditor = (CodeEditor)client;
2296             EditBox editBox = codeEditor.editBox;
2297
2298             statusBar.AddField(pos);
2299
2300             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2301             statusBar.AddField(caps);
2302
2303             ovr = { width = 30, text = $"OVR", color = editBox.overwrite ? black : Color { 128, 128, 128 } };
2304             statusBar.AddField(ovr);
2305
2306             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2307             statusBar.AddField(num);
2308
2309             //statusBar.text = "Ready";
2310
2311             if(projectView && projectView.project)
2312             {
2313                ProjectNode node = projectView.GetNodeFromWindow(client, null);
2314                if(node)
2315                {
2316                   char name[1024];
2317                   sprintf(name, $"Compile %s", node.name);
2318                   projectCompileItem = 
2319                   {
2320                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2321
2322                      bool NotifySelect(MenuItem selection, Modifiers mods)
2323                      {
2324                         if(projectView)
2325                         {
2326                            ProjectNode node = projectView.GetNodeFromWindow(activeClient, null);
2327                            if(node)
2328                               projectView.Compile(node);
2329                         }
2330                         return true;
2331                      }
2332                   };
2333                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2334                }
2335             }
2336          }
2337          else
2338          {
2339             caps = ovr = num = null;
2340          }
2341       }
2342    }
2343
2344    bool OnClose(bool parentClosing)
2345    {
2346       //return !projectView.buildInProgress;
2347       if(projectView && projectView.buildInProgress)
2348          return false;
2349       if(DontTerminateDebugSession($"Close IDE"))
2350          return false;
2351       if(findInFilesDialog)
2352          findInFilesDialog.SearchStop();
2353       if(workspace)
2354       {
2355          workspace.timer.Stop();
2356          workspace.Save();
2357       }
2358       ideMainFrame.Destroy(0);
2359       return true;
2360    }
2361
2362    bool OnPostCreate()
2363    {
2364       int c;
2365       for(c = 1; c<app.argc; c++)
2366       {
2367          char fullPath[MAX_LOCATION];
2368          GetWorkingDir(fullPath, MAX_LOCATION);
2369          PathCat(fullPath, app.argv[c]);
2370          if(FileExists(fullPath))
2371             ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2372       }
2373       return true;
2374    }
2375
2376    void OnDestroy()
2377    {
2378       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2379       if(projectView)
2380       {
2381          projectView.visible = false;
2382          projectView.Destroy(0);
2383          projectView = null;
2384       }
2385 #ifdef GDB_DEBUG_GUI
2386       gdbDialog.Destroy(0);
2387       delete gdbDialog;
2388 #endif
2389    }
2390
2391    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
2392    {
2393       int c, len, count;
2394       char * newList;
2395       char * oldPaths[128];
2396       String oldList = new char[maxPathLen];
2397       Array<String> newExePaths { };
2398       //Map<String, bool> exePathExists { };
2399       bool found = false;
2400 #if defined(__unix__) || defined(__APPLE__)
2401       Array<String> newLibPaths { };
2402       Map<String, bool> libPathExists { };
2403 #endif
2404
2405       if(projectsDirs)
2406       {
2407          for(prj : workspace.projects)
2408          {
2409             DirExpression targetDirExp;
2410
2411             // SKIP FIRST PROJECT...
2412             if(prj == workspace.projects.firstIterator.data) continue;
2413
2414             // NOTE: Right now the additional project config dir will be
2415             //       obtained when the debugger is started, so toggling it
2416             //       while building will change which library gets used.
2417             //       To go with the initial state, e.g. when F5 was pressed,
2418             //       we nould need to keep a list of all project's active
2419             //       config upon startup.
2420             targetDirExp = prj.GetTargetDir(compiler, prj.config);
2421
2422             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2423                cfg = prj.config;
2424             else
2425             {
2426                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2427                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2428                      break;
2429                if(!cfg)
2430                {
2431                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2432                      if(cfg.targetType == sharedLibrary && cfg.debug)
2433                         break;
2434                }
2435             }*/
2436             if(targetDirExp.dir)
2437             {
2438                char buffer[MAX_LOCATION];
2439 #if defined(__WIN32__)
2440                Array<String> paths = newExePaths;
2441 #else
2442                Array<String> paths = newLibPaths;
2443 #endif
2444                GetSystemPathBuffer(buffer, prj.topNode.path);
2445                PathCat(buffer, targetDirExp.dir);
2446                for(p : paths)
2447                {
2448                   if(!fstrcmp(p, buffer))
2449                   {
2450                      found = true;
2451                      break;
2452                   }
2453                }
2454                if(!found)
2455                   paths.Add(CopyString(buffer));
2456             }
2457             delete targetDirExp;
2458          }
2459       }
2460
2461       for(item : compiler.executableDirs)
2462       {
2463          found = false;
2464          for(p : newExePaths)
2465          {
2466             if(!fstrcmp(p, item))
2467             {
2468                found = true;
2469                break;
2470             }
2471          }
2472          if(!found)
2473             newExePaths.Add(CopySystemPath(item));
2474       }
2475
2476       GetEnvironment("PATH", oldList, maxPathLen);
2477 /*#ifdef _DEBUG
2478       printf("Old value of PATH: %s\n", oldList);
2479 #endif*/
2480       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2481       for(c = 0; c < count; c++)
2482       {
2483          found = false;
2484          for(p : newExePaths)
2485          {
2486             if(!fstrcmp(p, oldPaths[c]))
2487             {
2488                found = true;
2489                break;
2490             }
2491          }
2492          if(!found)
2493             newExePaths.Add(CopySystemPath(oldPaths[c]));
2494       }
2495
2496       len = 0;
2497       for(path : newExePaths)
2498          len += strlen(path) + 1;
2499       newList = new char[len + 1];
2500       newList[0] = '\0';
2501       for(path : newExePaths)
2502       {
2503          strcat(newList, path);
2504          strcat(newList, pathListSep);
2505       }
2506       newList[len - 1] = '\0';
2507       SetEnvironment("PATH", newList);
2508 /*#ifdef _DEBUG
2509       printf("New value of PATH: %s\n", newList);
2510 #endif*/
2511       delete newList;
2512
2513       newExePaths.Free();
2514       delete newExePaths;
2515
2516 #if defined(__unix__) || defined(__APPLE__)
2517
2518       for(item : compiler.libraryDirs)
2519       {
2520          if(!libPathExists[item])  // fstrcmp should be used
2521          {
2522             newLibPaths.Add(item);
2523             libPathExists[item] = true;
2524          }
2525       }
2526
2527 #if defined(__APPLE__)
2528       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2529 #else
2530       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2531 #endif
2532 /*#ifdef _DEBUG
2533       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
2534 #endif*/
2535       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2536       for(c = 0; c < count; c++)
2537       {
2538          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2539          {
2540             newLibPaths.Add(oldPaths[c]);
2541             libPathExists[oldPaths[c]] = true;
2542          }
2543       }
2544
2545       len = 0;
2546       for(path : newLibPaths)
2547          len += strlen(path) + 1;
2548       newList = new char[len + 1];
2549       newList[0] = '\0';
2550       for(path : newLibPaths)
2551       {
2552          strcat(newList, path);
2553          strcat(newList, pathListSep);
2554       }
2555       newList[len - 1] = '\0';
2556 #if defined(__APPLE__)
2557       SetEnvironment("DYLD_LIBRARY_PATH", newList);
2558 #else
2559       SetEnvironment("LD_LIBRARY_PATH", newList);
2560 #endif
2561 /*#ifdef _DEBUG
2562       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
2563 #endif*/
2564       delete newList;
2565
2566       delete newLibPaths;
2567       delete libPathExists;
2568 #endif
2569
2570       if(compiler.distccEnabled && compiler.distccHosts)
2571          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
2572
2573       delete oldList;
2574    }
2575
2576    void DestroyTemporaryProjectDir()
2577    {
2578       if(tmpPrjDir && tmpPrjDir[0])
2579       {
2580          if(FileExists(tmpPrjDir).isDirectory)
2581             DestroyDir(tmpPrjDir);
2582          property::tmpPrjDir = null;
2583       }
2584    }
2585
2586    IDEWorkSpace()
2587    {
2588       // Graphics Driver Menu
2589       int c;
2590
2591       /*
2592       app.currentSkin.selectionColor = selectionColor;
2593       app.currentSkin.selectionText = selectionText;
2594       */
2595
2596 /*
2597       driverItems = new MenuItem[app.numDrivers];
2598       for(c = 0; c < app.numDrivers; c++)
2599       {
2600          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
2601          driverItems[c].id = c;
2602          driverItems[c].isRadio = true;         
2603       }
2604 */
2605       driverItems = new MenuItem[2];
2606 #if defined(__unix__)
2607          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
2608          driverItems[0].id = 0;
2609          driverItems[0].isRadio = true;         
2610 #else
2611          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
2612          driverItems[0].id = 0;
2613          driverItems[0].isRadio = true;         
2614 #endif
2615          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
2616          driverItems[1].id = 1;
2617          driverItems[1].isRadio = true;         
2618
2619 /*      skinItems = new MenuItem[app.numSkins];
2620       for(c = 0; c < app.numSkins; c++)
2621       {
2622          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
2623          skinItems[c].id = c;
2624          skinItems[c].isRadio = true;         
2625       }
2626 */
2627       ideFileDialog.master = this;
2628       ideProjectFileDialog.master = this;
2629
2630       //SetDriverAndSkin();
2631       return true;
2632    }
2633
2634    void UpdateRecentMenus()
2635    {
2636       int c;
2637       Menu fileMenu = menu.FindMenu($"File");
2638       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
2639       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
2640       char itemName[MAX_LOCATION + 4];
2641       MenuItem item;
2642
2643       recentFiles.Clear();
2644       c = 0;
2645
2646       for(recent : ideSettings.recentFiles)
2647       {
2648          sprintf(itemName, "%d %s", 1 + c, recent);
2649          MakeSystemPath(itemName);
2650          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
2651          c++;
2652       }
2653
2654       recentProjects.Clear();
2655       c = 0;
2656       for(recent : ideSettings.recentProjects)
2657       {
2658          sprintf(itemName, "%d %s", 1 + c, recent);
2659          MakeSystemPath(itemName);
2660          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
2661          c++;
2662       }
2663    }
2664
2665    ~IDEWorkSpace()
2666    {
2667       delete driverItems;
2668       delete skinItems;
2669       delete ideSettings;
2670    }
2671 }
2672
2673 void DestroyDir(char * path)
2674 {
2675    RecursiveDeleteFolderFSI fsi { };
2676    fsi.Iterate(path);
2677    delete fsi;
2678 }
2679
2680 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
2681 {
2682    bool preserveRootFolder;
2683
2684    void OutFolder(char * folderPath, bool isRoot)
2685    {
2686       if(!(preserveRootFolder && isRoot))
2687          RemoveDir(folderPath);
2688    }
2689
2690    bool OnFile(char * filePath)
2691    {
2692       DeleteFile(filePath);
2693       return true;
2694    }
2695 }
2696
2697 class IDEApp : GuiApplication
2698 {
2699    //driver = "Win32Console";
2700    // driver = "OpenGL";
2701    // skin = "Aqua";
2702    //skin = "TVision";
2703    bool Init()
2704    {
2705       SetLoggingMode(stdOut, null);
2706       //SetLoggingMode(debug, null);
2707
2708       settingsContainer.Load();
2709 #if defined(__unix__) || defined(__APPLE__)
2710       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
2711 #else
2712       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
2713 #endif
2714       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
2715
2716       SetInIDE(true);
2717
2718       desktop.text = titleECEREIDE;
2719       /*
2720       int c;
2721       for(c = 1; c<app.argc; c++)
2722       {
2723          char fullPath[MAX_LOCATION];
2724          GetWorkingDir(fullPath, MAX_LOCATION);
2725          PathCat(fullPath, app.argv[c]);
2726          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2727       }
2728       */
2729       return true;
2730    }
2731 }
2732
2733 IDEMainFrame ideMainFrame { };
2734
2735 define app = ((IDEApp)__thisModule);
2736 #ifdef _DEBUG
2737 define titleECEREIDE = $"ECERE IDE (Debug)";
2738 #else
2739 define titleECEREIDE = $"ECERE IDE";
2740 #endif