/* * Attrs.c -- Various attributes manipulation routines. * * Authors : Patrick Lecoanet. * Creation date : Fri Dec 31 10:03:34 1999 * * $Id$ */ /* * Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet -- * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This code 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this code; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "Attrs.h" #include "Item.h" #include "List.h" #include "Geo.h" #include #include #include static const char rcsid[] = "$Id$"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; /* ****************************************************************** * * Code for label formats. * ****************************************************************** */ static Tcl_HashTable format_cache; static ZnBool format_inited = False; #if 0 static char AttachToChar(char attach) { switch (attach) { case LF_ATTACH_FWD: return '>'; case LF_ATTACH_BWD: return '<'; case LF_ATTACH_LEFT: return '^'; case LF_ATTACH_RIGHT: return '$'; case LF_ATTACH_PIXEL: default: return '+'; } } static char DimToChar(char dim) { switch (dim) { case LF_DIM_FONT: return 'f'; case LF_DIM_ICON: return 'i'; case LF_DIM_AUTO: return 'a'; case LF_DIM_PIXEL: default: return 'x'; } } #endif static char CharToAttach(char attach) { switch (attach) { case '>': return LF_ATTACH_FWD; case '<': return LF_ATTACH_BWD; case '^': return LF_ATTACH_LEFT; case '$': return LF_ATTACH_RIGHT; case '+': default: return LF_ATTACH_PIXEL; } } static char CharToDim(char dim) { switch (dim) { case 'f': return LF_DIM_FONT; case 'i': return LF_DIM_ICON; case 'a': return LF_DIM_AUTO; case 'l': return LF_DIM_LABEL; case 'x': default: return LF_DIM_PIXEL; } } /* * The new format is as follow. Parameters between [] are * optional and take default values when omitted. The spaces can appear * between blocks but not inside. * * [ WidthxHeight ] [ field0Spec ][ field1Spec ]...[ fieldnSpec ] * * Width and Height set the size of the clipping box surrounding * the label. If it is not specified, there will be no clipping. * It it is specified alone it is the size of the only displayed * field (0). * * fieldSpec is: * sChar fieldWidth sChar fieldHeight [pChar fieldX pChar fieldY]. * * Each field description refers to the field of same index in the field * array. * If sChar is 'x', the dimension is in pixel. If sChar is 'f', the * dimension is in percentage of the mean width/height of a character (in the * field font). If sChar is 'i', the dimension is in percentage of the size * of the image in the field. If sChar is 'a', the dimension is automatically * adjusted to match the field's content plus the given value in pixels. * If pChar is '+' the position is in pixel (possibly negative). If it is * '<' the position is the index of the field at the left/top of which the * current field should be attached. If it is '>' the position is the index * of the field at the right/bottom of which the current field should be * attached. If pChar is '^' the position is the index of the field used to * align the left/top border (left on left or top on top). If pChar is '$' the * position is the index of the field used to align the right/bottom border * (right on right or bottom on bottom). * The positional parameters can be omitted if there is only one field. * */ ZnLabelFormat LabelFormatCreate(Tcl_Interp *interp, char *format_str, int num_fields) { ZnList fields; Tcl_HashEntry *entry; ZnFieldFormatStruct field_struct; ZnFieldFormat field_array; ZnLabelFormat format; int width, height; ZnDim c_width=0.0, c_height=0.0; int index, num_tok, num_ffs, new; int field_index=0; char *ptr = format_str, *next_ptr; char x_char, y_char; if (!format_inited) { Tcl_InitHashTable(&format_cache, TCL_STRING_KEYS); format_inited = True; } entry = Tcl_CreateHashEntry(&format_cache, format_str, &new); if (!new) { format = (ZnLabelFormat) Tcl_GetHashValue(entry); if (format->num_fields <= num_fields) { format->ref_count++; return format; } else { Tcl_AppendResult(interp, "too many fields in label format: \"", format_str, "\"", NULL); return NULL; } } fields = ZnListNew(1, sizeof(ZnFieldFormatStruct)); /* * Try to see if it starts with a number or a leader spec. */ while (*ptr && (*ptr == ' ')) { ptr++; } if (!*ptr) { goto lf_error_syn; } if ((*ptr != 'x') && (*ptr != 'f') && (*ptr != 'i') && (*ptr != 'a') && (*ptr != 'l')) { c_width = (ZnDim) strtod(ptr, &next_ptr); if ((ptr == next_ptr) || (*next_ptr != 'x')) { lf_error_syn: Tcl_AppendResult(interp, "invalid label format specification \"", ptr, "\"", NULL); lf_error: ZnListFree(fields); return NULL; } ptr = next_ptr+1; c_height = (ZnDim) strtod(ptr, &next_ptr); if (ptr == next_ptr) { goto lf_error_syn; } ptr = next_ptr; } if (!*ptr) { /* It is a simple spec, one field. */ field_struct.x_attach = field_struct.y_attach = LF_ATTACH_PIXEL; field_struct.x_dim = field_struct.y_dim = LF_DIM_PIXEL; field_struct.x_spec = field_struct.y_spec = 0; field_struct.width_spec = (short) c_width; field_struct.height_spec = (short) c_height; c_width = c_height = 0.0; ZnListAdd(fields, &field_struct, ZnListTail); goto lf_end_parse; } /* * Parse the field specs. */ lf_parse2: while (*ptr && (*ptr == ' ')) { ptr++; } if (!*ptr) { goto lf_end_parse; } /* Preset the default field values. */ field_struct.x_spec = field_struct.y_spec = 0; field_struct.x_attach = field_struct.y_attach = LF_ATTACH_PIXEL; field_struct.x_dim = field_struct.y_dim = LF_DIM_PIXEL; if ((*ptr == 'x') || (*ptr == 'f') || (*ptr == 'i') || (*ptr == 'a') || (*ptr == 'l')) { num_tok = sscanf(ptr, "%c%d%c%d%n", &x_char, &width, &y_char, &height, &index); if (num_tok != 4) { goto lf_error_syn; } if (width < 0) { width = 0; } if (height < 0) { height = 0; } field_struct.x_dim = CharToDim(x_char); field_struct.y_dim = CharToDim(y_char); ptr += index; if ((*ptr == '>') || (*ptr == '<') || (*ptr == '+') || (*ptr == '^') || (*ptr == '$')) { num_tok = sscanf(ptr, "%c%d%c%d%n", &x_char, &field_struct.x_spec, &y_char, &field_struct.y_spec, &index); if (num_tok != 4) { goto lf_error_syn; } field_struct.x_attach = CharToAttach(x_char); field_struct.y_attach = CharToAttach(y_char); ptr += index; } else if (!*ptr || (field_index != 0)) { /* An incomplete field spec is an error if there are several fields. */ Tcl_AppendResult(interp, "incomplete field in label format: \"", ptr-index, "\"", NULL); goto lf_error; } if (field_index >= num_fields) { Tcl_AppendResult(interp, "too many fields in label format: \"", format_str, "\"", NULL); goto lf_error; } field_struct.width_spec = (short) width; field_struct.height_spec = (short) height; ZnListAdd(fields, &field_struct, ZnListTail); field_index++; goto lf_parse2; } else { goto lf_error_syn; } lf_end_parse: field_array = (ZnFieldFormat) ZnListArray(fields); num_ffs = ZnListSize(fields); format = (ZnLabelFormat) ZnMalloc(sizeof(ZnLabelFormatStruct) + (num_ffs-1) * sizeof(ZnFieldFormatStruct)); format->clip_width = (short) c_width; format->clip_height = (short) c_height; format->num_fields = num_ffs; memcpy(&format->fields, field_array, num_ffs * sizeof(ZnFieldFormatStruct)); ZnListFree(fields); format->ref_count = 1; format->entry = entry; Tcl_SetHashValue(entry, (ClientData) format); return format; } ZnLabelFormat LabelFormatDuplicate(ZnLabelFormat lf) { lf->ref_count++; return lf; } void LabelFormatDelete(ZnLabelFormat lf) { lf->ref_count--; if (lf->ref_count == 0) { Tcl_DeleteHashEntry(lf->entry); ZnFree(lf); } } char * LabelFormatGetString(ZnLabelFormat lf) { return Tcl_GetHashKey(&format_cache, lf->entry); #if 0 ZnFieldFormat ff; char *ptr; char x_char, y_char, w_char, h_char; int i, count; ptr = str; if ((lf->clip_width != 0) || (lf->clip_height != 0)) { count = sprintf(ptr, "%dx%d", lf->clip_width, lf->clip_height); ptr += count; } if (lf->left_y < 0) { count = sprintf(ptr, "|%d", lf->left_x); } else { count = sprintf(ptr, "%%%dx%d", lf->left_x, lf->left_y); } ptr += count; if (lf->right_y < 0) { count = sprintf(ptr, "|%d", lf->right_x); } else { count = sprintf(ptr, "%%%dx%d", lf->right_x, lf->right_y); } ptr += count; for (i = 0; i < lf->num_fields; i++) { ff = &lf->fields[i]; x_char = AttachToChar(ff->x_attach); y_char = AttachToChar(ff->y_attach); w_char = DimToChar(ff->x_dim); h_char = DimToChar(ff->y_dim); count = sprintf(ptr, "%c%d%c%d%c%d%c%d", w_char, ff->width_spec, h_char, ff->height_spec, x_char, ff->x_spec, y_char, ff->y_spec); ptr += count; } *ptr = 0; #endif } /* * If the clip box has both its width and its height * set to zero, it means that there is no clipbox. */ ZnBool LabelFormatGetClipBox(ZnLabelFormat lf, ZnDim *w, ZnDim *h) { if ((lf->clip_width == 0) && (lf->clip_height == 0)) { return False; } *w = (ZnDim) lf->clip_width; *h = (ZnDim) lf->clip_height; return True; } void LabelFormatGetField(ZnLabelFormat lf, int field, char *x_attach, char *y_attach, char *x_dim, char *y_dim, int *x_spec, int *y_spec, short *width_spec, short *height_spec) { ZnFieldFormat fptr; fptr = &lf->fields[field]; *x_attach = fptr->x_attach; *y_attach = fptr->y_attach; *x_dim = fptr->x_dim; *y_dim = fptr->y_dim; *x_spec = fptr->x_spec; *y_spec = fptr->y_spec; *width_spec = fptr->width_spec; *height_spec = fptr->height_spec; } /* **************************************************************** * * Code for line ends. * **************************************************************** */ static Tcl_HashTable line_end_cache; static ZnBool line_end_inited = False; ZnLineEnd LineEndCreate(Tcl_Interp *interp, char *line_end_str) { Tcl_HashEntry *entry; ZnLineEnd le; int new, argc; ZnReal a, b, c; if (!line_end_inited) { Tcl_InitHashTable(&line_end_cache, TCL_STRING_KEYS); line_end_inited = True; } entry = Tcl_CreateHashEntry(&line_end_cache, line_end_str, &new); if (!new) { le = (ZnLineEnd) Tcl_GetHashValue(entry); le->ref_count++; return le; } argc = sscanf(line_end_str, "%lf %lf %lf", &a, &b, &c); if (argc == 3) { le = (ZnLineEnd) ZnMalloc(sizeof(ZnLineEndStruct)); le->shape_a = a; le->shape_b = b; le->shape_c = c; le->entry = entry; le->ref_count = 1; Tcl_SetHashValue(entry, (ClientData) le); return le; } else { Tcl_AppendResult(interp, "incorrect line end spec: \"", line_end_str, "\", should be: shapeA shapeB shapeC", NULL); return NULL; } } char * LineEndGetString(ZnLineEnd le) { return Tcl_GetHashKey(&line_end_cache, le->entry); } void LineEndDelete(ZnLineEnd le) { le->ref_count--; if (le->ref_count == 0) { Tcl_DeleteHashEntry(le->entry); ZnFree(le); } } ZnLineEnd LineEndDuplicate(ZnLineEnd le) { le->ref_count++; return le; } /* **************************************************************** * * Code for gradient geometry. * **************************************************************** */ static Tcl_HashTable gradient_geom_cache; static ZnBool gradient_inited = False; ZnGradientGeom GradientGeomCreate(Tcl_Interp *interp, char *grad_geom_str) { Tcl_HashEntry *entry; ZnGradientGeom gg; int new; char *next_ptr, *ptr; int d1, d2, angle; if (!gradient_inited) { Tcl_InitHashTable(&gradient_geom_cache, TCL_STRING_KEYS); gradient_inited = True; } entry = Tcl_CreateHashEntry(&gradient_geom_cache, grad_geom_str, &new); if (!new) { gg = (ZnGradientGeom) Tcl_GetHashValue(entry); gg->ref_count++; return gg; } d1 = d2 = 50; angle = 0; ptr = grad_geom_str; if (!*ptr) { error_gg: Tcl_AppendResult(interp, "incorrect gradient geometry spec: \"", grad_geom_str, "\", should be: [threshold1][-threshold2][/angle]", NULL); return NULL; } if (*ptr != '/') { d1 = strtol(ptr, &next_ptr, 10); if (next_ptr == ptr) { goto error_gg; } ptr = next_ptr; if (*next_ptr == '-') { ptr++; d2 = strtol(ptr, &next_ptr, 10); if (next_ptr == ptr) { goto error_gg; } ptr = next_ptr; } } if (*ptr == '/') { ptr++; angle = strtol(ptr, &next_ptr, 10); if (*next_ptr != 0) { goto error_gg; } ptr = next_ptr; while (angle < 0) { angle += 360; } while (angle >= 360) { angle -= 360; } } else if (*next_ptr != 0) { goto error_gg; } gg = (ZnGradientGeom) ZnMalloc(sizeof(ZnGradientGeomStruct)); gg->d1 = d1; gg->d2 = d2; gg->angle = angle; gg->entry = entry; gg->ref_count = 1; Tcl_SetHashValue(entry, (ClientData) gg); return gg; } char * GradientGeomGetString(ZnGradientGeom gg) { return Tcl_GetHashKey(&gradient_geom_cache, gg->entry); } void GradientGeomDelete(ZnGradientGeom gg) { gg->ref_count--; if (gg->ref_count == 0) { Tcl_DeleteHashEntry(gg->entry); ZnFree(gg); } } ZnGradientGeom GradientGeomDuplicate(ZnGradientGeom gg) { gg->ref_count++; return gg; }