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 #ifndef DISABLE_PYTHON
75 #include "BPY_extern.h"
76 #include "BPY_menus.h"
83 #include <ctype.h> /* ispunct */
86 /***********************/ /*
90 All word-wrap functions follow the algorithm below to maintain consistency.
91 line The line to wrap (tabs converted to spaces)
92 view_width The maximum number of characters displayable in the region
93 This equals region_width/font_width for the region
94 wrap_chars Characters that allow wrapping. This equals [' ', '\t', '-']
96 def wrap(line, view_width, wrap_chars):
101 if pos-draw_start >= view_width:
102 print line[draw_start:draw_end]
103 draw_start = draw_end
104 draw_end += view_width
105 elif c in wrap_chars:
108 print line[draw_start:]
110 */ /***********************/
114 #define SUGG_LIST_SIZE 7
115 #define SUGG_LIST_WIDTH 20
117 #define DOC_HEIGHT 10
119 #define TOOL_SUGG_LIST 0x01
120 #define TOOL_DOCUMENT 0x02
122 #define TMARK_GRP_CUSTOM 0x00010000 /* Lower 2 bytes used for Python groups */
123 #define TMARK_GRP_FINDALL 0x00020000
125 /* forward declarations */
127 void drawtextspace(ScrArea *sa, void *spacedata);
128 void winqreadtextspace(struct ScrArea *sa, void *spacedata, struct BWinEvent *evt);
129 void redraw_alltext(void);
131 static void txt_copy_selectbuffer(Text *text);
132 static void draw_brackets(SpaceText *st);
133 static void get_selection_buffer(Text *text);
134 static int check_bracket(char ch);
135 static int check_delim(char ch);
136 static int check_digit(char ch);
137 static int check_identifier(char ch);
138 static int check_whitespace(char ch);
140 static int get_wrap_width(SpaceText *st);
141 static void get_suggest_prefix(Text *text, int offset);
142 static void confirm_suggestion(Text *text, int skipleft);
144 #define TXT_MAXFINDSTR 255
145 static int g_find_flags= TXT_FIND_WRAP;
146 static char *g_find_str= NULL;
147 static char *g_replace_str= NULL;
149 static int doc_scroll= 0;
150 static int jump_to= 0;
151 static double last_jump= 0;
153 static BMF_Font *spacetext_get_font(SpaceText *st)
155 static BMF_Font *scr12= NULL;
156 static BMF_Font *scr15= NULL;
158 switch (st->font_id) {
162 scr12= BMF_GetFont(BMF_kScreen12);
166 scr15= BMF_GetFont(BMF_kScreen15);
171 static int spacetext_get_fontwidth(SpaceText *st)
173 return BMF_GetCharacterWidth(spacetext_get_font(st), ' ');
176 static char *temp_char_buf= NULL;
177 static int *temp_char_accum= NULL;
178 static int temp_char_len= 0;
179 static int temp_char_pos= 0;
181 static void temp_char_write(char c, int accum)
183 if (temp_char_len==0 || temp_char_pos>=temp_char_len) {
184 char *nbuf; int *naccum;
185 int olen= temp_char_len;
187 if (olen) temp_char_len*= 2;
188 else temp_char_len= 256;
190 nbuf= MEM_mallocN(sizeof(*temp_char_buf)*temp_char_len, "temp_char_buf");
191 naccum= MEM_mallocN(sizeof(*temp_char_accum)*temp_char_len, "temp_char_accum");
194 memcpy(nbuf, temp_char_buf, olen);
195 memcpy(naccum, temp_char_accum, olen);
197 MEM_freeN(temp_char_buf);
198 MEM_freeN(temp_char_accum);
202 temp_char_accum= naccum;
205 temp_char_buf[temp_char_pos]= c;
206 temp_char_accum[temp_char_pos]= accum;
208 if (c==0) temp_char_pos= 0;
209 else temp_char_pos++;
212 void free_txt_data(void)
214 txt_free_cut_buffer();
216 if (g_find_str) MEM_freeN(g_find_str);
217 if (g_replace_str) MEM_freeN(g_replace_str);
218 if (temp_char_buf) MEM_freeN(temp_char_buf);
219 if (temp_char_accum) MEM_freeN(temp_char_accum);
222 static int render_string (SpaceText *st, char *in)
228 if (temp_char_pos && *(in-1)=='\t') i= st->tabnumber;
229 else if (st->tabnumber > 0) i= st->tabnumber - (temp_char_pos%st->tabnumber);
230 while(i--) temp_char_write(' ', r);
231 } else temp_char_write(*in, r);
237 temp_char_write(0, 0);
242 /* Checks the specified source string for a Python built-in function name. This
243 name must start at the beginning of the source string and must be followed by
244 a non-identifier (see check_identifier(char)) or null character.
246 If a built-in function is found, the length of the matching name is returned.
247 Otherwise, -1 is returned.
249 static int find_builtinfunc(char *string)
252 char builtinfuncs[][11] = {"and", "as", "assert", "break", "class", "continue", "def",
253 "del", "elif", "else", "except", "exec", "finally",
254 "for", "from", "global", "if", "import", "in",
255 "is", "lambda", "not", "or", "pass", "print",
256 "raise", "return", "try", "while", "yield"};
257 for (a=0; a<30; a++) {
260 /* If we hit the end of a keyword... (eg. "def") */
261 if (builtinfuncs[a][i]=='\0') {
262 /* If we still have identifier chars in the source (eg. "definate") */
263 if (check_identifier(string[i]))
264 i = -1; /* No match */
265 break; /* Next keyword if no match, otherwise we're done */
267 /* If chars mismatch, move on to next keyword */
268 } else if (string[i]!=builtinfuncs[a][i]) {
270 break; /* Break inner loop, start next keyword */
274 if (i>0) break; /* If we have a match, we're done */
279 /* Checks the specified source string for a Python special name. This name must
280 start at the beginning of the source string and must be followed by a non-
281 identifier (see check_identifier(char)) or null character.
283 If a special name is found, the length of the matching name is returned.
284 Otherwise, -1 is returned.
286 static int find_specialvar(char *string)
289 /* Check for "def" */
290 if (string[0]=='d' && string[1]=='e' && string[2]=='f')
292 /* Check for "class" */
293 else if (string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
295 /* If next source char is an identifier (eg. 'i' in "definate") no match */
296 if (i==0 || check_identifier(string[i]))
301 /* Ensures the format string for the given line is long enough, reallocating
302 as needed. Allocation is done here, alone, to ensure consitency.
304 static int check_format_len(TextLine *line, unsigned int len)
307 if (strlen(line->format) < len) {
308 MEM_freeN(line->format);
309 line->format = MEM_mallocN(len+2, "SyntaxFormat");
310 if (!line->format) return 0;
313 line->format = MEM_mallocN(len+2, "SyntaxFormat");
314 if (!line->format) return 0;
319 /* Formats the specified line. If do_next is set, the process will move on to
320 the succeeding line if it is affected (eg. multiline strings). Format strings
321 may contain any of the following characters:
324 '!' Punctuation and other symbols
327 'v' Special variables (class, def)
328 'b' Built-in names (print, for, etc.)
329 'q' Other text (identifiers, etc.)
330 It is terminated with a null-terminator '\0' followed by a continuation
331 flag indicating whether the line is part of a multi-line string.
333 void txt_format_line(SpaceText *st, TextLine *line, int do_next)
335 char *str, *fmt, orig, cont, find, prev = ' ';
338 /* Get continuation from previous line */
339 if (line->prev && line->prev->format != NULL) {
340 fmt= line->prev->format;
341 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
344 /* Get original continuation from this line */
345 if (line->format != NULL) {
347 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
350 render_string(st, line->line);
353 if (!check_format_len(line, len)) return;
357 /* Handle escape sequences by skipping both \ and next char */
359 *fmt = prev; fmt++; str++;
360 if (*str == '\0') break;
361 *fmt = prev; fmt++; str++;
364 /* Handle continuations */
366 /* Triple strings ("""...""" or '''...''') */
367 if (cont & TXT_TRISTR) {
368 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
369 if (*str==find && *(str+1)==find && *(str+2)==find) {
370 *fmt = 'l'; fmt++; str++;
371 *fmt = 'l'; fmt++; str++;
374 /* Handle other strings */
376 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
377 if (*str == find) cont = 0;
381 /* Not in a string... */
383 /* Deal with comments first */
384 if (prev == '#' || *str == '#')
387 else if (*str == '"' || *str == '\'') {
389 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
390 if (*(str+1) == find && *(str+2) == find) {
391 *fmt = 'l'; fmt++; str++;
392 *fmt = 'l'; fmt++; str++;
397 /* Whitespace (all ws. has been converted to spaces) */
398 else if (*str == ' ')
400 /* Numbers (digits not part of an identifier and periods followed by digits) */
401 else if ((prev != 'q' && check_digit(*str)) || (*str == '.' && check_digit(*(str+1))))
404 else if (check_delim(*str))
406 /* Identifiers and other text (no previous ws. or delims. so text continues) */
407 else if (prev == 'q')
409 /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
411 /* Special vars(v) or built-in keywords(b) */
412 if ((i=find_specialvar(str)) != -1)
414 else if ((i=find_builtinfunc(str)) != -1)
418 *fmt = prev; fmt++; str++;
431 /* Terminate and add continuation char */
436 //print_format(st, line);
438 /* If continuation has changed and we're allowed, process the next line */
439 if (cont!=orig && do_next && line->next) {
440 txt_format_line(st, line->next, do_next);
444 /* Formats every line of the current text */
445 void txt_format_text(SpaceText *st)
449 if (!st->text) return;
451 for (linep=st->text->lines.first; linep; linep=linep->next)
452 txt_format_line(st, linep, 0);
455 /* Sets the current drawing color based on the format character specified */
456 static void format_draw_color(char formatchar)
458 switch (formatchar) {
459 case '_': /* Whitespace */
461 case '!': /* Symbols */
462 BIF_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
464 case '#': /* Comments */
465 BIF_ThemeColor(TH_SYNTAX_C);
467 case 'n': /* Numerals */
468 BIF_ThemeColor(TH_SYNTAX_N);
470 case 'l': /* Strings */
471 BIF_ThemeColor(TH_SYNTAX_L);
473 case 'v': /* Specials: class, def */
474 BIF_ThemeColor(TH_SYNTAX_V);
476 case 'b': /* Keywords: for, print, etc. */
477 BIF_ThemeColor(TH_SYNTAX_B);
479 case 'q': /* Other text (identifiers) */
481 BIF_ThemeColor(TH_TEXT);
486 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format)
488 int basex, i, a, len, start, end, max, lines;
490 len= render_string(st, str);
492 max= w/spacetext_get_fontwidth(st);
499 for (i=0; i<len; i++) {
500 if (i-start >= max) {
501 /* Draw the visible portion of text on the overshot line */
502 for (a=start; a<end; a++) {
503 if (st->showsyntax && format) format_draw_color(format[a]);
505 BMF_DrawCharacter(spacetext_get_font(st), str[a]);
506 x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
513 } else if (str[i]==' ' || str[i]=='-') {
517 /* Draw the remaining text */
518 for (a=start; a<len; a++) {
519 if (st->showsyntax && format) format_draw_color(format[a]);
521 BMF_DrawCharacter(spacetext_get_font(st), str[a]);
522 x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
527 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
533 w= render_string(st, str);
534 if(w<cshift ) return 0; /* String is shorter than shift */
536 in= temp_char_buf+cshift;
537 acc= temp_char_accum+cshift;
541 if(st->showsyntax && format) {
543 format = format+cshift;
547 for(a = 0; a < amount; a++) {
548 format_draw_color(format[a]);
550 BMF_DrawCharacter(spacetext_get_font(st), in[a]);
551 x = x+BMF_GetCharacterWidth(spacetext_get_font(st), in[a]);
555 BMF_DrawString(spacetext_get_font(st), in);
558 while (w-- && *acc++ < maxwidth) {
559 r+= spacetext_get_fontwidth(st);
563 if (cshift && r==0) return 0;
564 else if (st->showlinenrs)
565 return r+TXT_OFFSET+TEXTXLOC;
570 static void set_cursor_to_pos (SpaceText *st, int x, int y, int sel)
579 if(sel) { linep= &text->sell; charp= &text->selc; }
580 else { linep= &text->curl; charp= &text->curc; }
582 y= (curarea->winy - y)/st->lheight;
585 x-= TXT_OFFSET+TEXTXLOC;
590 x = (x/spacetext_get_fontwidth(st)) + st->left;
593 int i, j, endj, curs, max, chop, start, end, chars, loop;
596 /* Point to first visible line */
597 *linep= text->lines.first;
598 for (i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
600 max= get_wrap_width(st);
603 while (loop && *linep) {
610 for (i=0, j=0; loop; j++) {
612 /* Mimic replacement of tabs */
613 ch= (*linep)->line[j];
615 chars= st->tabnumber-i%st->tabnumber;
621 /* Gone too far, go back to last wrap point */
626 /* Exactly at the cursor, done */
627 } else if (y==0 && i-start==x) {
631 /* Prepare curs for next wrap */
632 } else if (i-end==x) {
641 if (y==0 && i-start>=x) {
646 } else if (ch==' ' || ch=='-' || ch=='\0') {
647 if (y==0 && i-start>=x) {
660 if (!loop || y<0) break;
662 if (!(*linep)->next) {
663 *charp= (*linep)->len;
667 /* On correct line but didn't meet cursor, must be at end */
669 *charp= (*linep)->len;
672 *linep= (*linep)->next;
677 y-= txt_get_span(text->lines.first, *linep) - st->top;
680 while (y-- != 0) if((*linep)->next) *linep= (*linep)->next;
682 while (y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
686 w= render_string(st, (*linep)->line);
687 if(x<w) *charp= temp_char_accum[x];
688 else *charp= (*linep)->len;
690 if(!sel) txt_pop_sel(text);
693 static int get_wrap_width(SpaceText *st)
697 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
698 max= (curarea->winx-x)/spacetext_get_fontwidth(st);
699 return max>8 ? max : 8;
702 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
703 static void wrap_offset(SpaceText *st, TextLine *linein, int cursin, int *offl, int *offc)
707 int i, j, start, end, chars, max, chop;
712 if (!st->text) return;
713 if (!st->wordwrap) return;
717 /* Move pointer to first visible line (top) */
718 linep= text->lines.first;
720 while (i>0 && linep) {
721 if (linep == linein) return; /* Line before top */
726 max= get_wrap_width(st);
734 for (i=0, j=0; linep->line[j]!='\0'; j++) {
736 /* Mimic replacement of tabs */
739 chars= st->tabnumber-i%st->tabnumber;
740 if (linep==linein && i<cursin) cursin += chars-1;
747 if (chop && linep==linein && i >= cursin)
754 } else if (ch==' ' || ch=='-') {
757 if (linep==linein && i >= cursin)
763 if (linep==linein) break;
768 static int get_char_pos(SpaceText *st, char *line, int cur)
772 for (i=0; i<cur && line[i]; i++) {
774 a += st->tabnumber-a%st->tabnumber;
781 static void draw_markers(SpaceText *st)
783 Text *text= st->text;
784 TextMarker *marker, *next;
785 TextLine *top, *bottom, *line;
786 int offl, offc, i, cy, x1, x2, y1, y2, x, y;
788 for (i=st->top, top= text->lines.first; top->next && i>0; i--) top= top->next;
789 for (i=st->viewlines-1, bottom=top; bottom->next && i>0; i--) bottom= bottom->next;
791 for (marker= text->markers.first; marker; marker= next) {
793 for (cy= 0, line= top; line; cy++, line= line->next) {
794 if (cy+st->top==marker->lineno) {
795 /* Remove broken markers */
796 if (marker->end>line->len || marker->start>marker->end) {
797 BLI_freelinkN(&text->markers, marker);
801 wrap_offset(st, line, marker->start, &offl, &offc);
802 x1= get_char_pos(st, line->line, marker->start) - st->left + offc;
804 wrap_offset(st, line, marker->end, &offl, &offc);
805 x2= get_char_pos(st, line->line, marker->end) - st->left + offc;
808 glColor3ub(marker->color[0], marker->color[1], marker->color[2]);
809 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
814 glBegin(GL_LINE_LOOP);
815 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
816 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
817 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
818 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
822 glBegin(GL_LINE_STRIP);
823 glVertex2i(curarea->winx, y);
824 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
825 glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
826 glVertex2i(curarea->winx, y-st->lheight);
829 for (i=y1+1; i<y2; i++) {
832 glVertex2i(curarea->winx, y);
833 glVertex2i(x, y-st->lheight);
834 glVertex2i(curarea->winx, y-st->lheight);
838 glBegin(GL_LINE_STRIP);
840 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
841 glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
842 glVertex2i(x, y-st->lheight);
848 if (line==bottom) break;
853 static void draw_cursor(SpaceText *st)
855 Text *text= st->text;
856 int vcurl, vcurc, vsell, vselc, hidden=0;
857 int offl, offc, x, y, w, i;
859 /* Draw the selection */
860 if (text->curl!=text->sell || text->curc!=text->selc) {
862 /* Convert all to view space character coordinates */
863 wrap_offset(st, text->curl, text->curc, &offl, &offc);
864 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
865 vcurc = get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
866 wrap_offset(st, text->sell, text->selc, &offl, &offc);
867 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
868 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
870 if (vcurc<0) vcurc=0;
871 if (vselc<0) vselc=0, hidden=1;
873 BIF_ThemeColor(TH_SHADE2);
874 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
878 y -= vcurl*st->lheight;
880 glRecti(x+vcurc*spacetext_get_fontwidth(st)-1, y, x+vselc*spacetext_get_fontwidth(st), y-st->lheight);
882 glRecti(x+vselc*spacetext_get_fontwidth(st)-1, y, x+vcurc*spacetext_get_fontwidth(st), y-st->lheight);
884 int froml, fromc, tol, toc;
886 froml= vcurl; tol= vsell;
887 fromc= vcurc; toc= vselc;
889 froml= vsell; tol= vcurl;
890 fromc= vselc; toc= vcurc;
892 y -= froml*st->lheight;
893 glRecti(x+fromc*spacetext_get_fontwidth(st)-1, y, curarea->winx, y-st->lheight); y-=st->lheight;
894 for (i=froml+1; i<tol; i++)
895 glRecti(x-4, y, curarea->winx, y-st->lheight), y-=st->lheight;
896 glRecti(x-4, y, x+toc*spacetext_get_fontwidth(st), y-st->lheight); y-=st->lheight;
899 wrap_offset(st, text->sell, text->selc, &offl, &offc);
900 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
901 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
902 if (vselc<0) vselc=0, hidden=1;
906 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
907 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
908 x += vselc*spacetext_get_fontwidth(st);
909 y= curarea->winy-2 - vsell*st->lheight;
912 char ch= text->sell->line[text->selc];
914 w= BMF_GetCharacterWidth(spacetext_get_font(st), ch);
915 BIF_ThemeColor(TH_HILITE);
916 glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
918 BIF_ThemeColor(TH_HILITE);
919 glRecti(x-1, y, x+1, y-st->lheight);
924 static void calc_text_rcts(SpaceText *st)
926 int lhlstart, lhlend, ltexth;
927 short barheight, barstart, hlstart, hlend, blank_lines;
928 short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
931 pix_bottom_margin = 4;
932 pix_available = curarea->winy - pix_top_margin - pix_bottom_margin;
933 ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
934 blank_lines = st->viewlines / 2;
936 /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
937 if (ltexth + blank_lines < st->top + st->viewlines) {
938 blank_lines = st->top + st->viewlines - ltexth;
941 ltexth += blank_lines;
943 barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
945 if (barheight < 20) {
946 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */
949 barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
952 st->txtbar.xmax = 17;
953 st->txtbar.ymax = curarea->winy - pix_top_margin - barstart;
954 st->txtbar.ymin = st->txtbar.ymax - barheight;
956 CLAMP(st->txtbar.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
957 CLAMP(st->txtbar.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
959 st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
960 if (st->pix_per_line<.1) st->pix_per_line=.1f;
962 lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl),
963 txt_get_span(st->text->lines.first, st->text->sell));
964 lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl),
965 txt_get_span(st->text->lines.first, st->text->sell));
968 hlstart = (lhlstart * pix_available)/ltexth;
969 hlend = (lhlend * pix_available)/ltexth;
971 /* the scrollbar is non-linear sized */
972 if (pix_bardiff > 0) {
973 /* the start of the highlight is in the current viewport */
974 if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) {
975 /* speed the progresion of the start of the highlight through the scrollbar */
976 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);
978 else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
979 /* push hl start down */
980 hlstart = barstart + barheight;
982 else if (lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
987 if (hlend <= hlstart) {
991 /* the end of the highlight is in the current viewport */
992 if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) {
993 /* speed the progresion of the end of the highlight through the scrollbar */
994 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);
996 else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
1000 else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
1002 hlend = barstart + barheight;
1005 if (hlend <= hlstart) {
1006 hlstart = hlend - 2;
1015 if (hlend - hlstart < 2) {
1016 hlend = hlstart + 2;
1019 st->txtscroll.xmin= 5;
1020 st->txtscroll.xmax= 17;
1021 st->txtscroll.ymax= curarea->winy - pix_top_margin - hlstart;
1022 st->txtscroll.ymin= curarea->winy - pix_top_margin - hlend;
1024 CLAMP(st->txtscroll.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
1025 CLAMP(st->txtscroll.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
1028 static void draw_textscroll(SpaceText *st)
1030 if (!st->text) return;
1034 BIF_ThemeColorShade(TH_SHADE1, -20);
1035 glRecti(2, 2, 20, curarea->winy-6);
1036 uiEmboss(2, 2, 20, curarea->winy-6, 1);
1038 BIF_ThemeColor(TH_SHADE1);
1039 glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);
1041 BIF_ThemeColor(TH_SHADE2);
1042 glRecti(st->txtscroll.xmin, st->txtscroll.ymin, st->txtscroll.xmax, st->txtscroll.ymax);
1044 uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
1047 /* Moves the view vertically by the specified number of lines */
1048 static void screen_skip(SpaceText *st, int lines)
1053 if (st->spacetype != SPACE_TEXT) return;
1054 if (!st->text) return;
1058 last= txt_get_span(st->text->lines.first, st->text->lines.last);
1059 last= last - (st->viewlines/2);
1061 if (st->top>last) st->top= last;
1062 if (st->top<0) st->top= 0;
1065 /* Moves the cursor vertically by the specified number of lines.
1066 If the destination line is shorter than the current cursor position, the
1067 cursor will be positioned at the end of this line.
1069 This is to replace screen_skip for PageUp/Down operations.
1071 static void cursor_skip(SpaceText *st, int lines, int sel)
1075 int oldl, oldc, *charp;
1078 if (st->spacetype != SPACE_TEXT) return;
1079 if (!st->text) return;
1083 if (sel) linep= &text->sell, charp= &text->selc;
1084 else linep= &text->curl, charp= &text->curc;
1085 oldl= txt_get_span(text->lines.first, *linep);
1088 while (lines>0 && (*linep)->next) {
1089 *linep= (*linep)->next;
1092 while (lines<0 && (*linep)->prev) {
1093 *linep= (*linep)->prev;
1097 if (*charp > (*linep)->len) *charp= (*linep)->len;
1099 if (!sel) txt_pop_sel(st->text);
1100 txt_undo_add_toop(st->text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
1103 /* Handles text scrolling via grabbing the view (MMB, mode 1) or with the
1106 static void do_textscroll(SpaceText *st, int mode)
1108 short delta[2]= {0, 0};
1109 short mval[2], hold[2], old[2];
1111 if (!st->text) return;
1115 st->flags|= ST_SCROLL_SELECT;
1117 scrarea_do_windraw(curarea);
1118 screen_swapbuffers();
1120 getmouseco_areawin(mval);
1121 old[0]= hold[0]= mval[0];
1122 old[1]= hold[1]= mval[1];
1124 while(get_mbut()&(L_MOUSE|M_MOUSE)) {
1125 getmouseco_areawin(mval);
1127 if(old[0]!=mval[0] || old[1]!=mval[1]) {
1129 delta[0]= (hold[0]-mval[0])/spacetext_get_fontwidth(st);
1130 delta[1]= (mval[1]-hold[1])/st->lheight;
1132 else delta[1]= (hold[1]-mval[1])*st->pix_per_line;
1134 if (delta[0] || delta[1]) {
1135 screen_skip(st, delta[1]);
1139 st->left+= delta[0];
1140 if (st->left<0) st->left= 0;
1142 scrarea_do_windraw(curarea);
1143 screen_swapbuffers();
1151 BIF_wait_for_statechange();
1154 st->flags^= ST_SCROLL_SELECT;
1156 scrarea_do_windraw(curarea);
1157 screen_swapbuffers();
1160 static void do_selection(SpaceText *st, int selecting)
1165 short mval[2], old[2];
1167 getmouseco_areawin(mval);
1172 int curl= txt_get_span(st->text->lines.first, st->text->curl);
1173 int curc= st->text->curc;
1176 set_cursor_to_pos(st, mval[0], mval[1], 0);
1178 linep2= txt_get_span(st->text->lines.first, st->text->curl);
1179 charp2= st->text->selc;
1181 if (curl!=linep2 || curc!=charp2)
1182 txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
1185 sell= txt_get_span(st->text->lines.first, st->text->sell);
1186 selc= st->text->selc;
1188 while(get_mbut()&L_MOUSE) {
1189 getmouseco_areawin(mval);
1191 if (mval[1]<0 || mval[1]>curarea->winy) {
1192 int d= (old[1]-mval[1])*st->pix_per_line;
1193 if (d) screen_skip(st, d);
1195 set_cursor_to_pos(st, mval[0], mval[1]<0?0:curarea->winy, 1);
1197 scrarea_do_windraw(curarea);
1198 screen_swapbuffers();
1200 else if (!st->wordwrap && (mval[0]<0 || mval[0]>curarea->winx)) {
1201 if (mval[0]>curarea->winx) st->left++;
1202 else if (mval[0]<0 && st->left>0) st->left--;
1204 set_cursor_to_pos(st, mval[0], mval[1], 1);
1206 scrarea_do_windraw(curarea);
1207 screen_swapbuffers();
1211 else if (first || old[0]!=mval[0] || old[1]!=mval[1]) {
1212 set_cursor_to_pos(st, mval[0], mval[1], 1);
1214 scrarea_do_windraw(curarea);
1215 screen_swapbuffers();
1222 BIF_wait_for_statechange();
1226 linep2= txt_get_span(st->text->lines.first, st->text->sell);
1227 charp2= st->text->selc;
1229 if (sell!=linep2 || selc!=charp2)
1230 txt_undo_add_toop(st->text, UNDO_STO, sell, selc, linep2, charp2);
1235 static int do_suggest_select(SpaceText *st)
1237 SuggItem *item, *first, *last, *sel;
1239 int l, x, y, w, h, i;
1243 if (!st || !st->text) return 0;
1244 if (!texttool_text_is_active(st->text)) return 0;
1246 first = texttool_suggest_first();
1247 last = texttool_suggest_last();
1248 sel = texttool_suggest_selected();
1249 top = texttool_suggest_top();
1251 if (!last || !first)
1254 /* Count the visible lines to the cursor */
1255 for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1258 if(st->showlinenrs) {
1259 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1261 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1263 y = curarea->winy - st->lheight*l - 2;
1265 w = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1266 h = SUGG_LIST_SIZE*st->lheight + 8;
1268 getmouseco_areawin(mval);
1270 if (mval[0]<x || x+w<mval[0] || mval[1]<y-h || y<mval[1])
1273 /* Work out which of the items is at the top of the visible list */
1274 for (i=0, item=first; i<*top && item->next; i++, item=item->next);
1276 /* Work out the target item index in the visible list */
1277 tgti = (y-mval[1]-4) / st->lheight;
1278 if (tgti<0 || tgti>SUGG_LIST_SIZE)
1281 for (i=tgti; i>0 && item->next; i--, item=item->next);
1283 texttool_suggest_select(item);
1287 static void pop_suggest_list()
1289 SuggItem *item, *sel;
1292 item= texttool_suggest_first();
1293 sel= texttool_suggest_selected();
1294 top= texttool_suggest_top();
1297 while (item && item != sel) {
1301 if (i > *top+SUGG_LIST_SIZE-1)
1302 *top= i-SUGG_LIST_SIZE+1;
1307 void draw_documentation(SpaceText *st)
1310 char *docs, buf[DOC_WIDTH+1], *p;
1311 int len, i, br, lines;
1312 int boxw, boxh, l, x, y, top;
1314 if (!st || !st->text) return;
1315 if (!texttool_text_is_active(st->text)) return;
1317 docs = texttool_docs_get();
1321 /* Count the visible lines to the cursor */
1322 for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1325 if(st->showlinenrs) {
1326 x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1328 x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1330 if (texttool_suggest_first()) {
1331 x += SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 50;
1334 top= y= curarea->winy - st->lheight*l - 2;
1336 boxw= DOC_WIDTH*spacetext_get_fontwidth(st) + 20;
1337 boxh= (DOC_HEIGHT+1)*st->lheight;
1340 BIF_ThemeColor(TH_BACK);
1341 glRecti(x, y, x+boxw, y-boxh);
1342 BIF_ThemeColor(TH_SHADE1);
1343 glBegin(GL_LINE_LOOP);
1345 glVertex2i(x+boxw, y);
1346 glVertex2i(x+boxw, y-boxh);
1347 glVertex2i(x, y-boxh);
1349 glBegin(GL_LINE_LOOP);
1350 glVertex2i(x+boxw-10, y-7);
1351 glVertex2i(x+boxw-4, y-7);
1352 glVertex2i(x+boxw-7, y-2);
1354 glBegin(GL_LINE_LOOP);
1355 glVertex2i(x+boxw-10, y-boxh+7);
1356 glVertex2i(x+boxw-4, y-boxh+7);
1357 glVertex2i(x+boxw-7, y-boxh+2);
1359 BIF_ThemeColor(TH_TEXT);
1361 i= 0; br= DOC_WIDTH; lines= -doc_scroll;
1362 for (p=docs; *p; p++) {
1363 if (*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
1364 if (*p == ' ' || *p == '\t')
1366 else if (*p == '\n') {
1370 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1372 i= 0; br= DOC_WIDTH; lines++;
1375 if (i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
1379 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1381 p -= i-br-1; /* Rewind pointer to last break */
1382 i= 0; br= DOC_WIDTH; lines++;
1384 if (lines >= DOC_HEIGHT) break;
1386 if (doc_scroll > 0 && lines < DOC_HEIGHT) {
1388 draw_documentation(st);
1392 void draw_suggestion_list(SpaceText *st)
1394 SuggItem *item, *first, *last, *sel;
1396 char str[SUGG_LIST_WIDTH+1];
1397 int w, boxw=0, boxh, i, l, x, y, b, *top;
1399 if (!st || !st->text) return;
1400 if (!texttool_text_is_active(st->text)) return;
1402 first = texttool_suggest_first();
1403 last = texttool_suggest_last();
1405 if (!first || !last) return;
1408 sel = texttool_suggest_selected();
1409 top = texttool_suggest_top();
1411 /* Count the visible lines to the cursor */
1412 for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1415 if(st->showlinenrs) {
1416 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1418 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1420 y = curarea->winy - st->lheight*l - 2;
1422 boxw = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1423 boxh = SUGG_LIST_SIZE*st->lheight + 8;
1425 BIF_ThemeColor(TH_SHADE1);
1426 glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1427 BIF_ThemeColor(TH_BACK);
1428 glRecti(x, y, x+boxw, y-boxh);
1430 /* Set the top 'item' of the visible list */
1431 for (i=0, item=first; i<*top && item->next; i++, item=item->next);
1433 for (i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1437 strncpy(str, item->name, SUGG_LIST_WIDTH);
1438 str[SUGG_LIST_WIDTH] = '\0';
1440 w = BMF_GetStringWidth(spacetext_get_font(st), str);
1443 BIF_ThemeColor(TH_SHADE2);
1444 glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1446 b=1; /* b=1 colour block, text is default. b=0 no block, colour text */
1447 switch (item->type) {
1448 case 'k': BIF_ThemeColor(TH_SYNTAX_B); b=0; break;
1449 case 'm': BIF_ThemeColor(TH_TEXT); break;
1450 case 'f': BIF_ThemeColor(TH_SYNTAX_L); break;
1451 case 'v': BIF_ThemeColor(TH_SYNTAX_N); break;
1452 case '?': BIF_ThemeColor(TH_TEXT); b=0; break;
1455 glRecti(x+8, y+2, x+11, y+5);
1456 BIF_ThemeColor(TH_TEXT);
1458 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1460 if (item == last) break;
1464 static short check_blockhandler(SpaceText *st, short handler)
1468 for(a=0; a<SPACE_MAXHANDLER; a+=2)
1469 if (st->blockhandler[a]==handler) return 1;
1473 /* Find and replace GUI panel */
1474 static void text_panel_find(short cntrl) // TEXT_HANDLER_FIND
1478 /* Ensure that find and replace buffers have been allocated */
1479 if (!g_find_str || !g_replace_str) {
1480 g_find_str= MEM_mallocN(TXT_MAXFINDSTR+1, "find_string");
1481 g_replace_str= MEM_mallocN(TXT_MAXFINDSTR+1, "replace_string");
1482 g_find_str[0]= g_replace_str[0]= '\0';
1485 block= uiNewBlock(&curarea->uiblocks, "text_panel_find", UI_EMBOSS, UI_HELV, curarea->win);
1486 uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
1487 uiSetPanelHandler(TEXT_HANDLER_FIND); // for close and esc
1488 if(uiNewPanel(curarea, block, "Find & Replace", "Text", curarea->winx-230, curarea->winy-130, 260, 120)==0) return;
1490 uiBlockBeginAlign(block);
1491 uiDefButC(block, TEX, 0, "Find: ", 0,80,220,20, g_find_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
1492 uiDefIconBut(block, BUT, B_PASTEFIND, ICON_TEXT, 220,80,20,20, NULL, 0,0,0,0, "Copy from selection");
1493 uiDefButC(block, TEX, 0, "Replace: ", 0,60,220,20, g_replace_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
1494 uiDefIconBut(block, BUT, B_PASTEREPLACE, ICON_TEXT, 220,60,20,20, NULL, 0,0,0,0, "Copy from selection");
1495 uiBlockEndAlign(block);
1496 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");
1497 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");
1498 uiDefBut(block, BUT, B_TEXTFIND, "Find", 0,0,50,20, NULL, 0,0,0,0, "Find next");
1499 uiDefBut(block, BUT, B_TEXTREPLACE, "Replace/Find", 50,0,110,20, NULL, 0,0,0,0, "Replace then find next");
1500 uiDefBut(block, BUT, B_TEXTMARKALL, "Mark All", 160,0,80,20, NULL, 0,0,0,0, "Mark each occurrence to edit all from one");
1503 /* mode: 0 find only, 1 replace/find, 2 mark all occurrences */
1504 void find_and_replace(SpaceText *st, short mode)
1506 Text *start= NULL, *text= st->text;
1507 int flags, first= 1;
1510 if (!check_blockhandler(st, TEXT_HANDLER_FIND)) {
1511 toggle_blockhandler(st->area, TEXT_HANDLER_FIND, UI_PNL_TO_MOUSE);
1515 if (!g_find_str || !g_replace_str) return;
1516 if (g_find_str[0] == '\0') return;
1517 flags= g_find_flags;
1518 if (flags & TXT_FIND_ALLTEXTS) flags ^= TXT_FIND_WRAP;
1522 txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
1525 /* Replace current */
1526 if (mode && txt_has_sel(text)) {
1527 tmp= txt_sel_to_buf(text);
1528 if (strcmp(g_find_str, tmp)==0) {
1530 txt_insert_buf(text, g_replace_str);
1531 if (st->showsyntax) txt_format_line(st, text->curl, 1);
1532 } else if (mode==2) {
1534 BIF_GetThemeColor4ubv(TH_SHADE2, color);
1535 if (txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
1536 if (tmp) MEM_freeN(tmp), tmp=NULL;
1539 txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
1547 if (txt_find_string(text, g_find_str, flags & TXT_FIND_WRAP)) {
1549 } else if (flags & TXT_FIND_ALLTEXTS) {
1550 if (text==start) break;
1551 if (!start) start= text;
1553 text= st->text= text->id.next;
1555 text= st->text= G.main->text.first;
1556 txt_move_toline(text, 0, 0);
1560 okee("Text not found: %s", g_find_str);
1566 static void do_find_buttons(val)
1572 st= curarea->spacedata.first;
1573 if (!st || st->spacetype != SPACE_TEXT) return;
1579 if (!g_find_str) break;
1580 tmp= txt_sel_to_buf(text);
1581 strncpy(g_find_str, tmp, TXT_MAXFINDSTR);
1584 case B_PASTEREPLACE:
1585 if (!g_replace_str) break;
1586 tmp= txt_sel_to_buf(text);
1587 strncpy(g_replace_str, tmp, TXT_MAXFINDSTR);
1591 find_and_replace(st, 0);
1594 find_and_replace(st, 1);
1597 find_and_replace(st, 2);
1602 static void text_blockhandlers(ScrArea *sa)
1604 SpaceText *st= sa->spacedata.first;
1607 /* warning; blocks need to be freed each time, handlers dont remove */
1608 uiFreeBlocksWin(&sa->uiblocks, sa->win);
1610 for(a=0; a<SPACE_MAXHANDLER; a+=2) {
1611 /* clear action value for event */
1612 switch(st->blockhandler[a]) {
1613 case TEXT_HANDLER_FIND:
1614 text_panel_find(st->blockhandler[a+1]);
1618 uiDrawBlocksPanels(sa, 0);
1621 void drawtextspace(ScrArea *sa, void *spacedata)
1623 SpaceText *st= curarea->spacedata.first;
1631 if (st==NULL || st->spacetype != SPACE_TEXT) return;
1633 bwin_clear_viewmat(sa->win); /* clear buttons view */
1636 BIF_GetThemeColor3fv(TH_BACK, col);
1637 glClearColor(col[0], col[1], col[2], 0.0);
1638 glClear(GL_COLOR_BUFFER_BIT);
1639 myortho2(-0.375, (float)(sa->winx)-0.375, -0.375, (float)(sa->winy)-0.375);
1641 draw_area_emboss(sa);
1646 /* Make sure all the positional pointers exist */
1647 if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1648 txt_clean_text(text);
1650 if(st->lheight) st->viewlines= (int) curarea->winy/st->lheight;
1651 else st->viewlines= 0;
1653 if(st->showlinenrs) {
1654 BIF_ThemeColor(TH_GRID);
1655 glRecti(23, 0, (st->lheight==15)?63:59, curarea->winy - 2);
1660 tmp= text->lines.first;
1661 for (i= 0; i<st->top && tmp; i++) {
1662 if (st->showsyntax && !tmp->format) txt_format_line(st, tmp, 0);
1667 y= curarea->winy-st->lheight;
1668 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1670 BIF_ThemeColor(TH_TEXT);
1671 for (i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1672 if (st->showsyntax && !tmp->format) {
1673 txt_format_line(st, tmp, 0);
1675 if(st->showlinenrs) {
1676 /*Change the color of the current line the cursor is on*/
1677 if(tmp == text->curl) {
1678 BIF_ThemeColor(TH_HILITE);
1680 BIF_ThemeColor(TH_TEXT);
1682 if(((float)(i + linecount + 1)/10000.0) < 1.0) {
1683 sprintf(linenr, "%4d", i + linecount + 1);
1684 glRasterPos2i(TXT_OFFSET - 7, y);
1686 sprintf(linenr, "%5d", i + linecount + 1);
1687 glRasterPos2i(TXT_OFFSET - 11, y);
1689 BIF_ThemeColor(TH_TEXT);
1690 BMF_DrawString(spacetext_get_font(st), linenr);
1693 int lines = text_draw_wrapped(st, tmp->line, x, y, curarea->winx-x, tmp->format);
1694 y -= lines*st->lheight;
1696 text_draw(st, tmp->line, st->left, 0, 1, x, y, tmp->format);
1704 draw_textscroll(st);
1705 draw_documentation(st);
1706 draw_suggestion_list(st);
1708 bwin_scalematrix(sa->win, st->blockscale, st->blockscale, st->blockscale);
1709 text_blockhandlers(sa);
1711 curarea->win_swap= WIN_BACK_OK;
1714 /* Moves the view to the cursor location,
1715 also used to make sure the view isnt outside the file */
1716 void pop_space_text (SpaceText *st)
1721 if(!st->text) return;
1722 if(!st->text->curl) return;
1724 i= txt_get_span(st->text->lines.first, st->text->sell);
1725 if (st->top+st->viewlines <= i || st->top > i) {
1726 st->top= i - st->viewlines/2;
1732 x= text_draw(st, st->text->sell->line, st->left, st->text->selc, 0, 0, 0, NULL);
1734 if (x==0 || x>curarea->winx) {
1735 st->left= st->text->curc-0.5*(curarea->winx)/spacetext_get_fontwidth(st);
1739 if (st->top < 0) st->top= 0;
1740 if (st->left <0) st->left= 0;
1743 void add_text_fs(char *file) /* bad but cant pass an as arg here */
1745 SpaceText *st= curarea->spacedata.first;
1748 if (st==NULL || st->spacetype != SPACE_TEXT) return;
1750 text= add_text(file);
1756 if (st->showsyntax) txt_format_text(st);
1757 allqueue(REDRAWTEXT, 0);
1758 allqueue(REDRAWHEADERS, 0);
1761 void free_textspace(SpaceText *st)
1768 static void save_mem_text(char *str)
1770 SpaceText *st= curarea->spacedata.first;
1776 if (st->spacetype != SPACE_TEXT) return;
1781 if (text->name) MEM_freeN(text->name);
1782 text->name= MEM_mallocN(strlen(str)+1, "textname");
1783 strcpy(text->name, str);
1785 text->flags ^= TXT_ISMEM;
1787 txt_write_file(text);
1790 void txt_write_file(Text *text)
1796 char file[FILE_MAXDIR+FILE_MAXFILE];
1798 /* Do we need to get a filename? */
1799 if (text->flags & TXT_ISMEM) {
1801 activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->name, save_mem_text);
1803 activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->id.name+2, save_mem_text);
1807 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
1808 BLI_convertstringcode(file, G.sce);
1810 /* Should we ask to save over? */
1811 if (text->flags & TXT_ISTMP) {
1812 if (BLI_exists(file)) {
1813 if (!okee("Save over")) return;
1814 } else if (!okee("Create new file")) return;
1816 text->flags ^= TXT_ISTMP;
1819 fp= fopen(file, "w");
1821 error("Unable to save file");
1825 tmp= text->lines.first;
1827 if (tmp->next) fprintf(fp, "%s\n", tmp->line);
1828 else fprintf(fp, "%s", tmp->line);
1835 res= stat(file, &st);
1836 text->mtime= st.st_mtime;
1838 if (text->flags & TXT_ISDIRTY) text->flags ^= TXT_ISDIRTY;
1841 void unlink_text(Text *text)
1847 #ifndef DISABLE_PYTHON
1848 /* check if this text was used as script link:
1849 * this check function unsets the pointers and returns how many
1850 * script links used this Text */
1851 if (BPY_check_all_scriptlinks (text)) {
1852 allqueue(REDRAWBUTSSCRIPT, 0);
1854 /* equivalently for pynodes: */
1855 if (nodeDynamicUnlinkText ((ID*)text)) {
1856 allqueue(REDRAWNODE, 0);
1860 for (scr= G.main->screen.first; scr; scr= scr->id.next) {
1861 for (area= scr->areabase.first; area; area= area->next) {
1862 for (sl= area->spacedata.first; sl; sl= sl->next) {
1863 if (sl->spacetype==SPACE_TEXT) {
1864 SpaceText *st= (SpaceText*) sl;
1866 if (st->text==text) {
1870 if (st==area->spacedata.first) {
1871 scrarea_queue_redraw(area);
1880 int jumptoline_interactive(SpaceText *st)
1882 short nlines= txt_get_span(st->text->lines.first, st->text->lines.last)+1;
1883 short tmp= txt_get_span(st->text->lines.first, st->text->curl)+1;
1885 if (button(&tmp, 1, nlines, "Jump to line:")) {
1886 txt_move_toline(st->text, tmp-1, 0);
1896 static char *copybuffer = NULL;
1898 static void txt_copy_selectbuffer (Text *text)
1901 TextLine *tmp, *linef, *linel;
1905 if (!text->curl) return;
1906 if (!text->sell) return;
1908 if (!txt_has_sel(text)) return;
1911 MEM_freeN(copybuffer);
1915 if (text->curl==text->sell) {
1916 linef= linel= text->curl;
1918 if (text->curc < text->selc) {
1925 } else if (txt_get_span(text->curl, text->sell)<0) {
1939 if (linef == linel) {
1940 length= charl-charf;
1942 copybuffer= MEM_mallocN(length+1, "cut buffera");
1944 BLI_strncpy(copybuffer, linef->line + charf, length+1);
1946 length+= linef->len - charf;
1948 length++; /* For the '\n' */
1951 while (tmp && tmp!= linel) {
1952 length+= tmp->len+1;
1956 copybuffer= MEM_mallocN(length+1, "cut bufferb");
1958 strncpy(copybuffer, linef->line+ charf, linef->len-charf);
1959 length= linef->len-charf;
1961 copybuffer[length++]='\n';
1964 while (tmp && tmp!=linel) {
1965 strncpy(copybuffer+length, tmp->line, tmp->len);
1968 copybuffer[length++]='\n';
1972 strncpy(copybuffer+length, linel->line, charl);
1975 copybuffer[length]=0;
1978 bufferlength = length;
1981 static char *unixNewLine(char *buffer)
1983 char *p, *p2, *output;
1985 /* we can afford the few extra bytes */
1986 output= MEM_callocN(strlen(buffer)+1, "unixnewline");
1987 for (p= buffer, p2= output; *p; p++)
1988 if (*p != '\r') *(p2++)= *p;
1994 static char *winNewLine(char *buffer)
1996 char *p, *p2, *output;
1999 for (p= buffer; *p; p++)
2000 if (*p == '\n') add++;
2002 bufferlength= p-buffer+add+1;
2003 output= MEM_callocN(bufferlength, "winnewline");
2004 for (p= buffer, p2= output; *p; p++, p2++) {
2006 *(p2++)= '\r'; *p2= '\n';
2014 void txt_paste_clipboard(Text *text)
2019 buff = (char*)getClipboard(0);
2021 temp_buff = unixNewLine(buff);
2023 txt_insert_buf(text, temp_buff);
2024 if(buff){free((void*)buff);}
2025 if(temp_buff){MEM_freeN(temp_buff);}
2029 void get_selection_buffer(Text *text)
2031 char *buff = getClipboard(1);
2033 txt_insert_buf(text, buff);
2036 void txt_copy_clipboard(Text *text)
2040 txt_copy_selectbuffer(text);
2043 copybuffer[bufferlength] = '\0';
2044 temp = winNewLine(copybuffer);
2046 putClipboard(temp, 0);
2048 MEM_freeN(copybuffer);
2053 void run_python_script(SpaceText *st)
2055 Text *text=st->text;
2057 #ifdef DISABLE_PYTHON
2058 error("python disabled in this build");
2060 if (!BPY_txt_do_python_Text(text)) {
2061 int lineno = BPY_Err_getLinenumber();
2062 // jump to error if happened in current text:
2063 py_filename = (char*) BPY_Err_getFilename();
2065 /* st->text can become NULL: user called Blender.Load(blendfile)
2066 * before the end of the script. */
2067 if (!st->text) return;
2069 if (!strcmp(py_filename, st->text->id.name+2)) {
2072 txt_move_toline(text, lineno-1, 0);
2077 error("Error in other (possibly external) file, "\
2084 static void set_tabs(Text *text)
2086 SpaceText *st = curarea->spacedata.first;
2088 st->currtab_set = setcurr_tab(text);
2091 static void wrap_move_bol(SpaceText *st, short sel)
2093 Text *text= st->text;
2094 int offl, offc, lin;
2096 lin= txt_get_span(text->lines.first, text->sell);
2097 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2100 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
2103 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
2109 static void wrap_move_eol(SpaceText *st, short sel)
2111 Text *text= st->text;
2112 int offl, offc, lin, startl, c;
2114 lin= txt_get_span(text->lines.first, text->sell);
2115 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2118 while (offl==startl && text->sell->line[c]!='\0') {
2120 wrap_offset(st, text->sell, c, &offl, &offc);
2121 } if (offl!=startl) c--;
2124 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
2127 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
2133 static void wrap_move_up(SpaceText *st, short sel)
2135 Text *text= st->text;
2136 int offl, offl_1, offc, fromline, toline, c, target;
2138 wrap_offset(st, text->sell, 0, &offl_1, &offc);
2139 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2140 fromline= toline= txt_get_span(text->lines.first, text->sell);
2141 target= text->selc + offc;
2144 if (!text->sell->prev) {
2145 txt_move_bol(text, sel);
2149 c= text->sell->prev->len; /* End of prev. line */
2150 wrap_offset(st, text->sell->prev, c, &offl, &offc);
2153 c= -offc-1; /* End of prev. line */
2154 wrap_offset(st, text->sell, c, &offl, &offc);
2160 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
2161 if (toline<fromline) text->sell= text->sell->prev;
2163 if (c>text->sell->len) c= text->sell->len;
2167 else if(text->curl) {
2168 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
2169 if (toline<fromline) text->curl= text->curl->prev;
2171 if (c>text->curl->len) c= text->curl->len;
2178 static void wrap_move_down(SpaceText *st, short sel)
2180 Text *text= st->text;
2181 int offl, startoff, offc, fromline, toline, c, target;
2183 wrap_offset(st, text->sell, text->selc, &offl, &offc);
2184 fromline= toline= txt_get_span(text->lines.first, text->sell);
2185 target= text->selc + offc;
2188 while (offl==startoff && text->sell->line[c]!='\0') {
2190 wrap_offset(st, text->sell, c, &offl, &offc);
2193 if (text->sell->line[c]=='\0') {
2194 if (!text->sell->next) {
2195 txt_move_eol(text, sel);
2202 if (c > text->sell->len) c= text->sell->len;
2207 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
2208 if (toline>fromline) text->sell= text->sell->next;
2210 if (c>text->sell->len) c= text->sell->len;
2214 else if(text->curl) {
2215 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
2216 if (toline>fromline) text->curl= text->curl->next;
2218 if (c > text->curl->len) c= text->curl->len;
2225 static void get_suggest_prefix(Text *text, int offset)
2228 char *line, tmp[256];
2231 if (!texttool_text_is_active(text)) return;
2233 line= text->curl->line;
2234 for (i=text->curc-1+offset; i>=0; i--)
2235 if (!check_identifier(line[i]))
2238 len= text->curc-i+offset;
2240 printf("Suggestion prefix too long\n");
2243 strncpy(tmp, line+i, len);
2245 texttool_suggest_prefix(tmp);
2248 static void confirm_suggestion(Text *text, int skipleft)
2255 if (!texttool_text_is_active(text)) return;
2257 sel = texttool_suggest_selected();
2260 line= text->curl->line;
2261 i=text->curc-skipleft-1;
2263 if (!check_identifier(line[i]))
2269 for (i=0; i<skipleft; i++)
2270 txt_move_left(text, 0);
2271 for (i=0; i<over; i++)
2272 txt_move_left(text, 1);
2274 txt_insert_buf(text, sel->name);
2276 for (i=0; i<skipleft; i++)
2277 txt_move_right(text, 0);
2279 texttool_text_clear();
2282 static short do_texttools(SpaceText *st, char ascii, unsigned short evnt, short val)
2284 int draw=0, tools=0, swallow=0, scroll=1;
2285 if (!texttool_text_is_active(st->text)) return 0;
2286 if (!st->text || st->text->id.lib) return 0;
2288 if (st->doplugins && texttool_text_is_active(st->text)) {
2289 if (texttool_suggest_first()) tools |= TOOL_SUGG_LIST;
2290 if (texttool_docs_get()) tools |= TOOL_DOCUMENT;
2294 if (tools & TOOL_SUGG_LIST) {
2295 if ((ascii != '_' && ascii != '*' && ispunct(ascii)) || check_whitespace(ascii)) {
2296 confirm_suggestion(st->text, 0);
2297 if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2298 } else if ((st->overwrite && txt_replace_char(st->text, ascii)) || txt_add_char(st->text, ascii)) {
2299 get_suggest_prefix(st->text, 0);
2305 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2307 } else if (val==1 && evnt) {
2310 if (do_suggest_select(st))
2313 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2314 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2319 if (do_suggest_select(st)) {
2320 confirm_suggestion(st->text, 0);
2321 if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2324 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2325 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2331 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2332 else if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2333 else draw= swallow= 0;
2336 if (tools & TOOL_SUGG_LIST) {
2337 confirm_suggestion(st->text, 0);
2338 if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2342 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2346 if (tools & TOOL_SUGG_LIST) {
2348 texttool_suggest_clear();
2350 /* Work out which char we are about to delete/pass */
2351 if (st->text->curl && st->text->curc > 0) {
2352 char ch= st->text->curl->line[st->text->curc-1];
2353 if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
2354 get_suggest_prefix(st->text, -1);
2358 texttool_suggest_clear();
2360 texttool_suggest_clear();
2363 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2366 if (tools & TOOL_SUGG_LIST) {
2368 texttool_suggest_clear();
2370 /* Work out which char we are about to pass */
2371 if (st->text->curl && st->text->curc < st->text->curl->len) {
2372 char ch= st->text->curl->line[st->text->curc+1];
2373 if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
2374 get_suggest_prefix(st->text, 1);
2378 texttool_suggest_clear();
2380 texttool_suggest_clear();
2383 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2386 scroll= SUGG_LIST_SIZE-1;
2387 case WHEELDOWNMOUSE:
2389 if (tools & TOOL_DOCUMENT) {
2394 } else if (tools & TOOL_SUGG_LIST) {
2395 SuggItem *sel = texttool_suggest_selected();
2397 texttool_suggest_select(texttool_suggest_first());
2398 } else while (sel && sel!=texttool_suggest_last() && sel->next && scroll--) {
2399 texttool_suggest_select(sel->next);
2408 scroll= SUGG_LIST_SIZE-1;
2411 if (tools & TOOL_DOCUMENT) {
2412 if (doc_scroll>0) doc_scroll--;
2416 } else if (tools & TOOL_SUGG_LIST) {
2417 SuggItem *sel = texttool_suggest_selected();
2418 while (sel && sel!=texttool_suggest_first() && sel->prev && scroll--) {
2419 texttool_suggest_select(sel->prev);
2431 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear(), draw= 1;
2432 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2442 static short do_markers(SpaceText *st, char ascii, unsigned short evnt, short val)
2445 TextMarker *marker, *mrk, *nxt;
2446 int c, s, draw=0, swallow=0;
2449 if (!text || text->id.lib || text->curl != text->sell) return 0;
2451 marker= txt_find_marker(text, text->sell, text->selc, 0, 0);
2452 if (marker && (marker->start > text->curc || marker->end < text->curc))
2456 /* Find the next temporary marker */
2458 int lineno= txt_get_span(text->lines.first, text->curl);
2459 TextMarker *mrk= text->markers.first;
2461 if (!marker && (mrk->flags & TMARK_TEMP)) marker= mrk;
2462 if ((mrk->flags & TMARK_TEMP) && (mrk->lineno > lineno || (mrk->lineno==lineno && mrk->end > text->curc))) {
2469 txt_move_to(text, marker->lineno, marker->start, 0);
2470 txt_move_to(text, marker->lineno, marker->end, 1);
2472 evnt= ascii= val= 0;
2476 } else if (evnt==ESCKEY) {
2477 if (txt_clear_markers(text, 0, TMARK_TEMP)) swallow= 1;
2478 else if (txt_clear_markers(text, 0, 0)) swallow= 1;
2480 evnt= ascii= val= 0;
2483 if (!swallow) return 0;
2487 if (marker->flags & TMARK_EDITALL) {
2488 c= text->curc-marker->start;
2489 s= text->selc-marker->start;
2490 if (s<0 || s>marker->end-marker->start) return 0;
2492 mrk= txt_next_marker(text, marker);
2494 nxt=txt_next_marker(text, mrk); /* mrk may become invalid */
2495 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2496 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2497 if (st->overwrite) {
2498 if (txt_replace_char(text, ascii))
2499 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2501 if (txt_add_char(text, ascii)) {
2502 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2506 if (mrk==marker || mrk==nxt) break;
2515 if (marker->flags & TMARK_EDITALL) {
2516 c= text->curc-marker->start;
2517 s= text->selc-marker->start;
2518 if (s<0 || s>marker->end-marker->start) return 0;
2520 mrk= txt_next_marker(text, marker);
2522 nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
2523 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2524 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2525 txt_backspace_char(text);
2526 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2527 if (mrk==marker || mrk==nxt) break;
2535 if (marker->flags & TMARK_EDITALL) {
2536 c= text->curc-marker->start;
2537 s= text->selc-marker->start;
2538 if (s<0 || s>marker->end-marker->start) return 0;
2540 mrk= txt_next_marker(text, marker);
2542 nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
2543 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2544 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2545 txt_delete_char(text);
2546 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2547 if (mrk==marker || mrk==nxt) break;
2555 if (G.qual & LR_SHIFTKEY) {
2557 if (!nxt) nxt= text->markers.last;
2560 if (!nxt) nxt= text->markers.first;
2562 if (marker->flags & TMARK_TEMP) {
2563 if (nxt==marker) nxt= NULL;
2564 BLI_freelinkN(&text->markers, marker);
2568 txt_move_to(text, mrk->lineno, mrk->start, 0);
2569 txt_move_to(text, mrk->lineno, mrk->end, 1);
2576 /* Events that should clear markers */
2577 case UKEY: if (!(G.qual & LR_ALTKEY)) break;
2578 case ZKEY: if (evnt==ZKEY && !(G.qual & LR_CTRLKEY)) break;
2581 if (marker->flags & (TMARK_EDITALL | TMARK_TEMP))
2582 txt_clear_markers(text, marker->group, 0);
2584 BLI_freelinkN(&text->markers, marker);
2588 case RIGHTMOUSE: /* Marker context menu? */
2591 case FKEY: /* Allow find */
2592 if (G.qual & LR_SHIFTKEY) swallow= 1;
2596 if (G.qual!=0 && G.qual!=LR_SHIFTKEY)
2597 swallow= 1; /* Swallow all other shortcut events */
2608 void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2610 SpaceText *st= curarea->spacedata.first;
2613 unsigned short event= evt->event;
2614 short val= evt->val;
2615 char ascii= evt->ascii;
2617 if (st==NULL || st->spacetype != SPACE_TEXT) return;
2619 /* smartass code to prevent the CTRL/ALT events below from not working! */
2620 if(G.qual & (LR_ALTKEY|LR_CTRLKEY))
2627 if (event==RIGHTMOUSE) {
2628 switch (pupmenu("File %t|New %x0|Open... %x1")) {
2630 st->text= add_empty_text("Text");
2633 allqueue(REDRAWTEXT, 0);
2634 allqueue(REDRAWHEADERS, 0);
2637 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2641 if (val && !ELEM(G.qual, 0, LR_SHIFTKEY)) {
2642 if (event==FKEY && G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2643 switch (pupmenu("File %t|New %x0|Open... %x1")) {
2645 st->text= add_empty_text("Text");
2648 allqueue(REDRAWTEXT, 0);
2649 allqueue(REDRAWHEADERS, 0);
2652 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2656 else if (event==QKEY) {
2657 if (G.qual & LR_CTRLKEY) {
2658 if(okee("Quit Blender")) exit_usiblender();
2661 else if (event==NKEY) {
2662 if (G.qual & LR_ALTKEY) {
2663 st->text= add_empty_text("Text");
2666 allqueue(REDRAWTEXT, 0);
2667 allqueue(REDRAWHEADERS, 0);
2670 else if (event==OKEY) {
2671 if (G.qual & LR_ALTKEY) {
2672 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2679 if (val && uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING) event= 0;
2681 if (st->doplugins && do_texttools(st, ascii, event, val)) return;
2682 if (do_markers(st, ascii, event, val)) return;
2684 if (event==UI_BUT_EVENT) {
2685 do_find_buttons(val);
2687 } else if (event==LEFTMOUSE) {
2692 getmouseco_areawin(mval);
2694 if (mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<curarea->winy-2) {
2695 do_textscroll(st, 2);
2697 do_selection(st, G.qual&LR_SHIFTKEY);
2698 if (txt_has_sel(text)) {
2699 buffer = txt_sel_to_buf(text);
2700 putClipboard(buffer, 1);
2706 } else if (event==MIDDLEMOUSE) {
2708 if (U.uiflag & USER_MMB_PASTE) {
2709 do_selection(st, G.qual&LR_SHIFTKEY);
2710 get_selection_buffer(text);
2713 do_textscroll(st, 1);
2716 } else if (event==RIGHTMOUSE) {
2718 if (txt_has_sel(text))
2719 p= pupmenu("Text %t|Cut%x10|Copy%x11|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
2721 p= pupmenu("Text %t|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
2725 st->text= add_empty_text("Text");
2728 allqueue(REDRAWTEXT, 0);
2729 allqueue(REDRAWHEADERS, 0);
2733 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2737 text->flags |= TXT_ISMEM;
2740 txt_write_file(text);
2744 run_python_script(st);
2748 if (text && text->id.lib) {
2752 txt_copy_clipboard(text);
2758 //txt_copy_sel(text);
2759 txt_copy_clipboard(text);
2762 if (text && text->id.lib) {
2766 txt_paste_clipboard(text);
2767 if (st->showsyntax) txt_format_text(st);
2773 if (text && text->id.lib) {
2777 getmouseco_areawin(mval);
2778 if (st->showlinenrs && mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<curarea->winy-2) {
2779 if (ascii>='0' && ascii<='9') {
2780 double time = PIL_check_seconds_timer();
2781 if (last_jump < time-1) jump_to= 0;
2782 jump_to *= 10; jump_to += (int)(ascii-'0');
2783 txt_move_toline(text, jump_to-1, 0);
2786 } else if ((st->overwrite && txt_replace_char(text, ascii)) || txt_add_char(text, ascii)) {
2787 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2795 if (G.qual & LR_ALTKEY) {
2796 txt_move_bol(text, G.qual & LR_SHIFTKEY);
2799 } else if (G.qual & LR_CTRLKEY) {
2803 break; /* BREAK A */
2805 if (G.qual & LR_ALTKEY || G.qual & LR_CTRLKEY) {
2806 if(G.qual & LR_SHIFTKEY)
2807 txt_copy_clipboard(text);
2809 txt_copy_clipboard(text);
2813 break; /* BREAK C */
2815 if (text && text->id.lib) {
2819 if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
2821 txt_order_cursors(text);
2824 if (st->showsyntax) txt_format_text(st);
2826 } else if (G.qual == LR_CTRLKEY) {
2827 txt_delete_char(text);
2828 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2832 break; /* BREAK D */
2834 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2835 switch(pupmenu("Edit %t|Cut %x0|Copy %x1|Paste %x2|Print Cut Buffer %x3")) {
2837 if (text && text->id.lib) {
2841 txt_copy_clipboard(text); //First copy to clipboard
2846 txt_copy_clipboard(text);
2847 //txt_copy_sel(text);
2851 if (text && text->id.lib) {
2856 txt_paste_clipboard(text);
2857 if (st->showsyntax) txt_format_text(st);
2861 txt_print_cutbuffer();
2865 else if (G.qual == LR_CTRLKEY || G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
2866 txt_move_eol(text, G.qual & LR_SHIFTKEY);
2870 break; /* BREAK E */
2872 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2873 switch(pupmenu("File %t|New %x0|Open... %x1|Save %x2|Save As...%x3")) {
2875 st->text= add_empty_text("Text");
2878 allqueue(REDRAWTEXT, 0);
2879 allqueue(REDRAWHEADERS, 0);
2882 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2885 text->flags |= TXT_ISMEM;
2887 txt_write_file(text);
2892 else if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
2893 find_and_replace(st, 0);
2896 break; /* BREAK F */
2898 if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
2899 find_and_replace(st, 1);
2902 break; /* BREAK H */
2904 if (G.qual == LR_ALTKEY) {
2905 do_draw= jumptoline_interactive(st);
2907 break; /* BREAK J */
2909 if (G.qual == LR_ALTKEY) {
2910 txt_export_to_object(text);
2913 break; /* BREAK M */
2915 if (G.qual == LR_ALTKEY) {
2916 st->text= add_empty_text("Text");
2919 allqueue(REDRAWTEXT, 0);
2920 allqueue(REDRAWHEADERS, 0);
2922 break; /* BREAK N */
2924 if (G.qual == LR_ALTKEY) {
2925 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2927 break; /* BREAK O */
2929 if (G.qual == LR_ALTKEY) {
2930 run_python_script(st);
2933 break; /* BREAK P */
2935 if(okee("Quit Blender")) exit_usiblender();
2936 break; /* BREAK Q */
2938 if (G.qual == LR_ALTKEY) {
2939 #ifndef DISABLE_PYTHON
2940 if (text->compiled) BPY_free_compiled_text(text);
2941 text->compiled = NULL;
2943 if (okee("Reopen text")) {
2944 if (!reopen_text(text))
2945 error("Could not reopen file");
2946 if (st->showsyntax) txt_format_text(st);
2950 break; /* BREAK R */
2952 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2953 p= pupmenu("Select %t|"
2956 "Jump to Line %x3");
2969 do_draw= jumptoline_interactive(st);
2973 else if (G.qual & LR_ALTKEY) {
2974 /* Event treatment CANNOT enter this if
2975 if (G.qual & LR_SHIFTKEY)
2976 if (text) text->flags |= TXT_ISMEM;
2978 txt_write_file(text);
2981 break; /* BREAK S */
2983 //txt_print_undo(text); //debug buffer in console
2984 if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2989 if (G.qual == LR_ALTKEY)&