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