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