View source with raw comments or as raw
    1/*  Part of XPCE --- The SWI-Prolog GUI toolkit
    2
    3    Author:        Jan Wielemaker and Anjo Anjewierden
    4    E-mail:        jan@swi.psy.uva.nl
    5    WWW:           http://www.swi.psy.uva.nl/projects/xpce/
    6    Copyright (c)  1999-2011, University of Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(pce_tool_button, []).   36:- use_module(library(pce)).   37:- use_module(library(help_message)).   38:- use_module(library(imageops)).   39:- require([ default/3
   40           ]).   41
   42/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   43This library module defines  three   classes:  tool_bar, tool_button and
   44tool_status_button. It is intended for defining   rows  of buttons, also
   45called tool-bars. Each button represents an   action  on the `client' of
   46the tool_bar.
   47
   48Below is a typical example:
   49
   50resource(printer,       image,  image('16x16/print.xpm')).
   51resource(floppy,        image,  image('16x16/save.xpm')).
   52
   53:- pce_begin_class(myapp, frame).
   54
   55initialise(MyApp) :->
   56        send_super(MyApp, initialise, 'My application'),
   57        send(F, append, new(D, tool_dialog(MyApp))),
   58        send_list(TB, append,
   59                  [ tool_button(load,
   60                                resource(floppy),
   61                                load),
   62                    gap,                % skip a little
   63                    tool_button(print,
   64                                resource(printer),
   65                                print)
   66                  ]),
   67        ...
   68
   69print(MyApp) :->
   70        <Print the current document>
   71
   72- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
   73
   74:- pce_begin_class(tool_bar, dialog_group,
   75                   "Row of buttons").
   76
   77variable(orientation,   {horizontal,vertical},  get,  "Stacking direction").
   78variable(client,        object*,                both, "Receiving object").
   79
   80initialise(BG, Client:[object]*, Orientation:[{horizontal,vertical}]) :->
   81    default(Client, @nil, C),
   82    default(Orientation, horizontal, O),
   83    send(BG, send_super, initialise, @default, group),
   84    send(BG, slot, client, C),
   85    send(BG, slot, orientation, O),
   86    send(BG, gap, size(0,0)).
   87
   88append(BG, B:'tool_button|{gap}') :->
   89    "Append button or gap"::
   90    (   get(BG, orientation, horizontal)
   91    ->  Where = right
   92    ;   Where = next_row
   93    ),
   94    (   B == gap
   95    ->  What = graphical(0, 0, 5, 5)
   96    ;   What = B
   97    ),
   98    send(BG, send_super, append, What, Where).
   99
  100activate(BG) :->
  101    "Update activation of member buttons"::
  102    send(BG?graphicals, for_some,
  103         if(message(@arg1, has_send_method, activate),
  104            message(@arg1, activate))).
  105
  106reference(BG, Ref:point) :<-
  107    "Reference is at the baseline"::
  108    get(BG, height, H),
  109    new(Ref, point(0, H)).
  110
  111compute(BG) :->
  112    "Make all buttons the same size"::
  113    (   get(BG, request_compute, @nil)
  114    ->  true
  115    ;   new(S, size(0,0)),
  116        send(BG?graphicals, for_all,
  117             if(message(@arg1, instance_of, button),
  118                message(S, union, @arg1?size))),
  119        send(BG?graphicals, for_all,
  120             if(message(@arg1, instance_of, button),
  121                message(@arg1, size, S))),
  122        send_super(BG, compute)
  123    ).
  124
  125:- pce_end_class.
  126
  127:- pce_begin_class(tool_button(client, name), button,
  128                   "Button for a tool_bar").
  129
  130variable(condition,     code*,       get, "Condition for activation").
  131
  132initialise(TB,
  133           Action:action='name|code',
  134           Label:label='name|image',
  135           Balloon:balloon=[name|string],
  136           Condition:condition=[code]*,
  137           Name:name=[name]) :->
  138    default(Condition, @nil, Cond),
  139    make_label(Label, Lbl),
  140    make_name(Action, Name, ButtonName),
  141    make_message(Action, Msg),
  142    send(TB, send_super, initialise, ButtonName, Msg),
  143    send(TB, label, Lbl),
  144    send(TB, slot, condition, Cond),
  145    (   Balloon == @default
  146    ->  true
  147    ;   atom(Balloon)
  148    ->  get(Balloon, label_name, Text),
  149        send(TB, help_message, tag, Text)
  150    ;   send(TB, help_message, tag, Balloon)
  151    ).
  152
  153client(TB, Client:object) :<-
  154    get(TB, device, Dev),
  155    get(Dev, client, Client).
  156
  157make_message(Name, @default) :-
  158    atomic(Name),
  159    !.
  160make_message(Code, Code).
  161
  162
  163make_name(Action, @default, Name) :-
  164    !,
  165    make_name(Action, Name).
  166make_name(_, Name, Name).
  167
  168make_name(Name, Name) :-
  169    atomic(Name),
  170    !.
  171make_name(Code, Name) :-
  172    send(Code, instance_of, message),
  173    get(Code, selector, Name),
  174    atom(Name),
  175    !.
  176make_name(_, 'tool_button').
  177
  178
  179make_label(Image, Image) :-
  180    send(Image, instance_of, image),
  181    !.
  182make_label(Name, Image) :-
  183    pce_catch_error(cannot_find_file, new(Image, image(Name))),
  184    !.
  185make_label(Name, Image) :-
  186    new(T, text(Name, left, small)),
  187    get(T, size, size(W, H)),
  188    new(I0, image(@nil, W, H)),
  189    send(I0, draw_in, T),
  190    get(I0, scale, size(16,16), Image),
  191    free(T),
  192    free(I0).
  193
  194
  195forward(TB) :->
  196    "Send action to <-client"::
  197    get(TB, message, Msg),
  198    (   Msg == @default
  199    ->  get(TB, name, Selector),
  200        get(TB, client, Client),
  201        send(Client, Selector)
  202    ;   send(Msg, execute)
  203    ).
  204
  205
  206activate(TB) :->
  207    "Update the activation using <-condition"::
  208    (   get(TB, condition, Cond),
  209        Cond \== @nil
  210    ->  (   get(TB, client, Client),
  211            send(Cond, forward, Client)
  212        ->  send(TB, active, @on)
  213        ;   send(TB, active, @off)
  214        )
  215    ).
  216
  217active(TB, Val:bool) :->
  218    "(de)activate the menu-item"::
  219    send(TB, send_super, active, Val),
  220    (   get(TB, label, Image),
  221        send(Image, instance_of, image)
  222    ->  get(Image, active, Val, Activated),
  223        send(TB, label, Activated)
  224    ;   true
  225    ).
  226
  227:- pce_end_class.
  228
  229:- pce_begin_class(tool_status_button, tool_button,
  230                   "A tool button representing two states").
  231
  232variable(value,         bool := @off,   get, "Current value").
  233
  234execute(TB) :->
  235    "Switch status and send message"::
  236    get(TB, value, Value),
  237    get(Value, negate, NewValue),
  238    send(TB, value, NewValue),
  239    send(TB, flush),
  240    get(TB, message, Message),
  241    (   Message == @default
  242    ->  get(TB, client, Client),
  243        get(TB, name, Selector),
  244        send(Client, Selector, NewValue)
  245    ;   Message == @nil
  246    ->  true
  247    ;   send(Message, forward, NewValue)
  248    ).
  249
  250
  251value(TB, Value:bool) :->
  252    (   get(TB, value, Value)
  253    ->  true
  254    ;   send(TB, slot, value, Value),
  255        (   Value == @on
  256        ->  send(TB, status, execute)
  257        ;   send(TB, status, inactive)
  258        )
  259    ).
  260
  261
  262reset(TB) :->
  263    "Smooth handling of abort"::
  264    (   get(TB, value, @off)
  265    ->  send(TB, status, inactive)
  266    ;   send(TB, status, execute)
  267    ).
  268
  269:- pce_end_class(tool_status_button).
  270
  271
  272                 /*******************************
  273                 *            DIALOG            *
  274                 *******************************/
  275
  276:- pce_begin_class(tool_dialog, dialog,
  277                   "Dialog for menu-bar and toolbar").
  278
  279variable(client,        [object],       get, "The client").
  280
  281initialise(TD, Client:[object]) :->
  282    "Refine layout"::
  283    send_super(TD, initialise),
  284    send(TD, slot, client, Client),
  285    send(TD, pen, 0),
  286    send(TD, border, size(0, 2)).
  287
  288
  289menu_bar(TD, Create:[bool], MB:menu_bar) :<-
  290    "Get (or create) the menu_bar"::
  291    (   get(TD, member, menu_bar, MB)
  292    ->  true
  293    ;   Create == @on
  294    ->  (   get(TD, tool_bar, TB)
  295        ->  send(new(MB, menu_bar), above, TB)
  296        ;   send(TD, append, new(MB, menu_bar))
  297        )
  298    ).
  299
  300tool_bar(TD, Create:[bool], TB:tool_bar) :<-
  301    "Get (or create) the tool_bar"::
  302    (   get(TD, member, tool_bar, TB)
  303    ->  true
  304    ;   Create == @on
  305    ->  (   get(TD, client, Client),
  306            Client \== @default
  307        ->  true
  308        ;   get(TD, frame, Client)
  309        ),
  310        (   get(TD, menu_bar, MB)
  311        ->  send(new(TB, tool_bar(Client)), below, MB)
  312        ;   send(TD, append, new(TB, tool_bar(Client)))
  313        )
  314    ).
  315
  316
  317popup(TD, Name:name, Create:[bool], Popup:popup) :<-
  318    "Find named popup or create it"::
  319    get(TD, menu_bar, Create, MB),
  320    (   get(MB, member, Name, Popup)
  321    ->  true
  322    ;   Create == @on
  323    ->  send(MB, append, new(Popup, popup(Name))),
  324        send(Popup, message, message(TD, action, @arg1))
  325    ).
  326
  327
  328append(TD,
  329       B:'popup|menu_item|tool_button|graphical|{gap}',
  330       Where:where=[name], Before:before=[any]) :->
  331    "Append buttons or popup for manu-bar"::
  332    (   send(B, instance_of, popup)
  333    ->  get(TD, menu_bar, @on, MB),
  334        send(MB, append, B),
  335        (   get(B, message, @default)
  336        ->  send(B, message, message(TD, action, @arg1))
  337        ;   true
  338        )
  339    ;   send(B, instance_of, menu_item)
  340    ->  get(TD, popup, Where, @on, Popup),
  341        send(Popup, insert_before, B, Before)
  342    ;   (   send(B, instance_of, tool_button)
  343        ;   B == gap
  344        )
  345    ->  get(TD, tool_bar, @on, TB),
  346        send(TB, append, B)
  347    ;   send_super(TD, append, B, Where)
  348    ).
  349
  350action(TD, Action:name) :->
  351    "Forward popup-action to <-client"::
  352    (   get(TD, client, Client),
  353        Client \== @default
  354    ->  send(Client, Action)
  355    ;   send(TD, report, error, 'No client for posting action')
  356    ).
  357
  358:- pce_end_class(tool_dialog)