4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
38 #include "MEM_guardedalloc.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_arithb.h"
45 #include "DNA_text_types.h"
46 #include "DNA_space_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_userdef_types.h"
50 #include "BKE_utildefines.h"
52 #include "BKE_global.h"
55 #include "BKE_suggestions.h"
58 #include "BIF_glutil.h"
59 #include "BIF_keyval.h"
60 #include "BIF_interface.h"
61 #include "BIF_drawtext.h"
62 #include "BIF_editfont.h"
63 #include "BIF_spacetypes.h"
64 #include "BIF_usiblender.h"
65 #include "BIF_screen.h"
66 #include "BIF_toolbox.h"
67 #include "BIF_space.h"
68 #include "BIF_mywindow.h"
69 #include "BIF_resources.h"
70 #include "BIF_mainqueue.h"
72 #include "BSE_filesel.h"
74 #include "BPY_extern.h"
75 #include "BPY_menus.h"
81 #include <ctype.h> /* ispunct */
84 /***********************/ /*
88 All word-wrap functions follow the algorithm below to maintain consistency.
89 line The line to wrap (tabs converted to spaces)
90 view_width The maximum number of characters displayable in the region
91 This equals region_width/font_width for the region
92 wrap_chars Characters that allow wrapping. This equals [' ', '\t', '-']
94 def wrap(line, view_width, wrap_chars):
99 if pos-draw_start >= view_width:
100 print line[draw_start:draw_end]
101 draw_start = draw_end
102 draw_end += view_width
103 elif c in wrap_chars:
106 print line[draw_start:]
108 */ /***********************/
112 #define SUGG_LIST_SIZE 7
113 #define SUGG_LIST_WIDTH 20
115 #define DOC_HEIGHT 10
117 #define TOOL_SUGG_LIST 0x01
118 #define TOOL_DOCUMENT 0x02
120 #define TMARK_GRP_CUSTOM 0x00010000 /* Lower 2 bytes used for Python groups */
121 #define TMARK_GRP_FINDALL 0x00020000
123 /* forward declarations */
125 void drawtextspace(ScrArea *sa, void *spacedata);
126 void winqreadtextspace(struct ScrArea *sa, void *spacedata, struct BWinEvent *evt);
127 void redraw_alltext(void);
129 static void txt_copy_selectbuffer(Text *text);
130 static void draw_brackets(SpaceText *st);
131 static void get_selection_buffer(Text *text);
132 static int check_bracket(char ch);
133 static int check_delim(char ch);
134 static int check_digit(char ch);
135 static int check_identifier(char ch);
136 static int check_whitespace(char ch);
138 static int get_wrap_width(SpaceText *st);
139 static void get_suggest_prefix(Text *text, int offset);
140 static void confirm_suggestion(Text *text, int skipleft);
142 #define TXT_MAXFINDSTR 255
143 static int g_find_flags= TXT_FIND_WRAP;
144 static char *g_find_str= NULL;
145 static char *g_replace_str= NULL;
147 static int doc_scroll= 0;
148 static int jump_to= 0;
149 static double last_jump= 0;
151 static BMF_Font *spacetext_get_font(SpaceText *st)
153 static BMF_Font *scr12= NULL;
154 static BMF_Font *scr15= NULL;
156 switch (st->font_id) {
160 scr12= BMF_GetFont(BMF_kScreen12);
164 scr15= BMF_GetFont(BMF_kScreen15);
169 static int spacetext_get_fontwidth(SpaceText *st)
171 return BMF_GetCharacterWidth(spacetext_get_font(st), ' ');
174 static char *temp_char_buf= NULL;
175 static int *temp_char_accum= NULL;
176 static int temp_char_len= 0;
177 static int temp_char_pos= 0;
179 static void temp_char_write(char c, int accum)
181 if (temp_char_len==0 || temp_char_pos>=temp_char_len) {
182 char *nbuf; int *naccum;
183 int olen= temp_char_len;
185 if (olen) temp_char_len*= 2;
186 else temp_char_len= 256;
188 nbuf= MEM_mallocN(sizeof(*temp_char_buf)*temp_char_len, "temp_char_buf");
189 naccum= MEM_mallocN(sizeof(*temp_char_accum)*temp_char_len, "temp_char_accum");
192 memcpy(nbuf, temp_char_buf, olen);
193 memcpy(naccum, temp_char_accum, olen);
195 MEM_freeN(temp_char_buf);
196 MEM_freeN(temp_char_accum);
200 temp_char_accum= naccum;
203 temp_char_buf[temp_char_pos]= c;
204 temp_char_accum[temp_char_pos]= accum;
206 if (c==0) temp_char_pos= 0;
207 else temp_char_pos++;
210 void free_txt_data(void)
212 txt_free_cut_buffer();
214 if (g_find_str) MEM_freeN(g_find_str);
215 if (g_replace_str) MEM_freeN(g_replace_str);
216 if (temp_char_buf) MEM_freeN(temp_char_buf);
217 if (temp_char_accum) MEM_freeN(temp_char_accum);
220 static int render_string (SpaceText *st, char *in)
226 if (temp_char_pos && *(in-1)=='\t') i= st->tabnumber;
227 else if (st->tabnumber > 0) i= st->tabnumber - (temp_char_pos%st->tabnumber);
228 while(i--) temp_char_write(' ', r);
229 } else temp_char_write(*in, r);
235 temp_char_write(0, 0);
240 /* Checks the specified source string for a Python built-in function name. This
241 name must start at the beginning of the source string and must be followed by
242 a non-identifier (see check_identifier(char)) or null character.
244 If a built-in function is found, the length of the matching name is returned.
245 Otherwise, -1 is returned.
247 static int find_builtinfunc(char *string)
250 char builtinfuncs[][11] = {"and", "as", "assert", "break", "class", "continue", "def",
251 "del", "elif", "else", "except", "exec", "finally",
252 "for", "from", "global", "if", "import", "in",
253 "is", "lambda", "not", "or", "pass", "print",
254 "raise", "return", "try", "while", "yield"};
255 for (a=0; a<30; a++) {
258 /* If we hit the end of a keyword... (eg. "def") */
259 if (builtinfuncs[a][i]=='\0') {
260 /* If we still have identifier chars in the source (eg. "definate") */
261 if (check_identifier(string[i]))
262 i = -1; /* No match */
263 break; /* Next keyword if no match, otherwise we're done */
265 /* If chars mismatch, move on to next keyword */
266 } else if (string[i]!=builtinfuncs[a][i]) {
268 break; /* Break inner loop, start next keyword */
272 if (i>0) break; /* If we have a match, we're done */
277 /* Checks the specified source string for a Python special name. This name must
278 start at the beginning of the source string and must be followed by a non-
279 identifier (see check_identifier(char)) or null character.
281 If a special name is found, the length of the matching name is returned.
282 Otherwise, -1 is returned.
284 static int find_specialvar(char *string)
287 /* Check for "def" */
288 if (string[0]=='d' && string[1]=='e' && string[2]=='f')
290 /* Check for "class" */
291 else if (string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
293 /* If next source char is an identifier (eg. 'i' in "definate") no match */
294 if (i==0 || check_identifier(string[i]))
299 /* Ensures the format string for the given line is long enough, reallocating
300 as needed. Allocation is done here, alone, to ensure consitency.
302 static int check_format_len(TextLine *line, unsigned int len)
305 if (strlen(line->format) < len) {
306 MEM_freeN(line->format);
307 line->format = MEM_mallocN(len+2, "SyntaxFormat");
308 if (!line->format) return 0;
311 line->format = MEM_mallocN(len+2, "SyntaxFormat");
312 if (!line->format) return 0;
317 /* Formats the specified line. If do_next is set, the process will move on to
318 the succeeding line if it is affected (eg. multiline strings). Format strings
319 may contain any of the following characters:
322 '!' Punctuation and other symbols
325 'v' Special variables (class, def)
326 'b' Built-in names (print, for, etc.)
327 'q' Other text (identifiers, etc.)
328 It is terminated with a null-terminator '\0' followed by a continuation
329 flag indicating whether the line is part of a multi-line string.
331 void txt_format_line(SpaceText *st, TextLine *line, int do_next)
333 char *str, *fmt, orig, cont, find, prev = ' ';
336 /* Get continuation from previous line */
337 if (line->prev && line->prev->format != NULL) {
338 fmt= line->prev->format;
339 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
342 /* Get original continuation from this line */
343 if (line->format != NULL) {
345 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
348 render_string(st, line->line);
351 if (!check_format_len(line, len)) return;
355 /* Handle escape sequences by skipping both \ and next char */
357 *fmt = prev; fmt++; str++;
358 if (*str == '\0') break;
359 *fmt = prev; fmt++; str++;
362 /* Handle continuations */
364 /* Triple strings ("""...""" or '''...''') */
365 if (cont & TXT_TRISTR) {
366 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
367 if (*str==find && *(str+1)==find && *(str+2)==find) {
368 *fmt = 'l'; fmt++; str++;
369 *fmt = 'l'; fmt++; str++;
372 /* Handle other strings */
374 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
375 if (*str == find) cont = 0;
379 /* Not in a string... */
381 /* Deal with comments first */
382 if (prev == '#' || *str == '#')
385 else if (*str == '"' || *str == '\'') {
387 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
388 if (*(str+1) == find && *(str+2) == find) {
389 *fmt = 'l'; fmt++; str++;
390 *fmt = 'l'; fmt++; str++;
395 /* Whitespace (all ws. has been converted to spaces) */
396 else if (*str == ' ')
398 /* Numbers (digits not part of an identifier and periods followed by digits) */
399 else if ((prev != 'q' && check_digit(*str)) || (*str == '.' && check_digit(*(str+1))))
402 else if (check_delim(*str))
404 /* Identifiers and other text (no previous ws. or delims. so text continues) */
405 else if (prev == 'q')
407 /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
409 /* Special vars(v) or built-in keywords(b) */
410 if ((i=find_specialvar(str)) != -1)
412 else if ((i=find_builtinfunc(str)) != -1)
416 *fmt = prev; fmt++; str++;
429 /* Terminate and add continuation char */
434 //print_format(st, line);
436 /* If continuation has changed and we're allowed, process the next line */
437 if (cont!=orig && do_next && line->next) {
438 txt_format_line(st, line->next, do_next);
442 /* Formats every line of the current text */
443 void txt_format_text(SpaceText *st)
447 if (!st->text) return;
449 for (linep=st->text->lines.first; linep; linep=linep->next)
450 txt_format_line(st, linep, 0);
453 /* Sets the current drawing color based on the format character specified */
454 static void format_draw_color(char formatchar)
456 switch (formatchar) {
457 case '_': /* Whitespace */
459 case '!': /* Symbols */
460 BIF_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
462 case '#': /* Comments */
463 BIF_ThemeColor(TH_SYNTAX_C);
465 case 'n': /* Numerals */
466 BIF_ThemeColor(TH_SYNTAX_N);
468 case 'l': /* Strings */
469 BIF_ThemeColor(TH_SYNTAX_L);
471 case 'v': /* Specials: class, def */
472 BIF_ThemeColor(TH_SYNTAX_V);
474 case 'b': /* Keywords: for, print, etc. */
475 BIF_ThemeColor(TH_SYNTAX_B);
477 case 'q': /* Other text (identifiers) */
479 BIF_ThemeColor(TH_TEXT);
484 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format)
486 int basex, i, a, len, start, end, max, lines;
488 len= render_string(st, str);
490 max= w/spacetext_get_fontwidth(st);
497 for (i=0; i<len; i++) {
498 if (i-start >= max) {
499 /* Draw the visible portion of text on the overshot line */
500 for (a=start; a<end; a++) {
501 if (st->showsyntax && format) format_draw_color(format[a]);
503 BMF_DrawCharacter(spacetext_get_font(st), str[a]);
504 x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
511 } else if (str[i]==' ' || str[i]=='-') {
515 /* Draw the remaining text */
516 for (a=start; a<len; a++) {
517 if (st->showsyntax && format) format_draw_color(format[a]);
519 BMF_DrawCharacter(spacetext_get_font(st), str[a]);
520 x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
525 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
531 w= render_string(st, str);
532 if(w<cshift ) return 0; /* String is shorter than shift */
534 in= temp_char_buf+cshift;
535 acc= temp_char_accum+cshift;
539 if(st->showsyntax && format) {
541 format = format+cshift;
545 for(a = 0; a < amount; a++) {
546 format_draw_color(format[a]);
548 BMF_DrawCharacter(spacetext_get_font(st), in[a]);
549 x = x+BMF_GetCharacterWidth(spacetext_get_font(st), in[a]);
553 BMF_DrawString(spacetext_get_font(st), in);
556 while (w-- && *acc++ < maxwidth) {
557 r+= spacetext_get_fontwidth(st);
561 if (cshift && r==0) return 0;
562 else if (st->showlinenrs)
563 return r+TXT_OFFSET+TEXTXLOC;
568 static void set_cursor_to_pos (SpaceText *st, int x, int y, int sel)
577 if(sel) { linep= &text->sell; charp= &text->selc; }
578 else { linep= &text->curl; charp= &text->curc; }
580 y= (curarea->winy - y)/st->lheight;
583 x-= TXT_OFFSET+TEXTXLOC;
588 x = (x/spacetext_get_fontwidth(st)) + st->left;
591 int i, j, endj, curs, max, chop, start, end, chars, loop;
594 /* Point to first visible line */
595 *linep= text->lines.first;
596 for (i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
598 max= get_wrap_width(st);
601 while (loop && *linep) {
608 for (i=0, j=0; loop; j++) {
610 /* Mimic replacement of tabs */
611 ch= (*linep)->line[j];
613 chars= st->tabnumber-i%st->tabnumber;
619 /* Gone too far, go back to last wrap point */
624 /* Exactly at the cursor, done */
625 } else if (y==0 && i-start==x) {
629 /* Prepare curs for next wrap */
630 } else if (i-end==x) {
639 if (y==0 && i-start>=x) {
644 } else if (ch==' ' || ch=='-' || ch=='\0') {
645 if (y==0 && i-start>=x) {
658 if (!loop || y<0) break;
660 if (!(*linep)->next) {
661 *charp= (*linep)->len;
665 /* On correct line but didn't meet cursor, must be at end */
667 *charp= (*linep)->len;
670 *linep= (*linep)->next;
675 y-= txt_get_span(text->lines.first, *linep) - st->top;
678 while (y-- != 0) if((*linep)->next) *linep= (*linep)->next;
680 while (y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
684 w= render_string(st, (*linep)->line);
685 if(x<w) *charp= temp_char_accum[x];
686 else *charp= (*linep)->len;
688 if(!sel) txt_pop_sel(text);
691 static int get_wrap_width(SpaceText *st)
695 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
696 max= (curarea->winx-x)/spacetext_get_fontwidth(st);
697 return max>8 ? max : 8;
700 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
701 static void wrap_offset(SpaceText *st, TextLine *linein, int cursin, int *offl, int *offc)
705 int i, j, start, end, chars, max, chop;
710 if (!st->text) return;
711 if (!st->wordwrap) return;
715 /* Move pointer to first visible line (top) */
716 linep= text->lines.first;
718 while (i>0 && linep) {
719 if (linep == linein) return; /* Line before top */
724 max= get_wrap_width(st);
732 for (i=0, j=0; linep->line[j]!='\0'; j++) {
734 /* Mimic replacement of tabs */
737 chars= st->tabnumber-i%st->tabnumber;
738 if (linep==linein && i<cursin) cursin += chars-1;
745 if (chop && linep==linein && i >= cursin)
752 } else if (ch==' ' || ch=='-') {
755 if (linep==linein && i >= cursin)
761 if (linep==linein) break;
766 static int get_char_pos(SpaceText *st, char *line, int cur)
770 for (i=0; i<cur && line[i]; i++) {
772 a += st->tabnumber-a%st->tabnumber;
779 static void draw_markers(SpaceText *st)
781 Text *text= st->text;
782 TextMarker *marker, *next;
783 TextLine *top, *bottom, *line;
784 int offl, offc, i, cy, x1, x2, y1, y2, x, y;
786 for (i=st->top, top= text->lines.first; top->next && i>0; i--) top= top->next;
787 for (i=st->viewlines-1, bottom=top; bottom->next && i>0; i--) bottom= bottom->next;
789 for (marker= text->markers.first; marker; marker= next) {
791 for (cy= 0, line= top; line; cy++, line= line->next) {
792 if (cy+st->top==marker->lineno) {
793 /* Remove broken markers */
794 if (marker->end>line->len || marker->start>marker->end) {
795 BLI_freelinkN(&text->markers, marker);
799 wrap_offset(st, line, marker->start, &offl, &offc);
800 x1= get_char_pos(st, line->line, marker->start) - st->left + offc;
802 wrap_offset(st, line, marker->end, &offl, &offc);
803 x2= get_char_pos(st, line->line, marker->end) - st->left + offc;
806 glColor3ub(marker->color[0], marker->color[1], marker->color[2]);
807 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
812 glBegin(GL_LINE_LOOP);
813 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
814 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
815 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
816 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
820 glBegin(GL_LINE_STRIP);
821 glVertex2i(curarea->winx, y);
822 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
823 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
824 glVertex2i(curarea->winx, y-st->lheight);
827 for (i=y1+1; i<y2; i++) {
830 glVertex2i(curarea->winx, y);
831 glVertex2i(x, y-st->lheight);
832 glVertex2i(curarea->winx, y-st->lheight);
836 glBegin(GL_LINE_STRIP);
838 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
839 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
840 glVertex2i(x, y-st->lheight);
846 if (line==bottom) break;
851 static void draw_cursor(SpaceText *st)
853 Text *text= st->text;
854 int vcurl, vcurc, vsell, vselc, hidden=0;
855 int offl, offc, x, y, w, i;
857 /* Draw the selection */
858 if (text->curl!=text->sell || text->curc!=text->selc) {
860 /* Convert all to view space character coordinates */
861 wrap_offset(st, text->curl, text->curc, &offl, &offc);
862 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
863 vcurc = get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
864 wrap_offset(st, text->sell, text->selc, &offl, &offc);
865 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
866 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
868 if (vcurc<0) vcurc=0;
869 if (vselc<0) vselc=0, hidden=1;
871 BIF_ThemeColor(TH_SHADE2);
872 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
876 y -= vcurl*st->lheight;
878 glRecti(x+vcurc*spacetext_get_fontwidth(st)-1, y, x+vselc*spacetext_get_fontwidth(st), y-st->lheight);
880 glRecti(x+vselc*spacetext_get_fontwidth(st)-1, y, x+vcurc*spacetext_get_fontwidth(st), y-st->lheight);
882 int froml, fromc, tol, toc;
884 froml= vcurl; tol= vsell;
885 fromc= vcurc; toc= vselc;
887 froml= vsell; tol= vcurl;
888 fromc= vselc; toc= vcurc;
890 y -= froml*st->lheight;
891 glRecti(x+fromc*spacetext_get_fontwidth(st)-1, y, curarea->winx, y-st->lheight); y-=st->lheight;
892 for (i=froml+1; i<tol; i++)
893 glRecti(x-4, y, curarea->winx, y-st->lheight), y-=st->lheight;
894 glRecti(x-4, y, x+toc*spacetext_get_fontwidth(st), y-st->lheight); y-=st->lheight;
897 wrap_offset(st, text->sell, text->selc, &offl, &offc);
898 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
899 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
900 if (vselc<0) vselc=0, hidden=1;
904 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
905 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
906 x += vselc*spacetext_get_fontwidth(st);
907 y= curarea->winy-2 - vsell*st->lheight;
910 char ch= text->sell->line[text->selc];
912 w= BMF_GetCharacterWidth(spacetext_get_font(st), ch);
913 BIF_ThemeColor(TH_HILITE);
914 glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
916 BIF_ThemeColor(TH_HILITE);
917 glRecti(x-1, y, x+1, y-st->lheight);
922 static void calc_text_rcts(SpaceText *st)
924 int lhlstart, lhlend, ltexth;
925 short barheight, barstart, hlstart, hlend, blank_lines;
926 short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
929 pix_bottom_margin = 4;
930 pix_available = curarea->winy - pix_top_margin - pix_bottom_margin;
931 ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
932 blank_lines = st->viewlines / 2;
934 /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
935 if (ltexth + blank_lines < st->top + st->viewlines) {
936 blank_lines = st->top + st->viewlines - ltexth;
939 ltexth += blank_lines;
941 barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
943 if (barheight < 20) {
944 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */
947 barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
950 st->txtbar.xmax = 17;
951 st->txtbar.ymax = curarea->winy - pix_top_margin - barstart;
952 st->txtbar.ymin = st->txtbar.ymax - barheight;
954 CLAMP(st->txtbar.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
955 CLAMP(st->txtbar.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
957 st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
958 if (st->pix_per_line<.1) st->pix_per_line=.1f;
960 lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl),
961 txt_get_span(st->text->lines.first, st->text->sell));
962 lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl),
963 txt_get_span(st->text->lines.first, st->text->sell));
966 hlstart = (lhlstart * pix_available)/ltexth;
967 hlend = (lhlend * pix_available)/ltexth;
969 /* the scrollbar is non-linear sized */
970 if (pix_bardiff > 0) {
971 /* the start of the highlight is in the current viewport */
972 if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) {
973 /* speed the progresion of the start of the highlight through the scrollbar */
974 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);
976 else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
977 /* push hl start down */
978 hlstart = barstart + barheight;
980 else if (lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
985 if (hlend <= hlstart) {
989 /* the end of the highlight is in the current viewport */
990 if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) {
991 /* speed the progresion of the end of the highlight through the scrollbar */
992 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);
994 else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
998 else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
1000 hlend = barstart + barheight;
1003 if (hlend <= hlstart) {
1004 hlstart = hlend - 2;
1013 if (hlend - hlstart < 2) {
1014 hlend = hlstart + 2;
1017 st->txtscroll.xmin= 5;
1018 st->txtscroll.xmax= 17;
1019 st->txtscroll.ymax= curarea->winy - pix_top_margin - hlstart;
1020 st->txtscroll.ymin= curarea->winy - pix_top_margin - hlend;
1022 CLAMP(st->txtscroll.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
1023 CLAMP(st->txtscroll.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
1026 static void draw_textscroll(SpaceText *st)
1028 if (!st->text) return;
1032 BIF_ThemeColorShade(TH_SHADE1, -20);
1033 glRecti(2, 2, 20, curarea->winy-6);
1034 uiEmboss(2, 2, 20, curarea->winy-6, 1);
1036 BIF_ThemeColor(TH_SHADE1);
1037 glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);
1039 BIF_ThemeColor(TH_SHADE2);
1040 glRecti(st->txtscroll.xmin, st->txtscroll.ymin, st->txtscroll.xmax, st->txtscroll.ymax);
1042 uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
1045 /* Moves the view vertically by the specified number of lines */
1046 static void screen_skip(SpaceText *st, int lines)
1051 if (st->spacetype != SPACE_TEXT) return;
1052 if (!st->text) return;
1056 last= txt_get_span(st->text->lines.first, st->text->lines.last);
1057 last= last - (st->viewlines/2);
1059 if (st->top>last) st->top= last;
1060 if (st->top<0) st->top= 0;
1063 /* Moves the cursor vertically by the specified number of lines.
1064 If the destination line is shorter than the current cursor position, the
1065 cursor will be positioned at the end of this line.
1067 This is to replace screen_skip for PageUp/Down operations.
1069 static void cursor_skip(SpaceText *st, int lines, int sel)
1073 int oldl, oldc, *charp;
1076 if (st->spacetype != SPACE_TEXT) return;
1077 if (!st->text) return;
1081 if (sel) linep= &text->sell, charp= &text->selc;
1082 else linep= &text->curl, charp= &text->curc;
1083 oldl= txt_get_span(text->lines.first, *linep);
1086 while (lines>0 && (*linep)->next) {
1087 *linep= (*linep)->next;
1090 while (lines<0 && (*linep)->prev) {
1091 *linep= (*linep)->prev;
1095 if (*charp > (*linep)->len) *charp= (*linep)->len;
1097 if (!sel) txt_pop_sel(st->text);
1098 txt_undo_add_toop(st->text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
1101 /* Handles text scrolling via grabbing the view (MMB, mode 1) or with the
1104 static void do_textscroll(SpaceText *st, int mode)
1106 short delta[2]= {0, 0};
1107 short mval[2], hold[2], old[2];
1109 if (!st->text) return;
1113 st->flags|= ST_SCROLL_SELECT;
1115 scrarea_do_windraw(curarea);
1116 screen_swapbuffers();
1118 getmouseco_areawin(mval);
1119 old[0]= hold[0]= mval[0];
1120 old[1]= hold[1]= mval[1];
1122 while(get_mbut()&(L_MOUSE|M_MOUSE)) {
1123 getmouseco_areawin(mval);
1125 if(old[0]!=mval[0] || old[1]!=mval[1]) {
1127 delta[0]= (hold[0]-mval[0])/spacetext_get_fontwidth(st);
1128 delta[1]= (mval[1]-hold[1])/st->lheight;
1130 else delta[1]= (hold[1]-mval[1])*st->pix_per_line;
1132 if (delta[0] || delta[1]) {
1133 screen_skip(st, delta[1]);
1137 st->left+= delta[0];
1138 if (st->left<0) st->left= 0;
1140 scrarea_do_windraw(curarea);
1141 screen_swapbuffers();
1149 BIF_wait_for_statechange();
1152 st->flags^= ST_SCROLL_SELECT;
1154 scrarea_do_windraw(curarea);
1155 screen_swapbuffers();
1158 static void do_selection(SpaceText *st, int selecting)
1163 short mval[2], old[2];
1165 getmouseco_areawin(mval);
1170 int curl= txt_get_span(st->text->lines.first, st->text->curl);
1171 int curc= st->text->curc;
1174 set_cursor_to_pos(st, mval[0], mval[1], 0);
1176 linep2= txt_get_span(st->text->lines.first, st->text->curl);
1177 charp2= st->text->selc;
1179 if (curl!=linep2 || curc!=charp2)
1180 txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
1183 sell= txt_get_span(st->text->lines.first, st->text->sell);
1184 selc= st->text->selc;
1186 while(get_mbut()&L_MOUSE) {
1187 getmouseco_areawin(mval);
1189 if (mval[1]<0 || mval[1]>curarea->winy) {
1190 int d= (old[1]-mval[1])*st->pix_per_line;
1191 if (d) screen_skip(st, d);
1193 set_cursor_to_pos(st, mval[0], mval[1]<0?0:curarea->winy, 1);
1195 scrarea_do_windraw(curarea);
1196 screen_swapbuffers();
1198 else if (!st->wordwrap && (mval[0]<0 || mval[0]>curarea->winx)) {
1199 if (mval[0]>curarea->winx) st->left++;
1200 else if (mval[0]<0 && st->left>0) st->left--;
1202 set_cursor_to_pos(st, mval[0], mval[1], 1);
1204 scrarea_do_windraw(curarea);
1205 screen_swapbuffers();
1209 else if (first || old[0]!=mval[0] || old[1]!=mval[1]) {
1210 set_cursor_to_pos(st, mval[0], mval[1], 1);
1212 scrarea_do_windraw(curarea);
1213 screen_swapbuffers();
1220 BIF_wait_for_statechange();
1224 linep2= txt_get_span(st->text->lines.first, st->text->sell);
1225 charp2= st->text->selc;
1227 if (sell!=linep2 || selc!=charp2)
1228 txt_undo_add_toop(st->text, UNDO_STO, sell, selc, linep2, charp2);
1233 static int do_suggest_select(SpaceText *st)
1235 SuggItem *item, *first, *last, *sel;
1237 int l, x, y, w, h, i;
1241 if (!st || !st->text) return 0;
1242 if (!texttool_text_is_active(st->text)) return 0;
1244 first = texttool_suggest_first();
1245 last = texttool_suggest_last();
1246 sel = texttool_suggest_selected();
1247 top = texttool_suggest_top();
1249 if (!last || !first)
1252 /* Count the visible lines to the cursor */
1253 for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1256 if(st->showlinenrs) {
1257 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1259 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1261 y = curarea->winy - st->lheight*l - 2;
1263 w = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1264 h = SUGG_LIST_SIZE*st->lheight + 8;
1266 getmouseco_areawin(mval);
1268 if (mval[0]<x || x+w<mval[0] || mval[1]<y-h || y<mval[1])
1271 /* Work out which of the items is at the top of the visible list */
1272 for (i=0, item=first; i<*top && item->next; i++, item=item->next);
1274 /* Work out the target item index in the visible list */
1275 tgti = (y-mval[1]-4) / st->lheight;
1276 if (tgti<0 || tgti>SUGG_LIST_SIZE)
1279 for (i=tgti; i>0 && item->next; i--, item=item->next);
1281 texttool_suggest_select(item);
1285 static void pop_suggest_list()
1287 SuggItem *item, *sel;
1290 item= texttool_suggest_first();
1291 sel= texttool_suggest_selected();
1292 top= texttool_suggest_top();
1295 while (item && item != sel) {
1299 if (i > *top+SUGG_LIST_SIZE-1)
1300 *top= i-SUGG_LIST_SIZE+1;
1305 void draw_documentation(SpaceText *st)
1308 char *docs, buf[DOC_WIDTH+1], *p;
1309 int len, i, br, lines;
1310 int boxw, boxh, l, x, y, top;
1312 if (!st || !st->text) return;
1313 if (!texttool_text_is_active(st->text)) return;
1315 docs = texttool_docs_get();
1319 /* Count the visible lines to the cursor */
1320 for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1323 if(st->showlinenrs) {
1324 x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1326 x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1328 if (texttool_suggest_first()) {
1329 x += SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 50;
1332 top= y= curarea->winy - st->lheight*l - 2;
1334 boxw= DOC_WIDTH*spacetext_get_fontwidth(st) + 20;
1335 boxh= (DOC_HEIGHT+1)*st->lheight;
1338 BIF_ThemeColor(TH_BACK);
1339 glRecti(x, y, x+boxw, y-boxh);
1340 BIF_ThemeColor(TH_SHADE1);
1341 glBegin(GL_LINE_LOOP);
1343 glVertex2i(x+boxw, y);
1344 glVertex2i(x+boxw, y-boxh);
1345 glVertex2i(x, y-boxh);
1347 glBegin(GL_LINE_LOOP);
1348 glVertex2i(x+boxw-10, y-7);
1349 glVertex2i(x+boxw-4, y-7);
1350 glVertex2i(x+boxw-7, y-2);
1352 glBegin(GL_LINE_LOOP);
1353 glVertex2i(x+boxw-10, y-boxh+7);
1354 glVertex2i(x+boxw-4, y-boxh+7);
1355 glVertex2i(x+boxw-7, y-boxh+2);
1357 BIF_ThemeColor(TH_TEXT);
1359 i= 0; br= DOC_WIDTH; lines= -doc_scroll;
1360 for (p=docs; *p; p++) {
1361 if (*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
1362 if (*p == ' ' || *p == '\t')
1364 else if (*p == '\n') {
1368 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1370 i= 0; br= DOC_WIDTH; lines++;
1373 if (i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
1377 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1379 p -= i-br-1; /* Rewind pointer to last break */
1380 i= 0; br= DOC_WIDTH; lines++;
1382 if (lines >= DOC_HEIGHT) break;
1384 if (doc_scroll > 0 && lines < DOC_HEIGHT) {
1386 draw_documentation(st);
1390 void draw_suggestion_list(SpaceText *st)
1392 SuggItem *item, *first, *last, *sel;
1394 char str[SUGG_LIST_WIDTH+1];
1395 int w, boxw=0, boxh, i, l, x, y, b, *top;
1397 if (!st || !st->text) return;
1398 if (!texttool_text_is_active(st->text)) return;
1400 first = texttool_suggest_first();
1401 last = texttool_suggest_last();
1403 if (!first || !last) return;
1406 sel = texttool_suggest_selected();
1407 top = texttool_suggest_top();
1409 /* Count the visible lines to the cursor */
1410 for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1413 if(st->showlinenrs) {
1414 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1416 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1418 y = curarea->winy - st->lheight*l - 2;
1420 boxw = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1421 boxh = SUGG_LIST_SIZE*st->lheight + 8;
1423 BIF_ThemeColor(TH_SHADE1);
1424 glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1425 BIF_ThemeColor(TH_BACK);
1426 glRecti(x, y, x+boxw, y-boxh);
1428 /* Set the top 'item' of the visible list */
1429 for (i=0, item=first; i<*top && item->next; i++, item=item->next);
1431 for (i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1435 strncpy(str, item->name, SUGG_LIST_WIDTH);
1436 str[SUGG_LIST_WIDTH] = '\0';
1438 w = BMF_GetStringWidth(spacetext_get_font(st), str);
1441 BIF_ThemeColor(TH_SHADE2);
1442 glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1444 b=1; /* b=1 colour block, text is default. b=0 no block, colour text */
1445 switch (item->type) {
1446 case 'k': BIF_ThemeColor(TH_SYNTAX_B); b=0; break;
1447 case 'm': BIF_ThemeColor(TH_TEXT); break;
1448 case 'f': BIF_ThemeColor(TH_SYNTAX_L); break;
1449 case 'v': BIF_ThemeColor(TH_SYNTAX_N); break;
1450 case '?': BIF_ThemeColor(TH_TEXT); b=0; break;
1453 glRecti(x+8, y+2, x+11, y+5);
1454 BIF_ThemeColor(TH_TEXT);
1456 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1458 if (item == last) break;
1462 static short check_blockhandler(SpaceText *st, short handler)
1466 for(a=0; a<SPACE_MAXHANDLER; a+=2)
1467 if (st->blockhandler[a]==handler) return 1;
1471 /* Find and replace GUI panel */
1472 static void text_panel_find(short cntrl) // TEXT_HANDLER_FIND
1476 /* Ensure that find and replace buffers have been allocated */
1477 if (!g_find_str || !g_replace_str) {
1478 g_find_str= MEM_mallocN(TXT_MAXFINDSTR+1, "find_string");
1479 g_replace_str= MEM_mallocN(TXT_MAXFINDSTR+1, "replace_string");
1480 g_find_str[0]= g_replace_str[0]= '\0';
1483 block= uiNewBlock(&curarea->uiblocks, "text_panel_find", UI_EMBOSS, UI_HELV, curarea->win);
1484 uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
1485 uiSetPanelHandler(TEXT_HANDLER_FIND); // for close and esc
1486 if(uiNewPanel(curarea, block, "Find & Replace", "Text", curarea->winx-230, curarea->winy-130, 260, 120)==0) return;
1488 uiBlockBeginAlign(block);
1489 uiDefButC(block, TEX, 0, "Find: ", 0,80,220,20, g_find_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
1490 uiDefIconBut(block, BUT, B_PASTEFIND, ICON_TEXT, 220,80,20,20, NULL, 0,0,0,0, "Copy from selection");
1491 uiDefButC(block, TEX, 0, "Replace: ", 0,60,220,20, g_replace_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
1492 uiDefIconBut(block, BUT, B_PASTEREPLACE, ICON_TEXT, 220,60,20,20, NULL, 0,0,0,0, "Copy from selection");
1493 uiBlockEndAlign(block);
1494 uiDefButBitI(block, TOG, TXT_FIND_WRAP, 0,"Wrap Around", 0,30,110,20,&g_find_flags,0,0,0,0,"Wrap search around current text");
1495 uiDefButBitI(block, TOG, TXT_FIND_ALLTEXTS,0,"Search All Texts", 110,30,130,20,&g_find_flags,0,0,0,0,"Search in each text");
1496 uiDefBut(block, BUT, B_TEXTFIND, "Find", 0,0,50,20, NULL, 0,0,0,0, "Find next");
1497 uiDefBut(block, BUT, B_TEXTREPLACE, "Replace/Find", 50,0,110,20, NULL, 0,0,0,0, "Replace then find next");
1498 uiDefBut(block, BUT, B_TEXTMARKALL, "Mark All", 160,0,80,20, NULL, 0,0,0,0, "Mark each occurrence to edit all from one");
1501 /* mode: 0 find only, 1 replace/find, 2 mark all occurrences */
1502 void find_and_replace(SpaceText *st, short mode)
1504 Text *start= NULL, *text= st->text;
1505 int flags, first= 1;
1508 if (!check_blockhandler(st, TEXT_HANDLER_FIND)) {
1509 toggle_blockhandler(st->area, TEXT_HANDLER_FIND, UI_PNL_TO_MOUSE);
1513 if (!g_find_str || !g_replace_str) return;
1514 if (g_find_str[0] == '\0') return;
1515 flags= g_find_flags;
1516 if (flags & TXT_FIND_ALLTEXTS) flags ^= TXT_FIND_WRAP;
1520 txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
1523 /* Replace current */
1524 if (mode && txt_has_sel(text)) {
1525 tmp= txt_sel_to_buf(text);
1526 if (strcmp(g_find_str, tmp)==0) {
1528 txt_insert_buf(text, g_replace_str);
1529 if (st->showsyntax) txt_format_line(st, text->curl, 1);
1530 } else if (mode==2) {
1532 BIF_GetThemeColor4ubv(TH_SHADE2, color);
1533 if (txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
1534 if (tmp) MEM_freeN(tmp), tmp=NULL;
1537 txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
1545 if (txt_find_string(text, g_find_str, flags & TXT_FIND_WRAP)) {
1547 } else if (flags & TXT_FIND_ALLTEXTS) {
1548 if (text==start) break;
1549 if (!start) start= text;
1551 text= st->text= text->id.next;
1553 text= st->text= G.main->text.first;
1554 txt_move_toline(text, 0, 0);
1558 okee("Text not found: %s", g_find_str);
1564 static void do_find_buttons(val)
1570 st= curarea->spacedata.first;
1571 if (!st || st->spacetype != SPACE_TEXT) return;
1577 if (!g_find_str) break;
1578 tmp= txt_sel_to_buf(text);
1579 strncpy(g_find_str, tmp, TXT_MAXFINDSTR);
1582 case B_PASTEREPLACE:
1583 if (!g_replace_str) break;
1584 tmp= txt_sel_to_buf(text);
1585 strncpy(g_replace_str, tmp, TXT_MAXFINDSTR);
1589 find_and_replace(st, 0);
1592 find_and_replace(st, 1);
1595 find_and_replace(st, 2);
1600 static void text_blockhandlers(ScrArea *sa)
1602 SpaceText *st= sa->spacedata.first;
1605 /* warning; blocks need to be freed each time, handlers dont remove */
1606 uiFreeBlocksWin(&sa->uiblocks, sa->win);
1608 for(a=0; a<SPACE_MAXHANDLER; a+=2) {
1609 /* clear action value for event */
1610 switch(st->blockhandler[a]) {
1611 case TEXT_HANDLER_FIND:
1612 text_panel_find(st->blockhandler[a+1]);
1616 uiDrawBlocksPanels(sa, 0);
1619 void drawtextspace(ScrArea *sa, void *spacedata)
1621 SpaceText *st= curarea->spacedata.first;
1629 if (st==NULL || st->spacetype != SPACE_TEXT) return;
1631 bwin_clear_viewmat(sa->win); /* clear buttons view */
1634 BIF_GetThemeColor3fv(TH_BACK, col);
1635 glClearColor(col[0], col[1], col[2], 0.0);
1636 glClear(GL_COLOR_BUFFER_BIT);
1637 myortho2(-0.375, (float)(sa->winx)-0.375, -0.375, (float)(sa->winy)-0.375);
1639 draw_area_emboss(sa);
1644 /* Make sure all the positional pointers exist */
1645 if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1646 txt_clean_text(text);
1648 if(st->lheight) st->viewlines= (int) curarea->winy/st->lheight;
1649 else st->viewlines= 0;
1651 if(st->showlinenrs) {
1652 BIF_ThemeColor(TH_GRID);
1653 glRecti(23, 0, (st->lheight==15)?63:59, curarea->winy - 2);
1658 tmp= text->lines.first;
1659 for (i= 0; i<st->top && tmp; i++) {
1660 if (st->showsyntax && !tmp->format) txt_format_line(st, tmp, 0);
1665 y= curarea->winy-st->lheight;
1666 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1668 BIF_ThemeColor(TH_TEXT);
1669 for (i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1670 if (st->showsyntax && !tmp->format) {
1671 txt_format_line(st, tmp, 0);
1673 if(st->showlinenrs) {
1674 /*Change the color of the current line the cursor is on*/
1675 if(tmp == text->curl) {
1676 BIF_ThemeColor(TH_HILITE);
1678 BIF_ThemeColor(TH_TEXT);
1680 if(((float)(i + linecount + 1)/10000.0) < 1.0) {
1681 sprintf(linenr, "%4d", i + linecount + 1);
1682 glRasterPos2i(TXT_OFFSET - 7, y);
1684 sprintf(linenr, "%5d", i + linecount + 1);
1685 glRasterPos2i(TXT_OFFSET - 11, y);
1687 BIF_ThemeColor(TH_TEXT);
1688 BMF_DrawString(spacetext_get_font(st), linenr);
1691 int lines = text_draw_wrapped(st, tmp->line, x, y, curarea->winx-x, tmp->format);
1692 y -= lines*st->lheight;
1694 text_draw(st, tmp->line, st->left, 0, 1, x, y, tmp->format);
1702 draw_textscroll(st);
1703 draw_documentation(st);
1704 draw_suggestion_list(st);
1706 bwin_scalematrix(sa->win, st->blockscale, st->blockscale, st->blockscale);
1707 text_blockhandlers(sa);
1709 curarea->win_swap= WIN_BACK_OK;
1712 /* Moves the view to the cursor location,
1713 also used to make sure the view isnt outside the file */
1714 void pop_space_text (SpaceText *st)
1719 if(!st->text) return;
1720 if(!st->text->curl) return;
1722 i= txt_get_span(st->text->lines.first, st->text->sell);
1723 if (st->top+st->viewlines <= i || st->top > i) {
1724 st->top= i - st->viewlines/2;
1730 x= text_draw(st, st->text->sell->line, st->left, st->text->selc, 0, 0, 0, NULL);
1732 if (x==0 || x>curarea->winx) {
1733 st->left= st->text->curc-0.5*(curarea->winx)/spacetext_get_fontwidth(st);
1737 if (st->top < 0) st->top= 0;
1738 if (st->left <0) st->left= 0;
1741 void add_text_fs(char *file) /* bad but cant pass an as arg here */
1743 SpaceText *st= curarea->spacedata.first;
1746 if (st==NULL || st->spacetype != SPACE_TEXT) return;
1748 text= add_text(file);
1754 if (st->showsyntax) txt_format_text(st);
1755 allqueue(REDRAWTEXT, 0);
1756 allqueue(REDRAWHEADERS, 0);
1759 void free_textspace(SpaceText *st)
1766 static void save_mem_text(char *str)
1768 SpaceText *st= curarea->spacedata.first;
1774 if (st->spacetype != SPACE_TEXT) return;
1779 if (text->name) MEM_freeN(text->name);
1780 text->name= MEM_mallocN(strlen(str)+1, "textname");
1781 strcpy(text->name, str);
1783 text->flags ^= TXT_ISMEM;
1785 txt_write_file(text);
1788 void txt_write_file(Text *text)
1794 char file[FILE_MAXDIR+FILE_MAXFILE];
1796 /* Do we need to get a filename? */
1797 if (text->flags & TXT_ISMEM) {
1799 activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->name, save_mem_text);
1801 activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->id.name+2, save_mem_text);
1805 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
1806 BLI_convertstringcode(file, G.sce);
1808 /* Should we ask to save over? */
1809 if (text->flags & TXT_ISTMP) {
1810 if (BLI_exists(file)) {
1811 if (!okee("Save over")) return;
1812 } else if (!okee("Create new file")) return;
1814 text->flags ^= TXT_ISTMP;
1817 fp= fopen(file, "w");
1819 error("Unable to save file");
1823 tmp= text->lines.first;
1825 if (tmp->next) fprintf(fp, "%s\n", tmp->line);
1826 else fprintf(fp, "%s", tmp->line);
1833 res= stat(file, &st);
1834 text->mtime= st.st_mtime;
1836 if (text->flags & TXT_ISDIRTY) text->flags ^= TXT_ISDIRTY;
1839 void unlink_text(Text *text)
1845 /* check if this text was used as script link:
1846 * this check function unsets the pointers and returns how many
1847 * script links used this Text */
1848 if (BPY_check_all_scriptlinks (text)) {
1849 allqueue(REDRAWBUTSSCRIPT, 0);
1851 /* equivalently for pynodes: */
1852 if (nodeDynamicUnlinkText ((ID*)text)) {
1853 allqueue(REDRAWNODE, 0);
1856 for (scr= G.main->screen.first; scr; scr= scr->id.next) {
1857 for (area= scr->areabase.first; area; area= area->next) {
1858 for (sl= area->spacedata.first; sl; sl= sl->next) {
1859 if (sl->spacetype==SPACE_TEXT) {
1860 SpaceText *st= (SpaceText*) sl;
1862 if (st->text==text) {
1866 if (st==area->spacedata.first) {
1867 scrarea_queue_redraw(area);
1876 int jumptoline_interactive(SpaceText *st)
1878 short nlines= txt_get_span(st->text->lines.first, st->text->lines.last)+1;
1879 short tmp= txt_get_span(st->text->lines.first, st->text->curl)+1;
1881 if (button(&tmp, 1, nlines, "Jump to line:")) {
1882 txt_move_toline(st->text, tmp-1, 0);
1892 static char *copybuffer = NULL;
1894 static void txt_copy_selectbuffer (Text *text)
1897 TextLine *tmp, *linef, *linel;
1901 if (!text->curl) return;
1902 if (!text->sell) return;
1904 if (!txt_has_sel(text)) return;
1907 MEM_freeN(copybuffer);
1911 if (text->curl==text->sell) {
1912 linef= linel= text->curl;
1914 if (text->curc < text->selc) {
1921 } else if (txt_get_span(text->curl, text->sell)<0) {
1935 if (linef == linel) {
1936 length= charl-charf;
1938 copybuffer= MEM_mallocN(length+1, "cut buffera");
1940 BLI_strncpy(copybuffer, linef->line + charf, length+1);
1942 length+= linef->len - charf;
1944 length++; /* For the '\n' */
1947 while (tmp && tmp!= linel) {
1948 length+= tmp->len+1;
1952 copybuffer= MEM_mallocN(length+1, "cut bufferb");
1954 strncpy(copybuffer, linef->line+ charf, linef->len-charf);
1955 length= linef->len-charf;
1957 copybuffer[length++]='\n';
1960 while (tmp && tmp!=linel) {
1961 strncpy(copybuffer+length, tmp->line, tmp->len);
1964 copybuffer[length++]='\n';
1968 strncpy(copybuffer+length, linel->line, charl);
1971 copybuffer[length]=0;
1974 bufferlength = length;
1977 static char *unixNewLine(char *buffer)
1979 char *p, *p2, *output;
1981 /* we can afford the few extra bytes */
1982 output= MEM_callocN(strlen(buffer)+1, "unixnewline");
1983 for (p= buffer, p2= output; *p; p++)
1984 if (*p != '\r') *(p2++)= *p;
1990 static char *winNewLine(char *buffer)
1992 char *p, *p2, *output;
1995 for (p= buffer; *p; p++)
1996 if (*p == '\n') add++;
1998 bufferlength= p-buffer+add+1;
1999 output= MEM_callocN(bufferlength, "winnewline");
2000 for (p= buffer, p2= output; *p; p++, p2++) {
2002 *(p2++)= '\r'; *p2= '\n';
2010 void txt_paste_clipboard(Text *text)
2015 buff = (char*)getClipboard(0);
2017 temp_buff = unixNewLine(buff);
2019 txt_insert_buf(text, temp_buff);
2020 if(buff){free((void*)buff);}
2021 if(temp_buff){MEM_freeN(temp_buff);}
2025 void get_selection_buffer(Text *text)
2027 char *buff = getClipboard(1);
2029 txt_insert_buf(text, buff);
2032 void txt_copy_clipboard(Text *text)
2036 txt_copy_selectbuffer(text);
2039 copybuffer[bufferlength] = '\0';
2040 temp = winNewLine(copybuffer);
2042 putClipboard(temp, 0);
2044 MEM_freeN(copybuffer);
2049 void run_python_script(SpaceText *st)
2051 Text *text=st->text;
2054 if (!BPY_txt_do_python_Text(text)) {
2055 int lineno = BPY_Err_getLinenumber();
2056 // jump to error if happened in current text:
2057 py_filename = (char*) BPY_Err_getFilename();
2059 /* st->text can become NULL: user called Blender.Load(blendfile)
2060 * before the end of the script. */
2061 if (!st->text) return;
2063 if (!strcmp(py_filename, st->text->id.name+2)) {
2066 txt_move_toline(text, lineno-1, 0);
2071 error("Error in other (possibly external) file, "\
2077 static void set_tabs(Text *text)
2079 SpaceText *st = curarea->spacedata.first;
2081 st->currtab_set = setcurr_tab(text);
2084 static void wrap_move_bol(SpaceText *st, short sel)
2086 Text *text= st->text;
2087 int offl, offc, lin;
2089 lin= txt_get_span(text->lines.first, text->sell);
2090 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2093 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
2096 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
2102 static void wrap_move_eol(SpaceText *st, short sel)
2104 Text *text= st->text;
2105 int offl, offc, lin, startl, c;
2107 lin= txt_get_span(text->lines.first, text->sell);
2108 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2111 while (offl==startl && text->sell->line[c]!='\0') {
2113 wrap_offset(st, text->sell, c, &offl, &offc);
2114 } if (offl!=startl) c--;
2117 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
2120 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
2126 static void wrap_move_up(SpaceText *st, short sel)
2128 Text *text= st->text;
2129 int offl, offl_1, offc, fromline, toline, c, target;
2131 wrap_offset(st, text->sell, 0, &offl_1, &offc);
2132 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2133 fromline= toline= txt_get_span(text->lines.first, text->sell);
2134 target= text->selc + offc;
2137 if (!text->sell->prev) {
2138 txt_move_bol(text, sel);
2142 c= text->sell->prev->len; /* End of prev. line */
2143 wrap_offset(st, text->sell->prev, c, &offl, &offc);
2146 c= -offc-1; /* End of prev. line */
2147 wrap_offset(st, text->sell, c, &offl, &offc);
2153 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
2154 if (toline<fromline) text->sell= text->sell->prev;
2156 if (c>text->sell->len) c= text->sell->len;
2160 else if(text->curl) {
2161 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
2162 if (toline<fromline) text->curl= text->curl->prev;
2164 if (c>text->curl->len) c= text->curl->len;
2171 static void wrap_move_down(SpaceText *st, short sel)
2173 Text *text= st->text;
2174 int offl, startoff, offc, fromline, toline, c, target;
2176 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2177 fromline= toline= txt_get_span(text->lines.first, text->sell);
2178 target= text->selc + offc;
2181 while (offl==startoff && text->sell->line[c]!='\0') {
2183 wrap_offset(st, text->sell, c, &offl, &offc);
2186 if (text->sell->line[c]=='\0') {
2187 if (!text->sell->next) {
2188 txt_move_eol(text, sel);
2195 if (c > text->sell->len) c= text->sell->len;
2200 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
2201 if (toline>fromline) text->sell= text->sell->next;
2203 if (c>text->sell->len) c= text->sell->len;
2207 else if(text->curl) {
2208 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
2209 if (toline>fromline) text->curl= text->curl->next;
2211 if (c > text->curl->len) c= text->curl->len;
2218 static void get_suggest_prefix(Text *text, int offset)
2221 char *line, tmp[256];
2224 if (!texttool_text_is_active(text)) return;
2226 line= text->curl->line;
2227 for (i=text->curc-1+offset; i>=0; i--)
2228 if (!check_identifier(line[i]))
2231 len= text->curc-i+offset;
2233 printf("Suggestion prefix too long\n");
2236 strncpy(tmp, line+i, len);
2238 texttool_suggest_prefix(tmp);
2241 static void confirm_suggestion(Text *text, int skipleft)
2248 if (!texttool_text_is_active(text)) return;
2250 sel = texttool_suggest_selected();
2253 line= text->curl->line;
2254 i=text->curc-skipleft-1;
2256 if (!check_identifier(line[i]))
2262 for (i=0; i<skipleft; i++)
2263 txt_move_left(text, 0);
2264 for (i=0; i<over; i++)
2265 txt_move_left(text, 1);
2267 txt_insert_buf(text, sel->name);
2269 for (i=0; i<skipleft; i++)
2270 txt_move_right(text, 0);
2272 texttool_text_clear();
2275 static short do_texttools(SpaceText *st, char ascii, unsigned short evnt, short val)
2277 int draw=0, tools=0, swallow=0, scroll=1;
2278 if (!texttool_text_is_active(st->text)) return 0;
2279 if (!st->text || st->text->id.lib) return 0;
2281 if (st->doplugins && texttool_text_is_active(st->text)) {
2282 if (texttool_suggest_first()) tools |= TOOL_SUGG_LIST;
2283 if (texttool_docs_get()) tools |= TOOL_DOCUMENT;
2287 if (tools & TOOL_SUGG_LIST) {
2288 if ((ascii != '_' && ascii != '*' && ispunct(ascii)) || check_whitespace(ascii)) {
2289 confirm_suggestion(st->text, 0);
2290 if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2291 } else if ((st->overwrite && txt_replace_char(st->text, ascii)) || txt_add_char(st->text, ascii)) {
2292 get_suggest_prefix(st->text, 0);
2298 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2300 } else if (val==1 && evnt) {
2303 if (do_suggest_select(st))
2306 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2307 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2312 if (do_suggest_select(st)) {
2313 confirm_suggestion(st->text, 0);
2314 if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2317 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2318 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2324 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2325 else if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2326 else draw= swallow= 0;
2329 if (tools & TOOL_SUGG_LIST) {
2330 confirm_suggestion(st->text, 0);
2331 if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2335 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2339 if (tools & TOOL_SUGG_LIST) {
2341 texttool_suggest_clear();
2343 /* Work out which char we are about to delete/pass */
2344 if (st->text->curl && st->text->curc > 0) {
2345 char ch= st->text->curl->line[st->text->curc-1];
2346 if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
2347 get_suggest_prefix(st->text, -1);
2351 texttool_suggest_clear();
2353 texttool_suggest_clear();
2356 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2359 if (tools & TOOL_SUGG_LIST) {
2361 texttool_suggest_clear();
2363 /* Work out which char we are about to pass */
2364 if (st->text->curl && st->text->curc < st->text->curl->len) {
2365 char ch= st->text->curl->line[st->text->curc+1];
2366 if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
2367 get_suggest_prefix(st->text, 1);
2371 texttool_suggest_clear();
2373 texttool_suggest_clear();
2376 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2379 scroll= SUGG_LIST_SIZE-1;
2380 case WHEELDOWNMOUSE:
2382 if (tools & TOOL_DOCUMENT) {
2387 } else if (tools & TOOL_SUGG_LIST) {
2388 SuggItem *sel = texttool_suggest_selected();
2390 texttool_suggest_select(texttool_suggest_first());
2391 } else while (sel && sel!=texttool_suggest_last() && sel->next && scroll--) {
2392 texttool_suggest_select(sel->next);
2401 scroll= SUGG_LIST_SIZE-1;
2404 if (tools & TOOL_DOCUMENT) {
2405 if (doc_scroll>0) doc_scroll--;
2409 } else if (tools & TOOL_SUGG_LIST) {
2410 SuggItem *sel = texttool_suggest_selected();
2411 while (sel && sel!=texttool_suggest_first() && sel->prev && scroll--) {
2412 texttool_suggest_select(sel->prev);
2424 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear(), draw= 1;
2425 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2435 static short do_markers(SpaceText *st, char ascii, unsigned short evnt, short val)
2438 TextMarker *marker, *mrk, *nxt;
2439 int c, s, draw=0, swallow=0;
2442 if (!text || text->id.lib || text->curl != text->sell) return 0;
2444 marker= txt_find_marker(text, text->sell, text->selc, 0, 0);
2445 if (marker && (marker->start > text->curc || marker->end < text->curc))
2449 /* Find the next temporary marker */
2451 int lineno= txt_get_span(text->lines.first, text->curl);
2452 TextMarker *mrk= text->markers.first;
2454 if (!marker && (mrk->flags & TMARK_TEMP)) marker= mrk;
2455 if ((mrk->flags & TMARK_TEMP) && (mrk->lineno > lineno || (mrk->lineno==lineno && mrk->end > text->curc))) {
2462 txt_move_to(text, marker->lineno, marker->start, 0);
2463 txt_move_to(text, marker->lineno, marker->end, 1);
2465 evnt= ascii= val= 0;
2469 } else if (evnt==ESCKEY) {
2470 if (txt_clear_markers(text, 0, TMARK_TEMP)) swallow= 1;
2471 else if (txt_clear_markers(text, 0, 0)) swallow= 1;
2473 evnt= ascii= val= 0;
2476 if (!swallow) return 0;
2480 if (marker->flags & TMARK_EDITALL) {
2481 c= text->curc-marker->start;
2482 s= text->selc-marker->start;
2483 if (s<0 || s>marker->end-marker->start) return 0;
2485 mrk= txt_next_marker(text, marker);
2487 nxt=txt_next_marker(text, mrk); /* mrk may become invalid */
2488 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2489 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2490 if (st->overwrite) {
2491 if (txt_replace_char(text, ascii))
2492 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2494 if (txt_add_char(text, ascii)) {
2495 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2499 if (mrk==marker || mrk==nxt) break;
2508 if (marker->flags & TMARK_EDITALL) {
2509 c= text->curc-marker->start;
2510 s= text->selc-marker->start;
2511 if (s<0 || s>marker->end-marker->start) return 0;
2513 mrk= txt_next_marker(text, marker);
2515 nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
2516 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2517 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2518 txt_backspace_char(text);
2519 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2520 if (mrk==marker || mrk==nxt) break;
2528 if (marker->flags & TMARK_EDITALL) {
2529 c= text->curc-marker->start;
2530 s= text->selc-marker->start;
2531 if (s<0 || s>marker->end-marker->start) return 0;
2533 mrk= txt_next_marker(text, marker);
2535 nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
2536 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2537 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2538 txt_delete_char(text);
2539 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2540 if (mrk==marker || mrk==nxt) break;
2548 if (G.qual & LR_SHIFTKEY) {
2550 if (!nxt) nxt= text->markers.last;
2553 if (!nxt) nxt= text->markers.first;
2555 if (marker->flags & TMARK_TEMP) {
2556 if (nxt==marker) nxt= NULL;
2557 BLI_freelinkN(&text->markers, marker);
2561 txt_move_to(text, mrk->lineno, mrk->start, 0);
2562 txt_move_to(text, mrk->lineno, mrk->end, 1);
2569 /* Events that should clear markers */
2570 case UKEY: if (!(G.qual & LR_ALTKEY)) break;
2571 case ZKEY: if (evnt==ZKEY && !(G.qual & LR_CTRLKEY)) break;
2574 if (marker->flags & (TMARK_EDITALL | TMARK_TEMP))
2575 txt_clear_markers(text, marker->group, 0);
2577 BLI_freelinkN(&text->markers, marker);
2581 case RIGHTMOUSE: /* Marker context menu? */
2584 case FKEY: /* Allow find */
2585 if (G.qual & LR_SHIFTKEY) swallow= 1;
2589 if (G.qual!=0 && G.qual!=LR_SHIFTKEY)
2590 swallow= 1; /* Swallow all other shortcut events */
2601 void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2603 SpaceText *st= curarea->spacedata.first;
2606 unsigned short event= evt->event;
2607 short val= evt->val;
2608 char ascii= evt->ascii;
2610 if (st==NULL || st->spacetype != SPACE_TEXT) return;
2612 /* smartass code to prevent the CTRL/ALT events below from not working! */
2613 if(G.qual & (LR_ALTKEY|LR_CTRLKEY))
2620 if (event==RIGHTMOUSE) {
2621 switch (pupmenu("File %t|New %x0|Open... %x1")) {
2623 st->text= add_empty_text("Text");
2626 allqueue(REDRAWTEXT, 0);
2627 allqueue(REDRAWHEADERS, 0);
2630 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2634 if (val && !ELEM(G.qual, 0, LR_SHIFTKEY)) {
2635 if (event==FKEY && G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2636 switch (pupmenu("File %t|New %x0|Open... %x1")) {
2638 st->text= add_empty_text("Text");
2641 allqueue(REDRAWTEXT, 0);
2642 allqueue(REDRAWHEADERS, 0);
2645 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2649 else if (event==QKEY) {
2650 if (G.qual & LR_CTRLKEY) {
2651 if(okee("Quit Blender")) exit_usiblender();
2654 else if (event==NKEY) {
2655 if (G.qual & LR_ALTKEY) {
2656 st->text= add_empty_text("Text");
2659 allqueue(REDRAWTEXT, 0);
2660 allqueue(REDRAWHEADERS, 0);
2663 else if (event==OKEY) {
2664 if (G.qual & LR_ALTKEY) {
2665 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2672 if (val && uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING) event= 0;
2674 if (st->doplugins && do_texttools(st, ascii, event, val)) return;
2675 if (do_markers(st, ascii, event, val)) return;
2677 if (event==UI_BUT_EVENT) {
2678 do_find_buttons(val);
2680 } else if (event==LEFTMOUSE) {
2685 getmouseco_areawin(mval);
2687 if (mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<curarea->winy-2) {
2688 do_textscroll(st, 2);
2690 do_selection(st, G.qual&LR_SHIFTKEY);
2691 if (txt_has_sel(text)) {
2692 buffer = txt_sel_to_buf(text);
2693 putClipboard(buffer, 1);
2699 } else if (event==MIDDLEMOUSE) {
2701 if (U.uiflag & USER_MMB_PASTE) {
2702 do_selection(st, G.qual&LR_SHIFTKEY);
2703 get_selection_buffer(text);
2706 do_textscroll(st, 1);
2709 } else if (event==RIGHTMOUSE) {
2711 if (txt_has_sel(text))
2712 p= pupmenu("Text %t|Cut%x10|Copy%x11|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
2714 p= pupmenu("Text %t|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
2718 st->text= add_empty_text("Text");
2721 allqueue(REDRAWTEXT, 0);
2722 allqueue(REDRAWHEADERS, 0);
2726 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2730 text->flags |= TXT_ISMEM;
2733 txt_write_file(text);
2737 run_python_script(st);
2741 if (text && text->id.lib) {
2745 txt_copy_clipboard(text);
2751 //txt_copy_sel(text);
2752 txt_copy_clipboard(text);
2755 if (text && text->id.lib) {
2759 txt_paste_clipboard(text);
2760 if (st->showsyntax) txt_format_text(st);
2766 if (text && text->id.lib) {
2770 getmouseco_areawin(mval);
2771 if (st->showlinenrs && mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<curarea->winy-2) {
2772 if (ascii>='0' && ascii<='9') {
2773 double time = PIL_check_seconds_timer();
2774 if (last_jump < time-1) jump_to= 0;
2775 jump_to *= 10; jump_to += (int)(ascii-'0');
2776 txt_move_toline(text, jump_to-1, 0);
2779 } else if ((st->overwrite && txt_replace_char(text, ascii)) || txt_add_char(text, ascii)) {
2780 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2788 if (G.qual & LR_ALTKEY) {
2789 txt_move_bol(text, G.qual & LR_SHIFTKEY);
2792 } else if (G.qual & LR_CTRLKEY) {
2796 break; /* BREAK A */
2798 if (G.qual & LR_ALTKEY || G.qual & LR_CTRLKEY) {
2799 if(G.qual & LR_SHIFTKEY)
2800 txt_copy_clipboard(text);
2802 txt_copy_clipboard(text);
2806 break; /* BREAK C */
2808 if (text && text->id.lib) {
2812 if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
2814 txt_order_cursors(text);
2817 if (st->showsyntax) txt_format_text(st);
2819 } else if (G.qual == LR_CTRLKEY) {
2820 txt_delete_char(text);
2821 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2825 break; /* BREAK D */
2827 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2828 switch(pupmenu("Edit %t|Cut %x0|Copy %x1|Paste %x2|Print Cut Buffer %x3")) {
2830 if (text && text->id.lib) {
2834 txt_copy_clipboard(text); //First copy to clipboard
2839 txt_copy_clipboard(text);
2840 //txt_copy_sel(text);
2844 if (text && text->id.lib) {
2849 txt_paste_clipboard(text);
2850 if (st->showsyntax) txt_format_text(st);
2854 txt_print_cutbuffer();
2858 else if (G.qual == LR_CTRLKEY || G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
2859 txt_move_eol(text, G.qual & LR_SHIFTKEY);
2863 break; /* BREAK E */
2865 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2866 switch(pupmenu("File %t|New %x0|Open... %x1|Save %x2|Save As...%x3")) {
2868 st->text= add_empty_text("Text");
2871 allqueue(REDRAWTEXT, 0);
2872 allqueue(REDRAWHEADERS, 0);
2875 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2878 text->flags |= TXT_ISMEM;
2880 txt_write_file(text);
2885 else if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
2886 find_and_replace(st, 0);
2889 break; /* BREAK F */
2891 if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
2892 find_and_replace(st, 1);
2895 break; /* BREAK H */
2897 if (G.qual == LR_ALTKEY) {
2898 do_draw= jumptoline_interactive(st);
2900 break; /* BREAK J */
2902 if (G.qual == LR_ALTKEY) {
2903 txt_export_to_object(text);
2906 break; /* BREAK M */
2908 if (G.qual == LR_ALTKEY) {
2909 st->text= add_empty_text("Text");
2912 allqueue(REDRAWTEXT, 0);
2913 allqueue(REDRAWHEADERS, 0);
2915 break; /* BREAK N */
2917 if (G.qual == LR_ALTKEY) {
2918 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2920 break; /* BREAK O */
2922 if (G.qual == LR_ALTKEY) {
2923 run_python_script(st);
2926 break; /* BREAK P */
2928 if(okee("Quit Blender")) exit_usiblender();
2929 break; /* BREAK Q */
2931 if (G.qual == LR_ALTKEY) {
2932 if (text->compiled) BPY_free_compiled_text(text);
2933 text->compiled = NULL;
2934 if (okee("Reopen text")) {
2935 if (!reopen_text(text))
2936 error("Could not reopen file");
2937 if (st->showsyntax) txt_format_text(st);
2941 break; /* BREAK R */
2943 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2944 p= pupmenu("Select %t|"
2947 "Jump to Line %x3");
2960 do_draw= jumptoline_interactive(st);
2964 else if (G.qual & LR_ALTKEY) {
2965 /* Event treatment CANNOT enter this if
2966 if (G.qual & LR_SHIFTKEY)
2967 if (text) text->flags |= TXT_ISMEM;
2969 txt_write_file(text);
2972 break; /* BREAK S */
2974 //txt_print_undo(text); //debug buffer in console
2975 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2980 if (G.qual == LR_ALTKEY) {