svn merge -r 23207:23528 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender-staging.git] / source / blender / editors / space_console / console_ops.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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): Campbell Barton
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h> /* ispunct */
33 #include <sys/stat.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_scene_types.h"
38 #include "DNA_screen_types.h"
39 #include "DNA_space_types.h"
40 #include "DNA_userdef_types.h"
41 #include "DNA_windowmanager_types.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_dynstr.h"
45 #include "PIL_time.h"
46
47 #include "BKE_utildefines.h"
48 #include "BKE_context.h"
49 #include "BKE_depsgraph.h"
50 #include "BKE_global.h"
51 #include "BKE_library.h"
52 #include "BKE_main.h"
53 #include "BKE_report.h"
54 #include "BKE_text.h" /* only for character utility funcs */
55
56 #include "WM_api.h"
57 #include "WM_types.h"
58
59 #include "ED_screen.h"
60 #include "ED_types.h"
61 #include "UI_interface.h"
62 #include "UI_resources.h"
63
64 #include "RNA_access.h"
65 #include "RNA_define.h"
66
67 #include "console_intern.h"
68
69 void console_history_free(SpaceConsole *sc, ConsoleLine *cl)
70 {
71         BLI_remlink(&sc->history, cl);
72         MEM_freeN(cl->line);
73         MEM_freeN(cl);
74 }
75 void console_scrollback_free(SpaceConsole *sc, ConsoleLine *cl)
76 {
77         BLI_remlink(&sc->scrollback, cl);
78         MEM_freeN(cl->line);
79         MEM_freeN(cl);
80 }
81
82 void console_scrollback_limit(SpaceConsole *sc)
83 {
84         int tot;
85         
86         if (U.scrollback < 32) U.scrollback= 128; // XXX - save in user defaults
87         
88         for(tot= BLI_countlist(&sc->scrollback); tot > U.scrollback; tot--)
89                 console_scrollback_free(sc, sc->scrollback.first);
90 }
91
92 static ConsoleLine * console_history_find(SpaceConsole *sc, const char *str, ConsoleLine *cl_ignore)
93 {
94         ConsoleLine *cl;
95
96         for(cl= sc->history.last; cl; cl= cl->prev) {
97                 if (cl==cl_ignore)
98                         continue;
99
100                 if(strcmp(str, cl->line)==0)
101                         return cl;
102         }
103
104         return NULL;
105 }
106
107 /* return 0 if no change made, clamps the range */
108 static int console_line_cursor_set(ConsoleLine *cl, int cursor)
109 {
110         int cursor_new;
111         
112         if(cursor < 0)                          cursor_new= 0;
113         else if(cursor > cl->len)       cursor_new= cl->len;
114         else                                            cursor_new= cursor;
115         
116         if(cursor_new == cl->cursor)
117                 return 0;
118         
119         cl->cursor= cursor_new;
120         return 1;
121 }
122
123 static char cursor_char(ConsoleLine *cl)
124 {
125         /* assume cursor is clamped */
126         return cl->line[cl->cursor];
127 }
128
129 static char cursor_char_prev(ConsoleLine *cl)
130 {
131         /* assume cursor is clamped */
132         if(cl->cursor <= 0)
133                 return '\0';
134
135         return cl->line[cl->cursor-1];
136 }
137
138 static char cursor_char_next(ConsoleLine *cl)
139 {
140         /* assume cursor is clamped */
141         if(cl->cursor + 1 >= cl->len)
142                 return '\0';
143
144         return cl->line[cl->cursor+1];
145 }
146
147 static void console_lb_debug__internal(ListBase *lb)
148 {
149         ConsoleLine *cl;
150
151         printf("%d: ", BLI_countlist(lb));
152         for(cl= lb->first; cl; cl= cl->next)
153                 printf("<%s> ", cl->line);
154         printf("\n");
155
156 }
157
158 static void console_history_debug(const bContext *C)
159 {
160         SpaceConsole *sc= CTX_wm_space_console(C);
161
162         console_lb_debug__internal(&sc->history);
163 }
164
165 static ConsoleLine *console_lb_add__internal(ListBase *lb, ConsoleLine *from)
166 {
167         ConsoleLine *ci= MEM_callocN(sizeof(ConsoleLine), "ConsoleLine Add");
168         
169         if(from) {
170                 ci->line= BLI_strdup(from->line);
171                 ci->len= strlen(ci->line);
172                 ci->len_alloc= ci->len;
173                 
174                 ci->cursor= from->cursor;
175                 ci->type= from->type;
176         } else {
177                 ci->line= MEM_callocN(64, "console-in-line");
178                 ci->len_alloc= 64;
179                 ci->len= 0;
180         }
181         
182         BLI_addtail(lb, ci);
183         return ci;
184 }
185
186 static ConsoleLine *console_history_add(const bContext *C, ConsoleLine *from)
187 {
188         SpaceConsole *sc= CTX_wm_space_console(C);
189         
190         return console_lb_add__internal(&sc->history, from);
191 }
192
193 #if 0 /* may use later ? */
194 static ConsoleLine *console_scrollback_add(const bContext *C, ConsoleLine *from)
195 {
196         SpaceConsole *sc= CTX_wm_space_console(C);
197         
198         return console_lb_add__internal(&sc->scrollback, from);
199 }
200 #endif
201
202 static ConsoleLine *console_lb_add_str__internal(ListBase *lb, const bContext *C, char *str, int own)
203 {
204         ConsoleLine *ci= MEM_callocN(sizeof(ConsoleLine), "ConsoleLine Add");
205         if(own)         ci->line= str;
206         else            ci->line= BLI_strdup(str);
207         
208         ci->len = ci->len_alloc = strlen(str);
209         
210         BLI_addtail(lb, ci);
211         return ci;
212 }
213 ConsoleLine *console_history_add_str(const bContext *C, char *str, int own)
214 {
215         return console_lb_add_str__internal(&CTX_wm_space_console(C)->history, C, str, own);
216 }
217 ConsoleLine *console_scrollback_add_str(const bContext *C, char *str, int own)
218 {
219         return console_lb_add_str__internal(&CTX_wm_space_console(C)->scrollback, C, str, own);
220 }
221
222 ConsoleLine *console_history_verify(const bContext *C)
223 {
224         SpaceConsole *sc= CTX_wm_space_console(C);
225         ConsoleLine *ci= sc->history.last;
226         if(ci==NULL)
227                 ci= console_history_add(C, NULL);
228         
229         return ci;
230 }
231
232
233 static void console_line_verify_length(ConsoleLine *ci, int len)
234 {
235         /* resize the buffer if needed */
236         if(len >= ci->len_alloc) {
237                 int new_len= len * 2; /* new length */
238                 char *new_line= MEM_callocN(new_len, "console line");
239                 memcpy(new_line, ci->line, ci->len);
240                 MEM_freeN(ci->line);
241                 
242                 ci->line= new_line;
243                 ci->len_alloc= new_len;
244         }
245 }
246
247 static int console_line_insert(ConsoleLine *ci, char *str)
248 {
249         int len = strlen(str);
250         
251         if(len>0 && str[len-1]=='\n') {/* stop new lines being pasted at the end of lines */
252                 str[len-1]= '\0';
253                 len--;
254         }
255
256         if(len==0)
257                 return 0;
258         
259         console_line_verify_length(ci, len + ci->len);
260         
261         memmove(ci->line+ci->cursor+len, ci->line+ci->cursor, (ci->len - ci->cursor)+1);
262         memcpy(ci->line+ci->cursor, str, len);
263         
264         ci->len += len;
265         ci->cursor += len;
266         
267         return len;
268 }
269
270 static int console_edit_poll(bContext *C)
271 {
272         SpaceConsole *sc= CTX_wm_space_console(C);
273
274         if(!sc || sc->type != CONSOLE_TYPE_PYTHON)
275                 return 0;
276
277         return 1;
278 }
279
280 static int console_poll(bContext *C)
281 {
282         return (CTX_wm_space_console(C) != NULL);
283 }
284
285
286 /* static funcs for text editing */
287
288 /* similar to the text editor, with some not used. keep compatible */
289 static EnumPropertyItem move_type_items[]= {
290         {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
291         {LINE_END, "LINE_END", 0, "Line End", ""},
292         {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
293         {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
294         {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
295         {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
296         {0, NULL, 0, NULL, NULL}};
297
298 static int move_exec(bContext *C, wmOperator *op)
299 {
300         ConsoleLine *ci= console_history_verify(C);
301         
302         int type= RNA_enum_get(op->ptr, "type");
303         int done= 0;
304         
305         switch(type) {
306         case LINE_BEGIN:
307                 done= console_line_cursor_set(ci, 0);
308                 break;
309         case LINE_END:
310                 done= console_line_cursor_set(ci, INT_MAX);
311                 break;
312         case PREV_CHAR:
313                 done= console_line_cursor_set(ci, ci->cursor-1);
314                 break;
315         case NEXT_CHAR:
316                 done= console_line_cursor_set(ci, ci->cursor+1);
317                 break;
318
319         /* - if the character is a delimiter then skip delimiters (including white space)
320          * - when jump over the word */
321         case PREV_WORD:
322                 while(text_check_delim(cursor_char_prev(ci)))
323                         if(console_line_cursor_set(ci, ci->cursor-1)==FALSE)
324                                 break;
325
326                 while(text_check_delim(cursor_char_prev(ci))==FALSE)
327                         if(console_line_cursor_set(ci, ci->cursor-1)==FALSE)
328                                 break;
329
330                 /* This isnt used for NEXT_WORD because when going back
331                  * its more useful to have the cursor directly after a word then whitespace */
332                 while(text_check_whitespace(cursor_char_prev(ci))==TRUE)
333                         if(console_line_cursor_set(ci, ci->cursor-1)==FALSE)
334                                 break;
335
336                 done= 1; /* assume changed */
337                 break;
338         case NEXT_WORD:
339                 while(text_check_delim(cursor_char(ci))==TRUE)
340                         if (console_line_cursor_set(ci, ci->cursor+1)==FALSE)
341                                 break;
342
343                 while(text_check_delim(cursor_char(ci))==FALSE)
344                         if (console_line_cursor_set(ci, ci->cursor+1)==FALSE)
345                                 break;
346
347                 done= 1; /* assume changed */
348                 break;
349         }
350         
351         if(done) {
352                 ED_area_tag_redraw(CTX_wm_area(C));
353         }
354         
355         return OPERATOR_FINISHED;
356 }
357
358 void CONSOLE_OT_move(wmOperatorType *ot)
359 {
360         /* identifiers */
361         ot->name= "Move Cursor";
362     ot->description= "Move cursor position.";
363         ot->idname= "CONSOLE_OT_move";
364         
365         /* api callbacks */
366         ot->exec= move_exec;
367         ot->poll= console_edit_poll;
368
369         /* flags */
370         ot->flag= OPTYPE_REGISTER;
371
372         /* properties */
373         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
374 }
375
376
377 static int insert_exec(bContext *C, wmOperator *op)
378 {
379         ConsoleLine *ci= console_history_verify(C);
380         char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
381         
382         int len= console_line_insert(ci, str);
383         
384         MEM_freeN(str);
385         
386         if(len==0)
387                 return OPERATOR_CANCELLED;
388                 
389         ED_area_tag_redraw(CTX_wm_area(C));
390         
391         return OPERATOR_FINISHED;
392 }
393
394 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
395 {
396         if(!RNA_property_is_set(op->ptr, "text")) {
397                 char str[2] = {event->ascii, '\0'};
398                 RNA_string_set(op->ptr, "text", str);
399         }
400         return insert_exec(C, op);
401 }
402
403 void CONSOLE_OT_insert(wmOperatorType *ot)
404 {
405         /* identifiers */
406         ot->name= "Insert";
407     ot->description= "Insert text at cursor position.";
408         ot->idname= "CONSOLE_OT_insert";
409         
410         /* api callbacks */
411         ot->exec= insert_exec;
412         ot->invoke= insert_invoke;
413         ot->poll= console_edit_poll;
414
415         /* flags */
416         ot->flag= OPTYPE_REGISTER;
417
418         /* properties */
419         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
420 }
421
422
423 static EnumPropertyItem delete_type_items[]= {
424         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
425         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
426 //      {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
427 //      {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
428         {0, NULL, 0, NULL, NULL}};
429
430 static int delete_exec(bContext *C, wmOperator *op)
431 {
432         
433         ConsoleLine *ci= console_history_verify(C);
434         
435         
436         int done = 0;
437
438         int type= RNA_enum_get(op->ptr, "type");
439         
440         if(ci->len==0) {
441                 return OPERATOR_CANCELLED;
442         }
443         
444         switch(type) {
445         case DEL_NEXT_CHAR:
446                 if(ci->cursor < ci->len) {
447                         memmove(ci->line + ci->cursor, ci->line + ci->cursor+1, (ci->len - ci->cursor)+1);
448                         ci->len--;
449                         done= 1;
450                 }
451                 break;
452         case DEL_PREV_CHAR:
453                 if(ci->cursor > 0) {
454                         ci->cursor--; /* same as above */
455                         memmove(ci->line + ci->cursor, ci->line + ci->cursor+1, (ci->len - ci->cursor)+1);
456                         ci->len--;
457                         done= 1;
458                 }
459                 break;
460         }
461         
462         if(!done)
463                 return OPERATOR_CANCELLED;
464         
465         ED_area_tag_redraw(CTX_wm_area(C));
466         
467         return OPERATOR_FINISHED;
468 }
469
470
471 void CONSOLE_OT_delete(wmOperatorType *ot)
472 {
473         /* identifiers */
474         ot->name= "Delete";
475     ot->description= "Delete text by cursor position.";
476         ot->idname= "CONSOLE_OT_delete";
477         
478         /* api callbacks */
479         ot->exec= delete_exec;
480         ot->poll= console_edit_poll;
481
482         /* flags */
483         ot->flag= OPTYPE_REGISTER;
484
485         /* properties */
486         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
487 }
488
489
490 /* the python exec operator uses this */
491 static int clear_exec(bContext *C, wmOperator *op)
492 {
493         SpaceConsole *sc= CTX_wm_space_console(C);
494         
495         short scrollback= RNA_boolean_get(op->ptr, "scrollback");
496         short history= RNA_boolean_get(op->ptr, "history");
497         
498         /*ConsoleLine *ci= */ console_history_verify(C);
499         
500         if(scrollback) { /* last item in mistory */
501                 while(sc->scrollback.first)
502                         console_scrollback_free(sc, sc->scrollback.first);
503         }
504         
505         if(history) {
506                 while(sc->history.first)
507                         console_history_free(sc, sc->history.first);
508         }
509         
510         ED_area_tag_redraw(CTX_wm_area(C));
511         
512         return OPERATOR_FINISHED;
513 }
514
515 void CONSOLE_OT_clear(wmOperatorType *ot)
516 {
517         /* identifiers */
518         ot->name= "Clear";
519     ot->description= "Clear text by type.";
520         ot->idname= "CONSOLE_OT_clear";
521         
522         /* api callbacks */
523         ot->exec= clear_exec;
524         ot->poll= console_edit_poll;
525
526         /* flags */
527         ot->flag= OPTYPE_REGISTER;
528         
529         /* properties */
530         RNA_def_boolean(ot->srna, "scrollback", 1, "Scrollback", "Clear the scrollback history");
531         RNA_def_boolean(ot->srna, "history", 0, "History", "Clear the command history");
532 }
533
534
535
536 /* the python exec operator uses this */
537 static int history_cycle_exec(bContext *C, wmOperator *op)
538 {
539         SpaceConsole *sc= CTX_wm_space_console(C);
540         ConsoleLine *ci= console_history_verify(C); /* TODO - stupid, just prevernts crashes when no command line */
541         
542         short reverse= RNA_boolean_get(op->ptr, "reverse"); /* assumes down, reverse is up */
543
544         /* keep a copy of the line above so when history is cycled
545          * this is the only function that needs to know about the double-up */
546         if(ci->prev) {
547                 ConsoleLine *ci_prev= (ConsoleLine *)ci->prev;
548
549                 if(strcmp(ci->line, ci_prev->line)==0)
550                         console_history_free(sc, ci_prev);
551         }
552
553         if(reverse) { /* last item in mistory */
554                 ci= sc->history.last;
555                 BLI_remlink(&sc->history, ci);
556                 BLI_addhead(&sc->history, ci);
557         }
558         else {
559                 ci= sc->history.first;
560                 BLI_remlink(&sc->history, ci);
561                 BLI_addtail(&sc->history, ci);
562         }
563
564         {       /* add a duplicate of the new arg and remove all other instances */
565                 ConsoleLine *cl;
566                 while((cl= console_history_find(sc, ci->line, ci)))
567                         console_history_free(sc, cl);
568
569                 console_history_add(C, (ConsoleLine *)sc->history.last);
570         }
571
572         ED_area_tag_redraw(CTX_wm_area(C));
573
574         return OPERATOR_FINISHED;
575 }
576
577 void CONSOLE_OT_history_cycle(wmOperatorType *ot)
578 {
579         /* identifiers */
580         ot->name= "History Cycle";
581     ot->description= "Cycle through history.";
582         ot->idname= "CONSOLE_OT_history_cycle";
583         
584         /* api callbacks */
585         ot->exec= history_cycle_exec;
586         ot->poll= console_edit_poll;
587
588         /* flags */
589         ot->flag= OPTYPE_REGISTER;
590         
591         /* properties */
592         RNA_def_boolean(ot->srna, "reverse", 0, "Reverse", "reverse cycle history");
593 }
594
595
596 /* the python exec operator uses this */
597 static int history_append_exec(bContext *C, wmOperator *op)
598 {
599         ConsoleLine *ci= console_history_verify(C);
600         char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0); /* own this text in the new line, dont free */
601         int cursor= RNA_int_get(op->ptr, "current_character");
602         short rem_dupes= RNA_boolean_get(op->ptr, "remove_duplicates");
603
604         if(rem_dupes) {
605                 SpaceConsole *sc= CTX_wm_space_console(C);
606                 ConsoleLine *cl;
607
608                 while((cl= console_history_find(sc, ci->line, ci)))
609                         console_history_free(sc, cl);
610
611                 if(strcmp(str, ci->line)==0) {
612                         MEM_freeN(str);
613                         return OPERATOR_FINISHED;
614                 }
615         }
616
617         ci= console_history_add_str(C, str, 1); /* own the string */
618         console_line_cursor_set(ci, cursor);
619         
620         ED_area_tag_redraw(CTX_wm_area(C));
621         
622         return OPERATOR_FINISHED;
623 }
624
625 void CONSOLE_OT_history_append(wmOperatorType *ot)
626 {
627         /* identifiers */
628         ot->name= "History Append";
629     ot->description= "Append history at cursor position.";
630         ot->idname= "CONSOLE_OT_history_append";
631         
632         /* api callbacks */
633         ot->exec= history_append_exec;
634         ot->poll= console_edit_poll;
635
636         /* flags */
637         ot->flag= OPTYPE_REGISTER;
638         
639         /* properties */
640         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");      
641         RNA_def_int(ot->srna, "current_character", 0, 0, INT_MAX, "Cursor", "The index of the cursor.", 0, 10000);
642         RNA_def_boolean(ot->srna, "remove_duplicates", 0, "Remove Duplicates", "Remove duplicate items in the history");
643 }
644
645
646 /* the python exec operator uses this */
647 static int scrollback_append_exec(bContext *C, wmOperator *op)
648 {
649         SpaceConsole *sc= CTX_wm_space_console(C);
650         ConsoleLine *ci= console_history_verify(C);
651         
652         char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0); /* own this text in the new line, dont free */
653         int type= RNA_enum_get(op->ptr, "type");
654         
655         ci= console_scrollback_add_str(C, str, 1); /* own the string */
656         ci->type= type;
657         
658         console_scrollback_limit(sc);
659         
660         ED_area_tag_redraw(CTX_wm_area(C));
661         
662         return OPERATOR_FINISHED;
663 }
664
665 void CONSOLE_OT_scrollback_append(wmOperatorType *ot)
666 {
667         /* defined in DNA_space_types.h */
668         static EnumPropertyItem console_line_type_items[] = {
669                 {CONSOLE_LINE_OUTPUT,   "OUTPUT", 0, "Output", ""},
670                 {CONSOLE_LINE_INPUT,    "INPUT", 0, "Input", ""},
671                 {CONSOLE_LINE_INFO,             "INFO", 0, "Information", ""},
672                 {CONSOLE_LINE_ERROR,    "ERROR", 0, "Error", ""},
673                 {0, NULL, 0, NULL, NULL}};
674
675         /* identifiers */
676         ot->name= "Scrollback Append";
677     ot->description= "Append scrollback text by type.";
678         ot->idname= "CONSOLE_OT_scrollback_append";
679         
680         /* api callbacks */
681         ot->exec= scrollback_append_exec;
682         ot->poll= console_edit_poll;
683
684         /* flags */
685         ot->flag= OPTYPE_REGISTER;
686         
687         /* properties */
688         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");      
689         RNA_def_enum(ot->srna, "type", console_line_type_items, CONSOLE_LINE_OUTPUT, "Type", "Console output type.");
690 }
691
692
693 static int copy_exec(bContext *C, wmOperator *op)
694 {
695         SpaceConsole *sc= CTX_wm_space_console(C);
696
697         DynStr *buf_dyn= BLI_dynstr_new();
698         char *buf_str;
699         
700         ConsoleLine *cl;
701         
702         for(cl= sc->scrollback.first; cl; cl= cl->next) {
703                 BLI_dynstr_append(buf_dyn, cl->line);
704                 BLI_dynstr_append(buf_dyn, "\n");
705         }
706
707         buf_str= BLI_dynstr_get_cstring(buf_dyn);
708         BLI_dynstr_free(buf_dyn);
709
710         WM_clipboard_text_set(buf_str, 0);
711
712         MEM_freeN(buf_str);
713         return OPERATOR_FINISHED;
714 }
715
716 void CONSOLE_OT_copy(wmOperatorType *ot)
717 {
718         /* identifiers */
719         ot->name= "Copy to Clipboard";
720     ot->description= "Copy selected text to clipboard.";
721         ot->idname= "CONSOLE_OT_copy";
722
723         /* api callbacks */
724         ot->poll= console_edit_poll;
725         ot->exec= copy_exec;
726
727         /* flags */
728         ot->flag= OPTYPE_REGISTER;
729
730         /* properties */
731 }
732
733 static int paste_exec(bContext *C, wmOperator *op)
734 {
735         ConsoleLine *ci= console_history_verify(C);
736
737         char *buf_str= WM_clipboard_text_get(0);
738
739         if(buf_str==NULL)
740                 return OPERATOR_CANCELLED;
741
742         console_line_insert(ci, buf_str); /* TODO - Multiline copy?? */
743
744         MEM_freeN(buf_str);
745
746         ED_area_tag_redraw(CTX_wm_area(C));
747
748         return OPERATOR_FINISHED;
749 }
750
751 void CONSOLE_OT_paste(wmOperatorType *ot)
752 {
753         /* identifiers */
754         ot->name= "Paste from Clipboard";
755     ot->description= "Paste text from clipboard.";
756         ot->idname= "CONSOLE_OT_paste";
757
758         /* api callbacks */
759         ot->poll= console_edit_poll;
760         ot->exec= paste_exec;
761
762         /* flags */
763         ot->flag= OPTYPE_REGISTER;
764
765         /* properties */
766 }
767
768 static int zoom_exec(bContext *C, wmOperator *op)
769 {
770         SpaceConsole *sc= CTX_wm_space_console(C);
771         
772         int delta= RNA_int_get(op->ptr, "delta");
773         
774         sc->lheight += delta;
775         CLAMP(sc->lheight, 8, 32);
776         
777         ED_area_tag_redraw(CTX_wm_area(C));
778         
779         return OPERATOR_FINISHED;
780 }
781
782
783 void CONSOLE_OT_zoom(wmOperatorType *ot)
784 {
785         /* identifiers */
786         ot->name= "Console Zoom";
787     /*optionals - 
788       "Zoom view font." */
789     ot->description= "Zoom screen area.";
790         ot->idname= "CONSOLE_OT_zoom";
791         
792         /* api callbacks */
793         ot->exec= zoom_exec;
794         ot->poll= console_poll;
795
796         /* flags */
797         /* ot->flag= OPTYPE_REGISTER; */ /* super annoying */
798         
799         /* properties */
800         RNA_def_int(ot->srna, "delta", 0, 0, INT_MAX, "Delta", "Scale the view font.", 0, 1000);
801 }