Console Scrolling - reset while typing.
[blender.git] / source / blender / editors / space_console / space_console.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  * 
20  * Contributor(s): Campbell Barton
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file blender/editors/space_console/space_console.c
26  *  \ingroup spconsole
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31
32 #ifdef WIN32
33 #include "BLI_winstuff.h"
34 #endif
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_math.h"
40 #include "BLI_utildefines.h"
41
42 #include "BKE_context.h"
43 #include "BKE_screen.h"
44 #include "BKE_idcode.h"
45
46 #include "ED_space_api.h"
47 #include "ED_screen.h"
48
49 #include "BIF_gl.h"
50
51 #include "RNA_access.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "UI_resources.h"
57 #include "UI_view2d.h"
58
59 #include "console_intern.h"     // own include
60
61 /* ******************** default callbacks for console space ***************** */
62
63 static SpaceLink *console_new(const bContext *UNUSED(C))
64 {
65         ARegion *ar;
66         SpaceConsole *sconsole;
67         
68         sconsole= MEM_callocN(sizeof(SpaceConsole), "initconsole");
69         sconsole->spacetype= SPACE_CONSOLE;
70         
71         sconsole->lheight=      14;
72         
73         /* header */
74         ar= MEM_callocN(sizeof(ARegion), "header for console");
75         
76         BLI_addtail(&sconsole->regionbase, ar);
77         ar->regiontype= RGN_TYPE_HEADER;
78         ar->alignment= RGN_ALIGN_BOTTOM;
79         
80         
81         /* main area */
82         ar= MEM_callocN(sizeof(ARegion), "main area for text");
83         
84         BLI_addtail(&sconsole->regionbase, ar);
85         ar->regiontype= RGN_TYPE_WINDOW;
86         
87         /* keep in sync with info */
88         ar->v2d.scroll |= (V2D_SCROLL_RIGHT);
89         ar->v2d.align |= V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y; /* align bottom left */
90         ar->v2d.keepofs |= V2D_LOCKOFS_X;
91         ar->v2d.keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_LIMITZOOM|V2D_KEEPASPECT);
92         ar->v2d.keeptot= V2D_KEEPTOT_BOUNDS;
93         ar->v2d.minzoom= ar->v2d.maxzoom= 1.0f;
94
95         /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
96         //ar->v2d.keepzoom= (V2D_KEEPASPECT|V2D_LIMITZOOM);
97
98         return (SpaceLink *)sconsole;
99 }
100
101 /* not spacelink itself */
102 static void console_free(SpaceLink *sl)
103 {
104         SpaceConsole *sc= (SpaceConsole*) sl;
105         
106         while(sc->scrollback.first)
107                 console_scrollback_free(sc, sc->scrollback.first);
108         
109         while(sc->history.first)
110                 console_history_free(sc, sc->history.first);
111 }
112
113
114 /* spacetype; init callback */
115 static void console_init(struct wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa))
116 {
117
118 }
119
120 static SpaceLink *console_duplicate(SpaceLink *sl)
121 {
122         SpaceConsole *sconsolen= MEM_dupallocN(sl);
123         
124         /* clear or remove stuff from old */
125         
126         /* TODO - duplicate?, then we also need to duplicate the py namespace */
127         sconsolen->scrollback.first= sconsolen->scrollback.last= NULL;
128         sconsolen->history.first= sconsolen->history.last= NULL;
129         
130         return (SpaceLink *)sconsolen;
131 }
132
133
134
135 /* add handlers, stuff you only do once or on area/region changes */
136 static void console_main_area_init(wmWindowManager *wm, ARegion *ar)
137 {
138         wmKeyMap *keymap;
139         ListBase *lb;
140
141         const int prev_y_min= ar->v2d.cur.ymin; /* so resizing keeps the cursor visible */
142
143         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
144
145         /* always keep the bottom part of the view aligned, less annoying */
146         if(prev_y_min != ar->v2d.cur.ymin) {
147                 const float cur_y_range= ar->v2d.cur.ymax - ar->v2d.cur.ymin;
148                 ar->v2d.cur.ymin= prev_y_min;
149                 ar->v2d.cur.ymax= prev_y_min + cur_y_range;
150         }
151
152         /* own keymap */
153         keymap= WM_keymap_find(wm->defaultconf, "Console", SPACE_CONSOLE, 0);
154         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
155         
156         /* add drop boxes */
157         lb= WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
158         
159         WM_event_add_dropbox_handler(&ar->handlers, lb);
160 }
161
162
163 /* ************* dropboxes ************* */
164
165 static int id_drop_poll(bContext *UNUSED(C), wmDrag *drag, wmEvent *UNUSED(event))
166 {
167 //      SpaceConsole *sc= CTX_wm_space_console(C);
168         if(drag->type==WM_DRAG_ID)
169                 return 1;
170         return 0;
171 }
172
173 static void id_drop_copy(wmDrag *drag, wmDropBox *drop)
174 {
175         char text[64];
176         ID *id= drag->poin;
177         char id_esc[(sizeof(id->name) - 2) * 2];
178
179         BLI_strescape(id_esc, id->name+2, sizeof(id_esc));
180
181         BLI_snprintf(text, sizeof(text), "bpy.data.%s[\"%s\"]", BKE_idcode_to_name_plural(GS(id->name)), id_esc);
182
183         /* copy drag path to properties */
184         RNA_string_set(drop->ptr, "text", text);
185 }
186
187 static int path_drop_poll(bContext *UNUSED(C), wmDrag *drag, wmEvent *UNUSED(event))
188 {
189 //    SpaceConsole *sc= CTX_wm_space_console(C);
190         if(drag->type==WM_DRAG_PATH)
191                 return 1;
192         return 0;
193 }
194
195 static void path_drop_copy(wmDrag *drag, wmDropBox *drop)
196 {
197         char pathname[FILE_MAXDIR+FILE_MAXFILE+2];
198         BLI_snprintf(pathname, sizeof(pathname), "\"%s\"", drag->path);
199         RNA_string_set(drop->ptr, "text", pathname);
200 }
201
202
203 /* this region dropbox definition */
204 static void console_dropboxes(void)
205 {
206         ListBase *lb= WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
207         
208         WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy);
209         WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy);
210 }
211
212 /* ************* end drop *********** */
213
214 static void console_main_area_draw(const bContext *C, ARegion *ar)
215 {
216         /* draw entirely, view changes should be handled here */
217         SpaceConsole *sc= CTX_wm_space_console(C);
218         View2D *v2d= &ar->v2d;
219         View2DScrollers *scrollers;
220
221         if(sc->scrollback.first==NULL)
222                 WM_operator_name_call((bContext *)C, "CONSOLE_OT_banner", WM_OP_EXEC_DEFAULT, NULL);
223
224         /* clear and setup matrix */
225         UI_ThemeClearColor(TH_BACK);
226         glClear(GL_COLOR_BUFFER_BIT);
227
228         /* worlks best with no view2d matrix set */
229         UI_view2d_view_ortho(v2d);
230
231         /* data... */
232
233         console_history_verify(C); /* make sure we have some command line */
234         console_textview_main(sc, ar);
235         
236         /* reset view matrix */
237         UI_view2d_view_restore(C);
238         
239         /* scrollers */
240         scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_GRID_CLAMP);
241         UI_view2d_scrollers_draw(C, v2d, scrollers);
242         UI_view2d_scrollers_free(scrollers);
243 }
244
245 static void console_operatortypes(void)
246 {
247         /* console_ops.c */
248         WM_operatortype_append(CONSOLE_OT_move);
249         WM_operatortype_append(CONSOLE_OT_delete);
250         WM_operatortype_append(CONSOLE_OT_insert);
251         
252         /* for use by python only */
253         WM_operatortype_append(CONSOLE_OT_history_append); 
254         WM_operatortype_append(CONSOLE_OT_scrollback_append);
255         
256         WM_operatortype_append(CONSOLE_OT_clear); 
257         WM_operatortype_append(CONSOLE_OT_history_cycle);
258         WM_operatortype_append(CONSOLE_OT_copy);
259         WM_operatortype_append(CONSOLE_OT_paste);
260         WM_operatortype_append(CONSOLE_OT_select_set);
261 }
262
263 static void console_keymap(struct wmKeyConfig *keyconf)
264 {
265         wmKeyMap *keymap= WM_keymap_find(keyconf, "Console", SPACE_CONSOLE, 0);
266         wmKeyMapItem *kmi;
267         
268 #ifdef __APPLE__
269         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", LEFTARROWKEY, KM_PRESS, KM_OSKEY, 0)->ptr, "type", LINE_BEGIN);
270         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", RIGHTARROWKEY, KM_PRESS, KM_OSKEY, 0)->ptr, "type", LINE_END);
271 #endif
272
273         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", PREV_WORD);
274         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", NEXT_WORD);
275         
276         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", HOMEKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_BEGIN);
277         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", ENDKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_END);
278         
279         kmi = WM_keymap_add_item(keymap, "WM_OT_context_cycle_int", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0);
280         RNA_string_set(kmi->ptr, "data_path", "space_data.font_size");
281         RNA_boolean_set(kmi->ptr, "reverse", 0);
282         
283         kmi = WM_keymap_add_item(keymap, "WM_OT_context_cycle_int", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0);
284         RNA_string_set(kmi->ptr, "data_path", "space_data.font_size");
285         RNA_boolean_set(kmi->ptr, "reverse", 1);
286
287         kmi = WM_keymap_add_item(keymap, "WM_OT_context_cycle_int", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
288         RNA_string_set(kmi->ptr, "data_path", "space_data.font_size");
289         RNA_boolean_set(kmi->ptr, "reverse", 0);
290         
291         kmi = WM_keymap_add_item(keymap, "WM_OT_context_cycle_int", PADMINUS, KM_PRESS, KM_CTRL, 0);
292         RNA_string_set(kmi->ptr, "data_path", "space_data.font_size");
293         RNA_boolean_set(kmi->ptr, "reverse", 1);
294
295         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_CHAR);
296         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_CHAR);
297         
298         RNA_boolean_set(WM_keymap_add_item(keymap, "CONSOLE_OT_history_cycle", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "reverse", 1);
299         WM_keymap_add_item(keymap, "CONSOLE_OT_history_cycle", DOWNARROWKEY, KM_PRESS, 0, 0);
300         
301         /*
302         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", PREV_WORD);
303         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", NEXT_WORD);
304         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_LINE);
305         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_LINE);
306         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_PAGE);
307         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_PAGE);
308
309
310         //RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DELKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_NEXT_CHAR);
311         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_NEXT_CHAR);
312         //RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", BACKSPACEKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_PREV_CHAR);
313         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DELKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_NEXT_WORD);
314         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", BACKSPACEKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_PREV_WORD);
315         */
316         
317         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DELKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_NEXT_CHAR);
318         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", BACKSPACEKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_PREV_CHAR);
319         RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", BACKSPACEKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", DEL_PREV_CHAR);  /* same as above [#26623] */
320
321 #ifdef WITH_PYTHON
322         WM_keymap_add_item(keymap, "CONSOLE_OT_execute", RETKEY, KM_PRESS, 0, 0); /* python operator - space_text.py */
323         WM_keymap_add_item(keymap, "CONSOLE_OT_execute", PADENTER, KM_PRESS, 0, 0);
324         
325         //WM_keymap_add_item(keymap, "CONSOLE_OT_autocomplete", TABKEY, KM_PRESS, 0, 0); /* python operator - space_text.py */
326         WM_keymap_add_item(keymap, "CONSOLE_OT_autocomplete", SPACEKEY, KM_PRESS, KM_CTRL, 0); /* python operator - space_text.py */
327 #endif
328
329         WM_keymap_add_item(keymap, "CONSOLE_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0);
330         WM_keymap_add_item(keymap, "CONSOLE_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0);
331 #ifdef __APPLE__
332         WM_keymap_add_item(keymap, "CONSOLE_OT_copy", CKEY, KM_PRESS, KM_OSKEY, 0);
333         WM_keymap_add_item(keymap, "CONSOLE_OT_paste", VKEY, KM_PRESS, KM_OSKEY, 0);
334 #endif
335         
336         WM_keymap_add_item(keymap, "CONSOLE_OT_select_set", LEFTMOUSE, KM_PRESS, 0, 0);
337
338         RNA_string_set(WM_keymap_add_item(keymap, "CONSOLE_OT_insert", TABKEY, KM_PRESS, 0, 0)->ptr, "text", "\t"); /* fake tabs */
339         WM_keymap_add_item(keymap, "CONSOLE_OT_insert", KM_TEXTINPUT, KM_ANY, KM_ANY, 0); // last!
340 }
341
342 /****************** header region ******************/
343
344 /* add handlers, stuff you only do once or on area/region changes */
345 static void console_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
346 {
347         ED_region_header_init(ar);
348 }
349
350 static void console_header_area_draw(const bContext *C, ARegion *ar)
351 {
352         ED_region_header(C, ar);
353 }
354
355 static void console_main_area_listener(ARegion *ar, wmNotifier *wmn)
356 {
357         // SpaceInfo *sinfo= sa->spacedata.first;
358
359         /* context changes */
360         switch(wmn->category) {
361                 case NC_SPACE:
362                         if(wmn->data == ND_SPACE_CONSOLE) { /* generic redraw request */
363                                 ED_region_tag_redraw(ar);
364                         }
365                         break;
366         }
367 }
368
369 /* only called once, from space/spacetypes.c */
370 void ED_spacetype_console(void)
371 {
372         SpaceType *st= MEM_callocN(sizeof(SpaceType), "spacetype console");
373         ARegionType *art;
374         
375         st->spaceid= SPACE_CONSOLE;
376         strncpy(st->name, "Console", BKE_ST_MAXNAME);
377         
378         st->new= console_new;
379         st->free= console_free;
380         st->init= console_init;
381         st->duplicate= console_duplicate;
382         st->operatortypes= console_operatortypes;
383         st->keymap= console_keymap;
384         st->dropboxes= console_dropboxes;
385         
386         /* regions: main window */
387         art= MEM_callocN(sizeof(ARegionType), "spacetype console region");
388         art->regionid = RGN_TYPE_WINDOW;
389         art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D;
390
391         art->init= console_main_area_init;
392         art->draw= console_main_area_draw;
393         art->listener= console_main_area_listener;
394         
395         
396
397         BLI_addhead(&st->regiontypes, art);
398         
399         /* regions: header */
400         art= MEM_callocN(sizeof(ARegionType), "spacetype console region");
401         art->regionid = RGN_TYPE_HEADER;
402         art->prefsizey= HEADERY;
403         art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D|ED_KEYMAP_HEADER;
404         
405         art->init= console_header_area_init;
406         art->draw= console_header_area_draw;
407         
408         BLI_addhead(&st->regiontypes, art);
409
410
411         BKE_spacetype_register(st);
412 }