Revision: 15351
          
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=15351
Author:   quorn
Date:     2008-06-25 15:51:54 +0200 (Wed, 25 Jun 2008)

Log Message:
-----------
Added UI for suggestions list. Works with arrow-keys and mouse wheel, accept 
with Enter, reject with Esc or click elsewhere. Mouse selection not yet 
supported. The script is called from the File->Text Plugins menu.

Tidied python script, the C suggestions functions and fixed some bugs including 
suggestions not being freed properly.

Modified Paths:
--------------
    branches/soc-2008-quorn/release/scripts/textplugin_suggest.py
    branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h
    branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c
    branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c
    branches/soc-2008-quorn/source/blender/src/drawtext.c

Modified: branches/soc-2008-quorn/release/scripts/textplugin_suggest.py
===================================================================
--- branches/soc-2008-quorn/release/scripts/textplugin_suggest.py       
2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/release/scripts/textplugin_suggest.py       
2008-06-25 13:51:54 UTC (rev 15351)
@@ -6,12 +6,11 @@
 Tooltip: 'Suggests completions for the word at the cursor in a python script'
 """
 
-import bpy
+import bpy, __builtin__, token
 from Blender  import Text
 from StringIO import StringIO
 from inspect  import *
 from tokenize import generate_tokens
-import token
 
 TK_TYPE  = 0
 TK_TOKEN = 1
@@ -21,32 +20,48 @@
 TK_ROW = 0
 TK_COL = 1
 
+execs = [] # Used to establish the same import context across defs
+
 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 getBuiltins():
+       builtins = []
+       bi = dir(__builtin__)
+       for k in bi:
+               v = eval(k)
+               if ismodule(v): t='m'
+               elif callable(v): t='f'
+               else: t='v'
+               builtins.append((k, t))
+       return builtins
+
+
+def getKeywords():
+       global keywords
+       return [(k, 'k') for k in keywords]
+
+
 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
+       lines = txt.asLines()
+       str = '\n'.join(lines)
+       readline = StringIO(str).readline
+       g = generate_tokens(readline)
+       tokens = []
+       for t in g: tokens.append(t)
+       return tokens
 
+
 def isNameChar(s):
-       return s.isalnum() or s in ['_']
+       return (s.isalnum() or s == '_')
 
-# Returns words preceding the cursor that are separated by periods as a list 
in the
-# same order
+
+# 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()
@@ -68,7 +83,14 @@
 def getGlobals(txt):
        global execs
        
-       tokens = getTokens(txt)
+       # Unfortunately, tokenize may fail if the script leaves brackets or 
strings
+       # open. For now we return an empty list, leaving builtins and keywords 
as
+       # the only globals. (on the TODO list)
+       try:
+               tokens = getTokens(txt)
+       except:
+               return []
+       
        globals = dict()
        for i in range(len(tokens)):
                
@@ -92,6 +114,7 @@
                                                x = tokens[i][TK_LINE].strip()
                                                k = tokens[i][TK_TOKEN]
                                                execs.append(x)
+                                               exec 'try: '+x+'\nexcept: pass'
                                                
                                                # Add the symbol name to the 
return list
                                                globals[k] = 'm'
@@ -105,16 +128,17 @@
                                # Add the import to the execs list
                                x = tokens[i][TK_LINE].strip()
                                execs.append(x)
+                               exec 'try: '+x+'\nexcept: pass'
                                
                                # 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
+                               exec 'try: import '+parent+'\nexcept: pass'
                                
                                # All submodules, functions, etc.
                                if tokens[i][TK_TOKEN]=='*':
                                        
                                        # Add each symbol name to the return 
list
-                                       exec "d="+parent+".__dict__.items()"
+                                       d = eval(parent).__dict__.items()
                                        for k,v in d:
                                                if not globals.has_key(k) or 
not globals[k]:
                                                        t='v'
@@ -130,7 +154,7 @@
                                                        if not 
globals.has_key(k) or not globals[k]:
                                                                t='v'
                                                                try:
-                                                                       exec 
'v='+parent+'.'+k
+                                                                       v = 
eval(parent+'.'+k)
                                                                        if 
ismodule(v): t='m'
                                                                        elif 
callable(v): t='f'
                                                                except: pass
@@ -149,36 +173,14 @@
                                        t='v'
                                globals[k] = t
        
-       return globals
+       return globals.items()
 
-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)
+       return globals
 
+
 # Only works for 'static' members (eg. Text.Get)
 def memberSuggest(txt, cs):
        global execs
@@ -194,29 +196,29 @@
        suggestions = dict()
        (row, col) = txt.getCursorPos()
        
-       sub = cs[len(cs)-1].lower()
-       print 'Search:', sub
+       sub = cs[len(cs)-1]
        
-       t=None
+       m=None
        pre='.'.join(cs[:-1])
        try:
-               exec "t="+pre
+               m = eval(pre)
        except:
-               print 'Failed to assign '+pre
-               print execs
-               print cs
+               print pre+ ' not found or not imported.'
        
-       if t!=None:
-               for k,v in t.__dict__.items():
+       if m!=None:
+               for k,v in m.__dict__.items():
                        if ismodule(v): t='m'
                        elif callable(v): t='f'
                        else: t='v'
-                       if k.lower().startswith(sub):
-                               suggestions[k] = t
+                       suggestions[k] = t
        
-       l = list(suggestions.items())
-       return sorted (l, cmp=cmpi0)
+       return suggestions.items()
 
+
+def cmp0(x, y):
+       return cmp(x[0], y[0])
+
+
 def main():
        txt = bpy.data.texts.active
        if txt==None: return
@@ -225,10 +227,12 @@
        
        if len(cs)<=1:
                l = globalSuggest(txt, cs)
-               txt.suggest(l, cs[len(cs)-1])
-               
+               l.extend(getBuiltins())
+               l.extend(getKeywords())
        else:
                l = memberSuggest(txt, cs)
-               txt.suggest(l, cs[len(cs)-1])
+       
+       l.sort(cmp=cmp0)
+       txt.suggest(l, cs[len(cs)-1])
 
 main()

Modified: branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h
===================================================================
--- branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h 
2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h 
2008-06-25 13:51:54 UTC (rev 15351)
@@ -57,19 +57,23 @@
 typedef struct SuggList {
        SuggItem *first, *last;
        SuggItem *firstmatch, *lastmatch;
+       SuggItem *selected;
 } SuggList;
 
 void free_suggestions();
 
-void add_suggestion(const char *name, char type);
-void update_suggestions(const char *prefix);
+void suggest_add(const char *name, char type);
+void suggest_prefix(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);
+void suggest_set_text(Text *text);
+void suggest_clear_text();
+short suggest_is_active(Text *text);
 
+void suggest_set_selected(SuggItem *sel);
+SuggItem *suggest_get_selected();
+
 #ifdef __cplusplus
 }
 #endif

Modified: branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c
===================================================================
--- branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c      
2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c      
2008-06-25 13:51:54 UTC (rev 15351)
@@ -40,14 +40,17 @@
 static Text *suggText = NULL;
 
 void free_suggestions() {
-       SuggItem *item;
-       for (item = suggestions.last; item; item=item->prev)
+       SuggItem *item, *prev;
+       for (item = suggestions.last; item; item=prev) {
+               prev = item->prev;
                MEM_freeN(item);
+       }
        suggestions.first = suggestions.last = NULL;
        suggestions.firstmatch = suggestions.lastmatch = NULL;
+       suggestions.selected = NULL;
 }
 
-void add_suggestion(const char *name, char type) {
+void suggest_add(const char *name, char type) {
        SuggItem *newitem;
 
        newitem = MEM_mallocN(sizeof(SuggItem) + strlen(name) + 1, 
"SuggestionItem");
@@ -63,6 +66,7 @@
 
        if (!suggestions.first) {
                suggestions.first = suggestions.last = newitem;
+               suggestions.selected = newitem;
        } else {
                newitem->prev = suggestions.last;
                suggestions.last->next = newitem;
@@ -70,13 +74,13 @@
        }
 }
 
-void update_suggestions(const char *prefix) {
+void suggest_prefix(const char *prefix) {
        SuggItem *match, *first, *last;
        int cmp, len = strlen(prefix);
 
        if (!suggestions.first) return;
        if (len==0) {
-               suggestions.firstmatch = suggestions.first;
+               suggestions.selected = suggestions.firstmatch = 
suggestions.first;
                suggestions.lastmatch = suggestions.last;
                return;
        }
@@ -96,10 +100,10 @@
        }
        if (first) {
                if (!last) last = suggestions.last;
-               suggestions.firstmatch = first;
+               suggestions.selected = suggestions.firstmatch = first;
                suggestions.lastmatch = last;
        } else {
-               suggestions.firstmatch = suggestions.lastmatch = NULL;
+               suggestions.selected = suggestions.firstmatch = 
suggestions.lastmatch = NULL;
        }
 }
 
@@ -111,15 +115,23 @@
        return suggestions.lastmatch;
 }
 
-void set_suggest_text(Text *text) {
+void suggest_set_text(Text *text) {
        suggText = text;
 }
 
-void clear_suggest_text() {
+void suggest_clear_text() {
        free_suggestions();
        suggText = NULL;
 }
 
-short is_suggest_active(Text *text) {
+short suggest_is_active(Text *text) {
        return suggText==text ? 1 : 0;
 }
+
+void suggest_set_selected(SuggItem *sel) {
+       suggestions.selected = sel;
+}
+
+SuggItem *suggest_get_selected() {
+       return suggestions.selected;
+}

Modified: branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c
===================================================================
--- branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c        
2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c        
2008-06-25 13:51:54 UTC (rev 15351)
@@ -129,7 +129,7 @@
        {"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"},
+        "(list) - List of tuples of the form (name, type) where type is one of 
'm', 'v', 'f', 'k' for module, variable, function and keyword respectively"},
        {NULL, NULL, 0, NULL}
 };
 
@@ -544,7 +544,7 @@
                                "Active text area has no Text object");
        
        list_len = PyList_Size(list);
-       clear_suggest_text();
+       suggest_clear_text();
        
        for (i = 0; i < list_len; i++) {
                item = PyList_GetItem(list, i);
@@ -555,14 +555,14 @@
                name = PyString_AsString(PyTuple_GetItem(item, 0));

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to