#include #include #include #include "ncrswrap.h" #include "node.h" /* Internal allocator method for ct_node structures. */ static inline struct ct_node *__alloc_node(const enum ct_nodetype type) { struct ct_node *node = (struct ct_node *)malloc(sizeof(struct ct_node)); node->type = type; return node; } /* Construct a new window node (ct_node of type NODE_WIN). */ struct ct_node *init_window_node(WINDOW *const win) { struct ct_node *node = __alloc_node(NODE_WINDOW); node->win = win; return node; } static struct ct_node * auto_window_node(const struct ct_dims *const dims) { WINDOW *win = newwin(dims->height, dims->width, dims->y, dims->x); return init_window_node(win); } static struct ct_node *init_abstract_node(struct ct_node *const node0, struct ct_node *const node1, const enum ct_axis axis, const float ratio, struct ct_dims *const dims) { struct ct_node *node = __alloc_node(NODE_ABSTRACT); node->axis = axis; node->ratio = ratio; node->dims = dims; node->child[0] = node0; node->child[1] = node1; return node; } void destroy_node(struct ct_node *const node) { if (node->type == NODE_WINDOW) { /* Window Node */ delwin(node->win); goto end; } /* Abstract Node */ assert(node->type == NODE_ABSTRACT); destroy_node(node->child[0]); destroy_node(node->child[1]); free(node->dims); end: free(node); } static inline struct ct_dims *__alloc_dims(int x, int y, int width, int height) { struct ct_dims *dims; dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); *dims = (struct ct_dims){ .x = x, .y = y, .width = width, .height = height, }; return dims; } static inline struct ct_dims * __dup_dims(const struct ct_dims *const dims) { struct ct_dims *dup; dup = (struct ct_dims *)malloc(sizeof(struct ct_dims)); memcpy(dup, dims, sizeof(struct ct_dims)); return dup; } static inline struct ct_dims *termdims(void) { struct ct_dims *dims = __alloc_dims(0, 0, 0, 0); termsize(dims->width, dims->height); return dims; } static inline void nodesize(const struct ct_node *const node, int *const width, int *const height) { if (node->type == NODE_WINDOW) { /* Window Node */ getmaxyx(node->win, *height, *width); } else { /* Abstract Node */ assert(node->type == NODE_ABSTRACT); *width = node->dims->width; *height = node->dims->height; } } static inline struct ct_dims *nodedims(const struct ct_node *const node) { struct ct_dims *dims; if (node->type == NODE_WINDOW) { /* Window Node */ dims = (struct ct_dims *)malloc(sizeof(struct ct_dims)); getbegyx(node->win, dims->y, dims->x); getmaxyx(node->win, dims->height, dims->width); } else { /* Abstract Node */ assert(node->type == NODE_ABSTRACT); dims = __dup_dims(node->dims); } return dims; } /* Calculate the dimensions for nodes resulting from a bifurcation. * Returns 0 on success, and 1 on failure if any width/height are 0 characters. * WARNING: This function does not guarantee the x,y positions returned * WARNING: are valid screen coordinates. */ static int bifurcate_dims(const struct ct_dims *const parent_dims, const enum ct_axis axis, const float ratio, struct ct_dims **const dims0, struct ct_dims **const dims1) { assert(0 < ratio && ratio < 1); struct ct_dims *_dims0, *_dims1; _dims0 = __dup_dims(parent_dims); _dims1 = __dup_dims(parent_dims); if (axis == AXIS_X) { _dims0->width *= ratio; _dims1->width -= _dims0->width; _dims1->x += _dims0->width; } else { _dims0->height *= ratio; _dims1->height -= _dims0->height; _dims1->y += _dims0->height; } if (!_dims0->width || !_dims0->height || !_dims1->width || !_dims1->height) return 1; // propagate bifurcated dimensions *dims0 = _dims0; *dims1 = _dims1; return 0; } /* NOTE: resize_node calls wnoutrefresh(3x), which expects * NOTE: a call doupdate(3x) call afterwards to flush ncurses * NOTE: virtual screen to the physical screen. */ void resize_node(struct ct_node *const node, struct ct_dims *const new_dims) { if (node->type == NODE_WINDOW) { /* Window Node */ resizemv_window(new_dims->x, new_dims->y, new_dims->width, new_dims->height, node->win); free(new_dims); wnoutrefresh(node->win); } else { /* Abstract Node */ assert(node->type == NODE_ABSTRACT); struct ct_dims *dims0, *dims1; free(node->dims); node->dims = new_dims; bifurcate_dims(new_dims, node->axis, node->ratio, &dims0, &dims1); resize_node(node->child[0], dims0); resize_node(node->child[1], dims1); } } /* Subdivide a window node's allocated region into two window nodes * replacing the original node with an abstract node. * Parameters: * axis - controls which direction the subdivision occurs * invert_axis - invert index of the original node in the new abstract node */ void bifurcate_window_node(struct ct_node **const node, const enum ct_axis axis, const int invert_axis, const float ratio) { assert((*node)->type == NODE_WINDOW); struct ct_dims *dims0, *dims1; struct ct_node *node0, *node1; struct ct_dims *original_dims = nodedims(*node); if (bifurcate_dims(original_dims, axis, ratio, &dims0, &dims1)) { /* TODO: handle this error properly */ free(original_dims); exit(1); return; } if (invert_axis) { /* Inverted Bifurcation */ node0 = auto_window_node(dims0); node1 = *node; resize_node(node1, dims1); } else { /* Non-Inverted Bifurcation */ node0 = *node; node1 = auto_window_node(dims1); resize_node(node0, dims0); } *node = init_abstract_node(node0, node1, axis, ratio, original_dims); } /* Collapse an abstract node, killing one child node and resizing * the other to take its place. */ static void collapse_abstract_node(struct ct_node **const node, const int collapse_i) { assert((*node)->type == NODE_ABSTRACT); assert(0 <= collapse_i && collapse_i < NODE_CHILD_N); // WARNING: only works for NODE_CHILD_N=2 (binary trees) destroy_node((*node)->child[!collapse_i]); struct ct_node *collapse_target = (*node)->child[collapse_i]; free(*node); *node = collapse_target; }