Text plugin basis with plugin for suggestions/completions. The suggest plugin works for imported global variables, methods, modules and module members. For example typing:
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.
This commit is contained in:
234
release/scripts/textplugin_suggest.py
Normal file
234
release/scripts/textplugin_suggest.py
Normal file
@@ -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()
|
||||
77
source/blender/blenkernel/BKE_suggestions.h
Normal file
77
source/blender/blenkernel/BKE_suggestions.h
Normal file
@@ -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
|
||||
125
source/blender/blenkernel/intern/suggestions.c
Normal file
125
source/blender/blenkernel/intern/suggestions.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -34,8 +34,11 @@
|
||||
#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 */
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user