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