Merge with trunk r41342
[blender.git] / source / blender / blenkernel / intern / suggestions.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008, Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Ian Thompson.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/blenkernel/intern/suggestions.c
29  *  \ingroup bke
30  */
31
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #include "MEM_guardedalloc.h"
38 #include "DNA_text_types.h"
39 #include "BKE_suggestions.h"
40
41 /**********************/
42 /* Static definitions */
43 /**********************/
44
45 static Text *activeToolText = NULL;
46 static SuggList suggestions = {NULL, NULL, NULL, NULL, NULL};
47 static char *documentation = NULL;
48 //static int doc_lines = 0;
49
50 static int txttl_cmp(const char *first, const char *second, int len) {  
51         int cmp, i;
52         for (cmp=0, i=0; i<len; i++) {
53                 if ( (cmp= toupper(first[i])-toupper(second[i])) ) {
54                         break;
55                 }
56         }
57         return cmp;
58 }
59
60 static void txttl_free_suggest(void) {
61         SuggItem *item, *prev;
62         for (item = suggestions.last; item; item=prev) {
63                 prev = item->prev;
64                 MEM_freeN(item);
65         }
66         suggestions.first = suggestions.last = NULL;
67         suggestions.firstmatch = suggestions.lastmatch = NULL;
68         suggestions.selected = NULL;
69         suggestions.top = 0;
70 }
71
72 static void txttl_free_docs(void) {
73         if (documentation) {
74                 MEM_freeN(documentation);
75                 documentation = NULL;
76         }
77 }
78
79 /**************************/
80 /* General tool functions */
81 /**************************/
82
83 void free_texttools(void) {
84         txttl_free_suggest();
85         txttl_free_docs();
86 }
87
88 void texttool_text_set_active(Text *text) {
89         if (activeToolText == text) return;
90         texttool_text_clear();
91         activeToolText = text;
92 }
93
94 void texttool_text_clear(void) {
95         free_texttools();
96         activeToolText = NULL;
97 }
98
99 short texttool_text_is_active(Text *text) {
100         return activeToolText==text ? 1 : 0;
101 }
102
103 /***************************/
104 /* Suggestion list methods */
105 /***************************/
106
107 void texttool_suggest_add(const char *name, char type) {
108         SuggItem *newitem, *item;
109         int len, cmp;
110
111         newitem = MEM_mallocN(sizeof(SuggItem) + strlen(name) + 1, "SuggestionItem");
112         if (!newitem) {
113                 printf("Failed to allocate memory for suggestion.\n");
114                 return;
115         }
116
117         newitem->name = (char *) (newitem + 1);
118         len = strlen(name);
119         strncpy(newitem->name, name, len);
120         newitem->name[len] = '\0';
121         newitem->type = type;
122         newitem->prev = newitem->next = NULL;
123
124         /* Perform simple linear search for ordered storage */
125         if (!suggestions.first || !suggestions.last) {
126                 suggestions.first = suggestions.last = newitem;
127         } else {
128                 cmp = -1;
129                 for (item=suggestions.last; item; item=item->prev) {
130                         cmp = txttl_cmp(name, item->name, len);
131
132                         /* Newitem comes after this item, insert here */
133                         if (cmp >= 0) {
134                                 newitem->prev = item;
135                                 if (item->next)
136                                         item->next->prev = newitem;
137                                 newitem->next = item->next;
138                                 item->next = newitem;
139
140                                 /* At last item, set last pointer here */
141                                 if (item == suggestions.last)
142                                         suggestions.last = newitem;
143                                 break;
144                         }
145                 }
146                 /* Reached beginning of list, insert before first */
147                 if (cmp < 0) {
148                         newitem->next = suggestions.first;
149                         suggestions.first->prev = newitem;
150                         suggestions.first = newitem;
151                 }
152         }
153         suggestions.firstmatch = suggestions.lastmatch = suggestions.selected = NULL;
154         suggestions.top= 0;
155 }
156
157 void texttool_suggest_prefix(const char *prefix) {
158         SuggItem *match, *first, *last;
159         int cmp, len = strlen(prefix), top = 0;
160
161         if (!suggestions.first) return;
162         if (len==0) {
163                 suggestions.selected = suggestions.firstmatch = suggestions.first;
164                 suggestions.lastmatch = suggestions.last;
165                 return;
166         }
167         
168         first = last = NULL;
169         for (match=suggestions.first; match; match=match->next) {
170                 cmp = txttl_cmp(prefix, match->name, len);
171                 if (cmp==0) {
172                         if (!first) {
173                                 first = match;
174                                 suggestions.top = top;
175                         }
176                 } else if (cmp<0) {
177                         if (!last) {
178                                 last = match->prev;
179                                 break;
180                         }
181                 }
182                 top++;
183         }
184         if (first) {
185                 if (!last) last = suggestions.last;
186                 suggestions.firstmatch = first;
187                 suggestions.lastmatch = last;
188                 suggestions.selected = first;
189         } else {
190                 suggestions.firstmatch = NULL;
191                 suggestions.lastmatch = NULL;
192                 suggestions.selected = NULL;
193                 suggestions.top = 0;
194         }
195 }
196
197 void texttool_suggest_clear(void) {
198         txttl_free_suggest();
199 }
200
201 SuggItem *texttool_suggest_first(void) {
202         return suggestions.firstmatch;
203 }
204
205 SuggItem *texttool_suggest_last(void) {
206         return suggestions.lastmatch;
207 }
208
209 void texttool_suggest_select(SuggItem *sel) {
210         suggestions.selected = sel;
211 }
212
213 SuggItem *texttool_suggest_selected(void) {
214         return suggestions.selected;
215 }
216
217 int *texttool_suggest_top(void) {
218         return &suggestions.top;
219 }
220
221 /*************************/
222 /* Documentation methods */
223 /*************************/
224
225 void texttool_docs_show(const char *docs) {
226         int len;
227
228         if (!docs) return;
229
230         len = strlen(docs);
231
232         if (documentation) {
233                 MEM_freeN(documentation);
234                 documentation = NULL;
235         }
236
237         /* Ensure documentation ends with a '\n' */
238         if (docs[len-1] != '\n') {
239                 documentation = MEM_mallocN(len+2, "Documentation");
240                 strncpy(documentation, docs, len);
241                 documentation[len++] = '\n';
242         } else {
243                 documentation = MEM_mallocN(len+1, "Documentation");
244                 strncpy(documentation, docs, len);
245         }
246         documentation[len] = '\0';
247 }
248
249 char *texttool_docs_get(void) {
250         return documentation;
251 }
252
253 void texttool_docs_clear(void) {
254         txttl_free_docs();
255 }