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