Text plugin basis with plugin for suggestions/completions. The suggest plugin works...
authorIan Thompson <quornian@googlemail.com>
Tue, 24 Jun 2008 15:25:25 +0000 (15:25 +0000)
committerIan Thompson <quornian@googlemail.com>
Tue, 24 Jun 2008 15:25:25 +0000 (15:25 +0000)
import Blender
from Blender import *
| <- cursor here suggests globals
Blender.Draw.gl| <- cursor here suggests all Draw members starting gl

Currently suggestions are listed in the console when the space is redrawn but will be presented as a menu-style list soon. Also to add are shortcut/activation keys to allow plugins to respond to certain key strokes.

release/scripts/textplugin_suggest.py [new file with mode: 0644]
source/blender/blenkernel/BKE_suggestions.h [new file with mode: 0644]
source/blender/blenkernel/intern/suggestions.c [new file with mode: 0644]
source/blender/python/BPY_interface.c
source/blender/python/BPY_menus.c
source/blender/python/BPY_menus.h
source/blender/python/api2_2x/Text.c
source/blender/python/api2_2x/doc/Text.py
source/blender/src/drawtext.c
source/blender/src/header_text.c
source/blender/src/usiblender.c

diff --git a/release/scripts/textplugin_suggest.py b/release/scripts/textplugin_suggest.py
new file mode 100644 (file)
index 0000000..77ae048
--- /dev/null
@@ -0,0 +1,234 @@
+#!BPY
+"""
+Name: 'Suggest'
+Blender: 243
+Group: 'TextPlugin'
+Tooltip: 'Suggests completions for the word at the cursor in a python script'
+"""
+
+import bpy
+from Blender  import Text
+from StringIO import StringIO
+from inspect  import *
+from tokenize import generate_tokens
+import token
+
+TK_TYPE  = 0
+TK_TOKEN = 1
+TK_START = 2 #(srow, scol)
+TK_END   = 3 #(erow, ecol)
+TK_LINE  = 4
+TK_ROW = 0
+TK_COL = 1
+
+keywords = ['and', 'del', 'from', 'not', 'while', 'as', 'elif', 'global',
+                       'or', 'with', 'assert', 'else', 'if', 'pass', 'yield',
+                       'break', 'except', 'import', 'print', 'class', 'exec', 'in',
+                       'raise', 'continue', 'finally', 'is', 'return', 'def', 'for',
+                       'lambda', 'try' ]
+
+execs = [] # Used to establish the same import context across defs (import is scope sensitive)
+
+def getTokens(txt):
+       global tokens_cached
+       if tokens_cached==None:
+               lines = txt.asLines()
+               str = '\n'.join(lines)
+               readline = StringIO(str).readline
+               g = generate_tokens(readline)
+               tokens = []
+               for t in g: tokens.append(t)
+               tokens_cached = tokens
+       return tokens_cached
+tokens_cached = None
+
+def isNameChar(s):
+       return s.isalnum() or s in ['_']
+
+# Returns words preceding the cursor that are separated by periods as a list in the
+# same order
+def getCompletionSymbols(txt):
+       (l, c)= txt.getCursorPos()
+       lines = txt.asLines()
+       line = lines[l]
+       a=0
+       for a in range(1, c+1):
+               if not isNameChar(line[c-a]) and line[c-a]!='.':
+                       a -= 1
+                       break
+       return line[c-a:c].split('.')
+
+
+# Returns a list of tuples of symbol names and their types (name, type) where
+# type is one of:
+#   m (module/class)  Has its own members (includes classes)
+#   v (variable)      Has a type which may have its own members
+#   f (function)      Callable and may have a return type (with its own members)
+# It also updates the global import context (via execs)
+def getGlobals(txt):
+       global execs
+       
+       tokens = getTokens(txt)
+       globals = dict()
+       for i in range(len(tokens)):
+               
+               # Handle all import statements
+               if i>=1 and tokens[i-1][TK_TOKEN]=='import':
+                       
+                       # Find 'from' if it exists
+                       fr= -1
+                       for a in range(1, i):
+                               if tokens[i-a][TK_TYPE]==token.NEWLINE: break
+                               if tokens[i-a][TK_TOKEN]=='from':
+                                       fr=i-a
+                                       break
+                       
+                       # Handle: import ___[,___]
+                       if fr<0:
+                               
+                               while True:
+                                       if tokens[i][TK_TYPE]==token.NAME:
+                                               # Add the import to the execs list
+                                               x = tokens[i][TK_LINE].strip()
+                                               k = tokens[i][TK_TOKEN]
+                                               execs.append(x)
+                                               
+                                               # Add the symbol name to the return list
+                                               globals[k] = 'm'
+                                       elif tokens[i][TK_TOKEN]!=',':
+                                               break
+                                       i += 1
+                       
+                       # Handle statement: from ___[.___] import ___[,___]
+                       else: # fr>=0:
+                               
+                               # Add the import to the execs list
+                               x = tokens[i][TK_LINE].strip()
+                               execs.append(x)
+                               
+                               # Import parent module so we can process it for sub modules
+                               parent = ''.join([t[TK_TOKEN] for t in tokens[fr+1:i-1]])
+                               exec "import "+parent
+                               
+                               # All submodules, functions, etc.
+                               if tokens[i][TK_TOKEN]=='*':
+                                       
+                                       # Add each symbol name to the return list
+                                       exec "d="+parent+".__dict__.items()"
+                                       for k,v in d:
+                                               if not globals.has_key(k) or not globals[k]:
+                                                       t='v'
+                                                       if ismodule(v): t='m'
+                                                       elif callable(v): t='f'
+                                                       globals[k] = t
+                               
+                               # Specific function, submodule, etc.
+                               else:
+                                       while True:
+                                               if tokens[i][TK_TYPE]==token.NAME:
+                                                       k = tokens[i][TK_TOKEN]
+                                                       if not globals.has_key(k) or not globals[k]:
+                                                               t='v'
+                                                               try:
+                                                                       exec 'v='+parent+'.'+k
+                                                                       if ismodule(v): t='m'
+                                                                       elif callable(v): t='f'
+                                                               except: pass
+                                                               globals[k] = t
+                                               elif tokens[i][TK_TOKEN]!=',':
+                                                       break
+                                               i += 1
+                                       
+               elif tokens[i][TK_TYPE]==token.NAME and tokens[i][TK_TOKEN] not in keywords and (i==0 or tokens[i-1][TK_TOKEN]!='.'):
+                       k = tokens[i][TK_TOKEN]
+                       if not globals.has_key(k) or not globals[k]:
+                               t=None
+                               if (i>0 and tokens[i-1][TK_TOKEN]=='def'):
+                                       t='f'
+                               else:
+                                       t='v'
+                               globals[k] = t
+       
+       return globals
+
+def cmpi0(x, y):
+       return cmp(x[0].lower(), y[0].lower())
+
+def globalSuggest(txt, cs):
+       global execs
+       
+       suggestions = dict()
+       (row, col) = txt.getCursorPos()
+       globals = getGlobals(txt)
+       
+       # Sometimes we have conditional includes which will fail if the module
+       # cannot be found. So we protect outselves in a try block
+       for x in execs:
+               exec 'try: '+x+'\nexcept: pass'
+       
+       if len(cs)==0:
+               sub = ''
+       else:
+               sub = cs[0].lower()
+       print 'Search:', sub
+       
+       for k,t in globals.items():
+               if k.lower().startswith(sub):
+                       suggestions[k] = t
+       
+       l = list(suggestions.items())
+       return sorted (l, cmp=cmpi0)
+
+# Only works for 'static' members (eg. Text.Get)
+def memberSuggest(txt, cs):
+       global execs
+       
+       # Populate the execs for imports
+       getGlobals(txt)
+       
+       # Sometimes we have conditional includes which will fail if the module
+       # cannot be found. So we protect outselves in a try block
+       for x in execs:
+               exec 'try: '+x+'\nexcept: pass'
+       
+       suggestions = dict()
+       (row, col) = txt.getCursorPos()
+       
+       sub = cs[len(cs)-1].lower()
+       print 'Search:', sub
+       
+       t=None
+       pre='.'.join(cs[:-1])
+       try:
+               exec "t="+pre
+       except:
+               print 'Failed to assign '+pre
+               print execs
+               print cs
+       
+       if t!=None:
+               for k,v in t.__dict__.items():
+                       if ismodule(v): t='m'
+                       elif callable(v): t='f'
+                       else: t='v'
+                       if k.lower().startswith(sub):
+                               suggestions[k] = t
+       
+       l = list(suggestions.items())
+       return sorted (l, cmp=cmpi0)
+
+def main():
+       txt = bpy.data.texts.active
+       if txt==None: return
+       
+       cs = getCompletionSymbols(txt)
+       
+       if len(cs)<=1:
+               l = globalSuggest(txt, cs)
+               txt.suggest(l, cs[len(cs)-1])
+               
+       else:
+               l = memberSuggest(txt, cs)
+               txt.suggest(l, cs[len(cs)-1])
+
+main()
diff --git a/source/blender/blenkernel/BKE_suggestions.h b/source/blender/blenkernel/BKE_suggestions.h
new file mode 100644 (file)
index 0000000..bc4e18f
--- /dev/null
@@ -0,0 +1,77 @@
+/**    
+ * $Id: $ 
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef BKE_SUGGESTIONS_H
+#define BKE_SUGGESTIONS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ****************************************************************************
+Suggestions must be added in sorted order (no attempt is made to sort the list)
+The list is then divided up based on the prefix provided by update_suggestions:
+Example:
+  Prefix: ab
+  aaa <-- first
+  aab
+  aba <-- firstmatch
+  abb <-- lastmatch
+  baa
+  bab <-- last
+**************************************************************************** */
+
+struct Text;
+
+typedef struct SuggItem {
+       struct SuggItem *prev, *next;
+       char *name;
+       char type;
+} SuggItem;
+
+typedef struct SuggList {
+       SuggItem *first, *last;
+       SuggItem *firstmatch, *lastmatch;
+} SuggList;
+
+void free_suggestions();
+
+void add_suggestion(const char *name, char type);
+void update_suggestions(const char *prefix);
+SuggItem *suggest_first();
+SuggItem *suggest_last();
+
+void set_suggest_text(Text *text);
+void clear_suggest_text();
+short is_suggest_active(Text *text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/blender/blenkernel/intern/suggestions.c b/source/blender/blenkernel/intern/suggestions.c
new file mode 100644 (file)
index 0000000..3842146
--- /dev/null
@@ -0,0 +1,125 @@
+/**
+ * $Id: $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+#include "BLI_blenlib.h"
+#include "DNA_text_types.h"
+#include "BKE_text.h"
+#include "BKE_suggestions.h"
+
+static SuggList suggestions= {NULL, NULL, NULL, NULL};
+static Text *suggText = NULL;
+
+void free_suggestions() {
+       SuggItem *item;
+       for (item = suggestions.last; item; item=item->prev)
+               MEM_freeN(item);
+       suggestions.first = suggestions.last = NULL;
+       suggestions.firstmatch = suggestions.lastmatch = NULL;
+}
+
+void add_suggestion(const char *name, char type) {
+       SuggItem *newitem;
+
+       newitem = MEM_mallocN(sizeof(SuggItem) + strlen(name) + 1, "SuggestionItem");
+       if (!newitem) {
+               printf("Failed to allocate memory for suggestion.\n");
+               return;
+       }
+
+       newitem->name = (char *) (newitem + 1);
+       strcpy(newitem->name, name);
+       newitem->type = type;
+       newitem->prev = newitem->next = NULL;
+
+       if (!suggestions.first) {
+               suggestions.first = suggestions.last = newitem;
+       } else {
+               newitem->prev = suggestions.last;
+               suggestions.last->next = newitem;
+               suggestions.last = newitem;
+       }
+}
+
+void update_suggestions(const char *prefix) {
+       SuggItem *match, *first, *last;
+       int cmp, len = strlen(prefix);
+
+       if (!suggestions.first) return;
+       if (len==0) {
+               suggestions.firstmatch = suggestions.first;
+               suggestions.lastmatch = suggestions.last;
+               return;
+       }
+       
+       first = last = NULL;
+       for (match=suggestions.first; match; match=match->next) {
+               cmp = strncmp(prefix, match->name, len);
+               if (cmp==0) {
+                       if (!first)
+                               first = match;
+               } else if (cmp<0) {
+                       if (!last) {
+                               last = match->prev;
+                               break;
+                       }
+               }
+       }
+       if (first) {
+               if (!last) last = suggestions.last;
+               suggestions.firstmatch = first;
+               suggestions.lastmatch = last;
+       } else {
+               suggestions.firstmatch = suggestions.lastmatch = NULL;
+       }
+}
+
+SuggItem *suggest_first() {
+       return suggestions.firstmatch;
+}
+
+SuggItem *suggest_last() {
+       return suggestions.lastmatch;
+}
+
+void set_suggest_text(Text *text) {
+       suggText = text;
+}
+
+void clear_suggest_text() {
+       free_suggestions();
+       suggText = NULL;
+}
+
+short is_suggest_active(Text *text) {
+       return suggText==text ? 1 : 0;
+}
index 7c23c86d9ba19ea8d9c22f377169994cd2ddf6fe..360c8fd7f04ebf9635fd50d0175a1f62ff4ec179 100644 (file)
@@ -1066,6 +1066,7 @@ int BPY_menu_do_python( short menutype, int event )
        case PYMENU_RENDER:
        case PYMENU_WIZARDS:
        case PYMENU_SCRIPTTEMPLATE:
+       case PYMENU_TEXTPLUGIN:
        case PYMENU_MESHFACEKEY:
                break;
 
index 82da9edbee6f5f293a6787e6882d5b57b0d04f36..08691973a928968ffdaf838655b6df6961203f82 100644 (file)
@@ -106,6 +106,8 @@ static int bpymenu_group_atoi( char *str )
                return PYMENU_ARMATURE;
        else if( !strcmp( str, "ScriptTemplate" ) )
                return PYMENU_SCRIPTTEMPLATE;
+       else if( !strcmp( str, "TextPlugin" ) )
+               return PYMENU_TEXTPLUGIN;
        else if( !strcmp( str, "MeshFaceKey" ) )
                return PYMENU_MESHFACEKEY;
        else if( !strcmp( str, "AddMesh" ) )
@@ -184,6 +186,9 @@ char *BPyMenu_group_itoa( short menugroup )
        case PYMENU_SCRIPTTEMPLATE:
                return "ScriptTemplate";
                break;
+       case PYMENU_TEXTPLUGIN:
+               return "TextPlugin";
+               break;
        case PYMENU_MESHFACEKEY:
                return "MeshFaceKey";
                break;
index 1b557f79286a0a61217c10733dcba6287f4e8478..e8bca09d50eafda009e359008b8bd922acd77a90 100644 (file)
@@ -99,6 +99,7 @@ typedef enum {
        PYMENU_UVCALCULATION,
        PYMENU_ARMATURE,
        PYMENU_SCRIPTTEMPLATE,
+       PYMENU_TEXTPLUGIN,
        PYMENU_HELP,/*Main Help menu items - prob best to leave for 'official' ones*/
        PYMENU_HELPSYSTEM,/* Resources, troubleshooting, system tools */
        PYMENU_HELPWEBSITES,/* Help -> Websites submenu */
index 603deb768ad8afcc98764c3f415d115d2fa37724..63c77c0bb3e1bfecf359b66ad4b16c424571e0cf 100644 (file)
 #include "BKE_global.h"
 #include "BKE_main.h"
 #include "BIF_drawtext.h"
+#include "BIF_screen.h"
 #include "BKE_text.h"
+#include "BKE_suggestions.h"
 #include "BLI_blenlib.h"
+#include "DNA_screen_types.h"
 #include "DNA_space_types.h"
 #include "gen_utils.h"
 #include "gen_library.h"
@@ -96,6 +99,7 @@ static PyObject *Text_set( BPy_Text * self, PyObject * args );
 static PyObject *Text_asLines( BPy_Text * self );
 static PyObject *Text_getCursorPos( BPy_Text * self );
 static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args );
