ide; support goto code location on double click or enter from debug log with output...
[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, bool noParsing);
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, mods.ctrl && mods.shift);
132          return false;
133       }
134
135       bool NotifyKeyDown(EditBox editBox, Key key, unichar ch)
136       {
137          if(key.code == enter || key.code == keyPadEnter)
138          {
139             OnGotoError(editBox.line.text, key.ctrl && key.shift);
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       bool NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods)
157       {
158          OnCodeLocationParseAndGoTo(editBox.line.text);
159          return false;
160       }
161
162       bool NotifyKeyDown(EditBox editBox, Key key, unichar ch)
163       {
164          if((SmartKey)key == enter)
165          {
166             OnCodeLocationParseAndGoTo(editBox.line.text);
167             return false;
168          }
169          return true;
170       }
171    };
172    
173    LogBox findBox
174    {
175       parent = this, freeCaret = true, autoEmpty = true, multiLine = true;
176       readOnly = true, hasVertScroll = true, hasHorzScroll = true, visible = false;
177       anchor = Anchor { left = 0, right = 0, top = 23, bottom = 0 };
178       font = { panelFont.faceName, panelFont.size };
179       background = outputBackground;
180       foreground = outputText;
181       selectionColor = selectionColor, selectionText = selectionText;
182
183       bool NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods)
184       {
185          OnCodeLocationParseAndGoTo(editBox.line.text);
186          return false;
187       }
188
189       bool NotifyKeyDown(EditBox editBox, Key key, unichar ch)
190       {
191          if((SmartKey)key == enter)
192          {
193             OnCodeLocationParseAndGoTo(editBox.line.text);
194             return false;
195          }
196          return true;
197       }
198    };
199
200 #ifdef GDB_DEBUG_OUTPUT
201    LogBox gdbBox
202    {
203       parent = this, freeCaret = true, autoEmpty = true, multiLine = true;
204       readOnly = true, hasVertScroll = true, hasHorzScroll = true, visible = false;
205       anchor = Anchor { left = 0, right = 0, top = 23, bottom = 0 };
206       font = { panelFont.faceName, panelFont.size };
207       background = outputBackground;
208       foreground = outputText;
209       selectionColor = selectionColor, selectionText = selectionText;
210    };
211 #endif
212    
213    Menu editMenu { menu, $"Edit", e };
214
215    MenuItem item;
216    MenuItem copyItem
217    {
218       editMenu, $"Copy", c, ctrlC;
219       bool NotifySelect(MenuItem selection, Modifiers mods)
220       {
221          activeBox.Copy();
222          return true;
223       }
224    };
225    MenuDivider { editMenu };
226    MenuItem { editMenu, $"Find Previous", e, Key { f3, shift = true }, NotifySelect = MenuEditFind, id = 0 };
227    MenuItem { editMenu, $"Find Next", n, f3, NotifySelect = MenuEditFind, id = 1 };
228    MenuItem { editMenu, $"Find", f, ctrlF, NotifySelect = MenuEditFind, id = 2 };
229
230    bool MenuEditFind(MenuItem selection, Modifiers mods)
231    {
232       int64 id = selection.id;
233       char * searchString = findDialog.searchString;
234       if(id != 2 && searchString[0])
235       {
236          activeBox.Find(searchString, findDialog.wholeWord, findDialog.matchCase, (bool)id);
237          return true;
238       }
239       findDialog.Create();
240       return true;
241    }
242
243    void Show()
244    {
245       visible = true;
246       Activate();
247    }
248    void ShowClearSelectTab(OutputViewTab tab)
249    {
250       Show();
251       if(tab == build)
252          buildBox.Clear();
253       else if(tab == debug)
254          debugBox.Clear();
255       else if(tab == find)
256       {
257          findBox.Clear();
258       }
259 #ifdef GDB_DEBUG_OUTPUT
260       else if(tab == gdb)
261          gdbBox.Clear();
262 #endif
263       SelectTab(tab);
264    }
265
266    bool OnKeyDown(Key key, unichar ch)
267    {
268       switch(key)
269       {
270          case shiftTab:
271          {
272             OutputViewTab switchTo;
273             if(activeBox == buildBox)
274 #ifdef GDB_DEBUG_OUTPUT
275                switchTo = gdb;
276 #else
277                switchTo = find;
278 #endif
279             else if(activeBox == debugBox)
280                switchTo = build;
281             else if(activeBox == findBox)
282                switchTo = debug;
283 #ifdef GDB_DEBUG_OUTPUT
284             else if(activeBox == gdbBox)
285                switchTo = build;
286 #endif
287             SelectTab(switchTo);
288             break;
289          }
290          case tab:
291          {
292             OutputViewTab switchTo = debug;
293             if(activeBox == buildBox)
294                switchTo = debug;
295             else if(activeBox == debugBox)
296                switchTo = find;
297             else if(activeBox == findBox)
298 #ifdef GDB_DEBUG_OUTPUT
299                switchTo = gdb;
300             else if(activeBox == gdbBox)
301 #endif
302                switchTo = build;
303             SelectTab(switchTo);
304             break;
305          }
306       }
307       return true;
308    }
309 }
310
311 class LogBox : EditBox
312 {
313    bool moved, logging, tell;
314
315    void Logf(char * format, ...)
316    {
317       char string[MAX_F_STRING*10];
318       va_list args;
319       va_start(args, format);
320       vsnprintf(string, sizeof(string), format, args);
321       string[sizeof(string)-1] = 0;
322       va_end(args);
323
324       LogRaw(string);
325    }
326
327    void LogSprintf(char * entry)
328    {
329       char string[MAX_F_STRING];
330       sprintf(string, entry);
331       LogRaw(string);
332    }
333
334    void LogRaw(char * entry)
335    {
336       // Cut the line longer than 1024 because Logf prints to a buffer (and we don't want to output crazy long lines either)
337       //if(len > 1023) line[1023] = '\0';
338       /* Fixed, but disabled this ... Not sure if there's any reason to keep it? The EditBox should be fine with long lines
339          and it's easier to copy commands and go to errors */
340       /*int len = strlen(entry);
341       if(len > 1023)
342       {
343          char * newStart, * start = entry;
344          
345          while(len > 1023)
346          {
347             char backup[3];
348             newStart = start + 1020;
349             strncpy(backup, newStart, 3);
350             strncpy(newStart, "\n ", 3);
351             Log(start);
352             strncpy(newStart, backup, 3);
353             start = newStart;
354             len = strlen(start);
355          }
356          Log(start);
357       }
358       else*/
359          Log(entry);
360    }
361
362    void Log(char * string)
363    {
364       int x1, y1, x2, y2;
365       Point scrl;
366       EditLine line1;
367       EditLine line2;
368       logging = true;
369       if(moved)
370       {
371          GetSelPos(&line1, &y1, &x1, &line2, &y2, &x2, false);
372          scrl = scroll;
373       }
374       End();
375       if(tell)
376       {
377          ClearLine();
378          tell = false;
379       }
380       PutS(string);
381       Update(null);
382       if(moved)
383       {
384          scroll = scrl;
385          SetSelPos(line1, y1, x1, line2, y2, x2);
386       }
387       logging = false;
388    }
389
390    void Tellf(char * format, ...)
391    {
392       char string[MAX_F_STRING*10];
393       va_list args;
394       va_start(args, format);
395       vsnprintf(string, sizeof(string), format, args);
396       string[sizeof(string)-1] = 0;
397       va_end(args);
398
399       Tell(string);
400    }
401
402    void Tell(char * string)
403    {
404       Log(string);
405       if(!moved)
406       {
407          Point caretPos;
408          GetCaretPosition(caretPos);
409          SetCaret(0, caretPos.y, GetCaretSize());
410       }
411       tell = true;
412    }
413
414    void Clear()
415    {
416       EditBox::Clear();
417       moved = false;
418    }
419
420    void NotifyCaretMove(EditBox editBox, int line, int charPos)
421    {
422       LogBox logBox = (LogBox)editBox;
423       if(!logBox.logging)
424       {
425          int y1, y2;
426          logBox.GetSelPos(null, &y1, null, null, &y2, null, false);
427          logBox.moved = (y1 == logBox.numLines - 1 && y2 == y1) ? false : true;
428       }
429    }
430 }