window.c (9532B)
1 /** 2 * @file window.c 3 * @author Dimitrije Dobrota 4 * @date 9 June 2022 5 * @brief This file contains the implementation of the window interface 6 * 7 * This file handles the creation and deletion process of nodes in the binary 8 * tree of windows used for recalculating the ncurses WINDOW position in 9 * dimension when the terminal has been resized. It's possible to "split" the 10 * current window in two using a given ration or fixed size, as well as create 11 * a centered sub-window. It makes some of the tree data accessible via function 12 * so that other functions can know about the sizes and adapt to them. 13 */ 14 15 #include <curses.h> 16 #include <stdlib.h> 17 #include <string.h> 18 19 #include "utils.h" 20 #include "window.h" 21 22 #define H(c) c->param[0] 23 #define W(c) c->param[1] 24 #define Y(c) c->param[2] 25 #define X(c) c->param[3] 26 27 #define window_T T 28 typedef struct T *T; 29 30 /** 31 * @brief A node in a binary tree of windows 32 * 33 * window_T contains all of the information about the size and dimensions of 34 * ncurses WINDOW, as well as all the information on how to recalculate 35 * children when a terminal resize occurs. 36 */ 37 struct window_T { 38 WINDOW *win; ///< ncurses WINDOW* that represents current window_T 39 T c1; ///< left child, NULL if none 40 T c2; ///< right child, NULL if none 41 T sibling; ///< sibling, NULL if none 42 43 int param[4]; ///< windows size and dimension, refer to macros 44 int mod[3]; ///< Information an recreating children 45 46 char *title; ///< Window title to display, NULL if none 47 }; 48 49 /** 50 * @brief Apply settings, draw a border, print title for a give window 51 * 52 * @param title: title to be printed unless NULL 53 */ 54 void WINDOW_init(WINDOW *win, char *title) { 55 window_settings(win); 56 box(win, ACS_VLINE, ACS_HLINE); 57 if (title) 58 mvwprintw(win, 0, 1, " %s ", title); 59 } 60 61 /** 62 * @brief create new window_T 63 * 64 * Allocate the memory for a new window_T and set its fields to the default 65 * value. Should be the only way new window_T objects are created 66 */ 67 T window_new(void) { 68 T self; 69 70 MEM_CHECK(self = malloc(sizeof(*self))); 71 self->win = NULL; 72 self->c1 = NULL; 73 self->c2 = NULL; 74 self->sibling = NULL; 75 self->title = NULL; 76 H(self) = 0; 77 W(self) = 0; 78 Y(self) = 0; 79 X(self) = 0; 80 return self; 81 } 82 83 /// Getter: return the sibling of the current window 84 T window_sibiling(T self) { return self->sibling; } 85 86 /// Getter: return the height of the current window 87 int window_height(T self) { return H(self) - 2; } 88 89 /// Getter: return the of a current window 90 int window_width(T self) { return W(self) - 2; } 91 92 /// Getter: return the x coordinate of the current window 93 int window_x(T self) { return X(self); } 94 95 /// Getter: return the y coordinate of the current window 96 int window_y(T self) { return Y(self); } 97 98 /// Getter: return the ncurses WINDOW* of the current window 99 WINDOW *window_win(T self) { return self->win; } 100 101 /// Setter: set the title of the current window 102 void window_set_title(T self, char *title) { self->title = title; } 103 104 /** 105 * @brief create new ncurses WINDOW to the specification with default settings 106 * 107 * New ncurses WINDOW is created with newwin and it should be deleted with 108 * delwin. Setting are applied with the call to WINDOW_INIT 109 */ 110 WINDOW *window_win_new(T self) { 111 WINDOW *win = newwin(H(self), W(self), Y(self), X(self)); 112 WINDOW_init(win, self->title); 113 wrefresh(win); 114 return win; 115 } 116 117 /** 118 * @brief Initialize the window to be used as main 119 * 120 * This function will use the ncurses interface directly to set the screen size 121 * to match the whole terminal. This window object should always be used as a 122 * root of the tree and under no condition should it be used as a child of 123 * other window. 124 * 125 * There can be multiple instances of windows initialized with this function 126 */ 127 T window_init(T self) { 128 self->win = stdscr; 129 WINDOW_init(stdscr, self->title); 130 wrefresh(self->win); 131 H(self) = LINES; 132 W(self) = COLS; 133 return self; 134 } 135 136 /** 137 * @brief Free the window and it's children recursively 138 * 139 * Free the object itself and ncurses WINDOW* with delwin 140 */ 141 void window_free(T self) { 142 if (self == NULL) 143 return; 144 window_free(self->c1); 145 window_free(self->c2); 146 delwin(self->win); 147 free(self); 148 } 149 150 /** 151 * @brief Delete all children and sub-children of a window 152 */ 153 void window_unsplit(T self) { 154 window_free(self->c1); 155 window_free(self->c2); 156 self->c1 = NULL; 157 self->c2 = NULL; 158 } 159 160 /** 161 * @brief Given the window calculate the position and dimensions of it's 162 * children 163 * 164 * This function takes care of calculation for two windows in a given ration or 165 * fixed size, and single sub-window. 166 * If a given windows has 2 children then they are split with the following 167 * rule: 168 * - If mod[0] is 0, two created windows will be side by side 169 * - If mod[0] is not 0, two created windows will be one on top of the other 170 * - If mod[1] and mod[2] are 0, nothing happens 171 * - If mod[1] or mod[2] is 0, the window with a non zero value will be of a 172 * fixed size, while the other will fill the rest of the screen 173 * If a given windows has 1 child then: 174 * - Child dimensions will be mod[1] x mod[2] or made to fit in it's parent 175 */ 176 void window_calc_children(T self) { 177 T c1, c2, f, nf; 178 c1 = self->c1; 179 c2 = self->c2; 180 int fixed = 0, *fv; 181 int hor = self->mod[0], a = self->mod[1], b = self->mod[2]; 182 183 if (c2 != NULL) { 184 if (a == 0 && b == 0) 185 return; 186 187 if (a == 0) { 188 f = c2; 189 nf = c1; 190 fv = &b; 191 fixed = 1; 192 } else if (b == 0) { 193 f = c1; 194 nf = c2; 195 fv = &a; 196 fixed = 1; 197 } 198 199 if (hor) { 200 W(c1) = W(c2) = W(self) - 2; 201 202 if (!fixed) { 203 H(c1) = ((H(self) - 2) / (a + b)) * a; 204 } else { 205 H(f) = *fv; 206 } 207 H(nf) = (H(self) - 2) - H(f); 208 209 X(c1) = X(c2) = X(self) + 1; 210 Y(c1) = Y(self) + 1; 211 Y(c2) = Y(self) + H(c1) + 1; 212 } else { 213 H(c1) = H(c2) = H(self) - 2; 214 215 if (!fixed) { 216 W(c1) = ((W(self) - 2) / (a + b)) * a; 217 } else { 218 W(f) = *fv; 219 } 220 W(nf) = (W(self) - 2) - W(f); 221 222 Y(c1) = Y(c2) = Y(self) + 1; 223 X(c1) = X(self) + 1; 224 X(c2) = X(self) + W(c1) + 1; 225 } 226 } else { 227 H(c1) = ACLAMP(a + 2, 0, H(self)); 228 W(c1) = ACLAMP(b + 2, 0, W(self)); 229 Y(c1) = Y(self) + (H(self) - H(c1)) / 2; 230 X(c1) = X(self) + (W(self) - W(c1)) / 2; 231 } 232 } 233 234 /** 235 * @brief Split the window in a given ration into two windows with a name 236 * 237 * @param hor: should the split line be horizontal or vertical 238 * @param a: ration of the first window 239 * @param b: ration of the second window 240 * @param name1: name of the first window 241 * @param name2: name of the second window 242 * 243 * @see window_calc_children to see how the ratios are used 244 */ 245 T window_split(T self, int hor, int a, int b, char *name1, char *name2) { 246 if (self->c1) 247 window_free(self->c1); 248 if (self->c2) 249 window_free(self->c2); 250 251 self->c1 = window_new(); 252 self->c2 = window_new(); 253 254 self->c1->sibling = self->c2; 255 self->c2->sibling = self->c1; 256 257 self->mod[0] = hor; 258 self->mod[1] = a; 259 self->mod[2] = b; 260 261 window_calc_children(self); 262 263 self->c1->title = name1; 264 self->c2->title = name2; 265 266 self->c1->win = window_win_new(self->c1); 267 self->c2->win = window_win_new(self->c2); 268 269 return self->c1; 270 } 271 272 /** 273 * @brief Create a sub-window that will be of a given size or made to fit it's 274 * parent 275 * 276 * @param h: desired height of a new window_T 277 * @param w: desired of a new window_T 278 * @param name: name of the new window_T 279 */ 280 T window_center(T self, int h, int w, char *name) { 281 self->c1 = window_new(); 282 self->c2 = NULL; 283 284 self->mod[0] = -1; 285 self->mod[1] = h; 286 self->mod[2] = w; 287 288 window_calc_children(self); 289 290 self->c1->title = name; 291 self->c1->win = window_win_new(self->c1); 292 293 return self->c1; 294 } 295 296 /** 297 * @brief Recursively recalculate the size of each child and make new ncurses 298 * WINDOW 299 */ 300 void window_update_children(T self) { 301 if (self == NULL || self->c1 == NULL) 302 return; 303 304 window_calc_children(self); 305 306 delwin(self->c1->win); 307 self->c1->win = window_win_new(self->c1); 308 window_update_children(self->c1); 309 310 if (self->c2 != NULL) { 311 delwin(self->c2->win); 312 self->c2->win = window_win_new(self->c2); 313 window_update_children(self->c2); 314 } 315 } 316 317 /** 318 * @brief Clear the current window, reset the setting, keep the border call 319 * ncurses refresh() 320 */ 321 void window_clear(T self) { 322 werase(self->win); 323 WINDOW_init(self->win, self->title); 324 wrefresh(self->win); 325 } 326 327 /** 328 * @brief Same as window_clear but without calling ncurses refresh() 329 * 330 * @see window_clear 331 */ 332 void window_clear_noRefresh(T self) { 333 werase(self->win); 334 WINDOW_init(self->win, self->title); 335 } 336 337 /** 338 * @brief Default setting for each window 339 */ 340 void window_settings(WINDOW *win) { 341 keypad(win, TRUE); 342 wattrset(win, COLOR_PAIR(0)); 343 } 344 345 /** 346 * @brief Check if coordinates are in the window 347 */ 348 int window_clicked(T self, int y, int x) { 349 if (x <= X(self) || y <= Y(self)) 350 return 0; 351 352 if (x >= X(self) + W(self) || y >= Y(self) + H(self)) 353 return 0; 354 355 return 1; 356 } 357 358 /** 359 * @brief Move the cursor to the given why position and x position so that the 360 * text of len n will be centered 361 * 362 * @param y: y position 363 * @n length of the text to be centered 364 */ 365 void wcenter_horizontal(T window, int y, int n) { 366 wmove(window->win, y, (W(window) - n - 2) / 2 + 1); 367 } 368 369 /** 370 * @brief Return the y position of the cursor so that n lines will be centered 371 * 372 * @param n: number of lines to be centered 373 */ 374 int wcenter_vertical(T window, int n) { return (H(window) - n - 2) / 2 + 1; } 375 376 #undef T