Merge branch 'master' into blender2.8
[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
39 #include "BLI_string.h"
40
41 #include "DNA_text_types.h"
42 #include "BKE_suggestions.h"
43
44 /**********************/
45 /* Static definitions */
46 /**********************/
47
48 static Text *activeToolText = NULL;
49 static SuggList suggestions = {NULL, NULL, NULL, NULL, NULL};
50 static char *documentation = NULL;
51 //static int doc_lines = 0;
52
53 static void txttl_free_suggest(void)
54 {
55         SuggItem *item, *prev;
56         for (item = suggestions.last; item; item = prev) {
57                 prev = item->prev;
58                 MEM_freeN(item);
59         }
60         suggestions.first = suggestions.last = NULL;
61         suggestions.firstmatch = suggestions.lastmatch = NULL;
62         suggestions.selected = NULL;
63         suggestions.top = 0;
64 }
65
66 static void txttl_free_docs(void)
67 {
68         if (documentation) {
69                 MEM_freeN(documentation);
70                 documentation = NULL;
71         }
72 }
73
74 /**************************/
75 /* General tool functions */
76 /**************************/
77
78 void free_texttools(void)
79 {
80         txttl_free_suggest();
81         txttl_free_docs();
82 }
83
84 void texttool_text_set_active(Text *text)
85 {
86         if (activeToolText == text) return;
87         texttool_text_clear();
88         activeToolText = text;
89 }
90
91 void texttool_text_clear(void)
92 {
93         free_texttools();
94         activeToolText = NULL;
95 }
96
97 short texttool_text_is_active(Text *text)
98 {
99         return activeToolText == text ? 1 : 0;
100 }
101
102 /***************************/
103 /* Suggestion list methods */
104 /***************************/
105
106 void texttool_suggest_add(const char *name, char type)
107 {
108         const int len = strlen(name);
109         int cmp;
110         SuggItem *newitem, *item;
111
112         newitem = MEM_mallocN(sizeof(SuggItem) + len + 1, "SuggItem");
113         if (!newitem) {
114                 printf("Failed to allocate memory for suggestion.\n");
115                 return;
116         }
117
118         memcpy(newitem->name, name, len + 1);
119         newitem->type = type;
120         newitem->prev = newitem->next = NULL;
121
122         /* Perform simple linear search for ordered storage */
123         if (!suggestions.first || !suggestions.last) {
124                 suggestions.first = suggestions.last = newitem;
125         }
126         else {
127                 cmp = -1;
128                 for (item = suggestions.last; item; item = item->prev) {
129                         cmp = BLI_strncasecmp(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         suggestions.top = 0;
154 }
155
156 void texttool_suggest_prefix(const char *prefix, const int prefix_len)
157 {
158         SuggItem *match, *first, *last;
159         int cmp, top = 0;
160
161         if (!suggestions.first) return;
162         if (prefix_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 = BLI_strncasecmp(prefix, match->name, prefix_len);
171                 if (cmp == 0) {
172                         if (!first) {
173                                 first = match;
174                                 suggestions.top = top;
175                         }
176                 }
177                 else if (cmp < 0) {
178                         if (!last) {
179                                 last = match->prev;
180                                 break;
181                         }
182                 }
183                 top++;
184         }
185         if (first) {
186                 if (!last) last = suggestions.last;
187                 suggestions.firstmatch = first;
188                 suggestions.lastmatch = last;
189                 suggestions.selected = first;
190         }
191         else {
192                 suggestions.firstmatch = NULL;
193                 suggestions.lastmatch = NULL;
194                 suggestions.selected = NULL;
195                 suggestions.top = 0;
196         }
197 }
198
199 void texttool_suggest_clear(void)
200 {
201         txttl_free_suggest();
202 }
203
204 SuggItem *texttool_suggest_first(void)
205 {
206         return suggestions.firstmatch;
207 }
208
209 SuggItem *texttool_suggest_last(void)
210 {
211         return suggestions.lastmatch;
212 }
213
214 void texttool_suggest_select(SuggItem *sel)
215 {
216         suggestions.selected = sel;
217 }
218
219 SuggItem *texttool_suggest_selected(void)
220 {
221         return suggestions.selected;
222 }
223
224 int *texttool_suggest_top(void)
225 {
226         return &suggestions.top;
227 }
228
229 /*************************/
230 /* Documentation methods */
231 /*************************/
232
233 void texttool_docs_show(const char *docs)
234 {
235         int len;
236
237         if (!docs) return;
238
239         len = strlen(docs);
240
241         if (documentation) {
242                 MEM_freeN(documentation);
243                 documentation = NULL;
244         }
245
246         /* Ensure documentation ends with a '\n' */
247         if (docs[len - 1] != '\n') {
248                 documentation = MEM_mallocN(len + 2, "Documentation");
249                 BLI_strncpy(documentation, docs, len);
250                 documentation[len++] = '\n';
251         }
252         else {
253                 documentation = MEM_mallocN(len + 1, "Documentation");
254                 BLI_strncpy(documentation, docs, len);
255         }
256         documentation[len] = '\0';
257 }
258
259 char *texttool_docs_get(void)
260 {
261         return documentation;
262 }
263
264 void texttool_docs_clear(void)
265 {
266         txttl_free_docs();
267 }