* Merged changes in the trunk up to revision 33492.
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "DNA_text_types.h"
36 #include "BKE_suggestions.h"
37
38 /**********************/
39 /* Static definitions */
40 /**********************/
41
42 static Text *activeToolText = NULL;
43 static SuggList suggestions = {NULL, NULL, NULL, NULL, NULL};
44 static char *documentation = NULL;
45 //static int doc_lines = 0;
46
47 static int txttl_cmp(const char *first, const char *second, int len) {  
48         int cmp, i;
49         for (cmp=0, i=0; i<len; i++) {
50                 if ( (cmp= toupper(first[i])-toupper(second[i])) ) {
51                         break;
52                 }
53         }
54         return cmp;
55 }
56
57 static void txttl_free_suggest(void) {
58         SuggItem *item, *prev;
59         for (item = suggestions.last; item; item=prev) {
60                 prev = item->prev;
61                 MEM_freeN(item);
62         }
63         suggestions.first = suggestions.last = NULL;
64         suggestions.firstmatch = suggestions.lastmatch = NULL;
65         suggestions.selected = NULL;
66         suggestions.top = 0;
67 }
68
69 static void txttl_free_docs(void) {
70         if (documentation) {
71                 MEM_freeN(documentation);
72                 documentation = NULL;
73         }
74 }
75
76 /**************************/
77 /* General tool functions */
78 /**************************/
79
80 void free_texttools() {
81         txttl_free_suggest();
82         txttl_free_docs();
83 }
84
85 void texttool_text_set_active(Text *text) {
86         if (activeToolText == text) return;
87         texttool_text_clear();
88         activeToolText = text;
89 }
90
91 void texttool_text_clear() {
92         free_texttools();
93         activeToolText = NULL;
94 }
95
96 short texttool_text_is_active(Text *text) {
97         return activeToolText==text ? 1 : 0;
98 }
99
100 /***************************/
101 /* Suggestion list methods */
102 /***************************/
103
104 void texttool_suggest_add(const char *name, char type) {
105         SuggItem *newitem, *item;
106         int len, cmp;
107
108         newitem = MEM_mallocN(sizeof(SuggItem) + strlen(name) + 1, "SuggestionItem");
109         if (!newitem) {
110                 printf("Failed to allocate memory for suggestion.\n");
111                 return;
112         }
113
114         newitem->name = (char *) (newitem + 1);
115         len = strlen(name);
116         strncpy(newitem->name, name, len);
117         newitem->name[len] = '\0';
118         newitem->type = type;
119         newitem->prev = newitem->next = NULL;
120
121         /* Perform simple linear search for ordered storage */
122         if (!suggestions.first || !suggestions.last) {
123                 suggestions.first = suggestions.last = newitem;
124         } else {
125                 cmp = -1;
126                 for (item=suggestions.last; item; item=item->prev) {
127                         cmp = txttl_cmp(name, item->name, len);
128
129                         /* Newitem comes after this item, insert here */
130                         if (cmp >= 0) {
131                                 newitem->prev = item;
132                                 if (item->next)
133                                         item->next->prev = newitem;
134                                 newitem->next = item->next;
135                                 item->next = newitem;
136
137                                 /* At last item, set last pointer here */
138                                 if (item == suggestions.last)
139                                         suggestions.last = newitem;
140                                 break;
141                         }
142                 }
143                 /* Reached beginning of list, insert before first */
144                 if (cmp < 0) {
145                         newitem->next = suggestions.first;
146                         suggestions.first->prev = newitem;
147                         suggestions.first = newitem;
148                 }
149         }
150         suggestions.firstmatch = suggestions.lastmatch = suggestions.selected = NULL;
151         suggestions.top= 0;
152 }
153
154 void texttool_suggest_prefix(const char *prefix) {
155         SuggItem *match, *first, *last;
156         int cmp, len = strlen(prefix), top = 0;
157
158         if (!suggestions.first) return;
159         if (len==0) {
160                 suggestions.selected = suggestions.firstmatch = suggestions.first;
161                 suggestions.lastmatch = suggestions.last;
162                 return;
163         }
164         
165         first = last = NULL;
166         for (match=suggestions.first; match; match=match->next) {
167                 cmp = txttl_cmp(prefix, match->name, len);
168                 if (cmp==0) {
169                         if (!first) {
170                                 first = match;
171                                 suggestions.top = top;
172                         }
173                 } else if (cmp<0) {
174                         if (!last) {
175                                 last = match->prev;
176                                 break;
177                         }
178                 }
179                 top++;
180         }
181         if (first) {
182                 if (!last) last = suggestions.last;
183                 suggestions.firstmatch = first;
184                 suggestions.lastmatch = last;
185                 suggestions.selected = first;
186         } else {
187                 suggestions.firstmatch = NULL;
188                 suggestions.lastmatch = NULL;
189                 suggestions.selected = NULL;
190                 suggestions.top = 0;
191         }
192 }
193
194 void texttool_suggest_clear() {
195         txttl_free_suggest();
196 }
197
198 SuggItem *texttool_suggest_first() {
199         return suggestions.firstmatch;
200 }
201
202 SuggItem *texttool_suggest_last() {
203         return suggestions.lastmatch;
204 }
205
206 void texttool_suggest_select(SuggItem *sel) {
207         suggestions.selected = sel;
208 }
209
210 SuggItem *texttool_suggest_selected() {
211         return suggestions.selected;
212 }
213
214 int *texttool_suggest_top() {
215         return &suggestions.top;
216 }
217
218 /*************************/
219 /* Documentation methods */
220 /*************************/
221
222 void texttool_docs_show(const char *docs) {
223         int len;
224
225         if (!docs) return;
226
227         len = strlen(docs);
228
229         if (documentation) {
230                 MEM_freeN(documentation);
231                 documentation = NULL;
232         }
233
234         /* Ensure documentation ends with a '\n' */
235         if (docs[len-1] != '\n') {
236                 documentation = MEM_mallocN(len+2, "Documentation");
237                 strncpy(documentation, docs, len);
238                 documentation[len++] = '\n';
239         } else {
240                 documentation = MEM_mallocN(len+1, "Documentation");
241                 strncpy(documentation, docs, len);
242         }
243         documentation[len] = '\0';
244 }
245
246 char *texttool_docs_get() {
247         return documentation;
248 }
249
250 void texttool_docs_clear() {
251         txttl_free_docs();
252 }