/* * frame.c * * Copyright © 2013-2016 Thomas White * * This file is part of Colloquium. * * Colloquium is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "sc_parse.h" #include "frame.h" struct text_run { SCBlock *scblock; size_t offs_bytes; size_t len_bytes; PangoFontDescription *fontdesc; }; struct _paragraph { int n_runs; struct text_run *runs; int open; PangoLayout *layout; double height; }; PangoLayout *paragraph_layout(Paragraph *para) { return para->layout; } double paragraph_height(Paragraph *para) { return para->height; } static int alloc_ro(struct frame *fr) { struct frame **new_ro; new_ro = realloc(fr->children, fr->max_children*sizeof(struct frame *)); if ( new_ro == NULL ) return 1; fr->children = new_ro; return 0; } struct frame *frame_new() { struct frame *n; n = calloc(1, sizeof(struct frame)); if ( n == NULL ) return NULL; n->children = NULL; n->max_children = 32; if ( alloc_ro(n) ) { fprintf(stderr, "Couldn't allocate children\n"); free(n); return NULL; } n->num_children = 0; n->scblocks = NULL; n->n_paras = 0; n->paras = NULL; return n; } static void free_paragraph(Paragraph *para) { int i; for ( i=0; in_runs; i++ ) { pango_font_description_free(para->runs[i].fontdesc); } free(para->runs); if ( para->layout != NULL ) g_object_unref(para->layout); free(para); } void frame_free(struct frame *fr) { int i; if ( fr == NULL ) return; /* Free paragraphs */ if ( fr->paras != NULL ) { for ( i=0; in_paras; i++ ) { free_paragraph(fr->paras[i]); } free(fr->paras); } /* Free all children */ for ( i=0; inum_children; i++ ) { frame_free(fr->children[i]); } free(fr->children); free(fr); } struct frame *add_subframe(struct frame *fr) { struct frame *n; n = frame_new(); if ( n == NULL ) return NULL; if ( fr->num_children == fr->max_children ) { fr->max_children += 32; if ( alloc_ro(fr) ) return NULL; } fr->children[fr->num_children++] = n; return n; } void show_hierarchy(struct frame *fr, const char *t) { int i; char tn[1024]; strcpy(tn, t); strcat(tn, " "); printf("%s%p (%.2f x %.2f)\n", t, fr, fr->w, fr->h); for ( i=0; inum_children; i++ ) { show_hierarchy(fr->children[i], tn); } } static struct frame *find_parent(struct frame *fr, struct frame *search) { int i; for ( i=0; inum_children; i++ ) { if ( fr->children[i] == search ) { return fr; } } for ( i=0; inum_children; i++ ) { struct frame *tt; tt = find_parent(fr->children[i], search); if ( tt != NULL ) return tt; } return NULL; } void delete_subframe(struct frame *top, struct frame *fr) { struct frame *parent; int i, idx, found; parent = find_parent(top, fr); if ( parent == NULL ) { fprintf(stderr, "Couldn't find parent when deleting frame.\n"); return; } found = 0; for ( i=0; inum_children; i++ ) { if ( parent->children[i] == fr ) { idx = i; found = 1; break; } } if ( !found ) { fprintf(stderr, "Couldn't find child when deleting frame.\n"); return; } for ( i=idx; inum_children-1; i++ ) { parent->children[i] = parent->children[i+1]; } parent->num_children--; } struct frame *find_frame_with_scblocks(struct frame *fr, SCBlock *scblocks) { int i; if ( fr->scblocks == scblocks ) return fr; for ( i=0; inum_children; i++ ) { struct frame *tt; tt = find_frame_with_scblocks(fr->children[i], scblocks); if ( tt != NULL ) return tt; } return NULL; } void wrap_paragraph(Paragraph *para, PangoContext *pc, double w) { size_t total_len = 0; int i; char *text; PangoAttrList *attrs; PangoRectangle rect; size_t pos = 0; for ( i=0; in_runs; i++ ) { total_len += para->runs[i].len_bytes; } /* Allocate the complete text */ text = malloc(total_len+1); if ( text == NULL ) { fprintf(stderr, "Couldn't allocate combined text\n"); return; } /* Allocate the attributes */ attrs = pango_attr_list_new(); /* Put all of the text together */ text[0] = '\0'; for ( i=0; in_runs; i++ ) { PangoAttribute *attr; const char *run_text; run_text = sc_block_contents(para->runs[i].scblock) + para->runs[i].offs_bytes; attr = pango_attr_font_desc_new(para->runs[i].fontdesc); attr->start_index = pos; attr->end_index = pos + para->runs[i].len_bytes; pos += para->runs[i].len_bytes; strncat(text, run_text, para->runs[i].len_bytes); pango_attr_list_insert(attrs, attr); } if ( para->layout == NULL ) { para->layout = pango_layout_new(pc); } pango_layout_set_width(para->layout, pango_units_from_double(w)); pango_layout_set_text(para->layout, text, total_len); pango_layout_set_attributes(para->layout, attrs); free(text); pango_attr_list_unref(attrs); pango_layout_get_extents(para->layout, NULL, &rect); para->height = pango_units_to_double(rect.height); } void add_run(Paragraph *para, SCBlock *scblock, size_t offs_bytes, size_t len_bytes, PangoFontDescription *fdesc, int eop) { struct text_run *runs_new; if ( !para->open ) { fprintf(stderr, "Adding a run to a closed paragraph!\n"); return; } runs_new = realloc(para->runs, (para->n_runs+1)*sizeof(struct text_run)); if ( runs_new == NULL ) { fprintf(stderr, "Failed to add run.\n"); return; } para->runs = runs_new; para->runs[para->n_runs].scblock = scblock; para->runs[para->n_runs].offs_bytes = offs_bytes; para->runs[para->n_runs].len_bytes = len_bytes; para->runs[para->n_runs].fontdesc = pango_font_description_copy(fdesc); para->n_runs++; if ( eop ) para->open = 0; } void add_callback_para(struct frame *fr, double w, double h, SCCallbackDrawFunc draw_func, SCCallbackClickFunc click_func, void *bvp, void *vp) { /* FIXME */ } void add_image_para(struct frame *fr, const char *filename, double w, double h, int editable) { /* FIXME */ } double total_height(struct frame *fr) { int i; double t = 0.0; for ( i=0; in_paras; i++ ) { t += fr->paras[i]->height + 20.0; } return t; } Paragraph *last_open_para(struct frame *fr) { Paragraph **paras_new; Paragraph *pnew; if ( (fr->paras != NULL) && (fr->paras[fr->n_paras-1]->open) ) { return fr->paras[fr->n_paras-1]; } /* No open paragraph found, create a new one */ paras_new = realloc(fr->paras, (fr->n_paras+1)*sizeof(Paragraph *)); if ( paras_new == NULL ) return NULL; pnew = calloc(1, sizeof(struct _paragraph)); if ( pnew == NULL ) return NULL; fr->paras = paras_new; fr->paras[fr->n_paras++] = pnew; pnew->open = 1; pnew->n_runs = 0; pnew->runs = NULL; pnew->layout = NULL; pnew->height = 0.0; return pnew; } void close_last_paragraph(struct frame *fr) { if ( fr->paras == NULL ) return; fr->paras[fr->n_paras-1]->open = 0; }