home · contact · privacy
Overhauled large parts of window system to universalize scroll hints.
[plomrogue] / src / draw_wins.c
1 /* draw_wins.c */
2
3 #include "draw_wins.h"
4 #include <stdlib.h>      /* for free() */
5 #include <stdint.h>      /* for uint16_t */
6 #include <string.h>      /* for strlen() */
7 #include <ncurses.h>     /* for attri_t, chtype, mvwaddch(), wresize() */
8 #include "windows.h"     /* for struct Win */
9 #include "misc.h"        /* for try_malloc() */
10 #include "keybindings.h" /* for struct KeyBinding, for get_name_to_keycode() */
11 #include "map_objects.h" /* for structs MapObj, get_map_object_def(),
12                           * get_player()
13                           */
14 #include "map.h"         /* for Map struct */
15 #include "main.h"        /* for World struct */
16 #include "command_db.h"  /* for get_command_longdesc() */
17 #include "wincontrol.h"  /* for WinConf struct, get_winconf_by_win() */
18
19
20
21 /* Widen the ncurses window below "win" to "minx" if it is below that. */
22 static void rewiden_if_less_than(struct Win * win, uint16_t minx);
23
24 /* Write "text" into window "win". Start on row "start_y". Break text at
25  * right window edge. Also break at newlines.
26  */
27 static void draw_with_linebreaks(struct Win * win, char * text,
28                                  uint16_t start_y);
29
30 /* Write "line" into window "w" at line "y"; apply ncurses attribute
31  * "attri" to all characters drawn; if "fill" is non-zero, fill the
32  * entire line until the right window edge with empty characters
33  * ("attri" also applied on these).
34  */
35 static void draw_line(struct Win * w, uint16_t y, char * line, attr_t attri,
36                       uint8_t fill);
37
38 /* Write "text" with draw_with_linebreaks() as not starting from the top
39  * but from the bottom of "win".
40  */
41 static void draw_text_from_bottom(struct Win * win, char * text);
42
43 /* Draw onto "map" in "win" the objects in the chain at "start". */
44 static void draw_map_objects(struct World * world, struct MapObj * start,
45                              struct Map * map, struct Win * win);
46
47 /* Return keybinding list line via "kb_pp", iterate pointer pointed to by it. */
48 static char * get_kb_line_and_iterate(struct World * world,
49                                       struct KeyBinding ** kb_pp);
50
51 /* Draw from line "start" on config view for keybindings defined at "kb". */
52 static void draw_kb_view(struct World * world, struct Win * w,
53                          struct KeyBiData * kb, uint8_t start);
54
55 /* Draw into window "w" from line "start" on a "title" followed by an empty
56  * line followed by a list of all keybindings starting at kb_p.
57  */
58 static uint16_t draw_titled_keybinding_list(struct World * world, char * title,
59                                             struct Win * w, uint16_t start,
60                                             struct KeyBinding * kb_p);
61
62
63
64 static void rewiden_if_less_than(struct Win * win, uint16_t minx)
65 {
66     if (minx > getmaxx(win->frame.curses_win))
67     {
68         wresize(win->frame.curses_win, getmaxy(win->frame.curses_win), minx);
69     }
70 }
71
72
73
74 static void draw_with_linebreaks(struct Win * win, char * text,
75                                  uint16_t start_y)
76 {
77     uint16_t x, y;
78     int16_t z = -1;
79     rewiden_if_less_than(win, win->frame.size.x);
80     for (y = start_y; ; y++)
81     {
82         wresize(win->frame.curses_win, y + 1, getmaxx(win->frame.curses_win));
83         for (x = 0; x < win->frame.size.x; x++)
84         {
85             z++;
86             if ('\n' == text[z])
87             {
88                 break;
89             }
90             else
91             {
92                 mvwaddch(win->frame.curses_win, y, x, text[z]);
93             }
94             if ('\n' == text[z+1])
95             {
96                 z++;
97                 break;
98             }
99             else if (0 == text[z+1])
100             {
101                 return;
102             }
103         }
104     }
105 }
106
107
108
109 static void draw_line(struct Win * w, uint16_t y, char * line, attr_t attri,
110                       uint8_t fill)
111 {
112     uint16_t x = 0;
113     for (; x < strlen(line); x++)
114     {
115         rewiden_if_less_than(w, x + 1);
116         mvwaddch(w->frame.curses_win, y, x, line[x] | attri);
117     }
118     if (0 != fill)
119     {
120         for (; x < w->frame.size.x; x++)
121         {
122             rewiden_if_less_than(w, x + 1);
123             mvwaddch(w->frame.curses_win, y, x, ' ' | attri);
124         }
125     }
126 }
127
128
129
130 static void draw_text_from_bottom(struct Win * win, char * text)
131 {
132     /* Determine number of lines text would have in a window of win's width,
133      * but infinite height. Treat \n and \0 as control chars for incrementing
134      * y and stopping the loop. Make sure +they* don't count as cell space.
135      */
136     char toggle = 0;
137     uint16_t x, y, offset;
138     int16_t z = -1;
139     for (y = 0; 0 == toggle; y++)
140     {
141         for (x = 0; x < win->frame.size.x; x++)
142         {
143             z++;
144             if ('\n' == text[z])
145             {
146                 break;
147             }
148             if ('\n' == text[z+1])
149             {
150                 z++;
151                 break;
152             }
153             else if (0 == text[z+1])
154             {
155                 toggle = 1;
156                 break;
157             }
158         }
159     }
160     z = -1;
161
162     /* Depending on what's bigger, determine start point in window or text. */
163     uint16_t start_y = 0;
164     if (y < win->frame.size.y)
165     {
166         start_y = win->frame.size.y - y;
167     }
168     else if (y > win->frame.size.y)
169     {
170         offset = y - win->frame.size.y;
171         for (y = 0; y < offset; y++)
172         {
173             for (x = 0; x < win->frame.size.x; x++)
174             {
175                 z++;
176                 if ('\n' == text[z])
177                 {
178                     break;
179                 }
180                 if ('\n' == text[z+1])
181                 {
182                     z++;
183                     break;
184                 }
185             }
186         }
187         text = text + (sizeof(char) * (z + 1));
188     }
189
190     draw_with_linebreaks(win, text, start_y);
191 }
192
193
194
195 static void draw_map_objects(struct World * world, struct MapObj * start,
196                              struct Map * map, struct Win * win)
197 {
198     struct MapObj * o;
199     struct MapObjDef * d;
200     char c;
201     uint8_t i;
202     for (i = 0; i < 2; i++)
203     {
204         for (o = start; o != 0; o = o->next)
205         {
206             if ((   (0 == i && 0 == o->lifepoints)         /* Draw in-animate */
207                  || (1 == i && 0 < o->lifepoints)))        /* objects first.  */
208             {
209                 d = get_map_object_def(world, o->type);
210                 c = d->char_on_map;
211                 mvwaddch(win->frame.curses_win, o->pos.y, o->pos.x, c);
212             }
213         }
214     }
215 }
216
217
218
219 static char * get_kb_line_and_iterate(struct World * world,
220                                       struct KeyBinding ** kb_pp)
221 {
222     char * f_name = "get_kb_line_and_iterate()";
223     struct KeyBinding * kb_p = * kb_pp;
224     char * keyname = get_name_to_keycode(world, kb_p->key);
225     char * cmd_dsc = get_command_longdsc(world, kb_p->name);
226     uint16_t size = 9 + 1 + strlen(cmd_dsc) + 1;
227     char * line = try_malloc(size, world, f_name);
228     sprintf(line, "%-9s %s", keyname, cmd_dsc);
229     free(keyname);
230     * kb_pp = kb_p->next;
231     return line;
232 }
233
234
235
236 static void draw_kb_view(struct World * world, struct Win * w,
237                          struct KeyBiData * kb, uint8_t start)
238 {
239     if (0 == kb->kbs)
240     {
241         wresize(w->frame.curses_win, start + 1, getmaxx(w->frame.curses_win));
242         draw_line(w, start, "(none)", 0, 0);
243         return;
244     }
245     struct KeyBinding * kb_p = kb->kbs;
246     uint16_t y;
247     for (y = start; 0 != kb_p; y++)
248     {
249         wresize(w->frame.curses_win, y + 1, getmaxx(w->frame.curses_win));
250         attr_t attri = 0;
251         if (y - start == kb->select)
252         {
253             attri = A_REVERSE;
254             if (1 == kb->edit)
255             {
256                 attri = attri | A_BLINK;
257             }
258         }
259         char * kb_line = get_kb_line_and_iterate(world, &kb_p);
260         draw_line(w, y, kb_line, attri, 1);
261         free(kb_line);
262     }
263 }
264
265
266
267 static uint16_t draw_titled_keybinding_list(struct World * world, char * title,
268                                             struct Win * w, uint16_t start,
269                                             struct KeyBinding * kb_p)
270 {
271     uint16_t x, y;
272     uint16_t i = 0;
273     uint8_t state = 0;
274     for (y = start; (0 == state || 0 != kb_p); y++)
275     {
276         wresize(w->frame.curses_win, y + 1, getmaxx(w->frame.curses_win));
277         if (0 == state)
278         {
279             for (x = 0; ; x++)
280             {
281                 if (i == strlen(title))
282                 {
283                     y++;
284                     state = 1 + (0 == kb_p);
285                     i = 0;
286                     break;
287                 }
288                 rewiden_if_less_than(w, x + 1);
289                 mvwaddch(w->frame.curses_win, y, x, title[i]);
290                 i++;
291             }
292             continue;
293         }
294         char * kb_line = get_kb_line_and_iterate(world, &kb_p);
295         if (strlen(kb_line) > getmaxx(w->frame.curses_win))
296         {
297             wresize(w->frame.curses_win, y + 1, strlen(kb_line));
298         }
299         draw_line(w, y, kb_line, 0, 0);
300         free(kb_line);
301     }
302     if (2 == state)
303     {
304         char * none = "(none)";
305         if (strlen(none) > getmaxx(w->frame.curses_win))
306         {
307             wresize(w->frame.curses_win, y + 1, strlen(none));
308         }
309         else
310         {
311             wresize(w->frame.curses_win, y + 1, getmaxx(w->frame.curses_win));
312         }
313         draw_line(w, y, none, 0, 0);
314         y++;
315     }
316     return y;
317 }
318
319
320
321 extern void draw_win_log(struct Win * win)
322 {
323     struct World * world = (struct World *) win->data;
324     draw_text_from_bottom(win, world->log);
325 }
326
327
328
329 extern void draw_win_map(struct Win * win)
330 {
331     struct World * world = (struct World *) win->data;
332     struct Map * map = world->map;
333     char * cells = map->cells;
334     wresize(win->frame.curses_win, map->size.y, map->size.x);
335     uint16_t x, y, z;
336     for (y = 0; y < map->size.y; y++)
337     {
338         for (x = 0; x < map->size.x; x++)
339         {
340             mvwaddch(win->frame.curses_win, y, x, cells[z]);
341             z++;
342         }
343     }
344     draw_map_objects(world, world->map_objs, map, win);
345 }
346
347
348
349 extern void draw_win_info(struct Win * win)
350 {
351     struct World * world = (struct World *) win->data;
352     char * dsc_turn      = "Turn: ";
353     char * dsc_hitpoints = "\nHitpoints: ";
354     char * dsc_score     = "\nScore: ";
355     uint16_t maxl = strlen(dsc_turn) + strlen(dsc_hitpoints) + strlen(dsc_score)
356                     + 10 + 5 + 10;       /* max strlens of numbers to be used */
357     char text[maxl + 1];
358     struct MapObj * player = get_player(world);
359     sprintf(text, "%s%d%s%d%s%d",
360             dsc_turn, world->turn,
361             dsc_hitpoints, player->lifepoints,
362             dsc_score, world->score);
363     draw_with_linebreaks(win, text, 0);
364 }
365
366
367
368 extern void draw_win_inventory(struct Win * win)
369 {
370     struct World * world = (struct World *) win->data;
371     struct MapObj * player = get_player(world);
372     if (NULL == player->owns)
373     {
374         draw_line(win, 0, "(none)", 0, 0);
375         return;
376     }
377     win->center.y = world->inventory_select;
378     struct MapObj * owned = player->owns;
379     uint8_t y;
380     for (y = 0; NULL != owned; y++)
381     {
382         wresize(win->frame.curses_win, y + 1, getmaxx(win->frame.curses_win));
383         attr_t attri = 0;
384         if (y == world->inventory_select)
385         {
386             attri = A_REVERSE;
387         }
388         struct MapObjDef * mod = get_map_object_def(world, owned->type);
389         draw_line(win, y, mod->name, attri, 0);
390         owned = owned->next;
391     }
392 }
393
394
395
396 extern void draw_win_available_keybindings(struct Win * win)
397 {
398     struct World * world = (struct World *) win->data;
399     char * title = "Active window's keybindings:";
400     struct KeyBinding * kb_p;
401     struct WinConf * wc = get_winconf_by_win(world, world->wmeta->active);
402     if     (0 == wc->view)
403     {
404         kb_p = wc->kb.kbs;
405     }
406     else if (1 == wc->view)
407     {
408         kb_p = world->kb_wingeom.kbs;
409     }
410     else if (2 == wc->view)
411     {
412         kb_p = world->kb_winkeys.kbs;
413     }
414     uint16_t offset = draw_titled_keybinding_list(world, title, win, 0, kb_p);
415     draw_titled_keybinding_list(world, "Global keybindings", win, offset + 1,
416                                 world->kb_global.kbs);
417 }
418
419
420
421 extern void draw_win_keybindings_global(struct Win * win)
422 {
423     struct World * world = (struct World *) win->data;
424     win->center.y = world->kb_global.select;
425     draw_kb_view(world, win, &world->kb_global, 0);
426 }
427
428
429
430 extern void draw_win_keybindings_winconf_geometry(struct Win * win)
431 {
432     struct World * world = (struct World *) win->data;
433     win->center.y = world->kb_wingeom.select;
434     draw_kb_view(world, win, &world->kb_wingeom, 0);
435 }
436
437
438
439 extern void draw_win_keybindings_winconf_keybindings(struct Win * win)
440 {
441     struct World * world = (struct World *) win->data;
442     win->center.y = world->kb_winkeys.select;
443     draw_kb_view(world, win, &world->kb_winkeys, 0);
444 }
445
446
447
448 extern void draw_winconf_keybindings(struct Win * win)
449 {
450     struct World * world = (struct World *) win->data;
451     struct WinConf * wc = get_winconf_by_win(world, win);
452     char * title = "Window's keybindings:";
453     draw_line(win, 0, title, 0, 0);
454     draw_kb_view(world, win, &wc->kb, 2);
455     win->center.y = wc->kb.select + 2;
456 }
457
458
459
460 extern void draw_winconf_geometry(struct Win * win)
461 {
462     struct World * world = (struct World *) win->data;
463     struct WinConf * wcp = get_winconf_by_win(world, win);
464     char * title = "Window's geometry:\n";
465     char * h_d   = "\nHeight to save: ";
466     char * h_pos = " (width in cells)";
467     char * h_neg = " (negative diff: cells to maximum width)";
468     char * w_d   = "\n\nWidth to save: ";
469     char * w_pos = " (height in cells)";
470     char * w_neg = " (negative diff: cells to maximum height)";
471     char * h_t = h_pos;
472     char * w_t = w_pos;
473     if      (1 == wcp->height_type)
474     {
475         h_t = h_neg;
476     }
477     if     (1 == wcp->width_type)
478     {
479         w_t = w_neg;
480     }
481     uint16_t maxl = strlen(title)
482                     + strlen(h_t) + strlen(h_d) + 6
483                     + strlen(w_t) + strlen(w_d) + 6 + 1;
484     char text[maxl + 1];
485     sprintf(text, "%s%s%d%s%s%d%s", title, h_d, wcp->height, h_t,
486                                            w_d, wcp->width,  w_t);
487     draw_with_linebreaks(win, text, 0);
488 }