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