/* Part of XPCE --- The SWI-Prolog GUI toolkit Author: Jan Wielemaker and Anjo Anjewierden E-mail: jan@swi.psy.uva.nl WWW: http://www.swi.psy.uva.nl/projects/xpce/ Copyright (c) 1996-2011, University of Amsterdam 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 COPYRIGHT OWNER 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. */ #include #include #include static status nameDialogGroup(DialogGroup g, Name name); static status labelDialogGroup(DialogGroup g, Any name); /******************************** * CREATE * ********************************/ status initialiseDialogGroup(DialogGroup g, Name name, Name kind) { initialiseDevice((Device) g); if ( isDefault(name) ) name = getClassNameObject(g); assign(g, label, DEFAULT); /* see nameDialogGroup() */ assign(g, size, DEFAULT); assign(g, border, getClassVariableValueObject(g, NAME_border)); assign(g, auto_align, ON); assign(g, elevation, NIL); nameDialogGroup(g, name); if ( notDefault(kind) ) return qadSendv(g, NAME_kind, 1, (Any *)&kind); succeed; } /******************************* * COMPUTE * *******************************/ void compute_label_size_dialog_group(DialogGroup g, int *w, int *h) { if ( instanceOfObject(g->label, ClassImage) ) { Image i = g->label; *w = valInt(i->size->w); *h = valInt(i->size->h); } else { if ( instanceOfObject(g->label, ClassCharArray) ) { CharArray ca = g->label; str_size(&ca->data, g->label_font, w, h); } else { *w = *h = 0; } } } static void compute_label(DialogGroup g, int *x, int *y, int *w, int *h) { if ( notNil(g->label) ) { int tw, th; compute_label_size_dialog_group(g, &tw, &th); if ( w ) *w = tw; if ( h ) *h = th; if ( y ) { if ( g->label_format == NAME_top ) *y = 0; else if ( g->label_format == NAME_bottom ) *y = -th; else if ( g->label_format == NAME_hotSpot && instanceOfObject(g->label, ClassImage) ) { Image img = (Image)g->label; if ( notNil(img->hot_spot) ) *y = -valInt(img->hot_spot->y); else *y = -th/2; } else /* center */ *y = -th/2; } if ( x ) { *x = valInt(g->radius) + valInt(getExFont(g->label_font)); } } else { if ( x ) *x = 0; if ( y ) *y = 0; if ( w ) *w = 0; if ( h ) *h = 0; } } static status computeDialogGroup(DialogGroup g) { if ( notNil(g->request_compute) ) { int x, y, w, h; int lx, ly, lw, lh; Area a = g->area; Size border; obtainClassVariablesObject(g); border = (isDefault(g->border) ? g->gap : g->border); CHANGING_GRAPHICAL(g, { computeGraphicalsDevice((Device) g); compute_label(g, &lx, &ly, &lw, &lh); if ( isDefault(g->size) ) /* implicit size */ { if ( isNil(g->layout_manager) || !qadSendv(g->layout_manager, NAME_computeBoundingBox, 0, NULL) ) { Cell cell; clearArea(a); for_cell(cell, g->graphicals) { Graphical gr = cell->value; unionNormalisedArea(a, gr->area); } } relativeMoveArea(a, g->offset); x = valInt(a->x) - valInt(border->w); y = valInt(a->y) - valInt(border->h); w = valInt(a->w) + 2 * valInt(border->w); h = valInt(a->h) + 2 * valInt(border->h); } else /* explicit size */ { x = valInt(g->offset->x); y = valInt(g->offset->y); w = valInt(g->size->w); h = valInt(g->size->h); } if ( w < 2*lx + lw ) w = 2*lx + lw; if ( ly < 0 ) { h -= ly; y += ly; } assign(a, x, toInt(x)); assign(a, y, toInt(y)); assign(a, w, toInt(w)); assign(a, h, toInt(h)); }); assign(g, request_compute, NIL); } succeed; } /******************************* * GEOMETRY * *******************************/ static status geometryDialogGroup(DialogGroup g, Int x, Int y, Int w, Int h) { if ( notDefault(w) || notDefault(h) ) { Any size; if ( isDefault(w) ) w = getWidthGraphical((Graphical) g); if ( isDefault(h) ) { int ly; compute_label(g, NULL, &ly, NULL, NULL); h = getHeightGraphical((Graphical) g); if ( ly < 0 ) h = toInt(valInt(h) + ly); } size = newObject(ClassSize, w, h, EAV); qadSendv(g, NAME_size, 1, &size); doneObject(size); } return geometryDevice((Device) g, x, y, w, h); } static status sizeDialogGroup(DialogGroup g, Size s) { if ( g->size == s ) succeed; if ( notDefault(g->size) && notDefault(s) && equalSize(g->size, s) ) succeed; if ( notDefault(g->size) && notDefault(s) ) copySize(g->size, s); else assign(g, size, s); send(g, NAME_layoutDialog, EAV); return requestComputeGraphical(g, DEFAULT); } /******************************* * LAYOUT * *******************************/ static status layoutDialogDialogGroup(DialogGroup g) { obtainClassVariablesObject(g); if ( notNil(g->layout_manager) ) { if ( notNil(g->layout_manager->request_compute) ) qadSendv(g->layout_manager, NAME_compute, 0, NULL); } else layoutDialogDevice((Device)g, g->gap, g->size, g->border); succeed; } static status gapDialogGroup(DialogGroup g, Size gap) { if ( !equalSize(gap, g->gap) ) { assign(g, gap, gap); if ( isNil(g->request_compute) && notNil(g->device) ) send(g, NAME_layoutDialog, EAV); } succeed; } static status borderDialogGroup(DialogGroup g, Size border) { if ( (isDefault(border) && notDefault(g->border)) || (notDefault(border) && isDefault(g->border)) || (notDefault(border) && notDefault(g->border) && !equalSize(border, g->border)) ) { assign(g, border, border); if ( isNil(g->request_compute) && notNil(g->device) ) send(g, NAME_layoutDialog, EAV); } succeed; } /******************************* * NAME/LABEL * *******************************/ static status nameDialogGroup(DialogGroup g, Name name) { Any label; assign(g, name, name); if ( notNil(g->label) && (label = get(g, NAME_labelName, name, EAV)) ) labelDialogGroup(g, label ? label : name); succeed; } static status ChangedLabelDialogGroup(DialogGroup g) { requestComputeGraphical(g, DEFAULT); return changedEntireImageGraphical(g); } static status labelDialogGroup(DialogGroup g, Any label) { if ( g->label != label ) { assign(g, label, label); qadSendv(g, NAME_ChangedLabel, 0, NULL); } succeed; } static status labelFontDialogGroup(DialogGroup g, FontObj font) { if ( g->label_font != font ) { assign(g, label_font, font); qadSendv(g, NAME_ChangedLabel, 0, NULL); } succeed; } static status showLabelDialogGroup(DialogGroup g, BoolObj show) { if ( (show == OFF && isNil(g->label)) || (show == ON && notNil(g->label)) ) succeed; if ( show == OFF ) { labelDialogGroup(g, NIL); } else { assign(g, label, DEFAULT); nameDialogGroup(g, g->name); } succeed; } status labelFormatDialogGroup(DialogGroup g, Name fmt) { if ( g->label_format != fmt ) { assign(g, label_format, fmt); qadSendv(g, NAME_ChangedLabel, 0, NULL); } succeed; } static CharArray getLabelNameDialogGroup(DialogGroup g, Name name) { Any suffix, label = get(name, NAME_labelName, EAV); if ( label && (suffix = getClassVariableValueObject(g, NAME_labelSuffix)) ) label = getEnsureSuffixCharArray(label, suffix); answer(label); } static status radiusDialogGroup(DialogGroup g, Int radius) { return assignGraphical(g, NAME_radius, radius); } static status elevationDialogGroup(DialogGroup d, Elevation e) { return assignGraphical(d, NAME_elevation, e); } /******************************* * KIND * *******************************/ static status kindDialogGroup(DialogGroup g, Name kind) { if ( kind == NAME_box ) { assign(g, pen, toInt(1)); assign(g, border, getClassVariableValueObject(g, NAME_border)); nameDialogGroup(g, g->name); } else if ( kind == NAME_group ) { assign(g, pen, toInt(0)); assign(g, border, newObject(ClassSize, EAV)); assign(g, label, NIL); } else fail; return requestComputeGraphical(g, DEFAULT); } /******************************** * REDRAW * ********************************/ status RedrawLabelDialogGroup(DialogGroup g, int acc, int x, int y, int w, int h, Name hadjust, Name vadjust, int flags) { if ( instanceOfObject(g->label, ClassImage) ) { Image i = g->label; int iw = valInt(i->size->w); int ih = valInt(i->size->h); int ix, iy; if ( hadjust == NAME_left ) ix = x; else if ( hadjust == NAME_center ) ix = x + (w-iw)/2; else ix = x + w-iw; if ( vadjust == NAME_top ) iy = y; else if ( vadjust == NAME_center ) iy = y + (1+h-ih)/2; else iy = y + h-ih; r_image(i, 0, 0, ix, iy, iw, ih, ON); } else if ( instanceOfObject(g->label, ClassCharArray) ) { CharArray label = g->label; str_label(&label->data, acc, g->label_font, x, y, w, h, hadjust, vadjust, flags); } succeed; } static status RedrawAreaDialogGroup(DialogGroup g, Area a) { int x, y, w, h; int lx, ly, lw, lh; int eh; Any obg = NIL, bg = NIL; initialiseDeviceGraphical(g, &x, &y, &w, &h); compute_label(g, &lx, &ly, &lw, &lh); if ( g->pen != ZERO ) { Elevation e = getClassVariableValueObject(g, NAME_elevation); if ( e && instanceOfObject(e, ClassElevation) ) { int bx = x; int by = y-ly; int bw = w; int bh = h+ly; eh = valInt(e->height); r_3d_box(bx, by, bw, bh, valInt(g->radius), e, FALSE); bx += eh; by += eh; bw -= 2*eh; bh -= 2*eh; r_3d_box(bx, by, bw, bh, valInt(g->radius), e, TRUE); } else { eh = valInt(g->pen); r_thickness(eh); r_dash(g->texture); r_box(x, y-ly, w, h+ly, valInt(g->radius), NIL); } } else if ( notNil(g->elevation) ) { int bx = x; int by = y-ly; int bw = w; int bh = h+ly; r_3d_box(bx, by, bw, bh, valInt(g->radius), g->elevation, TRUE); bg = g->elevation->background; eh = valInt(g->elevation->height); } else eh = 0; if ( notNil(g->label) && g->label != NAME_ ) { int ex = valInt(getExFont(g->label_font)); r_clear(x+lx-ex/2, y, lw+ex, lh); RedrawLabelDialogGroup(g, 0, x+lx, y, lw, lh, NAME_center, NAME_center, 0); } { Cell cell; Int ax = a->x, ay = a->y; Point offset = g->offset; int ox = valInt(offset->x); int oy = valInt(offset->y); d_clip(x+eh, y-ly+eh, w-2*eh, h-2*eh+ly); /* check if needed! */ qassign(a, x, toInt(valInt(a->x) - ox)); qassign(a, y, toInt(valInt(a->y) - oy)); r_offset(ox, oy); if ( notNil(bg) ) obg = r_background(bg); if ( notNil(g->layout_manager) ) qadSendv(g->layout_manager, NAME_RedrawArea, 1, (Any*)&a); for_cell(cell, g->graphicals) { Graphical gr = cell->value; if ( gr->displayed == ON && overlapArea(a, gr->area) ) RedrawArea(gr, a); } if ( notNil(obg) ) r_background(obg); r_offset(-ox, -oy); qassign(a, x, ax); qassign(a, y, ay); d_clip_done(); } return RedrawAreaGraphical(g, a); } /******************************* * DIALOG INTEGRATION * *******************************/ static status WantsKeyboardFocusTextItem(DialogGroup g) { Cell cell; for_cell(cell, g->graphicals) { if ( qadSendv(cell->value, NAME_WantsKeyboardFocus, 0, NULL) ) succeed; } fail; } status eventDialogGroup(DialogGroup g, EventObj ev) { if ( isAEvent(ev, NAME_obtainKeyboardFocus) ) return send(g, NAME_advance, EAV); else return eventDevice((Device)g, ev); } static Button getDefaultButtonDialogGroup(DialogGroup g, BoolObj delegate) { Device d; Cell cell; for_cell(cell, g->graphicals) { if ( instanceOfObject(cell->value, ClassButton) ) { Button b = cell->value; if ( b->default_button == ON ) answer(b); } } if ( delegate != OFF ) { for(d= g->device; notNil(d); d = d->device) { if ( hasGetMethodObject(d, NAME_defaultButton) ) answer(get(d, NAME_defaultButton, EAV)); } } fail; } static status applyDialogGroup(DialogGroup g, BoolObj always) { DialogItem di; Graphical defb; for_chain(g->graphicals, di, send(di, NAME_apply, always, EAV)); if ( (defb = get(g, NAME_defaultButton, EAV)) ) send(defb, NAME_active, OFF, EAV); succeed; } static status restoreDialogGroup(DialogGroup g) { DialogItem di; Graphical defb; for_chain(g->graphicals, di, send(di, NAME_restore, EAV)); if ( (defb = get(g, NAME_defaultButton, EAV)) ) send(defb, NAME_active, OFF, EAV); succeed; } static status modifiedItemDialogGroup(DialogGroup g, Graphical gr, BoolObj m) { if ( m == ON ) { Button b; if ( (b = get(g, NAME_defaultButton, OFF, EAV)) ) { send(b, NAME_active, ON, EAV); if ( send(b, NAME_isApply, EAV) ) succeed; } if ( notNil(g->device) ) return send(g->device, NAME_modifiedItem, gr, ON, EAV); } fail; } /******************************* * DIALOG * *******************************/ static status appendDialogGroup(DialogGroup g, Graphical item, Name where) { return appendDialogItemDevice((Device) g, item, where); } static status openDialogGroup(DialogGroup g) { if ( isNil(g->device) ) { Dialog d; TRY( d = newObject(ClassDialog, EAV) ); TRY( send(d, NAME_append, g, EAV) ); } return send(g->device, NAME_open, EAV); } /******************************* * CLASS DECLARATION * *******************************/ /* Type declaractions */ static char T_kind[] = "kind=[{box,group}]"; static char *T_initialise[] = { "name=[name]", T_kind }; static char *T_geometry[] = { "x=[int]", "y=[int]", "width=[int]", "height=[int]" }; static char *T_append[] = { "item=graphical", "relative_to_last=[{below,right,next_row}]" }; static char *T_modifiedItem[] = { "item=graphical", "modified=bool" }; /* Instance Variables */ static vardecl var_diagroup[] = { IV(NAME_label, "[name|image]*", IV_GET, NAME_label, "Displayed label"), SV(NAME_labelFont, "font", IV_GET|IV_STORE, labelFontDialogGroup, NAME_appearance, "Font used to display textual label"), SV(NAME_labelFormat, "{top,center,bottom,hot_spot}", IV_GET|IV_STORE, labelFormatDialogGroup, NAME_appearance, "Alignment of label with top"), SV(NAME_elevation, "elevation*", IV_GET|IV_STORE, elevationDialogGroup, NAME_appearance, "Elevation from background"), SV(NAME_radius, "0..", IV_GET|IV_STORE, radiusDialogGroup, NAME_appearance, "Radius for the corners"), IV(NAME_size, "[size]", IV_GET, NAME_area, "Size of the contents"), SV(NAME_gap, "size", IV_GET|IV_STORE, gapDialogGroup, NAME_appearance, "Distance between the items"), SV(NAME_border, "[size]", IV_GET|IV_STORE, borderDialogGroup, NAME_appearance, "Space around the items"), IV(NAME_autoAlign, "bool", IV_BOTH, NAME_layout, "Automatically align in dialog (@on)"), IV(NAME_alignment, "{column,left,center,right}", IV_BOTH, NAME_layout, "Align in columns or right of item to the left") }; /* Send Methods */ static senddecl send_diagroup[] = { SM(NAME_initialise, 2, T_initialise, initialiseDialogGroup, DEFAULT, "Create a new group of dialog items"), SM(NAME_geometry, 4, T_geometry, geometryDialogGroup, DEFAULT, "Move/resize dialog group"), SM(NAME_position, 1, "point", positionGraphical, NAME_area, "Top-left corner of tab"), SM(NAME_x, 1, "int", xGraphical, NAME_area, "Left-side of object"), SM(NAME_y, 1, "int", yGraphical, NAME_area, "Top-side of object"), SM(NAME_size, 1, "[size]", sizeDialogGroup, NAME_area, "Size, @default implies minimal size"), SM(NAME_layoutDialog, 0, NULL, layoutDialogDialogGroup, NAME_layout, "(Re)compute layout of dialog_items"), SM(NAME_label, 1, "name|image", labelDialogGroup, NAME_name, "Change visual label"), SM(NAME_name, 1, "name", nameDialogGroup, NAME_name, "Change <-name, update <-label"), SM(NAME_kind, 1, T_kind, kindDialogGroup, NAME_appearance, "Set standard default appearance"), SM(NAME_reference, 1, "point", referenceGraphical, NAME_layout, "Dialog item integration"), SM(NAME_append, 2, T_append, appendDialogGroup, NAME_organisation, "Append dialog_item {below,right,next_row} last"), SM(NAME_compute, 0, NULL, computeDialogGroup, NAME_update, "Recompute area"), SM(NAME_ChangedLabel, 0, NULL, ChangedLabelDialogGroup, NAME_update, "Add label-area to the update"), SM(NAME_event, 1, "event", eventDialogGroup, NAME_event, "Process an event"), SM(NAME_WantsKeyboardFocus, 0, NULL, WantsKeyboardFocusTextItem, NAME_event, "Test if a member is ready for input"), SM(NAME_apply, 1, "always=[bool]", applyDialogGroup, NAME_apply, "->apply all changed items"), SM(NAME_modifiedItem, 2, T_modifiedItem, modifiedItemDialogGroup, NAME_apply, "Indicates item has changed state"), SM(NAME_restore, 0, NULL, restoreDialogGroup, NAME_apply, "->restore all items to their <-default"), SM(NAME_showLabel, 1, "bool", showLabelDialogGroup, NAME_appearance, "Whether label is visible"), SM(NAME_open, 0, NULL, openDialogGroup, NAME_organisation, "Create dialog with this item and ->open") }; /* Get Methods */ static getdecl get_diagroup[] = { GM(NAME_position, 0, "point", NULL, getPositionGraphical, NAME_area, "Top-left corner of tab"), GM(NAME_x, 0, "int", NULL, getXGraphical, NAME_area, "Left-side of tab"), GM(NAME_y, 0, "int", NULL, getYGraphical, NAME_area, "Top-side of tab"), GM(NAME_labelName, 1, "name", "name", getLabelNameDialogGroup, NAME_label, "Determine default-label from the name"), GM(NAME_defaultButton, 1, "button", "delegate=[bool]", getDefaultButtonDialogGroup, NAME_accelerator, "Current Button connected to `RET'") }; /* Resources */ static classvardecl rc_diagroup[] = { RC(NAME_alignment, "{column,left,center,right}", "column", "Alignment in the row"), RC(NAME_elevation, "elevation*", "when(@colour_display, 1, @nil)", "Elevation above environment"), RC(NAME_radius, "0..", "0", "Radius for the corners"), RC(NAME_gap, "size", "size(15,8)", "Distance between items in X and Y"), RC(NAME_border, "[size]", "@default", "Distance around the items in X and Y"), RC(NAME_labelFont, "font", "bold", "Font used to display the label"), RC(NAME_labelFormat, "{top,center,bottom}", "center", "Alignment of label with top-line"), RC(NAME_labelSuffix, "name", "", "Ensured suffix of label") }; /* Class Declaration */ ClassDecl(diagroup_decls, var_diagroup, send_diagroup, get_diagroup, rc_diagroup, ARGC_INHERIT, NULL, "$Rev$"); status makeClassDialogGroup(Class class) { declareClass(class, &diagroup_decls); setRedrawFunctionClass(class, RedrawAreaDialogGroup); succeed; }