+static PyObject *Text_suggest( BPy_Text * self, PyObject * args );
 
 /*****************************************************************************/
 /* Python BPy_Text methods table:                                            */
@@ -124,6 +128,8 @@ static PyMethodDef BPy_Text_methods[] = {
         "() - Return cursor position as (row, col) tuple"},
        {"setCursorPos", ( PyCFunction ) Text_setCursorPos, METH_VARARGS,
         "(row, col) - Set the cursor position to (row, col)"},
+       {"suggest", ( PyCFunction ) Text_suggest, METH_VARARGS,
+        "(list) - List of tuples of the form (name, type) where type is one of 'm', 'v', 'f' for module, variable and function respectively"},
        {NULL, NULL, 0, NULL}
 };
 
@@ -511,6 +517,57 @@ static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args )
        Py_RETURN_NONE;
 }
 
+static PyObject *Text_suggest( BPy_Text * self, PyObject * args )
+{
+       PyObject *item = NULL;
+       PyObject *list = NULL, *resl = NULL;
+       int list_len, i;
+       char *prefix, *name, type;
+       SpaceText *st;
+
+       if(!self->text)
+               return EXPP_ReturnPyObjError(PyExc_RuntimeError,
+                               "This object isn't linked to a Blender Text Object");
+
+       /* Parse args for a list of tuples */
+       if(!PyArg_ParseTuple(args, "O!s", &PyList_Type, &list, &prefix))
+               return EXPP_ReturnPyObjError(PyExc_TypeError,
+                               "expected list of tuples followed by a string");
+
+       if (curarea->spacetype != SPACE_TEXT)
+               return EXPP_ReturnPyObjError(PyExc_RuntimeError,
+                               "Active space type is not text");
+       
+       st = curarea->spacedata.first;
+       if (!st || !st->text)
+               return EXPP_ReturnPyObjError(PyExc_RuntimeError,
+                               "Active text area has no Text object");
+       
+       list_len = PyList_Size(list);
+       clear_suggest_text();
+       
+       for (i = 0; i < list_len; i++) {
+               item = PyList_GetItem(list, i);
+               if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2)
+                       return EXPP_ReturnPyObjError(PyExc_AttributeError,
+                                       "list must contain only tuples of size 2" );
+
+               name = PyString_AsString(PyTuple_GetItem(item, 0));
+               type = PyString_AsString(PyTuple_GetItem(item, 1))[0];
+
+               if (!strlen(name) || (type!='m' && type!='v' && type!='f'))
+                       return EXPP_ReturnPyObjError(PyExc_AttributeError,
+                                       "layer values must be in the range [1, 20]" );
+
+               add_suggestion(name, type);
+       }
+       update_suggestions(prefix);
+       set_suggest_text(st->text);
+       scrarea_queue_redraw(curarea);
+
+       Py_RETURN_NONE;
+}
+
 /*****************************************************************************/
 /* Function:    Text_compare                                                 */
 /* Description: This is a callback function for the BPy_Text type. It        */
