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