7e31b3bb25bf49b59233ced48530e0721371964c
[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         txt_add_marker(text, text->curl, text->curc, text->selc, clr, ((group+2)<<16)|flags);
712         
713         Py_RETURN_NONE;
714 }
715
716 static PyObject *Text_suggest( BPy_Text * self, PyObject * args )
717 {
718         PyObject *item = NULL, *tup1 = NULL, *tup2 = NULL;
719         PyObject *list = NULL, *resl = NULL;
720         int list_len, i;
721         char *prefix = NULL, *name, type;
722         SpaceText *st;
723
724         if (!self->text)
725                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
726                                 "This object isn't linked to a Blender Text Object");
727
728         /* Parse args for a list of strings/tuples */
729         if (!PyArg_ParseTuple(args, "O!|s", &PyList_Type, &list, &prefix))
730                 return EXPP_ReturnPyObjError(PyExc_TypeError,
731                                 "expected list of strings or tuples followed by an optional string");
732
733         if (curarea->spacetype != SPACE_TEXT)
734                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
735                                 "Active space type is not text");
736         
737         st = curarea->spacedata.first;
738         if (!st || !st->text)
739                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
740                                 "Active text area has no Text object");
741         
742         texttool_suggest_clear();
743         texttool_text_set_active(st->text);
744         list_len = PyList_Size(list);
745         
746         for (i = 0; i < list_len; i++) {
747                 item = PyList_GetItem(list, i);
748
749                 if (PyString_Check(item)) {
750                         name = PyString_AsString(item);
751                         type = '?';
752                 } else if (PyTuple_Check(item) && PyTuple_GET_SIZE(item) == 2) {
753                         tup1 = PyTuple_GetItem(item, 0);
754                         tup2 = PyTuple_GetItem(item, 1);
755                         if (PyString_Check(tup1) && PyString_Check(tup2)) {
756                                 name = PyString_AsString(tup1);
757                                 type = PyString_AsString(tup2)[0];
758                         } else
759                                 return EXPP_ReturnPyObjError(PyExc_AttributeError,
760                                                 "list must contain tuples of two strings only: (name, type)" );
761                 } else
762                         return EXPP_ReturnPyObjError(PyExc_AttributeError,
763                                         "list must contain only individual strings or tuples of size 2" );
764
765                 if (!strlen(name) || (type!='m' && type!='v' && type!='f' && type!='k' && type!='?'))
766                         return EXPP_ReturnPyObjError(PyExc_AttributeError,
767                                         "names must be non-empty and types in ['m', 'v', 'f', 'k', '?']" );
768
769                 texttool_suggest_add(name, type);
770         }
771         if (!prefix)
772                 prefix = "";
773         texttool_suggest_prefix(prefix);
774         scrarea_queue_redraw(curarea);
775
776         Py_RETURN_NONE;
777 }
778
779 static PyObject *Text_showDocs( BPy_Text * self, PyObject * args )
780 {
781         char *docs;
782         SpaceText *st;
783
784         if (!self->text)
785                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
786                                 "This object isn't linked to a Blender Text Object");
787
788         if (!PyArg_ParseTuple(args, "s", &docs))
789                 return EXPP_ReturnPyObjError( PyExc_TypeError,
790                                               "expected a string as argument" );
791
792         if (curarea->spacetype != SPACE_TEXT)
793                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
794                                 "Active space type is not text");
795         
796         st = curarea->spacedata.first;
797         if (!st || !st->text)
798                 return EXPP_ReturnPyObjError(PyExc_RuntimeError,
799                                 "Active text area has no Text object");
800
801         texttool_text_set_active(st->text);
802         texttool_docs_show(docs);
803         scrarea_queue_redraw(curarea);
804
805         Py_RETURN_NONE;
806 }
807
808 /*****************************************************************************/
809 /* Function:    Text_compare                                                 */
810 /* Description: This is a callback function for the BPy_Text type. It        */
811 /*              compares two Text_Type objects. Only the "==" and "!="       */
812 /*              comparisons are meaninful. Returns 0 for equality and -1 if  */
813 /*              they don't point to the same Blender Text struct.            */
814 /*              In Python it becomes 1 if they are equal, 0 otherwise.       */
815 /*****************************************************************************/
816 static int Text_compare( BPy_Text * a, BPy_Text * b )
817 {
818         return ( a->text == b->text ) ? 0 : -1;
819 }
820
821 /*****************************************************************************/
822 /* Function:    Text_repr                                                    */
823 /* Description: This is a callback function for the BPy_Text type. It        */
824 /*              builds a meaninful string to represent text objects.         */
825 /*****************************************************************************/
826 static PyObject *Text_repr( BPy_Text * self )
827 {
828         if( self->text )
829                 return PyString_FromFormat( "[Text \"%s\"]",
830                                             self->text->id.name + 2 );
831         else
832                 return PyString_FromString( "[Text <deleted>]" );
833 }
834
835 /*****************************************************************************/
836 /* Python attributes get/set functions:                                      */
837 /*****************************************************************************/
838 static PyObject *Text_getMode(BPy_Text * self)
839 {
840         return PyInt_FromLong( self->text->flags );
841 }
842
843 /*****************************************************************************/
844 /* Python attributes get/set structure:                                      */
845 /*****************************************************************************/
846 static PyGetSetDef BPy_Text_getseters[] = {
847         GENERIC_LIB_GETSETATTR,
848         {"filename", (getter)Text_getFilename, (setter)NULL,
849          "text filename", NULL},
850         {"mode", (getter)Text_getMode, (setter)NULL,
851          "text mode flag", NULL},
852         {"nlines", (getter)Text_getNLines, (setter)NULL,
853          "number of lines", NULL},
854         {NULL,NULL,NULL,NULL,NULL}  /* Sentinel */
855 };
856
857 /*****************************************************************************/
858 /* Python Text_Type structure definition:                                    */
859 /*****************************************************************************/
860 PyTypeObject Text_Type = {
861         PyObject_HEAD_INIT( NULL ) 
862         0,      /* ob_size */
863         "Blender Text",         /* tp_name */
864         sizeof( BPy_Text ),     /* tp_basicsize */
865         0,                      /* tp_itemsize */
866         /* methods */
867         NULL,   /* tp_dealloc */
868         NULL,                   /* tp_print */
869         NULL,   /* tp_getattr */
870         NULL,   /* tp_setattr */
871         ( cmpfunc ) Text_compare,       /* tp_compare */
872         ( reprfunc ) Text_repr, /* tp_repr */
873
874         /* Method suites for standard classes */
875
876         NULL,                       /* PyNumberMethods *tp_as_number; */
877         NULL,                       /* PySequenceMethods *tp_as_sequence; */
878         NULL,                       /* PyMappingMethods *tp_as_mapping; */
879
880         /* More standard operations (here for binary compatibility) */
881
882         ( hashfunc ) GenericLib_hash,   /* hashfunc tp_hash; */
883         NULL,                       /* ternaryfunc tp_call; */
884         NULL,                       /* reprfunc tp_str; */
885         NULL,                       /* getattrofunc tp_getattro; */
886         NULL,                       /* setattrofunc tp_setattro; */
887
888         /* Functions to access object as input/output buffer */
889         NULL,                       /* PyBufferProcs *tp_as_buffer; */
890  
891   /*** Flags to define presence of optional/expanded features ***/
892         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
893
894         NULL,                       /*  char *tp_doc;  Documentation string */
895   /*** Assigned meaning in release 2.0 ***/
896         /* call function for all accessible objects */
897         NULL,                       /* traverseproc tp_traverse; */
898
899         /* delete references to contained objects */
900         NULL,                       /* inquiry tp_clear; */
901
902   /***  Assigned meaning in release 2.1 ***/
903   /*** rich comparisons ***/
904         NULL,                       /* richcmpfunc tp_richcompare; */
905
906   /***  weak reference enabler ***/
907         0,                          /* long tp_weaklistoffset; */
908
909   /*** Added in release 2.2 ***/
910         /*   Iterators */
911         NULL,                       /* getiterfunc tp_iter; */
912         NULL,                       /* iternextfunc tp_iternext; */
913
914   /*** Attribute descriptor and subclassing stuff ***/
915         BPy_Text_methods,           /* struct PyMethodDef *tp_methods; */
916         NULL,                       /* struct PyMemberDef *tp_members; */
917         BPy_Text_getseters,         /* struct PyGetSetDef *tp_getset; */
918         NULL,                       /* struct _typeobject *tp_base; */
919         NULL,                       /* PyObject *tp_dict; */
920         NULL,                       /* descrgetfunc tp_descr_get; */
921         NULL,                       /* descrsetfunc tp_descr_set; */
922         0,                          /* long tp_dictoffset; */
923         NULL,                       /* initproc tp_init; */
924         NULL,                       /* allocfunc tp_alloc; */
925         NULL,                       /* newfunc tp_new; */
926         /*  Low-level free-memory routine */
927         NULL,                       /* freefunc tp_free;  */
928         /* For PyObject_IS_GC */
929         NULL,                       /* inquiry tp_is_gc;  */
930         NULL,                       /* PyObject *tp_bases; */
931         /* method resolution order */
932         NULL,                       /* PyObject *tp_mro;  */
933         NULL,                       /* PyObject *tp_cache; */
934         NULL,                       /* PyObject *tp_subclasses; */
935         NULL,                       /* PyObject *tp_weaklist; */
936         NULL
937 };