/* 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) 1985-2002, 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 static status initialiseButton(Button b, Name name, Message msg, Name acc) { createDialogItem(b, name); assign(b, default_button, OFF); assign(b, show_focus_border, ON); assign(b, message, msg); if ( notDefault(acc) ) assign(b, accelerator, acc); return requestComputeGraphical(b, DEFAULT); } int accelerator_code(Name a) { if ( isName(a) ) { char *s = strName(a); if ( s[0] == '\\' && s[1] == 'e' && isalpha(s[2]) && s[3] == EOS ) return s[2]; if ( s[1] == EOS && isalpha(s[0]) ) return s[0]; } return 0; } static status RedrawMenuBarButton(Button b, Area a) { int x, y, w, h; Any ofg = NIL; int flags = 0; initialiseDeviceGraphical(b, &x, &y, &w, &h); NormaliseArea(x, y, w, h); if ( b->status == NAME_preview ) { Elevation e; if ( b->look == NAME_gtkMenuBar && (e = getClassVariableValueObject(b, NAME_previewElevation)) && notNil(e) ) { r_3d_box(x, y, w, h, 0, e, TRUE); } else /* if ( b->look == NAME_winMenuBar ) */ { Any fg = getClassVariableValueObject(b, NAME_selectedForeground); Any bg = getClassVariableValueObject(b, NAME_selectedBackground); if ( !fg ) fg = WHITE_COLOUR; if ( !bg ) bg = BLACK_COLOUR; r_fill(x, y, w, h, bg); ofg = r_colour(fg); } } if ( b->active == OFF ) flags |= LABEL_INACTIVE; RedrawLabelDialogItem(b, accelerator_code(b->accelerator), x, y, w, h, NAME_center, NAME_center, flags); if ( notNil(ofg) ) r_colour(ofg); succeed; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Draw face of the button. This is really a mess. There are simply too many style and other options. In addition, the Motif/Gtk choice to draw a large sunken region around the focus/default button make the mess even bigger. We generally should not do that for all buttons (i.e. not for closely stacked buttons in a button-bar, which means normally not for buttons having images. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static void draw_generic_button_face(Button b, int x, int y, int w, int h, int up, int defb, int focus) { Elevation z = getClassVariableValueObject(b, NAME_elevation); int r = valInt(b->radius); if ( z && notNil(z) ) /* 3-d style */ { int up = (b->status == NAME_inactive || b->status == NAME_active); if ( b->look == NAME_motif || b->look == NAME_gtk || b->look == NAME_win ) { int bx = x, by = y, bw = w, bh = h; if ( b->look == NAME_motif || b->look == NAME_gtk ) { if ( b->show_focus_border == ON ) { PceWindow sw = getWindowGraphical((Graphical)b); Graphical kbfocus = (sw ? sw->keyboard_focus : NIL); if ( focus || kbfocus == (Graphical) b || /* inactive focus */ (defb && !instanceOfObject(kbfocus, ClassButton)) ) { static Elevation e = NULL; if ( !e ) e = newObject(ClassElevation, ONE, EAV); bx -= GTK_BUTTON_MARGIN; by -= GTK_BUTTON_MARGIN; bw += GTK_BUTTON_MARGIN * 2; bh += GTK_BUTTON_MARGIN * 2; r_3d_box(bx, by, bw, bh, r, e, FALSE); } } if ( focus ) { int pen = valInt(b->pen); if ( pen > 0 ) { r_thickness(pen); r_box(x-pen, y-pen, w+2*pen, h+2*pen, r, NIL); } } } else { if ( defb ) { int pen = valInt(b->pen); bx -= pen; by -= pen; bw += 2*pen; bh += 2*pen; r_thickness(pen); r_box(bx, by, bw, bh, r, NIL); } } } r_3d_box(x, y, w, h, r, z, up); if ( b->look == NAME_openLook && defb ) { Any old; old = r_colour(r_elevation_shadow(z)); r_box(x+2, y+2, w-4, h-4, r, NIL); r_colour(old); } } else /* 2-d style */ { int swapc = 0; int pen = valInt(b->pen); int radius = valInt(b->radius); int shadow = valInt(b->shadow); if ( defb && b->look != NAME_openLook ) pen++; r_thickness(pen); r_dash(b->texture); if ( up ) { r_shadow_box(x, y, w, h, radius, shadow, NIL); } else if ( b->status == NAME_preview ) { r_shadow_box(x, y, w, h, radius, shadow, BLACK_IMAGE); swapc = TRUE; } else if ( b->status == NAME_execute ) { r_shadow_box(x, y, w, h, radius, shadow, GREY25_IMAGE); } if ( swapc ) r_swap_background_and_foreground(); if ( defb && b->look == NAME_openLook ) r_box(x+pen, y+pen, w-2*pen-shadow, h-2*pen-shadow, radius, NIL); if ( swapc ) r_swap_background_and_foreground(); } } static int draw_button_popup_indicator(Button b, int x, int y, int w, int h, int up) { int rm; /* required right margin */ if ( notNil(b->popup_image) ) { int iw = valInt(b->popup_image->size->w); int ih = valInt(b->popup_image->size->h); rm = iw+8; r_image(b->popup_image, 0, 0, x+w-rm, y + (h-ih)/2, iw, ih, ON); } else { Elevation z = getClassVariableValueObject(b, NAME_elevation); if ( b->look == NAME_motif || b->look == NAME_gtk ) { int bw = 12; int bh = 8; rm = bw+8; r_3d_box(x+w-bw-8, y+(h-bh)/2, bw, bh, 0, z, TRUE); } else { int th = 8; int tw = 9; int tx, ty; rm = tw+8; tx = x+w-rm; ty = y + (h-th)/2; r_3d_triangle(tx+tw/2, ty+th, tx, ty, tx+tw, ty, z, up, 0x3); rm = tw; } } return rm; } status RedrawAreaButton(Button b, Area a) { int x, y, w, h; int defb; int rm = 0; /* right-margin */ PceWindow sw; int kbf; /* Button has keyboard focus */ int obhf; /* Other button has focus */ int focus; int swapbg = FALSE; int up; int flags = 0; Elevation z; if ( b->look == NAME_winMenuBar || b->look == NAME_gtkMenuBar ) return RedrawMenuBarButton(b, a); if ( b->active == OFF ) flags |= LABEL_INACTIVE; up = (b->status == NAME_active || b->status == NAME_inactive); defb = (b->default_button == ON); initialiseDeviceGraphical(b, &x, &y, &w, &h); NormaliseArea(x, y, w, h); if ( (sw = getWindowGraphical((Graphical)b)) ) { kbf = (sw->keyboard_focus == (Graphical) b); obhf = (!kbf && instanceOfObject(sw->keyboard_focus, ClassButton)); focus = (sw->input_focus == ON); } else kbf = obhf = focus = FALSE; /* should not happen */ if ( !ws_draw_button_face((DialogItem)b, x, y, w, h, up, defb, kbf && focus) ) draw_generic_button_face(b, x, y, w, h, up, defb, kbf && focus); if ( b->look == NAME_openLook && b->status == NAME_preview && !((z = getClassVariableValueObject(b, NAME_elevation)) && notNil(z)) ) { swapbg = TRUE; r_swap_background_and_foreground(); } if ( notNil(b->popup) && !instanceOfObject(b->label, ClassImage) ) rm = draw_button_popup_indicator(b, x, y, w, h, up); RedrawLabelDialogItem(b, accelerator_code(b->accelerator), x, y, w-rm, h, NAME_center, NAME_center, flags); if ( swapbg ) r_swap_background_and_foreground(); return RedrawAreaGraphical(b, a); } static status computeButton(Button b) { if ( notNil(b->request_compute) ) { int w, h, isimage; TRY(obtainClassVariablesObject(b)); dia_label_size(b, &w, &h, &isimage); if ( b->look == NAME_winMenuBar || b->look == NAME_gtkMenuBar ) { if ( !isimage ) { w += valInt(getExFont(b->label_font)) * 2; if ( b->look == NAME_gtkMenuBar ) h += 4; } else { w += 4; h += 4; } } else { if ( isimage ) { w += 4; h += 4; } else { Size size = getClassVariableValueObject(b, NAME_size); h += 6; w += 10 + valInt(b->radius); if ( notNil(b->popup) ) { if ( notNil(b->popup->popup_image) ) w += valInt(b->popup->popup_image->size->w) + 5; else if ( b->look == NAME_motif || b->look == NAME_gtk ) w += 12 + 5; else w += 9 + 5; } w = max(valInt(size->w), w); h = max(valInt(size->h), h); } } CHANGING_GRAPHICAL(b, assign(b->area, w, toInt(w)); assign(b->area, h, toInt(h))); assign(b, request_compute, NIL); } succeed; } Point getReferenceButton(Button b) { Point ref; if ( !(ref = getReferenceDialogItem(b)) && !instanceOfObject(b->label, ClassImage) ) { int fh, ascent, h, rx = 0; ComputeGraphical(b); fh = valInt(getHeightFont(b->label_font)); ascent = valInt(getAscentFont(b->label_font)); h = valInt(b->area->h); if ( b->look == NAME_winMenuBar || b->look == NAME_gtkMenuBar ) rx = valInt(getExFont(b->label_font)); ref = answerObject(ClassPoint, toInt(rx), toInt((h - fh)/2 + ascent), EAV); } answer(ref); } static status statusButton(Button b, Name stat) { if ( stat != b->status ) { Name oldstat = b->status; assign(b, status, stat); /* These are equal: do not redraw */ if ( !( (stat == NAME_active || stat == NAME_inactive) && (oldstat == NAME_active || oldstat == NAME_inactive) ) ) changedDialogItem(b); } succeed; } status makeButtonGesture() { if ( GESTURE_button != NULL ) succeed; GESTURE_button = globalObject(NAME_ButtonGesture, ClassClickGesture, NAME_left, DEFAULT, DEFAULT, newObject(ClassMessage, RECEIVER, NAME_execute, EAV), newObject(ClassMessage, RECEIVER, NAME_status,NAME_preview,EAV), newObject(ClassMessage, RECEIVER, NAME_cancel, EAV), EAV); assert(GESTURE_button); succeed; } static status WantsKeyboardFocusButton(Button b) { if ( b->active == ON && ( b->look == NAME_motif || b->look == NAME_gtk || b->look == NAME_win) ) succeed; fail; } static status eventButton(Button b, EventObj ev) { if ( eventDialogItem(b, ev) ) succeed; if ( b->active == ON ) { int infocus = (getKeyboardFocusGraphical((Graphical) b) == ON); makeButtonGesture(); if ( ev->id == toInt(13) && infocus ) /* RETURN */ { send(b, NAME_execute, EAV); succeed; } if ( isAEvent(ev, NAME_msLeftDown) && !infocus ) send(b, NAME_keyboardFocus, ON, EAV); if ( isAEvent(ev, NAME_focus) ) { changedDialogItem(b); succeed; } return eventGesture(GESTURE_button, ev); } fail; } static status keyButton(Button b, Name key) { if ( b->active == ON ) { static Name ret; if ( !ret ) ret = CtoName("RET"); if ( b->accelerator == key || (b->default_button == ON && key == ret) ) return send(b, NAME_execute, EAV); } fail; } static status executeButton(Button b) { if ( notNil(b->message) ) { DisplayObj d = getDisplayGraphical((Graphical) b); if ( d ) busyCursorDisplay(d, DEFAULT, DEFAULT); statusButton(b, NAME_execute); flushGraphical(b); send(b, NAME_forward, EAV); if ( d ) busyCursorDisplay(d, NIL, DEFAULT); if ( !isFreedObj(b) ) statusButton(b, NAME_inactive); } succeed; } static status forwardButton(Button b) { if ( isNil(b->message) ) succeed; if ( notDefault(b->message) ) return forwardReceiverCode(b->message, b, EAV); return send(b->device, b->name, EAV); } /******************************** * ATTRIBUTES * ********************************/ static status defaultButtonButton(Button b, BoolObj val) { if ( isDefault(val) ) val = ON; if ( hasSendMethodObject(b->device, NAME_defaultButton) ) return send(b->device, NAME_defaultButton, b, EAV); else assign(b, default_button, val); succeed; } status isApplyButton(Button b) { if ( b->name == NAME_apply ) succeed; if ( instanceOfObject(b->message, ClassMessage) ) { Message m = (Message)b->message; if ( m->selector == NAME_apply ) succeed; } fail; } static status radiusButton(Button b, Int radius) { return assignGraphical(b, NAME_radius, radius); } static status popupButton(Button b, PopupObj p) { return assignGraphical(b, NAME_popup, p); } static PopupObj getPopupButton(Button b, BoolObj create) { if ( notNil(b->popup) || create != ON ) answer(b->popup); else { PopupObj p = newObject(ClassPopup, b->label, EAV); send(p, NAME_append, newObject(ClassMenuItem, b->name, newObject(ClassMessage, Arg(1), NAME_execute, EAV), b->label, EAV), EAV); popupButton(b, p); answer(p); } } static status labelButton(Button b, Any label) { if ( b->label != label ) { int ltype = instanceOfObject(label, ClassImage); int sametype = (instanceOfObject(b->label, ClassImage) == ltype); if ( !sametype ) { assign(b, radius, ltype ? ZERO : getClassVariableValueObject(b, NAME_radius)); assign(b, show_focus_border, ltype ? OFF : ON); } assignGraphical(b, NAME_label, label); } succeed; } static status showFocusBorderButton(Button b, BoolObj show) { return assignGraphical(b, NAME_showFocusBorder, show); } static status shadowButton(Button b, Int shadow) { return assignGraphical(b, NAME_shadow, shadow); } static status popupImageButton(Button b, Image img) { return assignGraphical(b, NAME_popupImage, img); } static Name getSelectionButton(Button b) { answer(b->label); } /******************************* * CLASS DECLARATION * *******************************/ /* Type declarations */ static char *T_initialise[] = { "name=name", "message=[code]*", "label=[name]" }; /* Instance Variables */ static vardecl var_button[] = { SV(NAME_radius, "int", IV_GET|IV_STORE, radiusButton, NAME_appearance, "Rounding radius for corners"), SV(NAME_shadow, "int", IV_GET|IV_STORE, shadowButton, NAME_appearance, "Shadow shown around the box"), SV(NAME_popupImage, "image*", IV_GET|IV_STORE, popupImageButton, NAME_appearance, "Indication that button has a popup menu"), SV(NAME_defaultButton, "[bool]", IV_GET|IV_STORE, defaultButtonButton, NAME_accelerator, "Button is default button for its <-device"), SV(NAME_showFocusBorder, "bool", IV_GET|IV_STORE, showFocusBorderButton, NAME_appearance, "Show wide border around focus/default button") }; /* Send Methods */ static senddecl send_button[] = { SM(NAME_compute, 0, NULL, computeButton, DEFAULT, "Compute desired size (from command)"), SM(NAME_event, 1, "event", eventButton, DEFAULT, "Process an event"), SM(NAME_initialise, 3, T_initialise, initialiseButton, DEFAULT, "Create from name and command"), SM(NAME_status, 1, "{inactive,active,preview,execute}", statusButton, DEFAULT, "Status for event-processing"), SM(NAME_popup, 1, "popup*", popupButton, DEFAULT, "Associated popup menu"), SM(NAME_key, 1, "key=name", keyButton, NAME_accelerator, "Handle accelerator key `name'"), SM(NAME_execute, 0, NULL, executeButton, NAME_action, "->forward and deal with UI"), SM(NAME_forward, 0, NULL, forwardButton, NAME_action, "Perform associated action"), SM(NAME_font, 1, "font", labelFontDialogItem, NAME_appearance, "same as ->label_font"), SM(NAME_WantsKeyboardFocus, 0, NULL, WantsKeyboardFocusButton, NAME_event, "Test if ready to accept input"), SM(NAME_label, 1, "char_array|image*", labelButton, NAME_label, "Sets the visible label"), SM(NAME_selection, 1, "char_array|image*", labelButton, NAME_label, "Equivalent to ->label"), SM(NAME_isApply, 0, NULL, isApplyButton, NAME_apply, "Test if button ->apply the dialog") }; /* Get Methods */ static getdecl get_button[] = { GM(NAME_popup, 1, "popup*", "create=[bool]", getPopupButton, DEFAULT, "Associated popup (make one if create = @on)"), GM(NAME_reference, 0, "point", NULL, getReferenceButton, DEFAULT, "Left, baseline of label"), GM(NAME_selection, 0, "name", NULL, getSelectionButton, NAME_label, "Equivalent to <-label") }; /* Resources */ static classvardecl rc_button[] = { RC(NAME_look, RC_REFINE, UXWIN("gtk", "win"), NULL), RC(NAME_alignment, "{column,left,center,right}", "center", "Alignment in the row"), RC(NAME_labelFont, "font", "normal", "Default font for labels"), RC(NAME_labelSuffix, "name", "", "Ensured suffix of label"), RC(NAME_pen, "int", "1", "Thickness of box"), RC(NAME_selectedForeground, "colour", UXWIN("white", "win_highlighttext"), "Colour when in preview mode (Windows menu-bar)"), RC(NAME_selectedBackground, "colour", UXWIN("black", "win_highlight"), "Background when in preview mode (Windows menu-bar)"), RC(NAME_popupImage, "image*", "when(@colour_display, @nil, @ol_pulldown_image)", "Image to indicate presence of popup menu"), RC(NAME_radius, "int", "0", "Rounding radius of box"), RC(NAME_shadow, "int", "when(@colour_display, 0, 1)", "Shadow shown around the box"), RC(NAME_size, "size", UXWIN("size(50,20)", "size(80,24)"), "Minimum size in pixels"), RC(NAME_previewElevation, "elevation*", "elevation(preview, 1, hilited)", "Elevation of item in preview mode"), RC(NAME_elevation, RC_REFINE, UXWIN("when(@colour_display, button, @nil)", "elevation(@nil, 2, @_dialog_bg)"), NULL) }; /* Class Declaration */ static Name button_termnames[] = { NAME_label, NAME_message, NAME_accelerator }; ClassDecl(button_decls, var_button, send_button, get_button, rc_button, 3, button_termnames, "$Rev$"); status makeClassButton(Class class) { declareClass(class, &button_decls); setRedrawFunctionClass(class, RedrawAreaButton); succeed; }