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