1 /*
2 * $Id$
3 *
4 * (c) 2004 by Kalle Wallin <kaw@linux.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
21 #include "list_window.h"
22 #include "config.h"
23 #include "options.h"
24 #include "charset.h"
25 #include "support.h"
26 #include "command.h"
27 #include "colors.h"
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
34 extern void screen_bell(void);
36 struct list_window *
37 list_window_init(WINDOW *w, unsigned width, unsigned height)
38 {
39 struct list_window *lw;
41 lw = g_malloc0(sizeof(list_window_t));
42 lw->w = w;
43 lw->cols = width;
44 lw->rows = height;
45 return lw;
46 }
48 void
49 list_window_free(struct list_window *lw)
50 {
51 if (lw) {
52 memset(lw, 0, sizeof(list_window_t));
53 g_free(lw);
54 }
55 }
57 void
58 list_window_reset(struct list_window *lw)
59 {
60 lw->selected = 0;
61 lw->xoffset = 0;
62 lw->start = 0;
63 }
65 void
66 list_window_check_selected(struct list_window *lw, unsigned length)
67 {
68 if (lw->start + lw->rows > length) {
69 if (length > lw->rows)
70 lw->start = length - lw->rows;
71 else
72 lw->start = 0;
73 }
75 if (lw->selected < lw->start)
76 lw->selected = lw->start;
78 if (length > 0 && lw->selected >= length)
79 lw->selected = length - 1;
80 }
82 void
83 list_window_center(struct list_window *lw, unsigned rows, unsigned n)
84 {
85 if (n > lw->rows / 2)
86 lw->start = n - lw->rows / 2;
87 else
88 lw->start = 0;
90 if (lw->start + lw->rows > rows) {
91 if (lw->rows < rows)
92 lw->start = rows - lw->rows;
93 else
94 lw->start = 0;
95 }
96 }
98 void
99 list_window_set_selected(struct list_window *lw, unsigned n)
100 {
101 lw->selected = n;
102 }
104 static void
105 list_window_next(struct list_window *lw, unsigned length)
106 {
107 if (lw->selected + 1 < length)
108 lw->selected++;
109 else if (options.list_wrap)
110 lw->selected = 0;
111 }
113 static void
114 list_window_previous(struct list_window *lw, unsigned length)
115 {
116 if (lw->selected > 0)
117 lw->selected--;
118 else if (options.list_wrap)
119 lw->selected = length - 1;
120 }
122 static void
123 list_window_first(struct list_window *lw)
124 {
125 lw->xoffset = 0;
126 lw->selected = 0;
127 }
129 static void
130 list_window_last(struct list_window *lw, unsigned length)
131 {
132 lw->xoffset = 0;
133 if (length > 0)
134 lw->selected = length - 1;
135 else
136 lw->selected = 0;
137 }
139 static void
140 list_window_next_page(struct list_window *lw, unsigned length)
141 {
142 if (lw->rows < 2)
143 return;
144 if (lw->selected + lw->rows < length)
145 lw->selected += lw->rows - 1;
146 else
147 list_window_last(lw, length);
148 }
150 static void
151 list_window_previous_page(struct list_window *lw)
152 {
153 if (lw->rows < 2)
154 return;
155 if (lw->selected > lw->rows - 1)
156 lw->selected -= lw->rows - 1;
157 else
158 list_window_first(lw);
159 }
162 void
163 list_window_paint(struct list_window *lw,
164 list_window_callback_fn_t callback,
165 void *callback_data)
166 {
167 unsigned i;
168 int fill = options.wide_cursor;
169 int show_cursor = !(lw->flags & LW_HIDE_CURSOR);
171 if (show_cursor) {
172 if (lw->selected < lw->start)
173 lw->start = lw->selected;
175 if (lw->selected >= lw->start + lw->rows)
176 lw->start = lw->selected - lw->rows + 1;
177 }
179 for (i = 0; i < lw->rows; i++) {
180 int highlight = 0;
181 const char *label;
183 label = callback(lw->start + i, &highlight, callback_data);
184 wmove(lw->w, i, 0);
186 if (label) {
187 int selected = lw->start + i == lw->selected;
188 unsigned len = utf8_width(label);
190 if (highlight)
191 colors_use(lw->w, COLOR_LIST_BOLD);
192 else
193 colors_use(lw->w, COLOR_LIST);
195 if (show_cursor && selected)
196 wattron(lw->w, A_REVERSE);
198 //waddnstr(lw->w, label, lw->cols);
199 waddstr(lw->w, label);
200 if (fill && len < lw->cols)
201 whline(lw->w, ' ', lw->cols-len);
203 if (selected)
204 wattroff(lw->w, A_REVERSE);
206 if (!fill && len < lw->cols)
207 wclrtoeol(lw->w);
208 } else
209 wclrtoeol(lw->w);
210 }
211 }
213 int
214 list_window_find(struct list_window *lw,
215 list_window_callback_fn_t callback,
216 void *callback_data,
217 const char *str,
218 int wrap)
219 {
220 int h;
221 unsigned i = lw->selected + 1;
222 const char *label;
224 while (wrap || i == lw->selected + 1) {
225 while ((label = callback(i,&h,callback_data))) {
226 if (str && label && strcasestr(label, str)) {
227 lw->selected = i;
228 return 0;
229 }
230 if (wrap && i == lw->selected)
231 return 1;
232 i++;
233 }
234 if (wrap) {
235 if (i == 0) /* empty list */
236 return 1;
237 i=0; /* first item */
238 screen_bell();
239 }
240 }
242 return 1;
243 }
245 int
246 list_window_rfind(struct list_window *lw,
247 list_window_callback_fn_t callback,
248 void *callback_data,
249 const char *str,
250 int wrap,
251 unsigned rows)
252 {
253 int h;
254 int i = lw->selected - 1;
255 const char *label;
257 if (rows == 0)
258 return 1;
260 while (wrap || i == (int)lw->selected - 1) {
261 while (i >= 0 && (label = callback(i,&h,callback_data))) {
262 if( str && label && strcasestr(label, str) ) {
263 lw->selected = i;
264 return 0;
265 }
266 if (wrap && i == (int)lw->selected)
267 return 1;
268 i--;
269 }
270 if (wrap) {
271 i = rows - 1; /* last item */
272 screen_bell();
273 }
274 }
275 return 1;
276 }
278 /* perform basic list window commands (movement) */
279 int
280 list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd)
281 {
282 switch (cmd) {
283 case CMD_LIST_PREVIOUS:
284 list_window_previous(lw, rows);
285 break;
286 case CMD_LIST_NEXT:
287 list_window_next(lw, rows);
288 break;
289 case CMD_LIST_FIRST:
290 list_window_first(lw);
291 break;
292 case CMD_LIST_LAST:
293 list_window_last(lw, rows);
294 break;
295 case CMD_LIST_NEXT_PAGE:
296 list_window_next_page(lw, rows);
297 break;
298 case CMD_LIST_PREVIOUS_PAGE:
299 list_window_previous_page(lw);
300 break;
301 default:
302 return 0;
303 }
305 return 1;
306 }
308 int
309 list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd)
310 {
311 switch (cmd) {
312 case CMD_LIST_PREVIOUS:
313 if (lw->start > 0)
314 lw->start--;
315 break;
317 case CMD_LIST_NEXT:
318 if (lw->start + lw->rows < rows)
319 lw->start++;
320 break;
322 case CMD_LIST_FIRST:
323 lw->start = 0;
324 break;
326 case CMD_LIST_LAST:
327 if (rows > lw->rows)
328 lw->start = rows - lw->rows;
329 else
330 lw->start = 0;
331 break;
333 case CMD_LIST_NEXT_PAGE:
334 lw->start += lw->rows - 1;
335 if (lw->start + lw->rows > rows) {
336 if (rows > lw->rows)
337 lw->start = rows - lw->rows;
338 else
339 lw->start = 0;
340 }
341 break;
343 case CMD_LIST_PREVIOUS_PAGE:
344 if (lw->start > lw->rows)
345 lw->start -= lw->rows;
346 else
347 lw->start = 0;
348 break;
350 default:
351 return 0;
352 }
354 return 1;
355 }
357 #ifdef HAVE_GETMOUSE
358 int
359 list_window_mouse(struct list_window *lw, unsigned rows,
360 unsigned long bstate, int y)
361 {
362 assert(lw != NULL);
364 /* if the even occured above the list window move up */
365 if (y < 0) {
366 if (bstate & BUTTON3_CLICKED)
367 list_window_first(lw);
368 else
369 list_window_previous_page(lw);
370 return 1;
371 }
373 /* if the even occured below the list window move down */
374 if ((unsigned)y >= rows) {
375 if (bstate & BUTTON3_CLICKED)
376 list_window_last(lw, rows);
377 else
378 list_window_next_page(lw, rows);
379 return 1;
380 }
382 return 0;
383 }
384 #endif
386 list_window_state_t *
387 list_window_init_state(void)
388 {
389 return g_malloc0(sizeof(list_window_state_t));
390 }
392 void
393 list_window_free_state(list_window_state_t *state)
394 {
395 if (state) {
396 if (state->list) {
397 GList *list = state->list;
399 while (list) {
400 g_free(list->data);
401 list->data = NULL;
402 list = list->next;
403 }
405 g_list_free(state->list);
406 state->list = NULL;
407 }
409 g_free(state);
410 }
411 }
413 void
414 list_window_push_state(list_window_state_t *state, struct list_window *lw)
415 {
416 if (state) {
417 struct list_window *tmp = g_malloc(sizeof(list_window_t));
418 memcpy(tmp, lw, sizeof(list_window_t));
419 state->list = g_list_prepend(state->list, (gpointer) tmp);
420 list_window_reset(lw);
421 }
422 }
424 bool
425 list_window_pop_state(list_window_state_t *state, struct list_window *lw)
426 {
427 if (state && state->list) {
428 struct list_window *tmp = state->list->data;
430 memcpy(lw, tmp, sizeof(list_window_t));
431 g_free(tmp);
432 state->list->data = NULL;
433 state->list = g_list_delete_link(state->list, state->list);
434 }
436 // return TRUE if there are still states in the list
437 return (state && state->list) ? TRUE : FALSE;
438 }