/* * Copyright (c) 2004-2009 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Text entity. */ #include #include #include #include #include #include #include #include #include "vg.h" #include "vg_view.h" #include "icons.h" #include #include static void Init(void *p) { VG_Text *vt = p; vt->text[0] = '\0'; vt->p1 = NULL; vt->p2 = NULL; vt->align = VG_ALIGN_MC; vt->fontSize = agGUI ? agDefaultFont->size : 12; vt->fontFlags = agGUI ? agDefaultFont->flags : 0; vt->fontFace[0] = '\0'; vt->args = NULL; vt->argSizes = NULL; vt->vsObj = NULL; } static int Load(void *p, AG_DataSource *ds, const AG_Version *ver) { VG_Text *vt = p; if ((vt->p1 = VG_ReadRef(ds, vt, "Point")) == NULL || (vt->p2 = VG_ReadRef(ds, vt, "Point")) == NULL) return (-1); vt->align = (enum vg_alignment)AG_ReadUint8(ds); AG_CopyString(vt->fontFace, ds, sizeof(vt->fontFace)); vt->fontSize = (int)AG_ReadUint8(ds); vt->fontFlags = (Uint)AG_ReadUint16(ds); AG_CopyString(vt->text, ds, sizeof(vt->text)); return (0); } static void Save(void *p, AG_DataSource *ds) { VG_Text *vt = p; VG_WriteRef(ds, vt->p1); VG_WriteRef(ds, vt->p2); AG_WriteUint8(ds, (Uint8)vt->align); AG_WriteString(ds, vt->fontFace); AG_WriteUint8(ds, (Uint8)vt->fontSize); AG_WriteUint16(ds, (Uint16)vt->fontFlags); AG_WriteString(ds, vt->text); } /* Specify text with polled values. */ void VG_TextPrintfPolled(VG_Text *vt, const char *fmt, ...) { va_list ap; VG_Lock(VGNODE(vt)->vg); if (vt->args != NULL) { AG_ListDestroy(vt->args); vt->args = NULL; Free(vt->argSizes); vt->argSizes = NULL; } if (fmt != NULL) { Strlcpy(vt->text, fmt, sizeof(vt->text)); } else { vt->text[0] = '\0'; goto out; } va_start(ap, fmt); vt->args = AG_ListNew(); AG_PARSE_VARIABLE_ARGS(ap, fmt, vt->args, vt->argSizes); va_end(ap); out: VG_Unlock(VGNODE(vt)->vg); } /* Specify static text. */ void VG_TextPrintf(VG_Text *vt, const char *fmt, ...) { va_list ap; VG_Lock(VGNODE(vt)->vg); if (fmt != NULL) { va_start(ap, fmt); Vsnprintf(vt->text, sizeof(vt->text), fmt, ap); va_end(ap); } else { vt->text[0] = '\0'; } VG_Unlock(VGNODE(vt)->vg); } static void RenderText(VG_Text *vt, char *sIn, VG_View *vv) { char sSubst[VG_TEXT_MAX], *s; VG_Vector v1, v2, vMid; int x, y; if (vt->vsObj != NULL) { AG_VariableSubst(vt->vsObj, sIn, sSubst, sizeof(sSubst)); s = sSubst; } else { s = sIn; } AG_PushTextState(); if (vt->fontFace[0] != '\0' && ((agGUI && vt->fontSize != agDefaultFont->size) || (agGUI && vt->fontFlags != agDefaultFont->flags))) { AG_TextFontLookup(vt->fontFace, vt->fontSize, vt->fontFlags); } AG_TextColorVideo32(VG_MapColorRGB(VGNODE(vt)->color)); v1 = VG_Pos(vt->p1); v2 = VG_Pos(vt->p2); vMid.x = v1.x + (v2.x - v1.x)/2.0f; vMid.y = v1.y + (v2.y - v1.y)/2.0f; VG_GetViewCoords(vv, vMid, &x, &y); VG_DrawText(vv, x, y, VG_Degrees(VG_Atan2(v1.y-v2.y, v1.x-v2.x)), s); AG_PopTextState(); } static void RenderTextPolled(VG_Text *vt, VG_View *vv) { char val[64], s[VG_TEXT_MAX], *c; int argIdx = 0; s[0] = '\0'; for (c = &vt->text[0]; *c != '\0'; ) { if (c[0] != '%') { val[0] = *c; val[1] = '\0'; Strlcat(s, val, sizeof(s)); c++; continue; } if (c[1] == '\0' || c[1] == '%') { val[0] = '%'; val[1] = '\0'; Strlcat(s, val, sizeof(s)); c+=2; continue; } if ((argIdx+1) >= vt->args->n) { AG_FatalError("Argument inconsistency"); } AG_PrintVariable(val, sizeof(val), &vt->args->v[argIdx]); Strlcat(s, val, sizeof(s)); c += vt->argSizes[argIdx++]; } RenderText(vt, s, vv); } static void Draw(void *p, VG_View *vv) { VG_Text *vt = p; if (vt->args != NULL) { RenderTextPolled(vt, vv); } else { RenderText(vt, vt->text, vv); } } static void Extent(void *p, VG_View *vv, VG_Vector *a, VG_Vector *b) { VG_Text *vt = p; float wText, hText; VG_Vector v1, v2; int su; su = AG_TextCacheGet(vv->tCache, vt->text); wText = (float)WSURFACE(vv,su)->w/vv->scale; hText = (float)WSURFACE(vv,su)->h/vv->scale; v1 = VG_Pos(vt->p1); v2 = VG_Pos(vt->p2); a->x = MIN(v1.x,v2.x) - wText/2.0f; a->y = MIN(v1.y,v2.y) - hText/2.0f; b->x = MAX(v1.x,v2.x) + hText/2.0f; b->y = MAX(v1.y,v2.y) + hText/2.0f; } static float PointProximity(void *p, VG_View *vv, VG_Vector *vPt) { VG_Text *vt = p; VG_Vector v1 = VG_Pos(vt->p1); VG_Vector v2 = VG_Pos(vt->p2); /* XXX TODO */ return VG_PointLineDistance(v1, v2, vPt); } static void Delete(void *p) { VG_Text *vt = p; if (VG_DelRef(vt, vt->p1) == 0) VG_Delete(vt->p1); if (VG_DelRef(vt, vt->p2) == 0) VG_Delete(vt->p2); } static void SetAlign(AG_Event *event) { VG_Text *vt = AG_PTR(1); enum vg_alignment align = (enum vg_alignment)AG_INT(2); vt->align = align; } static void SelectFont(AG_Event *event) { VG_Text *vt = AG_PTR(1); AG_Window *win = AG_PTR(2); AG_FontSelector *fs = AG_PTR(3); Strlcpy(vt->fontFace, fs->curFace, sizeof(vt->fontFace)); vt->fontSize = fs->curSize; vt->fontFlags = 0; if (fs->curStyle & AG_FONT_BOLD) { vt->fontFlags |= VG_TEXT_BOLD; } if (fs->curStyle & AG_FONT_ITALIC) { vt->fontFlags |= VG_TEXT_ITALIC; } AG_ViewDetach(win); } static void SelectFontDlg(AG_Event *event) { VG_Text *vt = AG_PTR(1); VG_View *vv = AG_PTR(2); AG_Window *win, *winParent; AG_FontSelector *fs; AG_Box *hBox; win = AG_WindowNew(0); AG_WindowSetCaption(win, _("Font selection")); fs = AG_FontSelectorNew(win, AG_FONTSELECTOR_EXPAND); hBox = AG_BoxNewHoriz(win, AG_BOX_HFILL|AG_BOX_HOMOGENOUS); AG_ButtonNewFn(hBox, 0, _("OK"), SelectFont, "%p,%p,%p", vt, win, fs); AG_ButtonNewFn(hBox, 0, _("Close"), AG_WindowCloseGenEv, "%p", win); AG_WindowShow(win); if ((winParent = AG_ParentWindow(vv)) != NULL) AG_WindowAttach(winParent, win); } static void * Edit(void *p, VG_View *vv) { VG_Text *vt = p; AG_Box *box = AG_BoxNewVert(NULL, AG_BOX_EXPAND); AG_Pane *vPane; AG_Textbox *tb; AG_Box *bAl, *bAlv; vPane = AG_PaneNewVert(box, AG_PANE_EXPAND); AG_LabelNew(vPane->div[0], 0, _("Text: ")); tb = AG_TextboxNew(vPane->div[0], AG_TEXTBOX_MULTILINE|AG_TEXTBOX_EXPAND, NULL); AG_TextboxBindUTF8(tb, vt->text, sizeof(vt->text)); bAlv = AG_BoxNewVertNS(vPane->div[1], AG_BOX_HFILL|AG_BOX_FRAME); AG_LabelNew(bAlv, 0, _("Alignment: ")); bAl = AG_BoxNewHorizNS(bAlv, AG_BOX_HFILL|AG_BOX_HOMOGENOUS); AG_ButtonNewFn(bAl, 0, _("TL"), SetAlign, "%p,%i", vt, VG_ALIGN_TL); AG_ButtonNewFn(bAl, 0, _("TC"), SetAlign, "%p,%i", vt, VG_ALIGN_TC); AG_ButtonNewFn(bAl, 0, _("TR"), SetAlign, "%p,%i", vt, VG_ALIGN_TR); bAl = AG_BoxNewHorizNS(bAlv, AG_BOX_HFILL|AG_BOX_HOMOGENOUS); AG_ButtonNewFn(bAl, 0, _("ML"), SetAlign, "%p,%i", vt, VG_ALIGN_ML); AG_ButtonNewFn(bAl, 0, _("MC"), SetAlign, "%p,%i", vt, VG_ALIGN_MC); AG_ButtonNewFn(bAl, 0, _("MR"), SetAlign, "%p,%i", vt, VG_ALIGN_MR); bAl = AG_BoxNewHorizNS(bAlv, AG_BOX_HFILL|AG_BOX_HOMOGENOUS); AG_ButtonNewFn(bAl, 0, _("BL"), SetAlign, "%p,%i", vt, VG_ALIGN_BL); AG_ButtonNewFn(bAl, 0, _("BC"), SetAlign, "%p,%i", vt, VG_ALIGN_BC); AG_ButtonNewFn(bAl, 0, _("BR"), SetAlign, "%p,%i", vt, VG_ALIGN_BR); AG_ButtonNewFn(vPane->div[1], AG_BUTTON_HFILL, _("Select font"), SelectFontDlg, "%p,%p", vt, vv); AG_CheckboxNewFlag(vPane->div[1], 0, _("Underline"), &vt->fontFlags, VG_TEXT_UNDERLINE); AG_CheckboxNewFlag(vPane->div[1], 0, _("Scale to view"), &vt->fontFlags, VG_TEXT_SCALED); return (box); } VG_NodeOps vgTextOps = { N_("Text"), &vgIconText, sizeof(VG_Text), Init, NULL, /* destroy */ Load, Save, Draw, Extent, PointProximity, NULL, /* lineProximity */ Delete, NULL, /* moveNode */ Edit };