ide: Fixed crashes hitting Tab in output view
[sdk] / ide / src / panels / OutputView.ec
1 #include <stdarg.h>
2
3 import "ecere"
4 import "CodeEditor"
5
6 enum OutputViewTab { build, debug, find
7 #ifdef GDB_DEBUG_OUTPUT
8 , gdb
9 #endif
10 };
11
12 class OutputView : Window
13 {
14    visible = false;
15    borderStyle = sizable;
16    hasClose = true;
17    mergeMenus = false;
18    text = "Output";
19    menu = Menu { };
20    anchor = Anchor { left = 0, right = 0, bottom = 0 };
21    size.h = 240;
22    background = formColor;
23
24    virtual void OnGotoError(char * line);
25    virtual void OnCodeLocationParseAndGoTo(char * line);
26
27    FindDialog findDialog { master = this, editBox = buildBox, isModal = true, autoCreate = false, text = "Find" };
28
29    Button buildBtn
30    {
31       this, inactive = true, text = "Build", bevelOver = true, isRadio = true, bitmap = null, checked = true;
32       size = { 99, 20 };
33       anchor = { left = 0, top = 1 };
34       font = { $"Tahoma", 8.25f, bold = true };
35
36       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
37       {
38          SelectTab(build);
39          return true;
40       }
41    };
42    
43    Button debugBtn
44    {
45       this, inactive = true, text = "Debug", bevelOver = true, isRadio = true, bitmap = null;
46       size = { 99, 20 };
47       anchor = { left = 100, top = 1 };
48
49       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
50       {
51          SelectTab(debug);
52          return true;
53       }
54    };
55    
56    Button findBtn
57    {
58       this, inactive = true, text = "Find", bevelOver = true, isRadio = true, bitmap = null;
59       size = { 99, 20 };
60       anchor = { left = 200, top = 1 };
61
62       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
63       {
64          SelectTab(find);
65          return true;
66       }
67    };
68
69 #ifdef GDB_DEBUG_OUTPUT
70    Button gdbBtn
71    {
72       this, inactive = true, text = "GDB", bevelOver = true, isRadio = true, bitmap = null;
73       size = { 99, 20 };
74       anchor = { left = 300, top = 1 };
75
76       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
77       {
78          SelectTab(gdb);
79          return true;
80       }
81    };
82 #endif
83
84    void SelectTab(OutputViewTab tab)
85    {
86       Button activeBtn = null;
87       if(tab == build)
88          activeBtn = buildBtn, activeBox = buildBox;
89       else if(tab == debug)
90          activeBtn = debugBtn, activeBox = debugBox;
91       else if(tab == find)
92          activeBtn = findBtn, activeBox = findBox;
93 #ifdef GDB_DEBUG_OUTPUT
94       else if(tab == gdb)
95          activeBtn = gdbBtn, activeBox = gdbBox;
96 #endif
97       if(activeBtn && activeBox)
98       {
99          activeBtn.checked = true;
100          activeBtn.font = { $"Tahoma", 8.25f, bold = true };
101          if(buildBtn != activeBtn) buildBtn.font = null;
102          if(debugBtn != activeBtn) debugBtn.font = null;
103          if(findBtn != activeBtn) findBtn.font = null;
104 #ifdef GDB_DEBUG_OUTPUT
105          if(gdbBtn != activeBtn) gdbBtn.font = null;
106 #endif
107
108          activeBox.visible = false;
109          activeBtn.Activate();      // Ensure proper cycling (until tab order?)
110          activeBox.visible = true;
111          activeBox.Activate();
112          findDialog.editBox = activeBox;
113       }
114    }
115
116    EditBox activeBox;
117    activeBox = buildBox;
118    
119    LogBox buildBox
120    {
121       parent = this, freeCaret = true, autoEmpty = true, multiLine = true;
122       readOnly = true, hasVertScroll = true, hasHorzScroll = true;
123       anchor = Anchor { left = 0, right = 0, top = 23, bottom = 0 };
124       font = { panelFont.faceName, panelFont.size };
125       background = outputBackground;
126       foreground = outputText;
127       selectionColor = selectionColor, selectionText = selectionText;
128       
129       bool NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods)
130       {
131          OnGotoError(editBox.line.text);
132          return false; //true; // why not use true here? 
133       }
134
135       bool NotifyKeyDown(EditBox editBox, Key key, unichar ch)
136       {
137          if((SmartKey)key == enter)
138          {
139             OnGotoError(editBox.line.text);
140             return false;
141          }
142          return true;
143       }
144    };
145    
146    LogBox debugBox
147    {
148       parent = this, freeCaret = true, autoEmpty = true, multiLine = true;
149       readOnly = true, hasVertScroll = true, hasHorzScroll = true, visible = false;
150       anchor = Anchor { left = 0, right = 0, top = 23, bottom = 0 };
151       font = { panelFont.faceName, panelFont.size };
152       background = outputBackground;
153       foreground = outputText;
154       selectionColor = selectionColor, selectionText = selectionText;
155    };
156    
157    LogBox findBox
158    {
159       parent = this, freeCaret = true, autoEmpty = true, multiLine = true;
160       readOnly = true, hasVertScroll = true, hasHorzScroll = true, visible = false;
161       anchor = Anchor { left = 0, right = 0, top = 23, bottom = 0 };
162       font = { panelFont.faceName, panelFont.size };
163       background = outputBackground;
164       foreground = outputText;
165       selectionColor = selectionColor, selectionText = selectionText;
166
167       bool NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods)
168       {
169          OnCodeLocationParseAndGoTo(editBox.line.text);
170          return false; //true;
171       }
172
173       bool NotifyKeyDown(EditBox editBox, Key key, unichar ch)
174       {
175          if((SmartKey)key == enter)
176          {
177             OnCodeLocationParseAndGoTo(editBox.line.text);
178             return false;
179          }
180          return true;
181       }
182    };
183
184 #ifdef GDB_DEBUG_OUTPUT
185    LogBox gdbBox
186    {
187       parent = this, freeCaret = true, autoEmpty = true, multiLine = true;
188       readOnly = true, hasVertScroll = true, hasHorzScroll = true, visible = false;
189       anchor = Anchor { left = 0, right = 0, top = 23, bottom = 0 };
190       font = { panelFont.faceName, panelFont.size };
191    };
192 #endif
193    
194    Menu editMenu { menu, $"Edit", e };
195
196    MenuItem item;
197    MenuItem copyItem
198    {
199       editMenu, $"Copy", c, ctrlC;
200       bool NotifySelect(MenuItem selection, Modifiers mods)
201       {
202          activeBox.Copy();
203          return true;
204       }
205    };
206    MenuDivider { editMenu };
207    MenuItem { editMenu, $"Find Previous", e, Key { f3, shift = true }, NotifySelect = MenuEditFind, id = 0 };
208    MenuItem { editMenu, $"Find Next", n, f3, NotifySelect = MenuEditFind, id = 1 };
209    MenuItem { editMenu, $"Find", f, ctrlF, NotifySelect = MenuEditFind, id = 2 };
210
211    bool MenuEditFind(MenuItem selection, Modifiers mods)
212    {
213       int64 id = selection.id;
214       char * searchString = findDialog.searchString;
215       if(id != 2 && searchString[0])
216       {
217          activeBox.Find(searchString, findDialog.wholeWord, findDialog.matchCase, (bool)id);
218          return true;
219       }
220       findDialog.Create();
221       return true;
222    }
223
224    void Show()
225    {
226       visible = true;
227       Activate();
228    }
229    void ShowClearSelectTab(OutputViewTab tab)
230    {
231       Show();
232       if(tab == build)
233          buildBox.Clear();
234       else if(tab == debug)
235          debugBox.Clear();
236       else if(tab == find)
237       {
238          findBox.Clear();
239       }
240 #ifdef GDB_DEBUG_OUTPUT
241       else if(tab == gdb)
242          gdbBox.Clear();
243 #endif
244       SelectTab(tab);
245    }
246
247    bool OnKeyDown(Key key, unichar ch)
248    {
249       switch(key)
250       {
251          case shiftTab:
252          {
253             OutputViewTab switchTo;
254             if(activeBox == buildBox)
255 #ifdef GDB_DEBUG_OUTPUT
256                switchTo = gdb;
257 #else
258                switchTo = find;
259 #endif
260             else if(activeBox == debugBox)
261                switchTo = build;
262             else if(activeBox == findBox)
263                switchTo = debug;
264 #ifdef GDB_DEBUG_OUTPUT
265             else if(activeBox == gdbBox)
266                switchTo = build;
267 #endif
268             SelectTab(switchTo);
269             break;
270          }
271          case tab:
272          {
273             OutputViewTab switchTo = debug;
274             if(activeBox == buildBox)
275                switchTo = debug;
276             else if(activeBox == debugBox)
277                switchTo = find;
278             else if(activeBox == findBox)
279 #ifdef GDB_DEBUG_OUTPUT
280                switchTo = gdb;
281             else if(activeBox == gdbBox)
282 #endif
283                switchTo = build;
284             SelectTab(switchTo);
285             break;
286          }
287       }
288       return true;
289    }
290 }
291
292 class LogBox : EditBox
293 {
294    bool moved, logging, tell;
295
296    void Logf(char * format, ...)
297    {
298       char string[MAX_F_STRING*10];
299       va_list args;
300       va_start(args, format);
301       vsnprintf(string, sizeof(string), format, args);
302       string[sizeof(string)-1] = 0;
303       va_end(args);
304
305       LogRaw(string);
306    }
307
308    void LogSprintf(char * entry)
309    {
310       char string[MAX_F_STRING];
311       sprintf(string, entry);
312       LogRaw(string);
313    }
314
315    void LogRaw(char * entry)
316    {
317       // Cut the line longer than 1024 because Logf prints to a buffer (and we don't want to output crazy long lines either)
318       //if(len > 1023) line[1023] = '\0';
319       int len = strlen(entry);
320       if(len > 1023)
321       {
322          char backup[4];
323          char * newStart, * start = entry;
324          
325          while(len > 1023)
326          {
327             newStart = start + 1020;
328             strncpy(backup, newStart, 4);
329             strncpy(newStart, " \\ \0", 4);
330             Log(start);
331             strncpy(newStart, backup, 4);
332             start = newStart;
333             len = strlen(start);
334          }
335          
336          //while((len = strlen(start)) > 1023)
337          
338       }
339       else
340          Log(entry);
341    }
342
343    void Log(char * string)
344    {
345       int x1, y1, x2, y2;
346       Point scrl;
347       EditLine line1;
348       EditLine line2;
349       logging = true;
350       if(moved)
351       {
352          GetSelPos(&line1, &y1, &x1, &line2, &y2, &x2, false);
353          scrl = scroll;
354       }
355       End();
356       if(tell)
357       {
358          ClearLine();
359          tell = false;
360       }
361       PutS(string);
362       Update(null);
363       if(moved)
364       {
365          scroll = scrl;
366          SetSelPos(line1, y1, x1, line2, y2, x2);
367       }
368       logging = false;
369    }
370
371    void Tellf(char * format, ...)
372    {
373       char string[MAX_F_STRING*10];
374       va_list args;
375       va_start(args, format);
376       vsnprintf(string, sizeof(string), format, args);
377       string[sizeof(string)-1] = 0;
378       va_end(args);
379
380       Tell(string);
381    }
382
383    void Tell(char * string)
384    {
385       Log(string);
386       if(!moved)
387       {
388          Point caretPos;
389          GetCaretPosition(caretPos);
390          SetCaret(0, caretPos.y, GetCaretSize());
391       }
392       tell = true;
393    }
394
395    void Clear()
396    {
397       EditBox::Clear();
398       moved = false;
399    }
400
401    void NotifyCaretMove(EditBox editBox, int line, int charPos)
402    {
403       LogBox logBox = (LogBox)editBox;
404       if(!logBox.logging)
405       {
406          int y1, y2;
407          logBox.GetSelPos(null, &y1, null, null, &y2, null, false);
408          logBox.moved = (y1 == logBox.numLines - 1 && y2 == y1) ? false : true;
409       }
410    }
411 }