/* 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. */ :- module(dia_generate, [source/2, new_term/2]). :- use_module(library(pce)). :- use_module(proto). :- require([ append/3 , between/3 , chain_list/2 , delete/3 , forall/2 , genarg/3 , get_chain/3 , list_to_set/2 , maplist/3 , member/2 , memberchk/2 , portray_object/2 , subtract/3 , term_to_atom/2 ]). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Format description: dialog(( ...), [ := , ... ]). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ :- dynamic reference/2. source(Dialog, Source) :- retractall(reference(_,_)), findall(P, parameter(Dialog, P), Ps), menu_items(Dialog), findall(Attr := Value, source_attribute(Dialog, Attr, Value), List), reference(ObjectRef, Dialog), get(Dialog, name, Id), IdTerm =.. [Id|Ps], Source =.. [dialog, IdTerm, [object := ObjectRef | List]]. % retractall(reference(_,_)). % TBD :- discontiguous source_attribute/3. /******************************* * PARAMETERS * *******************************/ parameter(Dialog, P) :- get(Dialog, behaviour_model, Model), get_chain(Model, graphicals, Grs), findall('$aref'(RefName), (member(Gr, Grs), send(Gr, instance_of, msg_parameter_port), get(Gr, parameter_name, PName), prolog_variable_name(PName, RefName), asserta(reference('$aref'(RefName), Gr))), Parms), member(P, Parms). /******************************* * MENU_ITEMS * *******************************/ menu_items(Dialog) :- get(Dialog, behaviour_model, Model), get_chain(Model, graphicals, Grs), member(Gr, Grs), send(Gr, instance_of, msg_object), get(Gr, ui_object, UI), send(UI, instance_of, menu_item), get(UI, value, ValueName), prolog_variable_name(ValueName, RefName), asserta(reference('$aref'(RefName), UI)), fail. menu_items(_). /******************************* * PARTS * *******************************/ source_attribute(Dialog, parts, TheItems) :- get_chain(Dialog, graphicals, Grs), findall('$aref'(RefName) := NewTerm, (member(Gr, [Dialog|Grs]), \+ get(Dialog, overlay, Gr), new_term(Gr, NewTerm), get(Gr, name, GrName), prolog_variable_name(GrName, RefName), asserta(reference('$aref'(RefName), Gr))), Items), generate_unique_variable_names(Items, [], TheItems). generate_unique_variable_names([], _, []). generate_unique_variable_names([Part|Parts], Done, [NewPart|NewParts]) :- varname(Part, Ref), ( occurs(Done, Ref, DoneTimes), DoneTimes > 0 -> Id is DoneTimes + 1, atom_concat(Ref, Id, NewRef), set_varname(Part, NewRef, NewPart), generate_unique_variable_names(Parts, [Part|Done], NewParts) ; occurs(Parts, Ref, Times), ( Times == 0 -> NewPart = Part, generate_unique_variable_names(Parts, [Part|Done], NewParts) ; occurs(Done, Ref, DoneTimes), Id is DoneTimes + 1, atom_concat(Ref, Id, NewRef), set_varname(Part, NewRef, NewPart), generate_unique_variable_names(Parts, [Part|Done], NewParts) ) ). varname('$aref'(Name) := _, Name). set_varname('$aref'(Old) := Value, Var, '$aref'(Var) := Value) :- retract(reference('$aref'(Old), Gr)), !, assert(reference('$aref'(Var), Gr)). occurs([], _, 0). occurs([H|T], Var, Times) :- varname(H, Var), !, occurs(T, Var, T0), Times is T0 + 1. occurs([_H|T], Var, Times) :- occurs(T, Var, Times). /******************************* * MODIFICATIONS * *******************************/ source_attribute(Dialog, modifications, Modifications) :- get_chain(Dialog, graphicals, Grs), findall(Ref := Atts, (member(Gr, Grs), \+ get(Dialog, overlay, Gr), object_attributes(Gr, Atts), Atts \== [], reference(Ref, Gr)), Modifications). /******************************* * LAYOUT INFO * *******************************/ source_attribute(Dialog, layout, Layout) :- get_chain(Dialog, graphicals, Grs), get(Dialog, overlay, Overlay), delete(Grs, Overlay, Items), findall(Alignment, alignment(Items, Alignment), P0), maplist(canonicalise_alignment, P0, P1), list_to_set(P1, P2), maplist(symbolic_pair, P2, Pairs), aligned(P2, Aligned), subtract(Items, Aligned, Explicit), findall(area(Ref, Area), (member(I, Explicit), reference(Ref, I), get_argument(I, area, Area)), Positions), append(Pairs, Positions, Layout). alignment(Items, Alignment) :- pair(I1, I2, Items), alignment(I1, I2, Alignment). alignment(I1, I2, below(I1, I2)) :- get(I2, below, I1). alignment(I1, I2, above(I1, I2)) :- get(I2, above, I1). alignment(I1, I2, left(I1, I2)) :- get(I2, left, I1). alignment(I1, I2, right(I1, I2)) :- get(I2, right, I1). pair(I1, I2, [I1|L]) :- member(I2, L). pair(I1, I2, [_|T]) :- pair(I1, I2, T). canonicalise_alignment(left(I1, I2), right(I2, I1)) :- !. canonicalise_alignment(above(I1, I2), below(I2, I1)) :- !. canonicalise_alignment(A, A). aligned(Pairs, Items) :- aligned_(Pairs, I0), sort(I0, Items). % unqiue aligned_([], []). aligned_([H|T], [I1,I2|R]) :- H =.. [_, I1, I2], aligned_(T, R). symbolic_pair(Term, Refs) :- Term =.. L, maplist(to_reference, R, L), Refs =.. R. to_reference(Symbol, Ref) :- reference(Symbol, Ref), !. to_reference(X, X). /******************************* * POPUPS * *******************************/ source_attribute(Dialog, popups, Popups) :- get_chain(Dialog, graphicals, Grs), findall(Ref := Popup, (member(Gr, Grs), \+ get(Dialog, overlay, Gr), popup(Gr, Popup), reference(Ref, Gr)), Popups), Popups \== []. popup(Gr, [ popup := NewTerm, Attributes ]) :- ( send(Gr, instance_of, menu), get(Gr, kind, cycle) -> fail ; get(Gr, popup, Popup), Popup \== @nil, new_term(Popup, NewTerm), object_attributes(Popup, Attributes) ). /******************************* * BEHAVIOUR * *******************************/ behaviour_object(O, R) :- send(O, instance_of, menu_item), !, reference(R, O). behaviour_object(O, O) :- O = @Ref, atom(Ref), \+ send(O, instance_of, graphical). source_attribute(Dialog, behaviour, Behaviour) :- get_chain(Dialog, graphicals, Grs), findall(Ref := Dyns, (member(Gr, Grs), \+ get(Dialog, overlay, Gr), get(Gr, behaviour_model, Bgr), behaviour(Bgr, Dyns), Dyns \== [], reference(Ref, Gr)), B0), get(Dialog, behaviour_model, Model), get_chain(Model, graphicals, BMs), findall(R := Dyns, (member(BM, BMs), send(BM, instance_of, msg_object), get(BM, ui_object, O), behaviour_object(O, R), behaviour(BM, Dyns), Dyns \== []), B1), append(B0, B1, Behaviour), Behaviour \== []. source_attribute(Dialog, initialise, Initialise) :- get(Dialog, behaviour_model, Model), get(Model?graphicals, find_all, message(@arg1, instance_of, msg_init_port), Chain), chain_list(Chain, Ports), Ports \== [], findall(Send, (member(Port, Ports), port_message(Port, Message), ( message_to_call(Message, Send) -> true ; get(Port, name, Name), Send = (Name := Message) )), Initialise). message_to_call(Message, Send) :- functor(Message, message, _), !, Message =.. [_|Args], Send =.. [send|Args]. behaviour(Object, Dyns) :- get(Object?graphicals, find_all, message(@arg1, instance_of, msg_event_port), Chain), chain_list(Chain, EventPorts), findall(Name := Message, (member(Port, EventPorts), get(Port, name, Name), port_message(Port, Message)), Dyns). port_message(Port, Message) :- get(Port, connections, Cs), get(Cs, find_all, @arg1?type == activate, Activations), chain_list(Activations, List), maplist(activation_message, List, Messages), ( Messages = [Message] -> true ; Message =.. [and|Messages] % order? ). activation_message(C, Message) :- get(C, to, CallPort), get(CallPort, name, Selector), get(CallPort, object, Object), receiver(Object, Receiver), activation_arguments(C, Args), TheMessage =.. [message, Receiver, Selector | Args], activation_conditions(C, Conditions), ( Conditions == [] -> Message = TheMessage ; RawCond =.. [and | Conditions], simplify(RawCond, Cond), Message = if(Cond, TheMessage) ). activation_conditions(C, Conditions) :- ( get(C, connections, Cs) -> get(Cs, find_all, @arg1?type == condition, Cds), chain_list(Cds, List), maplist(condition, List, Conditions) ; Conditions = [] ). condition(C, Cond) :- activation_message(C, Cond). receiver(Object, Receiver) :- get(Object, connections, Cs), get(Cs, find, @arg1?type == expansion, E), !, get(E, from, Port), get(Port, object, O2), get(Port, name, Selector), receiver(O2, R2), activation_arguments(E, Args), Obtainer =.. [?, R2, Selector | Args], simplify(Obtainer, Receiver). receiver(Object, Receiver) :- get(Object, ui_object, Self), !, ( reference(Receiver, Self) -> true ; Self = @Atom, atom(Atom) -> Receiver = Self ; pce_to_prolog(Self, Receiver) ). activation_arguments(C, Args) :- ( get(C, connections, Cs) -> get(Cs, find_all, @arg1?type == argument, A0), chain_list(A0, L0), maplist(activation_argument, L0, ParmArgs), sort(ParmArgs, Sorted), argument_list(Sorted, PositionArgs, NamedArgs), append(PositionArgs, NamedArgs, Args) ; Args = [] ). argument_list([], [], []). argument_list([N := A|T], PosArgs, [N := A|R]) :- atom(N), !, argument_list(T, PosArgs, R). argument_list([_ := A|T], [A|R], NamedArgs) :- argument_list(T, R, NamedArgs). activation_argument(A, Parm := Arg) :- get(A, from, Port), get(A, parameter, Parm), ( send(Port, instance_of, msg_constant_port) -> get(Port, name, TermAtom), term_to_atom(Arg, TermAtom) ; send(Port, instance_of, msg_parameter_port) -> reference(Arg, Port) ; send(Port, instance_of, msg_get_port) -> get(Port, object, Object), get(Port, name, Selector), receiver(Object, Receiver), activation_arguments(A, Args), Obtainer =.. [?, Receiver, Selector | Args], simplify(Obtainer, Arg) ). simplify(X?self, S) :- !, simplify(X, S). simplify(and(X), S) :- !, simplify(X, S). simplify(X, S) :- simplify_forward(X, S), !. simplify(X, X). % map message(X, forward, a, b, c). Is this ok? What if not % all arguments are handled? simplify_forward(Message, Simple) :- Message =.. [message, _R, forward | Args], make_mapping(Args, Mapping), map_term(Mapping, Message, Mapped), Mapped =.. [message, Code, forward | Args], \+ argleft(Mapped), !, Simple = Code. simplify_forward(Obtainer, Simple) :- Obtainer =.. [?, _F, '_forward' | Args], make_mapping(Args, Mapping), map_term(Mapping, Obtainer, Mapped), Mapped =.. [?, Code, '_forward' | Args], \+ argleft(Mapped), !, ( Code = quote_function(Simple) ; Simple = Code ). argleft(@Ref) :- atom_concat(arg, N, Ref), get(@pce, convert, N, int, _), !. argleft(Atomic) :- atomic(Atomic), !, fail. argleft(Term) :- genarg(_, Term, Arg), argleft(Arg), !. make_mapping(Args, Mapping) :- make_mapping(Args, 1, Mapping). make_mapping([], _, []). make_mapping([H|T], N, [@ArgN = H|R]) :- atom_concat(arg, N, ArgN), NN is N + 1, make_mapping(T, NN, R). % map_term(+[From = To, ...], +Term, -MappedTerm). map_term(Mapping, Term, NewTerm) :- memberchk(Term = NewTerm, Mapping), !. map_term(Mapping, Term, NewTerm) :- functor(Term, Name, Arity), functor(NewTerm, Name, Arity), map_argument(1, Arity, Mapping, Term, NewTerm). map_argument(N, M, Mapping, Term, NewTerm) :- arg(N, Term, A1), !, arg(N, NewTerm, A2), map_term(Mapping, A1, A2), NN is N + 1, map_argument(NN, M, Mapping, Term, NewTerm). map_argument(_, _, _, _, _). /******************************* * NEW-TERM * *******************************/ new_term(Object, Term) :- proto(Object, Proto), proto_term(Proto, Functor, ProtoArgs), !, new_proto_term(Proto, Functor, Args), maplist(new_argument(Object), Args, Values), ( length(Args, L), length(ProtoArgs, L) -> true % no more arguments ; maplist(try_argument, Values, TryValues), new_term(Functor, TryValues, TryTerm), new(Tmp, TryTerm), ( equal_on_remaining_proto_args(Values, ProtoArgs, Object, Tmp) -> !, free(Tmp) ; free(Tmp), fail ) ), new_term(Functor, Values, RawTerm), ( Functor == menu_item, reference(Ref, Object) -> Term = new(Ref, RawTerm) ; Term = RawTerm ). new_term(Object, Term) :- portray_object(Object, Term). % new_term(+Class, +Args, -Term) % Create a term that can be handed as an argument to create an % instance of the class. new_term(Class, Args, Term) :- append(NonDef, Def, Args), maplist(=(@default), Def), !, ( NonDef == [] -> Term = new(Class) ; Term =.. [Class|NonDef] ). code_class(message). code_class(and). % try_argument(+Arg, -Try) % Code arguments normally contain $aref(_) terms and thus cannot % be handed to the temporary term. Just hand in a dummy argument % to fix to avoid the error. :- pce_global(@dia_dummy_code, new(or)). try_argument(A, @dia_dummy_code) :- functor(A, Class, _), code_class(Class), !. try_argument(A, A). new_argument(_, _Name := Value, Value) :- !. new_argument(O, Name, Value) :- get_argument(O, Name, Value). new_proto_term(Proto, Functor, Args) :- proto_term(Proto, Functor, ProtoArgs), delete_optional_new_arguments(ProtoArgs, Args). delete_optional_new_arguments([], []). delete_optional_new_arguments([[_]|_], []). delete_optional_new_arguments([[X]|T], [X|R]) :- !, delete_optional_new_arguments(T, R). delete_optional_new_arguments([H|T], [H|R]) :- delete_optional_new_arguments(T, R). equal_on_remaining_proto_args([], Args, O1, O2) :- equal_on_remaining_proto_args(Args, O1, O2). equal_on_remaining_proto_args([_|V], [_|T], O1, O2) :- equal_on_remaining_proto_args(V, T, O1, O2). equal_on_remaining_proto_args([], _, _). equal_on_remaining_proto_args([[Sel]|T], O1, O2) :- !, get_argument(O1, Sel, V1), get_argument(O2, Sel, V2), V1 = V2, equal_on_remaining_proto_args(T, O1, O2). equal_on_remaining_proto_args([_|T], O1, O2) :- equal_on_remaining_proto_args(T, O1, O2). /******************************* * OBJECT ATTRIBUTES * *******************************/ object_attributes(Object, List) :- new_term(Object, NewTerm), new(Tmp, NewTerm), send(Tmp, obtain_class_variables), findall(Attr := Value, object_attribute(Object, Tmp, Attr, Value), List), free(Tmp). object_attribute(Object, Tmp, Attr, Value) :- proto(Object, Proto), proto_source_attribute(_Mode, Proto, Attribute), ( atom(Attribute) -> Attr = Attribute ; functor(Attribute, Attr, _Arity) ), get_argument(Object, Attr, Value), send(Tmp, compute), ( get_argument(Tmp, Attr, DefValue), DefValue == Value -> fail % donot generate argument ; send(Tmp, Attr, Value) ). object_attribute(Object, _Tmp, append, Value) :- % menu_items of a menu send(Object, instance_of, menu), get(Object, members, Chain), chain_list(Chain, Items), findall(MenuItemTerm, (member(Item, Items), new_term(Item, NewTerm), simplify_item(NewTerm, MenuItemTerm)), Value). simplify_item(menu_item(Value), Value) :- !. simplify_item(Item, Item). delete_attribute(_, fixed_reference). delete_attribute(_, fixed_alignment). delete_attribute(_, has_popup). delete_attribute(_, popup_items(_)). delete_attribute(_, members). delete_attribute(popup, reference). attribute_mapping(reference_x, reference). attribute_mapping(reference_y, reference). map_attribute(A, M) :- attribute_mapping(A, M), !. map_attribute(A, A). proto_source_attribute(Mode, Proto, Attr) :- findall(A, attribute(Mode, Proto, A), S0), maplist(map_attribute, S0, S1), list_to_set(S1, S2), member(Attr, S2), \+ delete_attribute(Proto, Attr). /******************************* * BEHAVIOUR-MODEL * *******************************/ /* source_attribute(Target, behaviour_model, ModelSource) :- get(Target, hypered, behaviour_model, Model). */ /******************************* * GET-ARGUMENT * *******************************/ get_argument(Object, Name, PortMessage) :- get(Object, Name, Message), send(Message, instance_of, message), send(Object, has_get_method, proto), % dubious test get(Object, behaviour_model, Model), get(Message, receiver, Object), !, get(Model, member, Name, Port), ( port_message(Port, PortMessage) -> true ; PortMessage = @default ). get_argument(Object, Name, Value) :- get(Object, Name, PceValue), pce_to_prolog(PceValue, Value). pce_to_prolog(Atomic, Atomic) :- atomic(Atomic), !. pce_to_prolog(@Atom, @Atom) :- atom(Atom), !. pce_to_prolog(Ref, Term) :- portray_object(Ref, Term). % TBD /******************************* * PROPOSE A VARIABLE-NAME * *******************************/ translate_char(Char, Char) :- between(0'a, 0'z, Char), !. translate_char(Char, Char) :- between(0'A, 0'Z, Char), !. translate_char(_, 0'_). prolog_variable_name(GrName, VarName) :- new(Str, string(GrName)), send(Str, strip), get(Str, size, Size), End is Size - 1, forall(between(0, End, Index), (get(Str, character, Index, C0), translate_char(C0, C1), send(Str, character, Index, C1))), get(Str, label_name, Str2), send(Str2, translate, ' ', '_'), get(Str2, character, 0, First), ( between(0'a, 0'z, First) -> Up is First + 0'A - 0'a, send(Str2, character, 0, Up) ; true ), get(Str2, value, VarName). proto(Object, Proto) :- send(Object, has_get_method, proto), !, get(Object, proto, Proto). proto(Object, Proto) :- get(Object, class_name, Proto).