lwl | Less with links. |
| download: http://git.y1.nz/archives/lwl.tar.gz | |
| README | Files | Log | Refs |
state.c
1 #include <string.h> /* strstr */
2 #include <ctype.h> /* isprint */
3 #include <stdlib.h> /* malloc */
4
5 #include "config.h"
6 #include "state.h"
7
8 /* for printing dynamic messages into */
9 char message_buffer[MAX_SCREEN_WIDTH];
10
11 State *
12 init_state()
13 {
14 State *ret = malloc(sizeof(State));
15 ret->markup_count = 0;
16 ret->markup_index = 0;
17 ret->results = malloc(MAX_MARKUPS * sizeof(Markup *));
18 ret->result_count = 0;
19 ret->input = malloc(MAX_INPUT_LENGTH * sizeof(char));
20 ret->input[0] = '\0';
21 ret->input_length = 0;
22 ret->mode = MODE_GROUND;
23 ret->line_offset = 0;
24 return ret;
25 }
26
27 /* functions for managing text input */
28 void
29 set_mode(State *state, int mode)
30 {
31 state->mode = mode;
32 state->input_length = 0;
33 state->input[0] = '\0';
34 }
35 void
36 type_input_char(State *state, Wrap *wrap, char ch)
37 {
38 if (
39 state->input_length+1 >= wrap->width ||
40 !isprint(ch)
41 )
42 return;
43
44 state->input[state->input_length++] = ch;
45 state->input[state->input_length] = '\0';
46 }
47 void
48 type_input_backspace(State *state)
49 {
50 if (state->input_length == 0) {
51 state->mode = MODE_GROUND;
52 return;
53 }
54 state->input_length--;
55 state->input[state->input_length] = '\0';
56 }
57
58 /* then, some wrap-related utilities */
59 // TODO extract wrap module from here (state.c) and text.c?
60 int
61 get_screen_start(Wrap *wrap, int line_offset)
62 {
63 return wrap->lines[line_offset];
64 }
65 int
66 get_screen_end(Wrap *wrap, int line_offset)
67 {
68 int last_line = line_offset + wrap->height;
69 if (last_line > wrap->line_count) last_line = wrap->line_count;
70 return wrap->lines[last_line];
71 }
72 int
73 is_onscreen(Wrap *wrap, int line_offset, int index)
74 {
75 int first = get_screen_start(wrap, line_offset);
76 int last = get_screen_end(wrap, line_offset);
77 return index >= first && index <= last;
78 }
79 int
80 get_line_of(Wrap *wrap, int index)
81 {
82 // TODO use binary instead of linear search
83 int line = 0;
84 for (;;) {
85 if (
86 line+1 >= wrap->line_count ||
87 wrap->lines[line+1] > index
88 ) break;
89 line++;
90 }
91 return line;
92 }
93
94 void
95 scroll_to(State *state, Wrap *wrap, int offset)
96 {
97 /* clamp the offset */
98 if (offset + wrap->height - 1 > wrap->line_count)
99 offset = wrap->line_count + 1 - wrap->height;
100 if (offset < 0) offset = 0;
101
102 state->line_offset = offset;
103
104 /* update message, but don't overwrite old ones */
105 if (state->message == NULL) {
106 if (state->line_offset >= wrap->line_count - wrap->height)
107 state->message = "(END)";
108
109 if (state->line_offset == 0)
110 state->message = state->filename;
111 }
112 }
113
114 void
115 unset_active(State *state)
116 {
117 if (
118 state->markup_index >= state->markup_count ||
119 state->markups == NULL
120 ) return;
121 state->markups[state->markup_index]->active = 0;
122 }
123
124 void
125 set_markup_index(State *state, int next)
126 {
127 if (state->markup_count == 0) return;
128
129 // TODO do we actually still need to clamp this?
130 /* clamp the index */
131 if (next < 0) next = 0;
132 if (next > state->markup_count - 1)
133 next = state->markup_count - 1;
134
135 /* update colors */
136 unset_active(state);
137 state->markups[next]->active = 1;
138
139 state->markup_index = next;
140 }
141
142 void
143 set_markups(State *state, Markup **new_markups, int new_count)
144 {
145 unset_active(state);
146 state->markups = new_markups;
147 state->markup_count = new_count;
148 set_markup_index(state, 0);
149 }
150
151 int
152 get_first_markup(State *state, Wrap *wrap)
153 {
154 int i = state->markup_count - 1;
155 int limit = get_screen_start(wrap, state->line_offset);
156 for (;;) {
157 if (i-1 < 0 || state->markups[i-1]->start < limit)
158 return i;
159 i--;
160 }
161 }
162
163 int
164 get_last_markup(State *state, Wrap *wrap)
165 {
166 int i = 0;
167 int limit = get_screen_end(wrap, state->line_offset);
168 for (;;) {
169 if (
170 i+1 >= state->markup_count ||
171 state->markups[i+1]->start >= limit
172 ) return i;
173 i++;
174 }
175 }
176
177 void
178 do_search(FileContents *contents, State *state, Wrap *wrap)
179 {
180 char *start = contents->buffer;
181 char *match;
182 Markup *markup;
183
184 /* reset search state, free the old results */
185 state->mode = MODE_GROUND;
186 set_markups(state, contents->links, contents->link_count);
187 for (int i = 0; i < state->result_count; i++)
188 free_markup(state->results[i]);
189 state->result_count = 0;
190
191 /* return if the query is empty */
192 if (state->input_length == 0)
193 return;
194
195 for (;;) {
196 match = strstr(start, state->input);
197 if (match == NULL) break;
198
199 markup = create_markup(
200 match - contents->buffer,
201 state->input_length,
202 SEARCH_COLOR
203 );
204 state->results[state->result_count++] = markup;
205 start = match + state->input_length;
206 }
207
208 if (state->result_count == 0) {
209 state->message = "No matches found";
210 return;
211 }
212
213 set_markups(state, state->results, state->result_count);
214
215 state->message = message_buffer;
216 snprintf(message_buffer, wrap->width, "%d matches", state->result_count);
217 }
218
219 /* update scroll to ensure that a specific index is onscreen. */
220 void
221 scroll_to_show(State *state, Wrap *wrap, int index)
222 {
223 int new_line_offset, line_to_show = get_line_of(wrap, index);
224 int scrolling_up;
225
226 /* if the line is already onscreen, do nothing */
227 if (
228 line_to_show >= state->line_offset &&
229 line_to_show < state->line_offset + wrap->height
230 ) return;
231
232 /* calculate the line offset, minimizing distance moved */
233 scrolling_up = line_to_show < state->line_offset;
234 new_line_offset = scrolling_up ?
235 line_to_show:
236 line_to_show - wrap->height + 1;
237 scroll_to(state, wrap, new_line_offset);
238 }
239
240 void
241 show_markup_data(State *state, Wrap *wrap)
242 {
243 const char *data;
244 int width;
245
246 data = state->markups[state->markup_index]->data;
247 if (data == NULL) return;
248
249 /* -3 for "...", +1 for "\0" */
250 width = wrap->width - 3 + 1;
251
252 state->message = message_buffer;
253 int length = snprintf(message_buffer, width, "Data: %s", data);
254 if (length >= width)
255 snprintf(
256 message_buffer + (wrap->width-3),
257 3 + 1, "..."
258 );
259 }
260
261 void
262 next_markup(State *state, Wrap *wrap, int step)
263 {
264 int start;
265 int next = state->markup_index + step;
266
267 if (state->markup_count == 0) {
268 state->message = "None";
269 return;
270 }
271
272 start = state->markups[state->markup_index]->start;
273 if (!is_onscreen(wrap, state->line_offset, start)) {
274 next = step > 0 ?
275 get_first_markup(state, wrap) :
276 get_last_markup(state, wrap);
277 } else {
278 if (next < 0) next = 0;
279 if (next > state->markup_count-1) next = state->markup_count-1;
280 if (next == state->markup_index && state->markup_count != 1) {
281 state->message = "No more";
282 return;
283 }
284 }
285
286 set_markup_index(state, next);
287
288 /* scroll to the start of the markup */
289 start = state->markups[state->markup_index]->start;
290 scroll_to_show(state, wrap, start);
291
292 show_markup_data(state, wrap);
293 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.