Added a documentation panel with primitive word-wrap functionality. It can be display...
authorIan Thompson <quornian@googlemail.com>
Fri, 18 Jul 2008 23:12:19 +0000 (23:12 +0000)
committerIan Thompson <quornian@googlemail.com>
Fri, 18 Jul 2008 23:12:19 +0000 (23:12 +0000)
release/scripts/textplugin_functiondocs.py [new file with mode: 0644]
source/blender/blenkernel/BKE_suggestions.h
source/blender/blenkernel/intern/suggestions.c
source/blender/python/api2_2x/Text.c
source/blender/src/drawtext.c

diff --git a/release/scripts/textplugin_functiondocs.py b/release/scripts/textplugin_functiondocs.py
new file mode 100644 (file)
index 0000000..2277b7e
--- /dev/null
@@ -0,0 +1,68 @@
+#!BPY
+"""
+Name: 'Function Documentation'
+Blender: 246
+Group: 'TextPlugin'
+Shortcut: 'Ctrl+I'
+Tooltip: 'Attempts to display documentation about the function preceding the cursor.'
+"""
+
+# Only run if we have the required modules
+try:
+       import bpy
+       from BPyTextPlugin import *
+       OK = True
+except ImportError:
+       OK = False
+
+def main():
+       txt = bpy.data.texts.active
+       if not txt:
+               return
+       
+       (line, c) = current_line(txt)
+       
+       # Check we are in a normal context
+       if get_context(txt) != NORMAL:
+               return
+       
+       # Look backwards for first '(' without ')'
+       b = 0
+       for i in range(c-1, -1, -1):
+               if line[i] == ')': b += 1
+               elif line[i] == '(':
+                       b -= 1
+                       if b < 0:
+                               c = i
+                               break
+       
+       pre = get_targets(line, c)
+       
+       if len(pre) == 0:
+               return
+       
+       imports = get_imports(txt)
+       builtins = get_builtins()
+       
+       # Identify the root (root.sub.sub.)
+       if imports.has_key(pre[0]):
+               obj = imports[pre[0]]
+       elif builtins.has_key(pre[0]):
+               obj = builtins[pre[0]]
+       else:
+               return
+       
+       # Step through sub-attributes
+       try:
+               for name in pre[1:]:
+                       obj = getattr(obj, name)
+       except AttributeError:
+               print "Attribute not found '%s' in '%s'" % (name, '.'.join(pre))
+               return
+       
+       if hasattr(obj, '__doc__') and obj.__doc__:
+               txt.showDocs(obj.__doc__)
+
+# Check we are running as a script and not imported as a module
+if __name__ == "__main__" and OK:
+       main()
index d0f982263c001892c6e8f4f1de8cb38462d489d9..cbddbcfc8add144d5d4b9684361167b61d4b7f36 100644 (file)
@@ -60,20 +60,26 @@ typedef struct SuggList {
        SuggItem *selected;
 } SuggList;
 
+/* Free all suggestion related memory */
 void free_suggestions();
 
+/* Used to identify which Text object the current tools should appear against */
+void suggest_set_active(Text *text);
+void suggest_clear_active();
+short suggest_is_active(Text *text);
+
 void suggest_add(const char *name, char type);
 void suggest_prefix(const char *prefix);
 SuggItem *suggest_first();
 SuggItem *suggest_last();
 
-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();
 
+void suggest_documentation(const char *docs);
+char *suggest_get_docs();
+void suggest_clear_docs();
+
 #ifdef __cplusplus
 }
 #endif
index dd5b770db933eeb73118947c099cb081a6dec421..e2c951f228490d982dc1ba4f9e8b33ccc34028ff 100644 (file)
 #include "BKE_text.h"
 #include "BKE_suggestions.h"
 
-static SuggList suggestions= {NULL, NULL, NULL, NULL, NULL};
+static SuggList suggestions = {NULL, NULL, NULL, NULL, NULL};
 static Text *suggText = NULL;
-static SuggItem *lastInsert= NULL;
+static SuggItem *lastInsert = NULL;
+static char *documentation = NULL;
+static int doc_lines = 0;
 
