#include #include #include #include #include #include #include #include #include #include //--- // Node creation and destruction functions //--- #define special(node) \ ((node)->type == TEX_NODECLASS_TEXT || \ (node)->type == TEX_NODECLASS_ENV) enum cursorMoveResult move_cursor_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action) { if (node->type == 0) { // Text if (!context->elementIsText || context->cursorText != node) return CURSOR_NOT_HERE; if (action == CURSOR_MOVE_LEFT) { if (context->offset <= 0) return CURSOR_PAST_START; context->offset--; return SUCCESS; } if (action == CURSOR_MOVE_RIGHT) { if (context->offset >= strlen(node->text) - 2) return CURSOR_PAST_END; context->offset++; return SUCCESS; } } if (node->type > 1) { if (node->args[0] == NULL) return CURSOR_NOT_HERE; int returnCode = TeX_flow_cursor_action(node->args[0], context, action); if (returnCode == SUCCESS) return returnCode; if (returnCode == CURSOR_PAST_END) { // Go to the denominator if (node->args[1] == NULL) return CURSOR_PAST_END; context->elementIsText = false; context->cursorFlow = node->args[1]; context->offset = 0; return SUCCESS; } if (returnCode != CURSOR_NOT_HERE) return returnCode; if (node->args[1] == NULL) return CURSOR_NOT_HERE; returnCode = TeX_flow_cursor_action(node->args[1], context, action); if (returnCode == CURSOR_PAST_START) { // Go to the numerator context->elementIsText = false; context->cursorFlow = node->args[0]; context->offset = node->args[0]->elements; return SUCCESS; } return returnCode; } // TODO: matrices return CURSOR_NOT_HERE; } enum cursorMoveResult cursor_enter_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action) { enum cursorMoveResult skipPast = action == CURSOR_MOVE_RIGHT ? CURSOR_PAST_END : CURSOR_PAST_START; if (node->type == 0) { int len = strlen(node->text); if (len == 1) return skipPast; // Text context->elementIsText = true; context->cursorText = node; context->offset = action == CURSOR_MOVE_RIGHT ? 0 : len - 2; return SUCCESS; } if (node->type > 1) { if (action == CURSOR_MOVE_RIGHT) { // Enter the numerator if (node->args[0] == NULL) return CURSOR_PAST_END; context->elementIsText = false; context->cursorFlow = node->args[0]; context->offset = 0; return SUCCESS; } else if (action == CURSOR_MOVE_LEFT) { // Enter the denominator struct TeX_Flow *denom = node->args[1]; if (denom == NULL) denom = node->args[0]; if (denom == NULL) return CURSOR_PAST_START; context->elementIsText = false; context->cursorFlow = denom; context->offset = denom->elements; return SUCCESS; } } // TODO: matrices return skipPast; } /* TeX_node_text(): Make a text node */ struct TeX_Node *TeX_node_text(char const *utf8) { struct TeX_Node *node = calloc(1, sizeof *node); if(!node) return NULL; /* TODO: Don't use strdup(); convert from utf8 instead */ node->text = malloc(strlen(utf8) + 1); if(!node->text) { free(node); return NULL; } strcpy((void *)node->text, utf8); node->type = TEX_NODECLASS_TEXT; return node; } /* TeX_node_command(): Make a command node without arguments */ struct TeX_Node *TeX_node_command(char const *name) { /* Get the class id from the class table */ int type = TeX_class_find(name); if(type < 0) return NULL; struct TeX_Node *node = calloc(1, sizeof *node); if(!node) return NULL; node->type = type; node->next = NULL; return node; } /* TeX_node_add_arg(): Add an argument to a command node */ struct TeX_Node *TeX_node_add_arg(struct TeX_Node *node, struct TeX_Flow *arg) { if(!node || !arg) return node; /* Drop the argument if it's for a plain text or environment node */ if(special(node)) { TeX_flow_free(arg); return node; } /* Otherwise look up a free slot in the argument array of node */ int i = 0; while(i < TEX_NODE_MAX_CHILDREN && node->args[i]) i++; if(i < TEX_NODE_MAX_CHILDREN) node->args[i] = arg; else TeX_flow_free(arg); return node; } /* TeX_node_absorb(): Absorb text into a command node */ struct TeX_Node *TeX_node_absorb(struct TeX_Node *node, char const *text) { char const *class = TeX_class_of(node)->name; if(!strcmp(class, "left") || !strcmp(class, "right")) { node->subtype = text[0]; return node; } return TeX_node_add_arg(node, TeX_flow_add_node(NULL, TeX_node_text(text))); } /* TeX_node_env(): Make a environment node */ struct TeX_Node *TeX_node_env(struct TeX_Env *env) { struct TeX_Node *node = calloc(1, sizeof *node); if(!node) return NULL; node->env = env; node->type = TEX_NODECLASS_ENV; return node; } /* TeX_node_free(): Free a TeX_Node and all its children */ void TeX_node_free(struct TeX_Node *node) { /* Text nodes: free the allocated string */ if(node->type == TEX_NODECLASS_TEXT) free(node->text); /* Environment nodes: free the environment */ else if(node->type == TEX_NODECLASS_ENV) node->env->free(node->env); /* Class nodes: recursively free children */ else for(int i = 0; i < TEX_NODE_MAX_CHILDREN; i++) { if(node->args[i]) TeX_flow_free(node->args[i]); } /* TODO: Extend with environment nodes */ free(node); } //--- // Layout and rendering //--- /* TeX_node_layout(): Compute the layout of a node */ void TeX_node_layout(struct TeX_Node *node, int display) { /* First compute the layout of the children */ if(!special(node)) for(int i = 0; i < TEX_NODE_MAX_CHILDREN && node->args[i]; i++) TeX_flow_layout(node->args[i], display); /* Then use the class' special layout computation function */ TeX_class_of(node)->layout(node, display); } /* TeX_node_render(): Render a node and its children */ void TeX_node_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { /* Don't render hidden or invalid elements */ if(node->hidden) return; /* For non trivial classes, use the class' special function */ TeX_class_of(node)->render(node, context, x, y, color); } //--- // Printing function //--- #ifdef TEX_PRINT #include #include /* TeX_print_node(): Print a node's tree */ void TeX_print_node(struct TeX_Node *node, int indent) { printf("%*s", indent, ""); if(!node) { puts("node (null)"); return; } if(node->type == TEX_NODECLASS_TEXT) { printf("\"%s\"", node->text); printf(GRAY " %dx%d,%d %+d%+d" END "\n", node->width, node->height, node->line, node->x, node->l); } else if(node->type == TEX_NODECLASS_ENV) { printf("\\env" GRAY " %dx%d,%d %+d%+d" END "\n", node->width, node->height, node->line, node->x, node->l); TeX_print_env(node->env, indent + 4); } else { printf("<%s>", TeX_class_of(node)->name); if(node->subtype) printf(" subtype '%c'", node->subtype); printf(" "GRAY "%dx%d,%d %+d%+d" END "\n", node->width, node->height, node->line, node->x, node->l); for(int i = 0; i < TEX_NODE_MAX_CHILDREN && node->args[i]; i++) TeX_print_flow(node->args[i], indent + 4); } } #endif /* TEX_PRINT */