fix for compiling with the c90 standard, support for non-static variable initializers...
[blender.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_userdef_types.h"
38
39 #include "BLI_blenlib.h"
40 #include "BLI_dynstr.h"
41
42 #include "BKE_context.h"
43 #include "BKE_text.h" /* only for character utility funcs */
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "ED_screen.h"
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52
53 #include "console_intern.h"
54
55 static void console_select_offset(SpaceConsole *sc, const int offset)
56 {
57         sc->sel_start += offset;
58         sc->sel_end += offset;
59 }
60
61 void console_history_free(SpaceConsole *sc, ConsoleLine *cl)
62 {
63         BLI_remlink(&sc->history, cl);
64         MEM_freeN(cl->line);
65         MEM_freeN(cl);
66 }
67 void console_scrollback_free(SpaceConsole *sc, ConsoleLine *cl)
68 {
69         BLI_remlink(&sc->scrollback, cl);
70         MEM_freeN(cl->line);
71         MEM_freeN(cl);
72 }
73
74 void console_scrollback_limit(SpaceConsole *sc)
75 {
76         int tot;
77         
78         if (U.scrollback < 32) U.scrollback= 128; // XXX - save in user defaults
79         
80         for(tot= BLI_countlist(&sc->scrollback); tot > U.scrollback; tot--)
81                 console_scrollback_free(sc, sc->scrollback.first);
82 }
83
84 static ConsoleLine * console_history_find(SpaceConsole *sc, const char *str, ConsoleLine *cl_ignore)
85 {
86         ConsoleLine *cl;
87
88         for(cl= sc->history.last; cl; cl= cl->prev) {
89                 if (cl==cl_ignore)
90                         continue;
91
92                 if(strcmp(str, cl->line)==0)
93                         return cl;
94         }
95
96         return NULL;
97 }
98
99 /* return 0 if no change made, clamps the range */
100 static int console_line_cursor_set(ConsoleLine *cl, int cursor)
101 {
102         int cursor_new;
103         
104         if(cursor < 0)                          cursor_new= 0;
105         else if(cursor > cl->len)       cursor_new= cl->len;
106         else                                            cursor_new= cursor;
107         
108         if(cursor_new == cl->cursor)
109                 return 0;
110         
111         cl->cursor= cursor_new;
112         return 1;
113 }
114
115 static char cursor_char(ConsoleLine *cl)
116 {
117         /* assume cursor is clamped */
118         return cl->line[cl->cursor];
119 }
120
121 static char cursor_char_prev(ConsoleLine *cl)
122 {
123         /* assume cursor is clamped */
124         if(cl->cursor <= 0)
125                 return '\0';
126
127         return cl->line[cl->cursor-1];
128 }
129
130 #if 0 // XXX unused 
131 static char cursor_char_next(ConsoleLine *cl)
132 {
133         /* assume cursor is clamped */
134         if(cl->cursor + 1 >= cl->len)
135                 return '\0';
136
137         return cl->line[cl->cursor+1];
138 }
139
140 static void console_lb_debug__internal(ListBase *lb)
141 {
142         ConsoleLine *cl;
143
144         printf("%d: ", BLI_countlist(lb));
145         for(cl= lb->first; cl; cl= cl->next)
146                 printf("<%s> ", cl->line);
147         printf("\n");
148
149 }
150
151 static void console_history_debug(const bContext *C)
152 {
153         SpaceConsole *sc= CTX_wm_space_console(C);
154
155         
156         console_lb_debug__internal(&sc->history);
157 }
158 #endif
159
160 static ConsoleLine *console_lb_add__internal(ListBase *lb, ConsoleLine *from)
161 {
162         ConsoleLine *ci= MEM_callocN(sizeof(ConsoleLine), "ConsoleLine Add");
163         
164         if(from) {
165                 ci->line= BLI_strdup(from->line);
166                 ci->len= strlen(ci->line);
167                 ci->len_alloc= ci->len;
168                 
169                 ci->cursor= from->cursor;
170                 ci->type= from->type;
171         } else {
172                 ci->line= MEM_callocN(64, "console-in-line");
173                 ci->len_alloc= 64;
174                 ci->len= 0;
175         }
176         
177         BLI_addtail(lb, ci);
178         return ci;
179 }
180
181 static ConsoleLine *console_history_add(const bContext *C, ConsoleLine *from)
182 {
183         SpaceConsole *sc= CTX_wm_space_console(C);
184         
185         return console_lb_add__internal(&sc->history, from);
186 }
187
188 #if 0 /* may use later ? */
189 static ConsoleLine *console_scrollback_add(const bContext *C, ConsoleLine *from)
190 {
191         SpaceConsole *sc= CTX_wm_space_console(C);
192         
193         return console_lb_add__internal(&sc->scrollback, from);
194 }
195 #endif
196
197 static ConsoleLine *console_lb_add_str__internal(ListBase *lb, char *str, int own)
198 {
199         ConsoleLine *ci= MEM_callocN(sizeof(ConsoleLine), "ConsoleLine Add");
200         if(own)         ci->line= str;
201         else            ci->line= BLI_strdup(str);
202         
203         ci->len = ci->len_alloc = strlen(str);
204         
205         BLI_addtail(lb, ci);
206         return ci;
207 }
208 ConsoleLine *console_history_add_str(SpaceConsole *sc, char *str, int own)
209 {
210         return console_lb_add_str__internal(&sc->history, str, own);
211 }
212 ConsoleLine *console_scrollback_add_str(SpaceConsole *sc, char *str, int own)
213 {
214         ConsoleLine *ci= console_lb_add_str__internal(&sc->scrollback, str, own);
215         console_select_offset(sc, ci->len + 1);
216         return ci;
217 }
218
219 ConsoleLine *console_history_verify(const bContext *C)
220 {
221         SpaceConsole *sc= CTX_wm_space_console(C);
222         ConsoleLine *ci= sc->history.last;
223         if(ci==NULL)
224                 ci= console_history_add(C, NULL);
225         
226         return ci;
227 }
228
229
230 static void console_line_verify_length(ConsoleLine *ci, int len)
231 {
232         /* resize the buffer if needed */
233         if(len >= ci->len_alloc) {
234                 int new_len= len * 2; /* new length */
235                 char *new_line= MEM_callocN(new_len, "console line");
236                 memcpy(new_line, ci->line, ci->len);
237                 MEM_freeN(ci->line);
238                 
239                 ci->line= new_line;
240                 ci->len_alloc= new_len;
241         }
242 }
243
244 static int console_line_insert(ConsoleLine *ci, char *str)
245 {
246         int len = strlen(str);
247         
248         if(len>0 && str[len-1]=='\n') {/* stop new lines being pasted at the end of lines */
249                 str[len-1]= '\0';
250                 len--;
251         }
252
253         if(len==0)
254                 return 0;
255         
256         console_line_verify_length(ci, len + ci->len);
257         
258         memmove(ci->line+ci->cursor+len, ci->line+ci->cursor, (ci->len - ci->cursor)+1);
259         memcpy(ci->line+ci->cursor, str, len);
260         
261         ci->len += len;
262         ci->cursor += len;
263         
264         return len;
265 }
266
267 static int console_edit_poll(bContext *C)
268 {
269         SpaceConsole *sc= CTX_wm_space_console(C);
270
271         if(!sc || sc->type != CONSOLE_TYPE_PYTHON)
272                 return 0;
273
274         return 1;
275 }
276 #if 0
277 static int console_poll(bContext *C)
278 {
279         return (CTX_wm_space_console(C) != NULL);
280 }
281 #endif
282
283 /* static funcs for text editing */
284
285 /* similar to the text editor, with some not used. keep compatible */
286 static EnumPropertyItem move_type_items[]= {
287         {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
288         {LINE_END, "LINE_END", 0, "Line End", ""},
289         {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
290         {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
291         {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
292         {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
293         {0, NULL, 0, NULL, NULL}};
294
295 static int move_exec(bContext *C, wmOperator *op)
296 {
297         ConsoleLine *ci= console_history_verify(C);
298         
299         int type= RNA_enum_get(op->ptr, "type");
300         int done= 0;
301         
302         switch(type) {
303         case LINE_BEGIN:
304                 done= console_line_cursor_set(ci, 0);
305                 break;
306         case LINE_END:
307                 done= console_line_cursor_set(ci, INT_MAX);
308                 break;
309         case PREV_CHAR:
310                 done= console_line_cursor_set(ci, ci->cursor-1);
311                 break;
312         case NEXT_CHAR:
313                 done= console_line_cursor_set(ci, ci->cursor+1);
314                 break;
315
316         /* - if the character is a delimiter then skip delimiters (including white space)
317          * - when jump over the word */
318         case PREV_WORD:
319                 while(text_check_delim(cursor_char_prev(ci)))
320                         if(console_line_cursor_set(ci, ci->cursor-1)==FALSE)
321                                 break;
322
323                 while(text_check_delim(cursor_char_prev(ci))==FALSE)
324                         if(console_line_cursor_set(ci, ci->cursor-1)==FALSE)
325                                 break;
326
327                 /* This isnt used for NEXT_WORD because when going back
328                  * its more useful to have the cursor directly after a word then whitespace */
329                 while(text_check_whitespace(cursor_char_prev(ci))==TRUE)
330                         if(console_line_cursor_set(ci, ci->cursor-1)==FALSE)
331                                 break;
332
333                 done= 1; /* assume changed */
334                 break;
335         case NEXT_WORD:
336                 while(text_check_delim(cursor_char(ci))==TRUE)
337                         if (console_line_cursor_set(ci, ci->cursor+1)==FALSE)
338                                 break;
339
340                 while(text_check_delim(cursor_char(ci))==FALSE)
341                         if (console_line_cursor_set(ci, ci->cursor+1)==FALSE)
342                                 break;
343
344                 done= 1; /* assume changed */
345                 break;
346         }
347         
348         if(done) {
349                 ED_area_tag_redraw(CTX_wm_area(C));
350         }
351         
352         return OPERATOR_FINISHED;
353 }
354
355 void CONSOLE_OT_move(wmOperatorType *ot)
356 {
357         /* identifiers */
358         ot->name= "Move Cursor";
359         ot->description= "Move cursor position";
360         ot->idname= "CONSOLE_OT_move";
361         
362         /* api callbacks */
363         ot->exec= move_exec;
364         ot->poll= console_edit_poll;
365
366         /* properties */
367         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
368 }
369
370 #define TAB_LENGTH 4
371 static int insert_exec(bContext *C, wmOperator *op)
372 {
373         ConsoleLine *ci= console_history_verify(C);
374         char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
375         int len;
376
377         // XXX, alligned tab key hack
378         if(str[0]=='\t' && str[1]=='\0') {
379                 int len= TAB_LENGTH - (ci->cursor % TAB_LENGTH);
380                 MEM_freeN(str);
381                 str= MEM_mallocN(len + 1, "insert_exec");
382                 memset(str, ' ', len);
383                 str[len]= '\0';
384         }
385
386         len= console_line_insert(ci, str);
387         
388         MEM_freeN(str);
389         
390         if(len==0) {
391                 return OPERATOR_CANCELLED;
392         }
393         else {
394                 SpaceConsole *sc= CTX_wm_space_console(C);
395                 console_select_offset(sc, len);
396         }
397                 
398         ED_area_tag_redraw(CTX_wm_area(C));
399         
400         return OPERATOR_FINISHED;
401 }
402
403 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
404 {
405         // if(!RNA_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */
406         if(!RNA_string_length(op->ptr, "text")) {
407                 /* if alt/ctrl/super are pressed pass through */
408                 if(event->ctrl || event->oskey) {
409                         return OPERATOR_PASS_THROUGH;
410                 }
411                 else {
412                         char str[2];
413                         str[0]= event->ascii;
414                         str[1]= '\0';
415
416                         RNA_string_set(op->ptr, "text", str);
417                 }
418         }
419         return insert_exec(C, op);
420 }
421
422 void CONSOLE_OT_insert(wmOperatorType *ot)
423 {
424         /* identifiers */
425         ot->name= "Insert";
426         ot->description= "Insert text at cursor position";
427         ot->idname= "CONSOLE_OT_insert";
428         
429         /* api callbacks */
430         ot->exec= insert_exec;
431         ot->invoke= insert_invoke;
432         ot->poll= console_edit_poll;
433
434         /* properties */
435         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
436 }
437
438
439 static EnumPropertyItem delete_type_items[]= {
440         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
441         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
442 //      {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
443 //      {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
444         {0, NULL, 0, NULL, NULL}};
445
446 static int delete_exec(bContext *C, wmOperator *op)
447 {
448         
449         ConsoleLine *ci= console_history_verify(C);
450         
451         
452         int done = 0;
453
454         int type= RNA_enum_get(op->ptr, "type");
455         
456         if(ci->len==0) {
457                 return OPERATOR_CANCELLED;
458         }
459         
460         switch(type) {
461         case DEL_NEXT_CHAR:
462                 if(ci->cursor < ci->len) {
463                         memmove(ci->line + ci->cursor, ci->line + ci->cursor+1, (ci->len - ci->cursor)+1);
464                         ci->len--;
465                         done= 1;
466                 }
467                 break;
468         case DEL_PREV_CHAR:
469                 if(ci->cursor > 0) {
470                         ci->cursor--; /* same as above */
471                         memmove(ci->line + ci->cursor, ci->line + ci->cursor+1, (ci->len - ci->cursor)+1);
472                         ci->len--;
473                         done= 1;
474                 }
475                 break;
476         }
477         
478         if(!done) {
479                 return OPERATOR_CANCELLED;
480         }
481         else {
482                 SpaceConsole *sc= CTX_wm_space_console(C);
483                 console_select_offset(sc, -1);
484         }
485         
486         ED_area_tag_redraw(CTX_wm_area(C));
487         
488         return OPERATOR_FINISHED;
489 }
490
491
492 void CONSOLE_OT_delete(wmOperatorType *ot)
493 {
494         /* identifiers */
495         ot->name= "Delete";
496         ot->description= "Delete text by cursor position";
497         ot->idname= "CONSOLE_OT_delete";
498         
499         /* api callbacks */
500         ot->exec= delete_exec;
501         ot->poll= console_edit_poll;
502
503         /* properties */
504         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
505 }
506
507
508 /* the python exec operator uses this */
509 static int clear_exec(bContext *C, wmOperator *op)
510 {
511         SpaceConsole *sc= CTX_wm_space_console(C);
512         
513         short scrollback= RNA_boolean_get(op->ptr, "scrollback");
514         short history= RNA_boolean_get(op->ptr, "history");
515         
516         /*ConsoleLine *ci= */ console_history_verify(C);
517         
518         if(scrollback) { /* last item in mistory */
519                 while(sc->scrollback.first)
520                         console_scrollback_free(sc, sc->scrollback.first);
521         }
522         
523         if(history) {
524                 while(sc->history.first)
525                         console_history_free(sc, sc->history.first);
526         }
527         
528         ED_area_tag_redraw(CTX_wm_area(C));
529         
530         return OPERATOR_FINISHED;
531 }
532
533 void CONSOLE_OT_clear(wmOperatorType *ot)
534 {
535         /* identifiers */
536         ot->name= "Clear";
537         ot->description= "Clear text by type";
538         ot->idname= "CONSOLE_OT_clear";
539         
540         /* api callbacks */
541         ot->exec= clear_exec;
542         ot->poll= console_edit_poll;
543         
544         /* properties */
545         RNA_def_boolean(ot->srna, "scrollback", 1, "Scrollback", "Clear the scrollback history");
546         RNA_def_boolean(ot->srna, "history", 0, "History", "Clear the command history");
547 }
548
549
550
551 /* the python exec operator uses this */
552 static int history_cycle_exec(bContext *C, wmOperator *op)
553 {
554         SpaceConsole *sc= CTX_wm_space_console(C);
555         ConsoleLine *ci= console_history_verify(C); /* TODO - stupid, just prevernts crashes when no command line */
556         short reverse= RNA_boolean_get(op->ptr, "reverse"); /* assumes down, reverse is up */
557         int prev_len= ci->len;
558
559         /* keep a copy of the line above so when history is cycled
560          * this is the only function that needs to know about the double-up */
561         if(ci->prev) {
562                 ConsoleLine *ci_prev= (ConsoleLine *)ci->prev;
563
564                 if(strcmp(ci->line, ci_prev->line)==0)
565                         console_history_free(sc, ci_prev);
566         }
567
568         if(reverse) { /* last item in mistory */
569                 ci= sc->history.last;
570                 BLI_remlink(&sc->history, ci);
571                 BLI_addhead(&sc->history, ci);
572         }
573         else {
574                 ci= sc->history.first;
575                 BLI_remlink(&sc->history, ci);
576                 BLI_addtail(&sc->history, ci);
577         }
578
579         {       /* add a duplicate of the new arg and remove all other instances */
580                 ConsoleLine *cl;
581                 while((cl= console_history_find(sc, ci->line, ci)))
582                         console_history_free(sc, cl);
583
584                 console_history_add(C, (ConsoleLine *)sc->history.last);
585         }
586         
587         ci= sc->history.last;
588         console_select_offset(sc, ci->len - prev_len);
589
590         ED_area_tag_redraw(CTX_wm_area(C));
591
592         return OPERATOR_FINISHED;
593 }
594
595 void CONSOLE_OT_history_cycle(wmOperatorType *ot)
596 {
597         /* identifiers */
598         ot->name= "History Cycle";
599         ot->description= "Cycle through history";
600         ot->idname= "CONSOLE_OT_history_cycle";
601         
602         /* api callbacks */
603         ot->exec= history_cycle_exec;
604         ot->poll= console_edit_poll;
605         
606         /* properties */
607         RNA_def_boolean(ot->srna, "reverse", 0, "Reverse", "reverse cycle history");
608 }
609
610
611 /* the python exec operator uses this */
612 static int history_append_exec(bContext *C, wmOperator *op)
613 {
614         SpaceConsole *sc= CTX_wm_space_console(C);
615         ConsoleLine *ci= console_history_verify(C);
616         char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0); /* own this text in the new line, dont free */
617         int cursor= RNA_int_get(op->ptr, "current_character");
618         short rem_dupes= RNA_boolean_get(op->ptr, "remove_duplicates");
619         int prev_len= ci->len;
620
621         if(rem_dupes) {
622                 SpaceConsole *sc= CTX_wm_space_console(C);
623                 ConsoleLine *cl;
624
625                 while((cl= console_history_find(sc, ci->line, ci)))
626                         console_history_free(sc, cl);
627
628                 if(strcmp(str, ci->line)==0) {
629                         MEM_freeN(str);
630                         return OPERATOR_FINISHED;
631                 }
632         }
633
634         ci= console_history_add_str(sc, str, 1); /* own the string */
635         console_select_offset(sc, ci->len - prev_len);
636         console_line_cursor_set(ci, cursor);
637         
638         ED_area_tag_redraw(CTX_wm_area(C));
639         
640         return OPERATOR_FINISHED;
641 }
642
643 void CONSOLE_OT_history_append(wmOperatorType *ot)
644 {
645         /* identifiers */
646         ot->name= "History Append";
647         ot->description= "Append history at cursor position";
648         ot->idname= "CONSOLE_OT_history_append";
649         
650         /* api callbacks */
651         ot->exec= history_append_exec;
652         ot->poll= console_edit_poll;
653         
654         /* properties */
655         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");      
656         RNA_def_int(ot->srna, "current_character", 0, 0, INT_MAX, "Cursor", "The index of the cursor.", 0, 10000);
657         RNA_def_boolean(ot->srna, "remove_duplicates", 0, "Remove Duplicates", "Remove duplicate items in the history");
658 }
659
660
661 /* the python exec operator uses this */
662 static int scrollback_append_exec(bContext *C, wmOperator *op)
663 {
664         SpaceConsole *sc= CTX_wm_space_console(C);
665         ConsoleLine *ci= console_history_verify(C);
666         
667         char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0); /* own this text in the new line, dont free */
668         int type= RNA_enum_get(op->ptr, "type");
669         
670         ci= console_scrollback_add_str(sc, str, 1); /* own the string */
671         ci->type= type;
672         
673         console_scrollback_limit(sc);
674         
675         ED_area_tag_redraw(CTX_wm_area(C));
676         
677         return OPERATOR_FINISHED;
678 }
679
680 void CONSOLE_OT_scrollback_append(wmOperatorType *ot)
681 {
682         /* defined in DNA_space_types.h */
683         static EnumPropertyItem console_line_type_items[] = {
684                 {CONSOLE_LINE_OUTPUT,   "OUTPUT", 0, "Output", ""},
685                 {CONSOLE_LINE_INPUT,    "INPUT", 0, "Input", ""},
686                 {CONSOLE_LINE_INFO,             "INFO", 0, "Information", ""},
687                 {CONSOLE_LINE_ERROR,    "ERROR", 0, "Error", ""},
688                 {0, NULL, 0, NULL, NULL}};
689
690         /* identifiers */
691         ot->name= "Scrollback Append";
692         ot->description= "Append scrollback text by type";
693         ot->idname= "CONSOLE_OT_scrollback_append";
694         
695         /* api callbacks */
696         ot->exec= scrollback_append_exec;
697         ot->poll= console_edit_poll;
698         
699         /* properties */
700         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");      
701         RNA_def_enum(ot->srna, "type", console_line_type_items, CONSOLE_LINE_OUTPUT, "Type", "Console output type.");
702 }
703
704
705 static int copy_exec(bContext *C, wmOperator *UNUSED(op))
706 {
707         SpaceConsole *sc= CTX_wm_space_console(C);
708         int buf_len;
709
710         DynStr *buf_dyn= BLI_dynstr_new();
711         char *buf_str;
712         
713         ConsoleLine *cl;
714         int sel[2];
715         int offset= 0;
716
717         ConsoleLine cl_dummy= {0};
718
719 #if 0
720         /* copy whole file */
721         for(cl= sc->scrollback.first; cl; cl= cl->next) {
722                 BLI_dynstr_append(buf_dyn, cl->line);
723                 BLI_dynstr_append(buf_dyn, "\n");
724         }
725 #endif
726
727         if(sc->sel_start == sc->sel_end)
728                 return OPERATOR_CANCELLED;
729
730         console_scrollback_prompt_begin(sc, &cl_dummy);
731
732         for(cl= sc->scrollback.first; cl; cl= cl->next) {
733                 offset += cl->len + 1;
734         }
735
736         if(offset==0) {
737                 console_scrollback_prompt_end(sc, &cl_dummy);
738                 return OPERATOR_CANCELLED;
739         }
740
741         offset -= 1;
742         sel[0]= offset - sc->sel_end;
743         sel[1]= offset - sc->sel_start;
744
745         for(cl= sc->scrollback.first; cl; cl= cl->next) {
746                 if(sel[0] <= cl->len && sel[1] >= 0) {
747                         int sta= MAX2(sel[0], 0);
748                         int end= MIN2(sel[1], cl->len);
749
750                         if(BLI_dynstr_get_len(buf_dyn))
751                                 BLI_dynstr_append(buf_dyn, "\n");
752
753                         BLI_dynstr_nappend(buf_dyn, cl->line + sta, end - sta);
754                 }
755
756                 sel[0] -= cl->len + 1;
757                 sel[1] -= cl->len + 1;
758         }
759
760         buf_str= BLI_dynstr_get_cstring(buf_dyn);
761         buf_len= BLI_dynstr_get_len(buf_dyn);
762         BLI_dynstr_free(buf_dyn);
763         WM_clipboard_text_set(buf_str, 0);
764
765         MEM_freeN(buf_str);
766
767         console_scrollback_prompt_end(sc, &cl_dummy);
768
769         return OPERATOR_FINISHED;
770 }
771
772 void CONSOLE_OT_copy(wmOperatorType *ot)
773 {
774         /* identifiers */
775         ot->name= "Copy to Clipboard";
776         ot->description= "Copy selected text to clipboard";
777         ot->idname= "CONSOLE_OT_copy";
778
779         /* api callbacks */
780         ot->poll= console_edit_poll;
781         ot->exec= copy_exec;
782
783         /* properties */
784 }
785
786 static int paste_exec(bContext *C, wmOperator *UNUSED(op))
787 {
788         SpaceConsole *sc= CTX_wm_space_console(C);
789         ConsoleLine *ci= console_history_verify(C);
790
791         char *buf_str= WM_clipboard_text_get(0);
792         char *buf_step, *buf_next;
793
794         if(buf_str==NULL)
795                 return OPERATOR_CANCELLED;
796
797         buf_next= buf_str;
798         buf_step= buf_str;
799
800         while((buf_next=buf_step) && buf_next[0] != '\0') {
801                 buf_step= strchr(buf_next, '\n');
802                 if(buf_step) {
803                         *buf_step= '\0';
804                         buf_step++;
805                 }
806
807                 if(buf_next != buf_str) {
808                         WM_operator_name_call(C, "CONSOLE_OT_execute", WM_OP_EXEC_DEFAULT, NULL);
809                         ci= console_history_verify(C);
810                 }
811
812                 console_select_offset(sc, console_line_insert(ci, buf_next));
813         }
814
815         MEM_freeN(buf_str);
816
817         ED_area_tag_redraw(CTX_wm_area(C));
818
819         return OPERATOR_FINISHED;
820 }
821
822 void CONSOLE_OT_paste(wmOperatorType *ot)
823 {
824         /* identifiers */
825         ot->name= "Paste from Clipboard";
826         ot->description= "Paste text from clipboard";
827         ot->idname= "CONSOLE_OT_paste";
828
829         /* api callbacks */
830         ot->poll= console_edit_poll;
831         ot->exec= paste_exec;
832
833         /* properties */
834 }
835
836 typedef struct SetConsoleCursor {
837         int sel_old[2];
838         int sel_init;
839 } SetConsoleCursor;
840
841 // TODO, cursor placement without selection
842 static void set_cursor_to_pos(SpaceConsole *sc, ARegion *ar, SetConsoleCursor *scu, int mval[2], int UNUSED(sel))
843 {
844         int pos;
845         pos= console_char_pick(sc, ar, NULL, mval);
846
847         if(scu->sel_init == INT_MAX) {
848                 scu->sel_init= pos;
849                 sc->sel_start = sc->sel_end = pos;
850                 return;
851         }
852
853         if (pos < scu->sel_init) {
854                 sc->sel_start = pos;
855                 sc->sel_end = scu->sel_init;
856         }
857         else if (pos > sc->sel_start) {
858                 sc->sel_start = scu->sel_init;
859                 sc->sel_end = pos;
860         }
861         else {
862                 sc->sel_start = sc->sel_end = pos;
863         }
864 }
865
866 static void console_modal_select_apply(bContext *C, wmOperator *op, wmEvent *event)
867 {
868         SpaceConsole *sc= CTX_wm_space_console(C);
869         ARegion *ar= CTX_wm_region(C);
870         SetConsoleCursor *scu= op->customdata;
871         int mval[2];
872
873         mval[0]= event->mval[0];
874         mval[1]= event->mval[1];
875
876         set_cursor_to_pos(sc, ar, scu, mval, TRUE);
877         ED_area_tag_redraw(CTX_wm_area(C));
878 }
879
880 static void set_cursor_exit(bContext *UNUSED(C), wmOperator *op)
881 {
882 //      SpaceConsole *sc= CTX_wm_space_console(C);
883         SetConsoleCursor *scu= op->customdata;
884
885         /*
886         if(txt_has_sel(text)) {
887                 buffer = txt_sel_to_buf(text);
888                 WM_clipboard_text_set(buffer, 1);
889                 MEM_freeN(buffer);
890         }*/
891
892         MEM_freeN(scu);
893 }
894
895 static int console_modal_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
896 {
897         SpaceConsole *sc= CTX_wm_space_console(C);
898 //      ARegion *ar= CTX_wm_region(C);
899         SetConsoleCursor *scu;
900
901         op->customdata= MEM_callocN(sizeof(SetConsoleCursor), "SetConsoleCursor");
902         scu= op->customdata;
903
904         scu->sel_old[0]= sc->sel_start;
905         scu->sel_old[1]= sc->sel_end;
906
907         scu->sel_init = INT_MAX;
908
909         WM_event_add_modal_handler(C, op);
910
911         console_modal_select_apply(C, op, event);
912
913         return OPERATOR_RUNNING_MODAL;
914 }
915
916 static int console_modal_select(bContext *C, wmOperator *op, wmEvent *event)
917 {
918         switch(event->type) {
919                 case LEFTMOUSE:
920                 case MIDDLEMOUSE:
921                 case RIGHTMOUSE:
922                         set_cursor_exit(C, op);
923                         return OPERATOR_FINISHED;
924                 case MOUSEMOVE:
925                         console_modal_select_apply(C, op, event);
926                         break;
927         }
928
929         return OPERATOR_RUNNING_MODAL;
930 }
931
932 static int console_modal_select_cancel(bContext *C, wmOperator *op)
933 {
934         set_cursor_exit(C, op);
935         return OPERATOR_FINISHED;
936 }
937
938 void CONSOLE_OT_select_set(wmOperatorType *ot)
939 {
940         /* identifiers */
941         ot->name= "Set Selection";
942         ot->idname= "CONSOLE_OT_select_set";
943         ot->description= "Set the console selection";
944
945         /* api callbacks */
946         ot->invoke= console_modal_select_invoke;
947         ot->modal= console_modal_select;
948         ot->cancel= console_modal_select_cancel;
949         ot->poll= console_edit_poll;
950 }