1 /*
2 * (c) 2004 by Kalle Wallin <kaw@linux.se>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (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 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 */
19 #include "list_window.h"
20 #include "config.h"
21 #include "options.h"
22 #include "charset.h"
23 #include "match.h"
24 #include "command.h"
25 #include "colors.h"
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
32 extern void screen_bell(void);
34 struct list_window *
35 list_window_init(WINDOW *w, unsigned width, unsigned height)
36 {
37 struct list_window *lw;
39 lw = g_malloc0(sizeof(list_window_t));
40 lw->w = w;
41 lw->cols = width;
42 lw->rows = height;
43 return lw;
44 }
46 void
47 list_window_free(struct list_window *lw)
48 {
49 if (lw) {
50 memset(lw, 0, sizeof(list_window_t));
51 g_free(lw);
52 }
53 }
55 void
56 list_window_reset(struct list_window *lw)
57 {
58 lw->selected = 0;
59 lw->xoffset = 0;
60 lw->start = 0;
61 }
63 void
64 list_window_check_selected(struct list_window *lw, unsigned length)
65 {
66 if (lw->start + lw->rows > length) {
67 if (length > lw->rows)
68 lw->start = length - lw->rows;
69 else
70 lw->start = 0;
71 }
73 if (lw->selected < lw->start)
74 lw->selected = lw->start;
76 if (length == 0)
77 lw->selected = 0;
78 else if (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 bool fill = options.wide_cursor;
169 bool show_cursor = !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 bool highlight = false;
181 const char *label;
183 label = callback(lw->start + i, &highlight, callback_data);
184 wmove(lw->w, i, 0);
186 if (label) {
187 bool 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 bool
214 list_window_find(struct list_window *lw,
215 list_window_callback_fn_t callback,
216 void *callback_data,
217 const char *str,
218 bool wrap)
219 {
220 bool h;
221 unsigned i = lw->selected + 1;
222 const char *label;
224 do {
225 while ((label = callback(i,&h,callback_data))) {
226 if (str && label && match_line(label, str)) {
227 lw->selected = i;
228 return true;
229 }
230 if (wrap && i == lw->selected)
231 return false;
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 } while (wrap);
242 return false;
243 }
245 bool
246 list_window_rfind(struct list_window *lw,
247 list_window_callback_fn_t callback,
248 void *callback_data,
249 const char *str,
250 bool wrap,
251 unsigned rows)
252 {
253 bool h;
254 int i = lw->selected - 1;
255 const char *label;
257 if (rows == 0)
258 return false;
260 do {
261 while (i >= 0 && (label = callback(i,&h,callback_data))) {
262 if( str && label && match_line(label, str) ) {
263 lw->selected = i;
264 return true;
265 }
266 if (wrap && i == (int)lw->selected)
267 return false;
268 i--;
269 }
270 if (wrap) {
271 i = rows - 1; /* last item */
272 screen_bell();
273 }
274 } while (wrap);
276 return false;
277 }
279 /* perform basic list window commands (movement) */
280 bool
281 list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd)
282 {
283 switch (cmd) {
284 case CMD_LIST_PREVIOUS:
285 list_window_previous(lw, rows);
286 break;
287 case CMD_LIST_NEXT:
288 list_window_next(lw, rows);
289 break;
290 case CMD_LIST_FIRST:
291 list_window_first(lw);
292 break;
293 case CMD_LIST_LAST:
294 list_window_last(lw, rows);
295 break;
296 case CMD_LIST_NEXT_PAGE:
297 list_window_next_page(lw, rows);
298 break;
299 case CMD_LIST_PREVIOUS_PAGE:
300 list_window_previous_page(lw);
301 break;
302 default:
303 return false;
304 }
306 return true;
307 }
309 bool
310 list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd)
311 {
312 switch (cmd) {
313 case CMD_LIST_PREVIOUS:
314 if (lw->start > 0)
315 lw->start--;
316 break;
318 case CMD_LIST_NEXT:
319 if (lw->start + lw->rows < rows)
320 lw->start++;
321 break;
323 case CMD_LIST_FIRST:
324 lw->start = 0;
325 break;
327 case CMD_LIST_LAST:
328 if (rows > lw->rows)
329 lw->start = rows - lw->rows;
330 else
331 lw->start = 0;
332 break;
334 case CMD_LIST_NEXT_PAGE:
335 lw->start += lw->rows - 1;
336 if (lw->start + lw->rows > rows) {
337 if (rows > lw->rows)
338 lw->start = rows - lw->rows;
339 else
340 lw->start = 0;
341 }
342 break;
344 case CMD_LIST_PREVIOUS_PAGE:
345 if (lw->start > lw->rows)
346 lw->start -= lw->rows;
347 else
348 lw->start = 0;
349 break;
351 default:
352 return false;
353 }
355 return true;
356 }
358 #ifdef HAVE_GETMOUSE
359 bool
360 list_window_mouse(struct list_window *lw, unsigned rows,
361 unsigned long bstate, int y)
362 {
363 assert(lw != NULL);
365 /* if the even occured above the list window move up */
366 if (y < 0) {
367 if (bstate & BUTTON3_CLICKED)
368 list_window_first(lw);
369 else
370 list_window_previous_page(lw);
371 return true;
372 }
374 /* if the even occured below the list window move down */
375 if ((unsigned)y >= rows) {
376 if (bstate & BUTTON3_CLICKED)
377 list_window_last(lw, rows);
378 else
379 list_window_next_page(lw, rows);
380 return true;
381 }
383 return false;
384 }
385 #endif