-static suggest_cmp(const char *first, const char *second, int len) {   
+static int suggest_cmp(const char *first, const char *second, int len) {       
        int cmp, i;
        for (cmp=0, i=0; i<len; i++) {
-               if (cmp= toupper(first[i]) - toupper(second[i])) {
+               if (cmp= toupper(first[i])-toupper(second[i])) {
                        break;
                }
        }
        return cmp;
 }
-void free_suggestions() {
+
+static void sugg_free() {
        SuggItem *item, *prev;
        for (item = suggestions.last; item; item=prev) {
                prev = item->prev;
@@ -61,6 +64,18 @@ void free_suggestions() {
        suggestions.selected = NULL;
 }
 
+static void docs_free() {
+       if (documentation) {
+               MEM_freeN(documentation);
+               documentation = NULL;
+       }
+}
+
+void free_suggestions() {
+       sugg_free();
+       docs_free();
+}
+
 void suggest_add(const char *name, char type) {
        SuggItem *newitem;
 
@@ -87,7 +102,7 @@ void suggest_add(const char *name, char type) {
 
 void suggest_prefix(const char *prefix) {
        SuggItem *match, *first, *last;
-       int cmp, len = strlen(prefix), i;
+       int cmp, len = strlen(prefix);
 
        if (!suggestions.first) return;
        if (len==0) {
@@ -111,10 +126,13 @@ void suggest_prefix(const char *prefix) {
        }
        if (first) {
                if (!last) last = suggestions.last;
-               suggestions.selected = suggestions.firstmatch = first;
+               suggestions.firstmatch = first;
                suggestions.lastmatch = last;
+               suggestions.selected = first;
        } else {
-               suggestions.firstmatch = suggestions.lastmatch = NULL;
+               suggestions.firstmatch = NULL;
+               suggestions.lastmatch = NULL;
+               suggestions.selected = NULL;
        }
 }
 
@@ -126,11 +144,13 @@ SuggItem *suggest_last() {
        return suggestions.lastmatch;
 }
 
-void suggest_set_text(Text *text) {
+void suggest_set_active(Text *text) {
+       if (suggText == text) return;
+       suggest_clear_active();
        suggText = text;
 }
 
-void suggest_clear_text() {
+void suggest_clear_active() {
        free_suggestions();
        suggText = NULL;
 }
@@ -146,3 +166,37 @@ void suggest_set_selected(SuggItem *sel) {
 SuggItem *suggest_get_selected() {
        return suggestions.selected;
 }
+
+/* Documentation methods */
+
+void suggest_documentation(const char *docs) {
+       int len;
+
+       if (!docs) return;
+
+       len = strlen(docs);
+
+       if (documentation) {
+               MEM_freeN(documentation);
+               documentation = NULL;
+       }
+
+       /* Ensure documentation ends with a '\n' */
+       if (docs[len-1] != '\n') {
+               documentation = MEM_mallocN(len+2, "Documentation");
+               strncpy(documentation, docs, len);
+               documentation[len++] = '\n';
+       } else {
+               documentation = MEM_mallocN(len+1, "Documentation");
+               strncpy(documentation, docs, len);
+       }
+       documentation[len] = '\0';
+}
+
+char *suggest_get_docs() {
+       return documentation;
+}
+
+void suggest_clear_docs() {
+       docs_free();
+}
index 836d824e3b2774b7ce5f50131103d6bd73bfbfbb..ecfb6ba10187fa14f37f10946662179cd46707b2 100644 (file)
@@ -103,6 +103,7 @@ 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 );
+static PyObject *Text_showDocs( BPy_Text * self, PyObject * args );
 
 /*****************************************************************************/
 /* Python BPy_Text methods table:                                            */
@@ -136,7 +137,9 @@ static PyMethodDef BPy_Text_methods[] = {
        {"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', 'k' for module, variable, function and keyword respectively"},
+        "(list, prefix) - List of tuples of the form (name, type) where type is one of 'm', 'v', 'f', 'k' for module, variable, function and keyword respectively"},
+       {"showDocs", ( PyCFunction ) Text_showDocs, METH_VARARGS,
+        "(docs) - Documentation string"},
        {NULL, NULL, 0, NULL}
 };
 
@@ -548,7 +551,7 @@ static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args )
        int row, col;
        int oldstate;
 
-       if(!self->text)
+       if (!self->text)
                return EXPP_ReturnPyObjError(PyExc_RuntimeError,
                                              "This object isn't linked to a Blender Text Object");
 
@@ -573,12 +576,12 @@ static PyObject *Text_suggest( BPy_Text * self, PyObject * args )
        char *prefix, *name, type;
        SpaceText *st;
 
-       if(!self->text)
+       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))
+       if (!PyArg_ParseTuple(args, "O!s", &PyList_Type, &list, &prefix))
                return EXPP_ReturnPyObjError(PyExc_TypeError,
                                "expected list of tuples followed by a string");
 
@@ -591,8 +594,8 @@ static PyObject *Text_suggest( BPy_Text * self, PyObject * args )
                return EXPP_ReturnPyObjError(PyExc_RuntimeError,
                                "Active text area has no Text object");
        
+       suggest_set_active(st->text);
        list_len = PyList_Size(list);
-       suggest_clear_text();
        
        for (i = 0; i < list_len; i++) {
                item = PyList_GetItem(list, i);
@@ -610,7 +613,35 @@ static PyObject *Text_suggest( BPy_Text * self, PyObject * args )
                suggest_add(name, type);
        }
        suggest_prefix(prefix);
-       suggest_set_text(st->text);
+       scrarea_queue_redraw(curarea);
+
+       Py_RETURN_NONE;
+}
+
+static PyObject *Text_showDocs( BPy_Text * self, PyObject * args )
+{
+       char *docs;
+       SpaceText *st;
+
+       if (!self->text)
+               return EXPP_ReturnPyObjError(PyExc_RuntimeError,
+                               "This object isn't linked to a Blender Text Object");
+
+       if (!PyArg_ParseTuple(args, "s", &docs))
+               return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                             "expected a string as argument" );
+
+       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");
+
+       suggest_set_active(st->text);
+       suggest_documentation(docs);
        scrarea_queue_redraw(curarea);
 
        Py_RETURN_NONE;
index ae994b3e61dc828b6846a40d67782dda9cdef56a..4ae87f64c47deebdfd0ac76b82301febc3e55963 100644 (file)
 #include "blendef.h" 
 #include "winlay.h"
 
-#define TEXTXLOC       38
+#define TEXTXLOC               38
 
-#define SUGG_LIST_SIZE 7
-#define SUGG_LIST_WIDTH 20
+#define SUGG_LIST_SIZE 7
+#define SUGG_LIST_WIDTH        20
+#define DOC_WIDTH              40
+
+#define TOOL_SUGG_LIST 0x01
+#define TOOL_DOCUMENT  0x02
 
 /* forward declarations */
 
@@ -106,6 +110,7 @@ static int check_numbers(char *string);
 static int check_builtinfuncs(char *string);
 static int check_specialvars(char *string);
 static int check_identifier(char ch);
+static int check_whitespace(char ch);
 
 static void get_suggest_prefix(Text *text);
 static void confirm_suggestion(Text *text, int skipleft);
@@ -1068,7 +1073,78 @@ static int do_suggest_select(SpaceText *st)
        return 1;
 }
 
-void draw_suggestion_list(SpaceText *st) {
+void draw_documentation(SpaceText *st)
+{
+       TextLine *tmp;
+       char *docs, buf[DOC_WIDTH+1];
+       int len, prevsp, i, a;
+       int boxw=0, boxh, l, x, y;
+       
+       if (!st || !st->text) return;
+       if (!suggest_is_active(st->text)) return;
+       
+       docs = suggest_get_docs();
+
+       if (!docs) return;
+
+       /* Count the visible lines to the cursor */
+       for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
+       if (l<0) return;
+       
+       if(st->showlinenrs) {
+               x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
+       } else {
+               x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
+       }
+       if (suggest_first()) {
+               x += SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 50;
+       }
+       y = curarea->winy - st->lheight*l - 2;
+
+       len = strlen(docs);
+
+       boxw = DOC_WIDTH*spacetext_get_fontwidth(st) + 20;
+       boxh = (2*len/DOC_WIDTH+1)*st->lheight + 8; /* Rough guess at box height */
+       
+       BIF_ThemeColor(TH_SHADE1);
+       glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
+       BIF_ThemeColor(TH_BACK);
+       glRecti(x, y, x+boxw, y-boxh);
+       BIF_ThemeColor(TH_TEXT);
+
+       len = strlen(docs);
+       prevsp = a = 0;
+
+       for (i=0; i<len; i++) {
+               if (docs[i] == ' ' || docs[i] == '\t' || docs[i] == '\n') {
+
+                       /* If we would exceed the line length, print up to the last space */
+                       if (a + i-prevsp > DOC_WIDTH) {
+                               y -= st->lheight;
+                               buf[a] = '\0';
+                               text_draw(st, buf, 0, 0, 1, x+4, y-1, NULL);
+                               a = 0;
+                       }
+
+                       /* Buffer up the next bit ready to draw */
+                       if (i-prevsp > DOC_WIDTH) break; /* TODO: Deal with long, unbroken strings */
+                       strncpy(buf+a, docs+prevsp, i-prevsp);
+                       a += i-prevsp;
+                       prevsp = i;
+
+                       /* Hit a new line, print what we have */
+                       if (docs[i] == '\n') {
+                               y -= st->lheight;
+                               buf[a] = '\0';
+                               text_draw(st, buf, 0, 0, 1, x+4, y-1, NULL);
+                               a = 0;
+                       }
+               }
+       }
+}
+
+void draw_suggestion_list(SpaceText *st)
+{
        SuggItem *item, *first, *last, *sel;
        TextLine *tmp;
        char str[SUGG_LIST_WIDTH+1];
@@ -1079,6 +1155,9 @@ void draw_suggestion_list(SpaceText *st) {
 
        first = suggest_first();
        last = suggest_last();
+
+       if (!first || !last) return;
+
        sel = suggest_get_selected();
 
        /* Count the visible lines to the cursor */
@@ -1208,6 +1287,7 @@ void drawtextspace(ScrArea *sa, void *spacedata)
        }
 
        draw_textscroll(st);
+       draw_documentation(st);
        draw_suggestion_list(st);
 
        curarea->win_swap= WIN_BACK_OK;
@@ -1693,7 +1773,7 @@ static void confirm_suggestion(Text *text, int skipleft) {
        for (i=0; i<skipleft; i++)
                txt_move_right(text, 0);
 
-       suggest_clear_text();
+       suggest_clear_active();
 }
 
 void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
@@ -1704,7 +1784,7 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
        SpaceText *st= curarea->spacedata.first;
        Text *text;
        int do_draw=0, p;
-       int suggesting=0, do_suggest=0; /* 0:just redraw, -1:clear, 1:update prefix */
+       int tools=0, tools_cancel=0, tools_update=0; /* Bitmasks for operations */
        
        if (st==NULL || st->spacetype != SPACE_TEXT) return;
        
@@ -1768,7 +1848,10 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                return;
        }
 
-       suggesting = st->showsyntax && suggest_is_active(text);
+       if (st->showsyntax && suggest_is_active(text)) {
+               if (suggest_first()) tools |= TOOL_SUGG_LIST;
+               if (suggest_get_docs()) tools |= TOOL_DOCUMENT;
+       }
        
        if (event==LEFTMOUSE) {
                if (val) {
@@ -1779,10 +1862,9 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        
                        if (mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<curarea->winy-2) {
                                do_textscroll(st, 2);
-                               do_suggest= -1;
+                               tools_cancel |= TOOL_SUGG_LIST | TOOL_DOCUMENT;
                        } else if (do_suggest_select(st)) {
                                do_draw= 1;
-                               do_suggest= 0;
                        } else {
                                do_selection(st, G.qual&LR_SHIFTKEY);
                                if (txt_has_sel(text)) {
@@ -1791,7 +1873,7 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                        MEM_freeN(buffer);
                                }
                                do_draw= 1;
-                               do_suggest= -1;
+                               tools_cancel |= TOOL_SUGG_LIST | TOOL_DOCUMENT;
                        }
                }
        } else if (event==MIDDLEMOUSE) {
@@ -1799,15 +1881,16 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        if (do_suggest_select(st)) {
                                confirm_suggestion(text, 0);
                                do_draw= 1;
-                               do_suggest= 0;
-                       } else if (U.uiflag & USER_MMB_PASTE) {
-                               do_selection(st, G.qual&LR_SHIFTKEY);
-                               get_selection_buffer(text);
-                               do_draw= 1;
                        } else {
-                               do_textscroll(st, 1);
+                               if (U.uiflag & USER_MMB_PASTE) {
+                                       do_selection(st, G.qual&LR_SHIFTKEY);
+                                       get_selection_buffer(text);
+                                       do_draw= 1;
+                               } else {
+                                       do_textscroll(st, 1);
+                               }
+                               tools_cancel |= TOOL_SUGG_LIST | TOOL_DOCUMENT;
                        }
-                       do_suggest= -1;
                }
        } else if (event==RIGHTMOUSE) {
                if (val) {
@@ -1840,7 +1923,7 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                default:
                                        break;
                        }
-                       do_suggest= -1;
+                       tools_cancel |= TOOL_SUGG_LIST | TOOL_DOCUMENT;
                }
        } else if (ascii) {
                if (text && text->id.lib) {
@@ -1850,19 +1933,23 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        if (st->showsyntax) get_format_string(st);
                        pop_space_text(st);
                        do_draw= 1;
-                       if (suggesting) {
-                               if (ispunct(ascii) || check_whitespace(ascii)) {
+                       if (tools & TOOL_SUGG_LIST) {
+                               if ((ascii != '_' && ascii != '*' && ispunct(ascii)) || check_whitespace(ascii)) {
                                        confirm_suggestion(text, 1);
                                        if (st->showsyntax) get_format_string(st);
-                                       do_suggest= 0;
                                } else {
-                                       do_suggest= 1;
+                                       tools_update |= TOOL_SUGG_LIST;
                                }
                        }
+                       tools_cancel |= TOOL_DOCUMENT;
                }
        } else if (val) {
-               do_suggest= -1; /* Note that the default label sets this to 0,
-                                               so -1 only applies to the explicit cases below */
+
+               /* Cases that require tools not to be cancelled must explicitly say so.
+                * The default case does this to prevent window/mousemove events
+                * from cancelling. */
+               tools_cancel |= TOOL_SUGG_LIST | TOOL_DOCUMENT;
+
                switch (event) {
                case AKEY:
                        if (G.qual & LR_ALTKEY) {
@@ -2126,7 +2213,10 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        }
                        break;
                case ESCKEY:
-                       do_suggest= -1;
+                       /* To allow ESC to close one tool at a time we remove all others from the cancel list */
+                       if (tools & TOOL_DOCUMENT) {
+                               tools_cancel &= ~TOOL_SUGG_LIST;
+                       }
                        break;
                case TABKEY:
                        if (text && text->id.lib) {
@@ -2157,7 +2247,7 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                error_libdata();
                                break;
                        }
-                       if (suggesting) {
+                       if (tools & TOOL_SUGG_LIST) {
                                confirm_suggestion(text, 0);
                                if (st->showsyntax) get_format_string(st);
                                break;
@@ -2186,17 +2276,14 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        }
                        if (G.qual & (LR_ALTKEY | LR_CTRLKEY)) {
                                txt_backspace_word(text);
-                               do_suggest= -1;
+                               tools_cancel |= TOOL_SUGG_LIST;
                        } else {
                                /* Work out which char we are about to delete */
                                if (text && text->curl && text->curc > 0) {
                                        char ch= text->curl->line[text->curc-1];
-                                       if (ispunct(ch) || check_whitespace(ch))
-                                               do_suggest= -1;
-                                       else
-                                               do_suggest= 1;
-                               } else {
-                                       do_suggest= -1;
+                                       if (!ispunct(ch) && !check_whitespace(ch)) {
+                                               tools_update |= TOOL_SUGG_LIST;
+                                       }
                                }
                                txt_backspace_char(text);
                        }
@@ -2223,17 +2310,17 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                case INSERTKEY:
                        st->overwrite= !st->overwrite;
                        do_draw= 1;
-                       do_suggest= 0;
+                       tools_cancel = 0;
                        break;
                case DOWNARROWKEY:
-                       if (suggesting) {
+                       if (tools & TOOL_SUGG_LIST) {
                                SuggItem *sel = suggest_get_selected();
                                if (!sel) {
                                        suggest_set_selected(suggest_first());
                                } else if (sel!=suggest_last() && sel->next) {
                                        suggest_set_selected(sel->next);
                                }
-                               do_suggest= 0;
+                               tools_cancel &= ~TOOL_SUGG_LIST;
                                break;
                        }
                        txt_move_down(text, G.qual & LR_SHIFTKEY);
@@ -2264,11 +2351,11 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        pop_space_text(st);
                        break;
                case UPARROWKEY:
-                       if (suggesting) {
+                       if (tools & TOOL_SUGG_LIST) {
                                SuggItem *sel = suggest_get_selected();
                                if (sel && sel!=suggest_first() && sel->prev)
                                        suggest_set_selected(sel->prev);
-                               do_suggest= 0;
+                               tools_cancel &= ~TOOL_SUGG_LIST;
                                break;
                        }
                        txt_move_up(text, G.qual & LR_SHIFTKEY);
@@ -2277,14 +2364,14 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        pop_space_text(st);
                        break;
                case PAGEDOWNKEY:
-                       if (suggesting) {
+                       if (tools & TOOL_SUGG_LIST) {
                                int i;
                                SuggItem *sel = suggest_get_selected();
                                if (!sel)
                                        sel = suggest_first();
                                for (i=0; i<SUGG_LIST_SIZE-1 && sel && sel!=suggest_last() && sel->next; i++, sel=sel->next)
                                        suggest_set_selected(sel->next);
-                               do_suggest= 0;
+                               tools_cancel &= ~TOOL_SUGG_LIST;
                                break;
                        } else {
                                screen_skip(st, st->viewlines);
@@ -2292,12 +2379,12 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        do_draw= 1;
                        break;
                case PAGEUPKEY:
-                       if (suggesting) {
+                       if (tools & TOOL_SUGG_LIST) {
                                int i;
                                SuggItem *sel = suggest_get_selected();
                                for (i=0; i<SUGG_LIST_SIZE-1 && sel && sel!=suggest_first() && sel->prev; i++, sel=sel->prev)
                                        suggest_set_selected(sel->prev);
-                               do_suggest= 0;
+                               tools_cancel &= ~TOOL_SUGG_LIST;
                                break;
                        } else {
                                screen_skip(st, -st->viewlines);
@@ -2315,33 +2402,35 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        pop_space_text(st);
                        break;
                case WHEELUPMOUSE:
-                       if (suggesting) {
+                       if (tools & TOOL_SUGG_LIST) {
                                SuggItem *sel = suggest_get_selected();
                                if (sel && sel!=suggest_first() && sel->prev)
                                        suggest_set_selected(sel->prev);
-                               do_suggest= 0;
+                               tools_cancel &= ~TOOL_SUGG_LIST;
                        } else {
                                screen_skip(st, -U.wheellinescroll);
+                               tools_cancel &= ~TOOL_DOCUMENT;
                        }
                        do_draw= 1;
                        break;
                case WHEELDOWNMOUSE:
-                       if (suggesting) {
+                       if (tools & TOOL_SUGG_LIST) {
                                SuggItem *sel = suggest_get_selected();
                                if (!sel) {
                                        suggest_set_selected(suggest_first());
                                } else if (sel && sel!=suggest_last() && sel->next) {
                                        suggest_set_selected(sel->next);
                                }
-                               do_suggest= 0;
+                               tools_cancel &= ~TOOL_SUGG_LIST;
                        } else {
                                screen_skip(st, U.wheellinescroll);
+                               tools_cancel &= ~TOOL_DOCUMENT;
                        }
                        do_draw= 1;
                        break;
                default:
                        /* We don't want all sorts of events closing the suggestions box */
-                       do_suggest= 0;
+                       tools_cancel &= ~TOOL_SUGG_LIST & ~TOOL_DOCUMENT;
                }
        }
 
@@ -2404,11 +2493,17 @@ void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                }
        }
 
-       if (suggesting) {
-               if (do_suggest == -1) {
-                       suggest_clear_text();
-               } else if (do_suggest == 1) {
+       if (tools & TOOL_SUGG_LIST) {
+               if (tools_update & TOOL_SUGG_LIST) {
                        get_suggest_prefix(text);
+               } else if (tools_cancel & TOOL_SUGG_LIST) {
+                       suggest_clear_active();
+               }
+               do_draw= 1;
+       }
+       if (tools & TOOL_DOCUMENT) {
+               if (tools_cancel & TOOL_DOCUMENT) {
+                       suggest_clear_docs();
                }
                do_draw= 1;
        }