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