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