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