Added better grouping for text markers with a separate group field (instead of using...
[blender-staging.git] / source / blender / python / api2_2x / Text.c
1 /* 
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * This is a new part of Blender.
24  *
25  * Contributor(s): Willian P. Germano
26  *
27  * ***** END GPL LICENSE BLOCK *****
28 */
29
30 #include "Text.h" /*This must come first*/
31
32 #include "BKE_library.h"
33 #include "BKE_sca.h"
34 #include "BKE_global.h"
35 #include "BKE_main.h"
36 #include "BIF_drawtext.h"
37 #include "BIF_screen.h"
38 #include "BKE_text.h"
39 #include "BKE_suggestions.h"
40 #include "BLI_blenlib.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_space_types.h"
43 #include "MEM_guardedalloc.h"
44 #include "gen_utils.h"
45 #include "gen_library.h"
46 #include "../BPY_extern.h"
47
48 #define EXPP_TEXT_MODE_FOLLOW TXT_FOLLOW
49
50 /*****************************************************************************/
51 /* Python API function prototypes for the Text module.                       */
52 /*****************************************************************************/
53 static PyObject *M_Text_New( PyObject * self, PyObject * args);
54 static PyObject *M_Text_Get( PyObject * self, PyObject * args );
55 static PyObject *M_Text_Load( PyObject * self, PyObject * value );
56 static PyObject *M_Text_unlink( PyObject * self, PyObject * args );
57
58 /*****************************************************************************/
59 /* The following string definitions are used for documentation strings.      */
60 /* In Python these will be written to the console when doing a               */
61 /* Blender.Text.__doc__                                                      */
62 /*****************************************************************************/
63 static char M_Text_doc[] = "The Blender Text module\n\n";
64
65 static char M_Text_New_doc[] = "() - return a new Text object";
66
67 static char M_Text_Get_doc[] = "(name) - return the Text with name 'name', \
68 returns None if not found.\n If 'name' is not specified, \
69 it returns a list of all Texts in the\ncurrent scene.";
70
71 static char M_Text_Load_doc[] =
72         "(filename) - return text from file filename as a Text Object, \
73 returns None if not found.\n";
74
75 static char M_Text_unlink_doc[] =
76         "(text) - remove Text object 'text' from Blender";
77
78 /*****************************************************************************/
79 /* Python method structure definition for Blender.Text module:               */
80 /*****************************************************************************/
81 struct PyMethodDef M_Text_methods[] = {
82         {"New", M_Text_New, METH_VARARGS, M_Text_New_doc},
83         {"Get", M_Text_Get, METH_VARARGS, M_Text_Get_doc},
84         {"get", M_Text_Get, METH_VARARGS, M_Text_Get_doc},
85         {"Load", M_Text_Load, METH_O, M_Text_Load_doc},
86         {"unlink", M_Text_unlink, METH_VARARGS, M_Text_unlink_doc},
87         {NULL, NULL, 0, NULL}
88 };
89
90
91 /*****************************************************************************/
92 /* Python BPy_Text methods declarations:                                     */
93 /*****************************************************************************/
94 static PyObject *Text_getFilename( BPy_Text * self );
95 static PyObject *Text_getNLines( BPy_Text * self );
96 static PyObject *Text_clear( BPy_Text * self );
97 static PyObject *Text_reset( BPy_Text * self );
98 static PyObject *Text_readline( BPy_Text * self );
99 static PyObject *Text_write( BPy_Text * self, PyObject * value );
100 static PyObject *Text_insert( BPy_Text * self, PyObject * value );
101 static PyObject *Text_delete( BPy_Text * self, PyObject * value );
102 static PyObject *Text_set( BPy_Text * self, PyObject * args );
103 static PyObject *Text_asLines( BPy_Text * self, PyObject * args );
104 static PyObject *Text_getCursorPos( BPy_Text * self );
105 static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args );
106 static PyObject *Text_getSelectPos( BPy_Text * self );
107 static PyObject *Text_setSelectPos( BPy_Text * self, PyObject * args );
108 static PyObject *Text_markSelection( BPy_Text * self, PyObject * args );
109 static PyObject *Text_suggest( BPy_Text * self, PyObject * args );
110 static PyObject *Text_showDocs( BPy_Text * self, PyObject * args );
111
112 /*****************************************************************************/
113 /* Python BPy_Text methods table:                                            */
114 /*****************************************************************************/
115 static PyMethodDef BPy_Text_methods[] = {
116         /* name, method, flags, doc */
117         {"getName", ( PyCFunction ) GenericLib_getName, METH_NOARGS,
118          "() - Return Text Object name"},
119         {"getFilename", ( PyCFunction ) Text_getFilename, METH_VARARGS,
120          "() - Return Text Object filename"},
121         {"getNLines", ( PyCFunction ) Text_getNLines, METH_VARARGS,
122          "() - Return number of lines in text buffer"},
123         {"setName", ( PyCFunction ) GenericLib_setName_with_method, METH_VARARGS,
124          "(str) - Change Text Object name"},
125         {"clear", ( PyCFunction ) Text_clear, METH_NOARGS,
126          "() - Clear Text buffer"},
127         {"reset", ( PyCFunction ) Text_reset, METH_NOARGS,
128          "() - Moves the IO pointer back to the start of the Text buffer for reading"},
129         {"readline", ( PyCFunction ) Text_readline, METH_NOARGS,
130          "() - Reads a line of text from the buffer and returns it incrementing the internal IO pointer."},
131         {"write", ( PyCFunction ) Text_write, METH_O,
132          "(line) - Append string 'str' to Text buffer"},
133         {"insert", ( PyCFunction ) Text_insert, METH_O,
134          "(line) - Insert string 'str' to Text buffer at cursor location"},
135         {"delete", ( PyCFunction ) Text_delete, METH_O,
136          "(chars) - Deletes a number of characters to the left (chars<0) or right (chars>0)"},
137         {"set", ( PyCFunction ) Text_set, METH_VARARGS,
138          "(name, val) - Set attribute 'name' to value 'val'"},
139         {"asLines", ( PyCFunction ) Text_asLines, METH_VARARGS,
140          "(start=0, end=nlines) - Return text buffer as a list of lines between start and end"},
141         {"getCursorPos", ( PyCFunction ) Text_getCursorPos, METH_NOARGS,
142          "() - Return cursor position as (row, col) tuple"},
143         {"setCursorPos", ( PyCFunction ) Text_setCursorPos, METH_VARARGS,
144          "(row, col) - Set the cursor position to (row, col)"},
145         {"getSelectPos", ( PyCFunction ) Text_getSelectPos, METH_NOARGS,
146          "() - Return the selection cursor position as (row, col) tuple"},
147         {"setSelectPos", ( PyCFunction ) Text_setSelectPos, METH_VARARGS,
148          "(row, col) - Set the selection cursor position to (row, col)"},
149         {"markSelection", ( PyCFunction ) Text_markSelection, METH_VARARGS,
150          "(group, (r, g, b), flags) - Places a marker over the current selection. Group: number > 0, flags: TMARK_TEMP, TMARK_EDITALL, etc."},
151         {"suggest", ( PyCFunction ) Text_suggest, METH_VARARGS,
152          "(list, prefix='') - Presents a list of suggestions. List is of strings, or tuples. Tuples must be of the form (name, type) where type is one of 'm', 'v', 'f', 'k' for module, variable, function and keyword respectively or '?' for other types"},
153         {"showDocs", ( PyCFunction ) Text_showDocs, METH_VARARGS,
154          "(docs) - Documentation string"},
155         {NULL, NULL, 0, NULL}
156 };
157
158 /*****************************************************************************/
159 /* Python Text_Type callback function prototypes:                            */
160 /*****************************************************************************/
161 static int Text_compare( BPy_Text * a, BPy_Text * b );
162 static PyObject *Text_repr( BPy_Text * self );
163
164 /*****************************************************************************/
165 /* Function:              M_Text_New                                         */
166 /* Python equivalent:     Blender.Text.New                                   */
167 /*****************************************************************************/
168 static PyObject *M_Text_New( PyObject * self, PyObject * args)
169 {
170         char *name = "Text";
171         int follow = 0;
172         Text *bl_text;          /* blender text object */
173         PyObject *py_text;      /* python wrapper */
174
175         if( !PyArg_ParseTuple( args, "|si", &name, &follow ) )
176                 return EXPP_ReturnPyObjError( PyExc_AttributeError,
177                                               "expected string and int arguments (or nothing)" );
178
179         bl_text = add_empty_text( name );
180
181         if( bl_text ) {
182                 /* do not set user count because Text is already linked */
183
184                 /* create python wrapper obj */
185                 py_text = Text_CreatePyObject( bl_text );
186         } else
187                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
188                                               "couldn't create Text Object in Blender" );
189         if( !py_text )
190                 return EXPP_ReturnPyObjError( PyExc_MemoryError,
191                                               "couldn't create Text Object wrapper" );
192
193         if( follow )
194                 bl_text->flags |= EXPP_TEXT_MODE_FOLLOW;
195
196         return py_text;
197 }
198
199 /*****************************************************************************/
200 /* Function:              M_Text_Get                                         */
201 /* Python equivalent:     Blender.Text.Get                                   */
202 /* Description:           Receives a string and returns the text object      */
203 /*                        whose name matches the string.  If no argument is  */
204 /*                        passed in, a list of all text names in the current */
205 /*                        scene is returned.                                 */
206 /*****************************************************************************/
207 static PyObject *M_Text_Get( PyObject * self, PyObject * args )
208 {
209         char *name = NULL;
210         Text *txt_iter;
211
212         if( !PyArg_ParseTuple( args, "|s", &name ) )
213                 return ( EXPP_ReturnPyObjError( PyExc_TypeError,
214                                                 "expected string argument (or nothing)" ) );
215
216         txt_iter = G.main->text.first;
217
218         if( name ) {            /* (name) - Search text by name */
219
220                 PyObject *wanted_txt = NULL;
221
222                 while( ( txt_iter ) && ( wanted_txt == NULL ) ) {
223
224                         if( strcmp( name, txt_iter->id.name + 2 ) == 0 ) {
225                                 wanted_txt = Text_CreatePyObject( txt_iter );
226                         }
227
228                         txt_iter = txt_iter->id.next;
229                 }
230
231                 if( wanted_txt == NULL ) {      /* Requested text doesn't exist */
232                         char error_msg[64];
233                         PyOS_snprintf( error_msg, sizeof( error_msg ),
234                                 "Text \"%s\" not found", name );
235                         return ( EXPP_ReturnPyObjError
236                                 ( PyExc_NameError, error_msg ) );
237                 }
238
239                 return wanted_txt;
240         }
241
242         else {                  /* () - return a list of all texts in the scene */
243                 int index = 0;
244                 PyObject *txtlist, *pyobj;
245
246                 txtlist = PyList_New( BLI_countlist( &( G.main->text ) ) );
247
248                 if( txtlist == NULL )
249                         return ( EXPP_ReturnPyObjError( PyExc_MemoryError,
250                                                         "couldn't create PyList" ) );
251
252                 while( txt_iter ) {
253                         pyobj = Text_CreatePyObject( txt_iter );
254
255                         if( !pyobj ) {
256                                 Py_DECREF(txtlist);
257                                 return ( EXPP_ReturnPyObjError
258                                          ( PyExc_MemoryError,
259                                            "couldn't create PyString" ) );
260                         }
261                         PyList_SET_ITEM( txtlist, index, pyobj );
262
263                         txt_iter = txt_iter->id.next;
264                         index++;
265                 }
266
267                 return ( txtlist );
268         }
269 }
270
271 /*****************************************************************************/
272 /* Function:              M_Text_Load                                        */
273 /* Python equivalent:     Blender.Text.Load                                  */
274 /* Description:           Receives a filename and returns the text object    */
275 /*                        created from the corresponding file.               */
276 /*****************************************************************************/
277 static PyObject *M_Text_Load( PyObject * self, PyObject * value )
278 {
279         char *fname = PyString_AsString(value);
280         char fpath[FILE_MAXDIR + FILE_MAXFILE];
281         Text *txt_ptr = NULL;
282         unsigned int maxlen = FILE_MAXDIR + FILE_MAXFILE;
283
284         if( !fname )
285                 return ( EXPP_ReturnPyObjError( PyExc_TypeError,
286                                                 "expected string argument" ) );
287
288         if (strlen(fname) > (maxlen - 1))
289                 return EXPP_ReturnPyObjError (PyExc_AttributeError,
290                         "text filename too long");
291         else if (!BLI_exists(fname))
292                 return EXPP_ReturnPyObjError (PyExc_AttributeError,
293                         "text file not found");
294
295         BLI_strncpy(fpath, fname, maxlen);
296
297         txt_ptr = add_text( fpath );
298         if( !txt_ptr )
299                 return EXPP_ReturnPyObjError( PyExc_IOError,
300                                               "couldn't load text" );
301
302         return Text_CreatePyObject(txt_ptr);
303 }
304
305 /*****************************************************************************/
306 /* Function:              M_Text_unlink                                      */
307 /* Python equivalent:     Blender.Text.unlink                                */
308 /* Description:           Removes the given Text object from Blender         */
309 /*****************************************************************************/
310 static PyObject *M_Text_unlink( PyObject * self, PyObject * args )
311 {
312         BPy_Text *textobj;
313         Text *text;
314
315         if( !PyArg_ParseTuple( args, "O!", &Text_Type, &textobj ) )
316                 return EXPP_ReturnPyObjError( PyExc_TypeError,
317                                               "expected a Text object as argument" );
318
319         text = ( ( BPy_Text * ) textobj )->text;
320
321         if( !text )
322                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
323                                               "this text was already unlinked!" );
324
325         BPY_clear_bad_scriptlinks( text );
326         BPY_free_pyconstraint_links( text );
327         free_text_controllers( text );
328         unlink_text( text );
329
330         free_libblock( &G.main->text, text );
331
332         ( ( BPy_Text * ) textobj )->text = NULL;
333
334         Py_RETURN_NONE;
335 }
336
337 /*****************************************************************************/
338 /* Function:              Text_Init                                          */
339 /*****************************************************************************/
340 PyObject *Text_Init( void )
341 {
342         PyObject *submodule, *dict;
343
344         if( PyType_Ready( &Text_Type ) < 0 )
345                 return NULL;
346
347         submodule =
348                 Py_InitModule3( "Blender.Text", M_Text_methods, M_Text_doc );
349
350         dict = PyModule_GetDict( submodule );
351         
352 #define EXPP_ADDCONST(x) \
353         EXPP_dict_set_item_str(dict, #x, PyInt_FromLong(x))
354
355         /* So, for example:
356          * EXPP_ADDCONST(LEFTMOUSE) becomes
357          * EXPP_dict_set_item_str(dict, "LEFTMOUSE", PyInt_FromLong(LEFTMOUSE)) 
358          */
359
360         EXPP_ADDCONST( TMARK_TEMP );
361         EXPP_ADDCONST( TMARK_EDITALL );
362
363         return ( submodule );
364 }
365
366 /*****************************************************************************/
367 /* Function:              Text_CreatePyObject                                */
368 /*****************************************************************************/
369 PyObject *Text_CreatePyObject( Text * txt )
370 {
371         BPy_Text *pytxt;
372
373         pytxt = ( BPy_Text * ) PyObject_NEW( BPy_Text, &Text_Type );
374
375         if( !pytxt )
376                 return EXPP_ReturnPyObjError( PyExc_MemoryError,
377                                               "couldn't create BPy_Text PyObject" );
378
379         pytxt->text = txt;
380         pytxt->iol = NULL;
381         pytxt->ioc = -1;
382
383         return ( PyObject * ) pytxt;
384 }
385
386 /*****************************************************************************/
387 /* Python BPy_Text methods:                                                  */
388 /*****************************************************************************/
389 static PyObject *Text_getFilename( BPy_Text * self )
390 {
391         if( self->text->name )
392                 return PyString_FromString( self->text->name );
393         
394         Py_RETURN_NONE;
395 }
396
397 static PyObject *Text_getNLines( BPy_Text * self )
398 {                               /* text->nlines isn't updated in Blender (?) */
399         int nlines = 0;
400         TextLine *line;
401
402         line = self->text->lines.first;
403
404         while( line ) {         /* so we have to count them ourselves */
405                 line = line->next;
406                 nlines++;
407         }
408
409         self->text->nlines = nlines;    /* and update Blender, too (should we?) */
410
411         return PyInt_FromLong( nlines );
412 }
413
414 static PyObject *Text_clear( BPy_Text * self)
415 {
416         int oldstate;
417
418         if( !self->text )
419                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
420                                               "This object isn't linked to a Blender Text Object" );
421
422         oldstate = txt_get_undostate(  );
423         txt_set_undostate( 1 );
424         txt_sel_all( self->text );
425         txt_cut_sel( self->text );
426         txt_set_undostate( oldstate );
427
428         Py_RETURN_NONE;
429 }
430
431 static PyObject *Text_reset( BPy_Text * self )
432 {
433         self->iol = NULL;
434         self->ioc = -1;
435
436         Py_RETURN_NONE;
437 }
438
439 static PyObject *Text_readline( BPy_Text * self )
440 {
441         PyObject *tmpstr;
442         
443         if( !self->text )
444                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
445                                               "This object isn't linked to a Blender Text Object" );
446
447         /* Reset */
448         if (!self->iol && self->ioc == -1) {
449                 self->iol = self->text->lines.first;
450                 self->ioc = 0;
451         }
452
453         if (!self->iol) {
454                 PyErr_SetString( PyExc_StopIteration, "End of buffer reached" );
455                 return PyString_FromString( "" );
456         }
457
458         if (self->ioc > self->iol->len) {
459                 self->iol = NULL;
460                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
461                                                   "Line length exceeded, text may have changed while reading" );
462         }
463
464         tmpstr = PyString_FromString( self->iol->line + self->ioc );
465         if (self->iol->next)
466                 PyString_ConcatAndDel( &tmpstr, PyString_FromString("\n") );
467
468         self->iol = self->iol->next;
469         self->ioc = 0;
470
471         return tmpstr;
472 }
473
474 static PyObject *Text_write( BPy_Text * self, PyObject * value )
475 {
476         char *str = PyString_AsString(value);
477         int oldstate;
478
479         if( !self->text )
480                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
481                                               "This object isn't linked to a Blender Text Object" );
482
483         if( !str )
484                 return EXPP_ReturnPyObjError( PyExc_TypeError,
485                                               "expected string argument" );
486
487         oldstate = txt_get_undostate(  );
488         txt_insert_buf( self->text, str );
489         txt_move_eof( self->text, 0 );
490         txt_set_undostate( oldstate );
491
492         Text_reset( self );
493
494         Py_RETURN_NONE;
495 }
496
497 static PyObject *Text_insert( BPy_Text * self, PyObject * value )
498 {
499         char *str = PyString_AsString(value);
500         int oldstate;
501
502         if( !self->text )
503                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
504                                               "This object isn't linked to a Blender Text Object" );
505
506         if( !str )
507                 return EXPP_ReturnPyObjError( PyExc_TypeError,
508                                               "expected string argument" );
509
510         oldstate = txt_get_undostate(  );
511         txt_insert_buf( self->text, str );
512         txt_set_undostate( oldstate );
513
514         Text_reset( self );
515
516         Py_RETURN_NONE;
517 }
518
519 static PyObject *Text_delete( BPy_Text * self, PyObject * value )
520 {
521         int num = PyInt_AsLong(value);
522         int oldstate;
523
524         if( !self->text )
525                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
526                                               "This object isn't linked to a Blender Text Object" );
527
528         if( !num )
529                 return EXPP_ReturnPyObjError( PyExc_TypeError,
530                                               "expected non-zero int argument" );
531
532         oldstate = txt_get_undostate(  );
533         while (num<0) {
534                 txt_backspace_char(self->text);
535                 num++;
536         }
537         while (num>0) {
538                 txt_delete_char(self->text);
539                 num--;
540         }
541         txt_set_undostate( oldstate );
542         
543         Text_reset( self );
544
545         Py_RETURN_NONE;
546 }
547
548 static PyObject *Text_set( BPy_Text * self, PyObject * args )
549 {
550         int ival;
551         char *attr;
552
553         if( !PyArg_ParseTuple( args, "si", &attr, &ival ) )
554                 return EXPP_ReturnPyObjError( PyExc_TypeError,
555                                               "expected a string and an int as arguments" );
556
557         if( strcmp( "follow_cursor", attr ) == 0 ) {
558                 if( ival )
559                         self->text->flags |= EXPP_TEXT_MODE_FOLLOW;
560                 else
561                         self->text->flags &= EXPP_TEXT_MODE_FOLLOW;
562         }
563
564         Py_RETURN_NONE;
565 }
566
567 static PyObject *Text_asLines( BPy_Text * self, PyObject * args )
568 {
569         TextLine *line;
570         PyObject *list, *tmpstr;
571         int start=0, end=-1, i;
572
573         if( !self->text )
574                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
575                                               "This object isn't linked to a Blender Text Object" );
576
577         if( !PyArg_ParseTuple( args, "|ii", &start, &end ) )
578                         return EXPP_ReturnPyObjError( PyExc_TypeError,
579                                                           "expected upto two optional ints as arguments" );
580         
581         if (start<0)
582                 start=0;
583
584         line = self->text->lines.first;
585         for (i = 0; i < start && line->next; i++)
586                 line= line->next;
587
588         list = PyList_New( 0 );
589
590         if( !list )
591                 return EXPP_ReturnPyObjError( PyExc_MemoryError,
592                                               "couldn't create PyList" );
593
594         while( line && (i < end || end == -1) ) {
595                 tmpstr = PyString_FromString( line->line );
596                 PyList_Append( list, tmpstr );
597                 Py_DECREF(tmpstr);
598                 line = line->next;
599                 i++;
600         }
601
602         return list;
603 }
604
605 static PyObject *Text_getCursorPos( BPy_Text * self )
606 {
607         Text *text;
608         TextLine *linep;
609         int row, col;
610
611         text = self->text;
612         if( !text )
613                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
614                                               "This object isn't linked to a Blender Text Object" );
615
616         for (row=0,linep=text->lines.first; linep!=text->curl; linep=linep->next)
617                 row++;
618         col= text->curc;
619
620         return Py_BuildValue( "ii", row, col );
621 }
622
623 static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args )
624 {
625         int row, col;
626         SpaceText *st;
627
628         if (!self->text)
629                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
630                                               "This object isn't linked to a Blender Text Object");
631
632         if (!PyArg_ParseTuple(args, "ii", &row, &col))
633                 return EXPP_ReturnPyObjError(PyExc_TypeError,
634                                               "expected two ints as arguments.");
635         if (row<0) row=0;
636         if (col<0) col=0;
637
638         txt_move_to(self->text, row, col, 0);
639
640         if (curarea->spacetype == SPACE_TEXT && (st=curarea->spacedata.first))
641                 pop_space_text(st);
642
643         Py_RETURN_NONE;
644 }
645
646 static PyObject *Text_getSelectPos( BPy_Text * self )
647 {
648         Text *text;
649         TextLine *linep;
650         int row, col;
651
652         text = self->text;
653         if( !text )
654                 return EXPP_ReturnPyObjError( PyExc_RuntimeError,
655                                               "This object isn't linked to a Blender Text Object" );
656
657         for (row=0,linep=text->lines.first; linep!=text->sell; linep=linep->next)
658                 row++;
659         col= text->selc;
660
661         return Py_BuildValue( "ii", row, col );
662 }
663
664 static PyObject *Text_setSelectPos( BPy_Text * self, PyObject * args )
665 {
666         int row, col;
667         SpaceText *st;
668
669         if (!self->text)
670                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
671                                               "This object isn't linked to a Blender Text Object");
672
673         if (!PyArg_ParseTuple(args, "ii", &row, &col))
674                 return EXPP_ReturnPyObjError(PyExc_TypeError,
675                                               "expected two ints as arguments.");
676         if (row<0) row=0;
677         if (col<0) col=0;
678
679         txt_move_to(self->text, row, col, 1);
680
681         if (curarea->spacetype == SPACE_TEXT && (st=curarea->spacedata.first))
682                 pop_space_text(st);
683
684         Py_RETURN_NONE;
685 }
686
687 static PyObject *Text_markSelection( BPy_Text * self, PyObject * args )
688 {
689         int group = 0, flags = 0,r, g, b;
690         Text *text;
691         char clr[4];
692
693         text = self->text;
694         if (!text)
695                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
696                                               "This object isn't linked to a Blender Text Object");
697
698         if (!PyArg_ParseTuple(args, "i(iii)i", &group, &r, &g, &b, &flags))
699                 return EXPP_ReturnPyObjError(PyExc_TypeError,
700                                               "expected int, 3-tuple of ints and int as arguments.");
701
702         if (text->curl != text->sell)
703                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
704                                               "Cannot mark multi-line selection.");
705
706         clr[0] = (char) (r&0xFF);
707         clr[1] = (char) (g&0xFF);
708         clr[2] = (char) (b&0xFF);
709         clr[3] = 255;
710
711         group &= 0xFFFF;
712
713         txt_add_marker(text, text->curl, text->curc, text->selc, clr, group, flags);
714         
715         Py_RETURN_NONE;
716 }
717
718 static PyObject *Text_suggest( BPy_Text * self, PyObject * args )
719 {
720         PyObject *item = NULL, *tup1 = NULL, *tup2 = NULL;
721         PyObject *list = NULL, *resl = NULL;
722         int list_len, i;
723         char *prefix = NULL, *name, type;
724         SpaceText *st;
725
726         if (!self->text)
727                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
728                                 "This object isn't linked to a Blender Text Object");
729
730         /* Parse args for a list of strings/tuples */
731         if (!PyArg_ParseTuple(args, "O!|s", &PyList_Type, &list, &prefix))
732                 return EXPP_ReturnPyObjError(PyExc_TypeError,
733                                 "expected list of strings or tuples followed by an optional string");
734
735         if (curarea->spacetype != SPACE_TEXT)
736                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
737                                 "Active space type is not text");
738         
739         st = curarea->spacedata.first;
740         if (!st || !st->text)
741                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
742                                 "Active text area has no Text object");
743         
744         texttool_suggest_clear();
745         texttool_text_set_active(st->text);
746         list_len = PyList_Size(list);
747         
748         for (i = 0; i < list_len; i++) {
749                 item = PyList_GetItem(list, i);
750
751                 if (PyString_Check(item)) {
752                         name = PyString_AsString(item);
753                         type = '?';
754                 } else if (PyTuple_Check(item) && PyTuple_GET_SIZE(item) == 2) {
755                         tup1 = PyTuple_GetItem(item, 0);
756                         tup2 = PyTuple_GetItem(item, 1);
757                         if (PyString_Check(tup1) && PyString_Check(tup2)) {
758                                 name = PyString_AsString(tup1);
759                                 type = PyString_AsString(tup2)[0];
760                         } else
761                                 return EXPP_ReturnPyObjError(PyExc_AttributeError,
762                                                 "list must contain tuples of two strings only: (name, type)" );
763                 } else
764                         return EXPP_ReturnPyObjError(PyExc_AttributeError,
765                                         "list must contain only individual strings or tuples of size 2" );
766
767                 if (!strlen(name) || (type!='m' && type!='v' && type!='f' && type!='k' && type!='?'))
768                         return EXPP_ReturnPyObjError(PyExc_AttributeError,
769                                         "names must be non-empty and types in ['m', 'v', 'f', 'k', '?']" );
770
771                 texttool_suggest_add(name, type);
772         }
773         if (!prefix)
774                 prefix = "";
775         texttool_suggest_prefix(prefix);
776         scrarea_queue_redraw(curarea);
777
778         Py_RETURN_NONE;
779 }
780
781 static PyObject *Text_showDocs( BPy_Text * self, PyObject * args )
782 {
783         char *docs;
784         SpaceText *st;
785
786         if (!self->text)
787                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
788                                 "This object isn't linked to a Blender Text Object");
789
790         if (!PyArg_ParseTuple(args, "s", &docs))
791                 return EXPP_ReturnPyObjError( PyExc_TypeError,
792                                               "expected a string as argument" );
793
794         if (curarea->spacetype != SPACE_TEXT)
795                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
796                                 "Active space type is not text");
797         
798         st = curarea->spacedata.first;
799         if (!st || !st->text)
800                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
801                                 "Active text area has no Text object");
802
803         texttool_text_set_active(st->text);
804         texttool_docs_show(docs);
805         scrarea_queue_redraw(curarea);
806
807         Py_RETURN_NONE;
808 }
809
810 /*****************************************************************************/
811 /* Function:    Text_compare                                                 */
812 /* Description: This is a callback function for the BPy_Text type. It        */
813 /*              compares two Text_Type objects. Only the "==" and "!="       */
814 /*              comparisons are meaninful. Returns 0 for equality and -1 if  */
815 /*              they don't point to the same Blender Text struct.            */
816 /*              In Python it becomes 1 if they are equal, 0 otherwise.       */
817 /*****************************************************************************/
818 static int Text_compare( BPy_Text * a, BPy_Text * b )
819 {
820         return ( a->text == b->text ) ? 0 : -1;
821 }
822
823 /*****************************************************************************/
824 /* Function:    Text_repr                                                    */
825 /* Description: This is a callback function for the BPy_Text type. It        */
826 /*              builds a meaninful string to represent text objects.         */
827 /*****************************************************************************/
828 static PyObject *Text_repr( BPy_Text * self )
829 {
830         if( self->text )
831                 return PyString_FromFormat( "[Text \"%s\"]",
832                                             self->text->id.name + 2 );
833         else
834                 return PyString_FromString( "[Text <deleted>]" );
835 }
836
837 /*****************************************************************************/
838 /* Python attributes get/set functions:                                      */
839 /*****************************************************************************/
840 static PyObject *Text_getMode(BPy_Text * self)
841 {
842         return PyInt_FromLong( self->text->flags );
843 }
844
845 /*****************************************************************************/
846 /* Python attributes get/set structure:                                      */
847 /*****************************************************************************/
848 static PyGetSetDef BPy_Text_getseters[] = {
849         GENERIC_LIB_GETSETATTR,
850         {"filename", (getter)Text_getFilename, (setter)NULL,
851          "text filename", NULL},
852         {"mode", (getter)Text_getMode, (setter)NULL,
853          "text mode flag", NULL},
854         {"nlines", (getter)Text_getNLines, (setter)NULL,
855          "number of lines", NULL},
856         {NULL,NULL,NULL,NULL,NULL}  /* Sentinel */
857 };
858
859 /*****************************************************************************/
860 /* Python Text_Type structure definition:                                    */
861 /*****************************************************************************/
862 PyTypeObject Text_Type = {
863         PyObject_HEAD_INIT( NULL ) 
864         0,      /* ob_size */
865         "Blender Text",         /* tp_name */
866         sizeof( BPy_Text ),     /* tp_basicsize */
867         0,                      /* tp_itemsize */
868         /* methods */
869         NULL,   /* tp_dealloc */
870         NULL,                   /* tp_print */
871         NULL,   /* tp_getattr */
872         NULL,   /* tp_setattr */
873         ( cmpfunc ) Text_compare,       /* tp_compare */
874         ( reprfunc ) Text_repr, /* tp_repr */
875
876         /* Method suites for standard classes */
877
878         NULL,                       /* PyNumberMethods *tp_as_number; */
879         NULL,                       /* PySequenceMethods *tp_as_sequence; */
880         NULL,                       /* PyMappingMethods *tp_as_mapping; */
881
882         /* More standard operations (here for binary compatibility) */
883
884         ( hashfunc ) GenericLib_hash,   /* hashfunc tp_hash; */
885         NULL,                       /* ternaryfunc tp_call; */
886         NULL,                       /* reprfunc tp_str; */
887         NULL,                       /* getattrofunc tp_getattro; */
888         NULL,                       /* setattrofunc tp_setattro; */
889
890         /* Functions to access object as input/output buffer */
891         NULL,                       /* PyBufferProcs *tp_as_buffer; */
892  
893   /*** Flags to define presence of optional/expanded features ***/
894         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
895
896         NULL,                       /*  char *tp_doc;  Documentation string */
897   /*** Assigned meaning in release 2.0 ***/
898         /* call function for all accessible objects */
899         NULL,                       /* traverseproc tp_traverse; */
900
901         /* delete references to contained objects */
902         NULL,                       /* inquiry tp_clear; */
903
904   /***  Assigned meaning in release 2.1 ***/
905   /*** rich comparisons ***/
906         NULL,                       /* richcmpfunc tp_richcompare; */
907
908   /***  weak reference enabler ***/
909         0,                          /* long tp_weaklistoffset; */
910
911   /*** Added in release 2.2 ***/
912         /*   Iterators */
913         NULL,                       /* getiterfunc tp_iter; */
914         NULL,                       /* iternextfunc tp_iternext; */
915
916   /*** Attribute descriptor and subclassing stuff ***/
917         BPy_Text_methods,           /* struct PyMethodDef *tp_methods; */
918         NULL,                       /* struct PyMemberDef *tp_members; */
919         BPy_Text_getseters,         /* struct PyGetSetDef *tp_getset; */
920         NULL,                       /* struct _typeobject *tp_base; */
921         NULL,                       /* PyObject *tp_dict; */
922         NULL,                       /* descrgetfunc tp_descr_get; */
923         NULL,                       /* descrsetfunc tp_descr_set; */
924         0,                          /* long tp_dictoffset; */
925         NULL,                       /* initproc tp_init; */
926         NULL,                       /* allocfunc tp_alloc; */
927         NULL,                       /* newfunc tp_new; */
928         /*  Low-level free-memory routine */
929         NULL,                       /* freefunc tp_free;  */
930         /* For PyObject_IS_GC */
931         NULL,                       /* inquiry tp_is_gc;  */
932         NULL,                       /* PyObject *tp_bases; */
933         /* method resolution order */
934         NULL,                       /* PyObject *tp_mro;  */
935         NULL,                       /* PyObject *tp_cache; */
936         NULL,                       /* PyObject *tp_subclasses; */
937         NULL,                       /* PyObject *tp_weaklist; */
938         NULL
939 };