index 4099b13828d9e61c881475233334d58315db59aa..920908eef812e779ac7c3c104be40b7707f44d17 100644 (file)
@@ -150,5 +150,15 @@ class Text:
                        cursor.
                """
 
+       def suggest(list):
+               """
+               Set the suggestion list to the given list of tuples. This list *must* be
+               sorted by its first element, name.
+               @type list: list of tuples
+               @param list:  List of pair-tuples of the form (name, type) where name is
+                       the suggested name and type is one of 'm' (module or class), 'f'
+                       (function or method), 'v' (variable).
+               """
+
 import id_generics
-Text.__doc__ += id_generics.attributes
\ No newline at end of file
+Text.__doc__ += id_generics.attributes
index 882baa90c654929791e546c299c554333028a857..227d1f08c20953d0c2476739bf4b965c869d3f8d 100644 (file)
@@ -60,6 +60,7 @@
 #include "BKE_global.h"
 #include "BKE_main.h"
 #include "BKE_node.h"
+#include "BKE_suggestions.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
@@ -999,6 +1000,19 @@ static void do_selection(SpaceText *st, int selecting)
                txt_undo_add_toop(st->text, UNDO_STO, sell, selc, linep2, charp2);
 }
 
+void draw_suggestion_list(SpaceText *st) {
+       SuggItem *item, *last;
+       
+       if (!is_suggest_active(st->text)) return;
+
+       for (item=suggest_first(), last=suggest_last(); item; item=item->next) {
+               /* Useful for testing but soon to be replaced by UI list */
+               printf("Suggest: %c %s\n", item->type, item->name);
+               if (item == last)
+                       break;
+       }
+}
+
 void drawtextspace(ScrArea *sa, void *spacedata)
 {
        SpaceText *st= curarea->spacedata.first;
@@ -1072,6 +1086,7 @@ void drawtextspace(ScrArea *sa, void *spacedata)
        }
 
        draw_textscroll(st);
+       draw_suggestion_list(st);
 
        curarea->win_swap= WIN_BACK_OK;
 }
index 7f28109647938c80c095286e8c39ce53f2fa55f7..e371bd561605498985f58a549d06e97700507e68 100644 (file)
@@ -240,6 +240,37 @@ static uiBlock *text_template_scriptsmenu (void *args_unused)
        return block;
 }
 
+static void do_text_plugin_scriptsmenu(void *arg, int event)
+{
+       BPY_menu_do_python(PYMENU_TEXTPLUGIN, event);
+       
+       allqueue(REDRAWIMAGE, 0);
+}
+
+static uiBlock *text_plugin_scriptsmenu (void *args_unused)
+{
+       uiBlock *block;
+       BPyMenu *pym;
+       int i= 0;
+       short yco = 20, menuwidth = 120;
+       
+       block= uiNewBlock(&curarea->uiblocks, "text_plugin_scriptsmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
+       uiBlockSetButmFunc(block, do_text_plugin_scriptsmenu, NULL);
+       
+       /* note that we acount for the N previous entries with i+20: */
+       for (pym = BPyMenuTable[PYMENU_TEXTPLUGIN]; pym; pym = pym->next, i++) {
+               
+               uiDefIconTextBut(block, BUTM, 1, ICON_PYTHON, pym->name, 0, yco-=20, menuwidth, 19, 
+                                                NULL, 0.0, 0.0, 1, i, 
+                                                pym->tooltip?pym->tooltip:pym->filename);
+       }
+       
+       uiBlockSetDirection(block, UI_RIGHT);
+       uiTextBoundsBlock(block, 60);
+       
+       return block;
+}
+
 /* action executed after clicking in File menu */
 static void do_text_filemenu(void *arg, int event)
 {
@@ -726,6 +757,7 @@ static uiBlock *text_filemenu(void *arg_unused)
        }
        
        uiDefIconTextBlockBut(block, text_template_scriptsmenu, NULL, ICON_RIGHTARROW_THIN, "Script Templates", 0, yco-=20, 120, 19, "");
+       uiDefIconTextBlockBut(block, text_plugin_scriptsmenu, NULL, ICON_RIGHTARROW_THIN, "Text Plugins", 0, yco-=20, 120, 19, "");
 
        if(curarea->headertype==HEADERTOP) {
                uiBlockSetDirection(block, UI_DOWN);
index 6c0838288b84892a37052c4899d88eda2c8fb324..2e55f8cdbc237b64f48f27d80faf57bc8e3aa9e8 100644 (file)
@@ -67,6 +67,7 @@
 #include "DNA_sound_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
+#include "DNA_text_types.h"
 
 #include "BKE_blender.h"
 #include "BKE_curve.h"
@@ -79,6 +80,7 @@
 #include "BKE_mball.h"
 #include "BKE_node.h"
 #include "BKE_packedFile.h"
+#include "BKE_suggestions.h"
 #include "BKE_texture.h"
 #include "BKE_utildefines.h"
 #include "BKE_pointcache.h"
@@ -1091,6 +1093,7 @@ void exit_usiblender(void)
        free_actcopybuf();
        free_vertexpaint();
        free_imagepaint();
+       free_suggestions();
        
        /* editnurb can remain to exist outside editmode */
        freeNurblist(&editNurb);