1 /* Copyright (c) 2010
2  *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3  *      Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program (see the file COPYING); if not, see
17  * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
19  *
20  ****************************************************************
21  */
22 
23 #include "config.h"
24 #include "screen.h"
25 #include "list_generic.h"
26 #include "layer.h"
27 #include "extern.h"
28 
29 /* Deals with a generic list display */
30 
31 extern struct layer *flayer;
32 
33 static void ListProcess __P((char **, int *));
34 static void ListAbort __P((void));
35 static void ListRedisplayLine __P((int, int, int, int));
36 static void ListClearLine __P((int, int, int, int));
37 static int  ListRewrite __P((int, int, int, struct mchar *, int));
38 static int  ListResize __P((int, int));
39 static void ListRestore __P((void));
40 static void ListFree __P((void *));
41 
42 struct LayFuncs ListLf =
43 {
44   ListProcess,
45   ListAbort,
46   ListRedisplayLine,
47   ListClearLine,
48   ListRewrite,
49   ListResize,
50   ListRestore,
51   ListFree
52 };
53 
54 /** Returns non-zero on success. */
55 struct ListData *
glist_display(struct GenericList * list,const char * name)56 glist_display(struct GenericList *list, const char *name)
57 {
58   struct ListData *ldata;
59 
60   if (InitOverlayPage(sizeof(struct ListData), &ListLf, 0))
61     return NULL;
62   ldata = flayer->l_data;
63 
64   ldata->name = name;	/* We do not SaveStr, since the strings should be all static literals */
65   ldata->list_fn = list;
66 
67   flayer->l_mode = 1;
68   flayer->l_x = 0;
69   flayer->l_y = flayer->l_height - 1;
70 
71   return ldata;
72 }
73 
74 static void
glist_decide_top(struct ListData * ldata)75 glist_decide_top(struct ListData *ldata)
76 {
77   int count = flayer->l_height - 5;	/* 2 for header, 1 for footer */
78   struct ListRow *top = ldata->selected;
79   for (; count && top != ldata->root; top = top->prev, count--)
80     ;
81   ldata->top = top;
82 }
83 
84 static struct ListRow *
glist_search_dir(struct ListData * ldata,struct ListRow * start,int dir)85 glist_search_dir(struct ListData *ldata, struct ListRow *start, int dir)
86 {
87   struct ListRow *row = (dir == 1) ? start->next : start->prev;
88   for (; row; row = (dir == 1) ? row->next : row->prev)
89     if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
90       return row;
91 
92   if (dir == 1)
93     row = ldata->root;
94   else
95     {
96       /* First, go to the end */
97       if (!start->next)
98 	row = start;
99       else
100 	for (row = start->next; row->next; row = row->next)
101 	  ;
102     }
103 
104   for (; row != start; row = (dir == 1) ? row->next : row->prev)
105     if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
106       break;
107 
108   return row;
109 }
110 
111 static void
glist_search(char * buf,int len,char * data)112 glist_search(char *buf, int len, char *data)
113 {
114   struct ListData *ldata = (struct ListData *)data;
115   struct ListRow *row;
116 
117   if (ldata->search)
118     Free(ldata->search);
119   if (len > 0)
120     ldata->search = SaveStr(buf);
121   else
122     return;
123 
124   for (row = ldata->selected; row; row = row->next)
125     if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
126       break;
127 
128   if (!row)
129     for (row = ldata->root; row != ldata->selected; row = row->next)
130       if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
131 	break;
132 
133   if (row == ldata->selected)
134     return;
135 
136   ldata->selected = row;
137   if (ldata->selected->y == -1)
138     glist_decide_top(ldata);
139   glist_display_all(ldata);
140 }
141 
ListProcess(char ** ppbuf,int * plen)142 static void ListProcess(char **ppbuf, int *plen)
143 {
144   struct ListData *ldata = flayer->l_data;
145   int count = 0;
146 
147   while (*plen > 0)
148     {
149       struct ListRow *old;
150       unsigned char ch;
151 
152       if (!flayer->l_mouseevent.start && ldata->list_fn->gl_pinput &&
153 	  ldata->list_fn->gl_pinput(ldata, ppbuf, plen))
154 	continue;
155 
156       ch = **ppbuf;
157       ++*ppbuf;
158       --*plen;
159 
160       if (flayer->l_mouseevent.start)
161 	{
162 	  int r = LayProcessMouse(flayer, ch);
163 	  if (r == -1)
164 	    {
165 	      LayProcessMouseSwitch(flayer, 0);
166 	      continue;
167 	    }
168 	  else
169 	    {
170 	      if (r)
171 		ch = 0222;
172 	      else
173 		continue;
174 	    }
175 	}
176 
177       if (!ldata->selected)
178 	{
179 	  *plen = 0;
180 	  break;
181 	}
182 
183       old = ldata->selected;
184 
185 processchar:
186       switch (ch)
187 	{
188 	case ' ':
189 	  break;
190 
191 	case '\r':
192 	case '\n':
193 	  break;
194 
195 	case 0220:	/* up */
196 	case 16:	/* ^P */
197 	case 'k':
198 	  if (!ldata->selected->prev)	/* There's no where to go */
199 	    break;
200 	  ldata->selected = old->prev;
201 	  break;
202 
203 	case 0216:	/* down */
204 	case 14:	/* ^N like emacs */
205 	case 'j':
206 	  if (!ldata->selected->next)	/* Nothing to do */
207 	    break;
208 	  ldata->selected = old->next;
209 	  break;
210 
211 	case 033:	/* escape */
212 	case 007:	/* ^G */
213 	  ListAbort();
214 	  *plen = 0;
215 	  return;
216 
217 	case 0201:	/* home */
218 	case 0001:	/* ^A */
219 	  ldata->selected = ldata->root;
220 	  break;
221 
222 	case 0205:	/* end */
223 	case 0005:	/* ^E */
224 	  while (ldata->selected->next)
225 	    ldata->selected = ldata->selected->next;
226 	  if (ldata->selected->y != -1)
227 	    {
228 	      /* Both old and current selections are on the screen. So we can just
229 	       * redraw these two affected rows. */
230 	    }
231 	  break;
232 
233 	case 0004:	/* ^D (half-page down) */
234 	case 0006:	/* page-down, ^F */
235 	  count = (flayer->l_height - 4) >> (ch == 0004);
236 	  for (; ldata->selected->next && --count;
237 	      ldata->selected = ldata->selected->next)
238 	    ;
239 	  break;
240 
241 	case 0025:	/* ^U (half-page up) */
242 	case 0002:	/* page-up, ^B */
243 	  count = (flayer->l_height - 4) >> (ch == 0025);
244 	  for (; ldata->selected->prev && --count;
245 	      ldata->selected = ldata->selected->prev)
246 	    ;
247 	  break;
248 
249 	case '/':	/* start searching */
250 	  if (ldata->list_fn->gl_matchrow)
251 	    {
252 	      char *s;
253 	      Input("Search: ", 80, INP_COOKED, glist_search, (char *)ldata, 0);
254 	      if ((s = ldata->search))
255 		{
256 		  for (; *s; s++)
257 		    {
258 		      char *ss = s;
259 		      int n = 1;
260 		      LayProcess(&ss, &n);
261 		    }
262 		}
263 	    }
264 	  break;
265 
266 	/* The following deal with searching. */
267 
268 	case 'n':	/* search next */
269 	  if (ldata->list_fn->gl_matchrow && ldata->search)
270 	    ldata->selected = glist_search_dir(ldata, ldata->selected, 1);
271 	  break;
272 
273 	case 'N':	/* search prev */
274 	  if (ldata->list_fn->gl_matchrow && ldata->search)
275 	    ldata->selected = glist_search_dir(ldata, ldata->selected, -1);
276 	  break;
277 
278 	/* Now, mouse events. */
279 	case 0222:
280 	  if (flayer->l_mouseevent.start)
281 	    {
282 	      int button = flayer->l_mouseevent.buffer[0];
283 	      if (button == 'a') /* Scroll down */
284 		ch = 'j';
285 	      else if (button == '`') /* Scroll up */
286 		ch = 'k';
287 	      else if (button == ' ') /* Left click */
288 		{
289 		  int y = flayer->l_mouseevent.buffer[2];
290 		  struct ListRow *r = ldata->top;
291 		  for (r = ldata->top; r && r->y != -1 && r->y != y; r = r->next)
292 		    ;
293 		  if (r && r->y == y)
294 		    ldata->selected = r;
295 		  ch = 0;
296 		}
297 	      else
298 		ch = 0;
299 	      LayProcessMouseSwitch(flayer, 0);
300 	      if (ch)
301 		goto processchar;
302 	    }
303 	  else
304 	    LayProcessMouseSwitch(flayer, 1);
305 	  break;
306 	}
307 
308       if (old == ldata->selected)	/* The selection didn't change */
309 	continue;
310 
311       if (ldata->selected->y == -1)
312 	{
313 	  /* We need to list all the rows, since we are scrolling down. But first,
314 	   * find the top of the visible list. */
315 	  glist_decide_top(ldata);
316 	  glist_display_all(ldata);
317 	}
318       else
319 	{
320 	  /* just redisplay the two lines. */
321 	  ldata->list_fn->gl_printrow(ldata, old);
322 	  ldata->list_fn->gl_printrow(ldata, ldata->selected);
323 	  flayer->l_y = ldata->selected->y;
324 	  LaySetCursor();
325 	}
326     }
327 }
328 
ListAbort(void)329 static void ListAbort(void)
330 {
331   LAY_CALL_UP(LRefreshAll(flayer, 0));
332   ExitOverlayPage();
333 }
334 
ListFree(void * d)335 static void ListFree(void *d)
336 {
337   struct ListData *ldata = d;
338   glist_remove_rows(ldata);
339   if (ldata->list_fn->gl_free)
340     ldata->list_fn->gl_free(ldata);
341   if (ldata->search)
342     Free(ldata->search);
343 }
344 
ListRedisplayLine(int y,int xs,int xe,int isblank)345 static void ListRedisplayLine(int y, int xs, int xe, int isblank)
346 {
347   struct ListData *ldata;
348   ASSERT(flayer);
349 
350   ldata = flayer->l_data;
351   if (y < 0)
352     {
353       glist_display_all(ldata);
354       return;
355     }
356 
357   if (!isblank)
358     LClearArea(flayer, xs, y, xe, y, 0, 0);
359 
360   if (ldata->top && y < ldata->top->y)
361     ldata->list_fn->gl_printheader(ldata);
362   else if (y + 1 == flayer->l_height)
363     ldata->list_fn->gl_printfooter(ldata);
364   else
365     {
366       struct ListRow *row;
367       for (row = ldata->top; row && row->y != -1; row = row->next)
368 	if (row->y == y)
369 	  {
370 	    ldata->list_fn->gl_printrow(ldata, row);
371 	    break;
372 	  }
373     }
374 }
375 
ListClearLine(int y,int xs,int xe,int bce)376 static void ListClearLine(int y, int xs, int xe, int bce)
377 {
378   DefClearLine(y, xs, xe, bce);
379 }
380 
ListRewrite(int y,int xs,int xe,struct mchar * rend,int doit)381 static int  ListRewrite(int y, int xs, int xe, struct mchar *rend, int doit)
382 {
383   return EXPENSIVE;
384 }
385 
ListResize(int wi,int he)386 static int ListResize (int wi, int he)
387 {
388   if (wi < 10 || he < 5)
389     return -1;
390 
391   flayer->l_width = wi;
392   flayer->l_height = he;
393   flayer->l_y = he - 1;
394 
395   return 0;
396 }
397 
ListRestore(void)398 static void ListRestore (void)
399 {
400   DefRestore();
401 }
402 
403 struct ListRow *
glist_add_row(struct ListData * ldata,void * data,struct ListRow * after)404 glist_add_row(struct ListData *ldata, void *data, struct ListRow *after)
405 {
406   struct ListRow *r = calloc(1, sizeof(struct ListRow));
407   r->data = data;
408 
409   if (after)
410     {
411       r->next = after->next;
412       r->prev = after;
413       after->next = r;
414       if (r->next)
415 	r->next->prev = r;
416     }
417   else
418     {
419       r->next = ldata->root;
420       if (ldata->root)
421 	ldata->root->prev = r;
422       ldata->root = r;
423     }
424 
425   return r;
426 }
427 
428 void
glist_remove_rows(struct ListData * ldata)429 glist_remove_rows(struct ListData *ldata)
430 {
431   struct ListRow *row;
432   for (row = ldata->root; row; )
433     {
434       struct ListRow *r = row;
435       row = row->next;
436       ldata->list_fn->gl_freerow(ldata, r);
437       free(r);
438     }
439   ldata->root = ldata->selected = ldata->top = NULL;
440 }
441 
442 void
glist_display_all(struct ListData * list)443 glist_display_all(struct ListData *list)
444 {
445   int y;
446   struct ListRow *row;
447 
448   LClearAll(flayer, 0);
449 
450   y = list->list_fn->gl_printheader(list);
451 
452   if (!list->top)
453     list->top = list->root;
454   if (!list->selected)
455     list->selected = list->root;
456 
457   for (row = list->root; row != list->top; row = row->next)
458     row->y = -1;
459 
460   for (row = list->top; row; row = row->next)
461     {
462       row->y = y++;
463       if (!list->list_fn->gl_printrow(list, row))
464 	{
465 	  row->y = -1;
466 	  y--;
467 	}
468       if (y + 1 == flayer->l_height)
469 	break;
470     }
471   for (; row; row = row->next)
472     row->y = -1;
473 
474   list->list_fn->gl_printfooter(list);
475   if (list->selected && list->selected->y != -1)
476     flayer->l_y = list->selected->y;
477   else
478     flayer->l_y = flayer->l_height - 1;
479   LaySetCursor();
480 }
481 
glist_abort(void)482 void glist_abort(void)
483 {
484   ListAbort();
485 }
486 
487