%%% This file is part of RefactorErl.
%%%
%%% RefactorErl is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU Lesser General Public License as published
%%% by the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% RefactorErl is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%%% GNU Lesser General Public License for more details.
%%%
%%% You should have received a copy of the GNU Lesser General Public License
%%% along with RefactorErl.  If not, see <http://plc.inf.elte.hu/erlang/>.
%%%
%%% The Original Code is RefactorErl.
%%%
%%% The Initial Developer of the Original Code is Eötvös Loránd University.
%%% Portions created  by Eötvös Loránd University and ELTE-Soft Ltd.
%%% are Copyright 2007-2025 Eötvös Loránd University, ELTE-Soft Ltd.
%%% and Ericsson Hungary. All Rights Reserved.

%%% @author Jimmy <>
%%%
%%% @doc Erlang GS user interface utility functions for referl_gs.
%%%

-module(referl_gs_sup).

-svn("$Rev$ ").

%% Exports
-export([root/0, children/1, info/1, text/1, find_node/1,
         tr_buttons/1, tool_buttons/0]).

%% Includes
-include("ui.hrl").
-include("gs.hrl").

%% Node index
-define(Index(X), element(3, X)). % TODO: find a function!!!

%% Missing queries
-define(QFile, [file]).
-define(RecDef, [recdef]).
-define(RecDefBack, [{recdef, back}]).
-define(Field, [field]).
-define(FieldDef, [{fielddef, back}]).
-define(RecAttr, [attr]).
-define(VarBinds, [varbind]).
-define(VarRefs, [varref]).

%% Macros
-define(ia(IAA), list_to_atom(integer_to_list(IAA))).

%% The `undefined' problem
-ifndef(show_undefined).
-define(undefined(UD),
        case UD of
           undefined -> undefined;
           UDElse -> ?MISC:to_list(UDElse)
        end).
-else.
-define(undefined(UD), ?MIDC:to_list(UD)).
-endif.

%% Implementation

%% @doc Returns with a tag record, that represents the root node of the graph.
root() ->
    Root = hd(ui({graph_query, ?Query,exec,[[]]})),
    #tag{class=root, node=Root}.

%% @doc Returns with the children of a given node.
children(Tag) ->
    children(ui({syn_class, Tag#tag.node}), Tag#tag.node).

children(root, _) ->
    [ gather_file(X) || X <- ui({graph_query, ?Query,exec,[?QFile]})];
children(module, Module) ->
    ModFileQuery = ui({graph_query, ?Mod, file, []}),
    FileFormsQuery = ui({graph_query, ?File, forms, []}),
    Seq = ui({graph_query, ?Query,seq,[ModFileQuery, FileFormsQuery]}),
    Forms = ui({graph_query, ?Query, exec, [Module, Seq]}),
    [ gather_form(X) || X <- Forms ,
                        lists:member(ui({graph_query, ?Form, type, [X]}), [macro, record, func]) ];
children(func, Node) ->
    case ui({syn_class, Node}) of
        func ->
            Fundef = ui({graph_query, ?Fun, definition, []}),
            FormclausesQuery = ui({graph_query, ?Form, clauses, []}),
            Seq = ui({graph_query, ?Query,seq,[[Fundef, FormclausesQuery]]}),
            Clauses = ui({graph_query, ?Query, exec, [Node, Seq]}),
            [ #tag{class=clause,
                   type= ui({graph_query, ?Clause, var, [Clause]}),
                   kind= ui({graph_query, ?Clause, type, [Clause]}),
                   name=integer_to_list(X),
                   id=integer_to_list(?Index(Clause)),
                   node=Clause} ||
                {Clause, X} <- lists:zip(Clauses,
                                         ?MISC:seq2(1, length(Clauses)))];
        _ -> []
    end;
children(clause, Clause) ->
    CPatternsQuery = ui({graph_query, ?Clause, patterns, []}),
    Patterns = ui({graph_query, ?Query, exec, [Clause, CPatternsQuery]}),
    CGuardQuery = ui({graph_query, ?Clause, guard, []}),
    Guard = ui({graph_query, ?Query, exec, [Clause, CGuardQuery]}), 
    CBodyQuery = ui({graph_query, ?Clause, body, []}),
    Bodys = ui({graph_query, ?Query, exec, [Clause, CBodyQuery]}),
    Nodes = lists:flatten([Patterns, Guard, Bodys]),
    [ #tag{class=expr,
          type=ui({graph_query, ?Expr, role, [X]}),
          kind=ui({graph_query, ?Expr, type, [X]}),
          id=?Index(X),
          name=?undefined(ui({graph_query, ?Expr, value, [X]})),
          node=X} || X <- Nodes ];
children(expr, Expr) ->
    VarBinds = ui({graph_query, ?Query, exec, [Expr, ?VarBinds]}),
    VarRefs = ui({graph_query, ?Query, exec, [Expr, ?VarRefs]}),
    ExprChildrenQuery = ui({graph_query, ?Expr, children, []}),
    Children =ui({graph_query, ?Query, exec, [Expr, ExprChildrenQuery]}),
    ExprClausesQuery = ui({graph_query, ?Expr, clauses, []}),
    Clauses = ui({graph_query, ?Query, exec, [Expr, ExprClausesQuery]}),
    VarTags = [ #tag{class=variable,
                     id=integer_to_list(?Index(X)),
                     name=ui({graph_query, ?Var, name, [X]}),
                     node=X} || X <- VarBinds ++ VarRefs ],
    ExprTags = [ #tag{class=expr,
                      type=ui({graph_query, ?Expr, role, [X]}),
                      kind=ui({graph_query, ?Expr, type, [X]}),
                      id=integer_to_list(?Index(X)),
                      name=?undefined(ui({graph_query, ?Expr, value, [X]})),
                      node=X} || X <- Children ],
    ClauseTags = [ #tag{class=clause,
                        type=ui({graph_query, ?Clause, var, [Clause]}),
                        kind=ui({graph_query, ?Clause, type, [Clause]}),
                        id=integer_to_list(?Index(Clause)),
                        node=Clause} || Clause <- Clauses ],
    lists:flatten([VarTags, ExprTags, ClauseTags]);
children(record, Rec) ->
    RecFieldsQuery = ui({graph_query, ?Rec, fields, []}),
    RecFieldRefsQuery = ui({graph_query, ?RecField, references, []}),
    Seq = ui({graph_query, ?Query,seq,[RecFieldsQuery, RecFieldRefsQuery]}),      
    Attributes = 
        ui({graph_query, ?Query, exec, [Rec, Seq]}),
    [ #tag{class=ui({syn_class, Node}), 
           name=?undefined(ui({graph_query, ?Expr, value, [Node]})),
           type=ui({graph_query, ?Expr, role, [Node]}),
           kind=ui({graph_query, ?Expr, type, [Node]}),
           id=integer_to_list(?Index(Node)), node=Node} ||
        Node <- Attributes ];
children(_, _) ->
    [].

gather_file(File) ->
    Type = ui({graph_query, ?File, type, [File]}),
    ModuleQuery = ui({graph_query, ?File, module, []}),
    case Type of
        module ->
            [Node] = ui({graph_query, ?Query, exec, [File, ModuleQuery]}),
            Name = ?MISC:to_list(ui({graph_query, ?Mod, name, [Node]}));
        header ->
            Node = File,
            Name = filename:basename(ui({graph_query, ?File, path, [File]}))
    end,
    #tag{class=Type, id=integer_to_list(?Index(Node)),
         name=Name, node=Node}.

gather_form(Form) ->
    Type = ui({graph_query, ?Form, type, [Form]}),
    FormFuncQuery = ui({graph_query, ?Form, func, []}),
    case Type of
        macro ->
            Node = Form,
            Name = ui({graph_query, ?Macro, name, [Node]});
        record ->
            [Node] = ui({graph_query, ?Query, exec, [Form, ?RecDef]}),
            Name = ?MISC:to_list(ui({graph_query, ?Rec, name, [Node]}));
        func ->
            [Node] = ui({graph_query, ?Query, exec, [Form, FormFuncQuery]}),
            Name =
                ?MISC:to_list(ui({graph_query, ?Fun, name, [Node]})) ++ "/" ++
                integer_to_list(ui({graph_query, ?Fun, arity, [Node]}))
    end,
    #tag{class=Type, id=integer_to_list(?Index(Node)),
         name=Name, node=Node}.

%% @doc 
find_node(History) ->
    find_node(lists:reverse(History), []).

find_node([], [A | B]) -> {A, B};
find_node([Tag | Tail], NewHistory) ->
    try ui({graph_data, Tag#tag.node}) of
        _ -> find_node(Tail, [Tag | NewHistory])
    catch
        error:bad_node ->
            [A | B] = NewHistory,
            {A , B}
    end.

%% @doc Returns with a list, that contains information about a node.
%% This informations will be appear in the information text field.
info(Tag) ->
    info(Tag#tag.class, Tag#tag.node).

info(root, _) ->
    Files = ui({graph_query, ?Query, exec, [?QFile]}),
    FileNum = integer_to_list(length(Files)),
    [{files, {string, FileNum}}];
info(module, Mod) ->
    ModName = atom_to_list(ui({graph_query, ?Mod, name, [Mod]})),
    ModFileQuery = ui({graph_query, ?Mod, file, []}),
    [File] = ui({graph_query, ?Query, exec, [Mod, ModFileQuery]}),
    FileName = ui({graph_query, ?File, path, [File]}),
    ModLocalsQuery = ui({graph_query, ?Mod, locals, []}),
    Locals = ui({graph_query, ?Query, exec, [Mod, ModLocalsQuery]}),
    LocalNames = [ atom_to_list(X) ++ "/" ++ integer_to_list(Y) ||
                      {X, Y} <- [ {ui({graph_query, ?Fun, name, [F]}), ui({graph_query, ?Fun, arity, [F]})} ||
                                    F <- Locals ] ],
    ExportedNames = [ atom_to_list(X) ++ "/" ++ integer_to_list(Y) ||
                        {X, Y} <- [ {ui({graph_query, ?Fun, name, [F]}), ui({graph_query, ?Fun, arity, [F]})} ||
                                      F <- Locals,
                                      ui({graph_query, ?Fun, is_exported, [F]})] ],
    MacrosQuery = ui({graph_query, ?File, macros, []}),
    Macros = ui({graph_query, ?Query, exec, [File, MacrosQuery]}),
    MacroNames = [ ui({graph_query, ?Macro, name, [X]}) || X <- Macros],
    FileRecsQuery = ui({graph_query, ?File, records, []}),
    Records = ui({graph_query, ?Query, exec, [File, FileRecsQuery]}),
    RecordNames = [ atom_to_list(ui({graph_query, ?Rec, name, [X]})) || X <- Records],
    [{name, {string, ModName}},
     {file, {string, FileName}},
     {functions, {list, LocalNames}},
     {exported, {list, ExportedNames}},
     {macros, {list, MacroNames}},
     {records, {list, RecordNames}}];
info(macro, Macro) ->
    Name = ui({graph_query, ?Macro, name, [Macro]}),
    [{name, {string, Name}}];
info(record, Rec) ->
    Name = atom_to_list(ui({graph_query, ?Rec, name, [Rec]})),
    Fields = ui({graph_query, ?Query, exec, [Rec, ?Field]}),
    FieldsNum = integer_to_list(length(Fields)),
    FieldNames =
        [atom_to_list(ui({graph_query, ?RecField, name, [Field]})) || Field<-Fields],
    [{name, {string, Name}},
     {fieldnum, {string, FieldsNum}},
     {fieldnames, {list, FieldNames}}];
info(func, Fun) ->
    Name = atom_to_list(ui({graph_query, ?Fun, name, [Fun]})),
    Arity = integer_to_list(ui({graph_query, ?Fun, arity, [Fun]})),
    Exported = atom_to_list(ui({graph_query, ?Fun, is_exported, [Fun]})),
    FunDefQuery = ui({graph_query, ?Fun, definition, []}),
    FunDef = ui({graph_query, ?Query, exec, [Fun, FunDefQuery]}),
    FormClausesQuery = ui({graph_query, ?Form, clauses, []}),
    Clauses = ui({graph_query, ?Query, exec, [FunDef, FormClausesQuery]}),
    ClauseNum = integer_to_list(length(Clauses)),
    [{name, {string, Name}},
     {arity, {string, Arity}},
     {exported, {string, Exported}},
     {clausenum, {string, ClauseNum}}];
info(clause, Clause) ->
    ClauseGuardQuery = ui({graph_query, ?Clause, guard, []}),
    ClauseBodyQuery = ui({graph_query, ?Clause, body, []}),
    HasGuard = atom_to_list([] /= ui({graph_query, ?Query, exec, [Clause, ClauseGuardQuery]})),
    LineNum = integer_to_list(length(ui({graph_query, ?Query, exec, [Clause, ClauseBodyQuery]}))),
    [{hasguard, {string, HasGuard}},
     {linenum, {string, LineNum}}];
info(variable, Var) ->
    VarName = ui({graph_query, ?Var, name, [Var]}),
    VarBindsQuery = ui({graph_query, ?Var, bindings, []}),
    Bindings = ui({graph_query, ?Query, exec, [Var, VarBindsQuery]}),
    BindingNum = integer_to_list(length(Bindings)),
    VarRefsQuery = ui({graph_query, ?Var, references, []}),
    Refs = ui({graph_query, ?Query, exec, [Var, VarRefsQuery]}),
    RefNum = integer_to_list(length(Refs)),
    [{name, {string, VarName}},
     {varbindingnum, {string, BindingNum}},
     {varrefnum, {string, RefNum}}];
info(_, _) ->
    [].

%% @doc Returns with the source code belonging to the given node.
%% This will be appear in the source code text field.
text(Tag) ->
    text(Tag#tag.class, Tag#tag.node).

text(root, _) -> [];
text(variable, _) -> [];
text(module, Mod) ->
    FileQuery = ui({graph_query, ?Mod, file, []}),
    [File] = ui({graph_query, ?Query, exec, [Mod, FileQuery]}),
    ui({syn_tree_text, File});
text(func, Fun) ->
    FunDefQuery = ui({graph_query, ?Fun, definition, []}),
    [Form] = ui({graph_query, ?Query, exec, [Fun, FunDefQuery]}),
    ui({syn_tree_text, Form});
text(record, Rec) ->
    RecQuery = ui({graph_query, ?Rec, form, []}),
    [Form] = ui({graph_query, ?Query, exec, [Rec, RecQuery]}),
    ui({syn_tree_text, Form});
text(_, Node) ->
    ui({syn_tree_text, Node}).

%% @doc Returns with a list of tr records representing the tool 
%% buttons, that are always available from the interface.
tool_buttons() ->
    Undo =
        fun(_) ->
            ReqID = ?UI:getid(),
            request(ReqID,{undo,[]}),
            ReqID
        end,
    Refresh =
        fun(_) ->
            reload
        end,
    Add = 
        fun(Args) ->
            FilePath = proplists:get_value(filename, Args),
            ReqID = ?UI:getid(),
            request(ReqID,{add_dir, FilePath}),
            ReqID
        end,
    Query =
        fun(Args) ->
            StartNode = proplists:get_value(root, Args),
            QueryStr = proplists:get_value(querystr, Args),
            FullQueryStr = 
                case StartNode#tag.class of
                    module -> "@mod";
                    func -> "@fun";
                    variable -> "@var";
                    record -> "@rec";
                    macro -> "@macro";
                    expr -> "@expr";
                    _ -> "mods"
                end ++ 
                case QueryStr of
                    [] -> [];
                    _ -> "."++QueryStr
                end,
            StartOpt = 
                case StartNode#tag.class of
                    root -> [];
                    clause -> [];
                    _ -> 
                        [{node_list, [StartNode#tag.node]}]
                end,
            ReqID = ?UI:getid(),
            request(ReqID,{transform, semantic_query, 
                         [{display_opt,[{positions,linecol}]},
                          {start_opt,StartOpt},
                          {querystr,FullQueryStr}]}),
            ReqID
        end,
    [#tr{label="Add module",
         desc="Add module:",
         back=false,
         func=Add,
         map=true,
         image="add.jpg",
         prop=[{entry, filename, "Filepath: "}]},
    #tr{label="Undo",
        back=undo,
        func=Undo,
        kind=undo,
        image="undo.jpg"},
    #tr{label="Refresh",
        back=false,
        func=Refresh,
        map=false,
        image="refresh.jpg"},
    #tr{label="Run query",
        desc="Run query",
        back=false,
        func=Query,
        map=true,
        prop=[{entry, querystr, "Query: "}],
        image="query.jpg"}].

%% @doc Returns with a list of tr records representing optional 
%% buttons, that are available in different nodes depending on its 
%% class and node types and some other properties.
tr_buttons(#tag{class=variable, node=Var}) ->
    VarBindsQuery = ui({graph_query, ?Var, bindings, []}),
    [Token | _] = ui({graph_query, ?Query, exec, [Var, {seq, VarBindsQuery, [elex]}]}),
    TokenFileQuery = ui({graph_query, ?Token, file, []}),
    [File] = ui({graph_query, ?Query, exec, [Token, TokenFileQuery]}),
    FileName = ui({graph_query, ?File, path, [File]}),
    {Pos, _} = ui({graph_query, ?Token, pos, [Token]}),
    Rename =
        fun(Args) ->
            Name = proplists:get_value(varname, Args),
            ReqID = ?UI:getid(),
            request(ReqID,{transform, rename_var,
                               [{ask_missing, false}, {file, FileName}, 
                                {position, Pos}, {varname, Name}]}),
            ReqID
        end,
    Eliminate =
        fun(_) ->
            ReqID = ?UI:getid(),
            request(ReqID,{transform, elim_var,
                               [{ask_missing, false}, {file, FileName}, 
                                {position, Pos}]}),
            ReqID
        end,
    [#tr{label="Rename",
         map=true,
         desc="Rename Variable",
         prop=[{entry, varname, "New name: "}],
         back=true,
         func=Rename,
         image="rename.jpg"},
     #tr{label="Eliminate",
         back=true,
         func=Eliminate,
         image="eliminate.jpg"}];
tr_buttons(#tag{class=func, node=Fun}) ->
    FunModQuery = ui({graph_query, ?Fun, module, []}),
    [Mod] = ui({graph_query, ?Query, exec, [Fun, FunModQuery]}),
    ModName = ui({graph_query, ?Mod, name, [Mod]}),
    ModFileQuery = ui({graph_query, ?Mod, file, []}),
    ModFile = hd(ui({graph_query, ?Query, exec, [Mod, ModFileQuery]})),
    FileName = ui({graph_query, ?File, path, [ModFile]}),
    FunName = ui({graph_query, ?Fun, name, [Fun]}),
    Arity = ui({graph_query, ?Fun, arity, [Fun]}),
    FunDefQuery = ui({graph_query, ?Fun, definition, []}),
    FormClausesQuery = ui({graph_query, ?Form, clauses, []}),
    FirstClause = 
        hd(ui({graph_query, ?Query, exec, [Fun, {seq, FunDefQuery, FormClausesQuery}]})),
    ClausePatternsQuery = ui({graph_query, ?Clause, patterns, []}),
    Patterns = ui({graph_query, ?Query, exec, [FirstClause, ClausePatternsQuery]}),
    Rename =
        fun(Args) ->
            Name = proplists:get_value(name, Args),
            ReqID = ?UI:getid(),
            request(ReqID,{transform, rename_fun,
                               [{ask_missing, false}, {module, ModName}, 
                                {function, FunName}, {arity, Arity}, 
                                {name, Name}]}),
            ReqID
        end,
    Move =
        fun(Args) ->
            Name = proplists:get_value(name, Args),
            ReqID = ?UI:getid(),
            request(ReqID,{transform, move_fun,
                               [{ask_missing, false}, {file, FileName}, 
                                {funlist, [{FunName, Arity}]}, {name, Name}]}),
            ReqID
        end,
    Reorder =
        fun(Args) ->
            Order = [ case proplists:get_value(?ia(X), Args) of
                          [] -> 0;
                              N -> list_to_integer(N)
                          end || X <- ?MISC:seq2(1, Arity) ],
            ReqID = ?UI:getid(),
            request(ReqID,{transform, reorder_funpar,
                               [{ask_missing, false}, {module, ModName}, 
                                {function, FunName}, {arity, Arity}, 
                                {order, Order}]}),
            ReqID
        end,
    Tuple =
        fun(_) ->
            P1 = hd(Patterns),
            Pn = lists:last(Patterns),
            FstLex = hd(ui({graph_query, ?Query, exec, [P1, [elex]]})),
            LstLex = hd(ui({graph_query, ?Query, exec, [Pn, [elex]]})),
            {First, _} = ui({graph_query, ?Token, pos, [FstLex]}),
            {_, Last} = ui({graph_query, ?Token, pos, [LstLex]}),
            ReqID = ?UI:getid(),
            request(ReqID,{transform, tuple_funpar,
                               [{ask_missing, false}, {file, FileName}, 
                                {posrange, {First, Last}}]}),
            ReqID
        end,
    [#tr{label="Rename",
         map=true,
         desc="Rename Function",
         prop=[{entry, name, "New name: "}],
         back=true,
         func=Rename,
         image="rename.jpg"},
     #tr{label="Move",
         map=true,
         desc="Move Function",
         prop=[{entry, name, "Module: "}],
         back=true,
         func=Move,
         image="move.jpg"}] ++
        case Patterns of
            [] -> [];
            _ ->
                [#tr{label="Tuple",
                     back=true,
                     func=Tuple,
                     image="tuple.jpg"}]
        end ++
        if
            length(Patterns) < 2 -> [];
            true ->
                [#tr{label="Reorder",
                     map=true,
                     desc="Reorder Function Parameters",
                     prop=
                        [ {entry, ?ia(X), "Argument " 
                            ++ integer_to_list(X) 
                            ++ "'s new pos.: "} 
                        || X <- ?MISC:seq2(1, Arity)],
                     func=Reorder,
                     image="reorder.jpg"}] 
        end;
tr_buttons(#tag{class=macro, node=Macro}) ->
    FileQuery = ui({graph_query, ?Macro, file, []}),
    FileNode = hd(ui({graph_query, ?Query, exec, [Macro, FileQuery]})),
    File = ui({graph_query, ?File, path, [FileNode]}),
    MacName = ui({graph_query, ?Macro, name, [Macro]}),
    Rename =
        fun(Args) ->
            Name = proplists:get_value(name, Args),
            ReqID = ?UI:getid(),
            request(ReqID,{transform, rename_mac,
                               [{ask_missing, false}, {file, File}, 
                                {macro, MacName}, {macname, Name}]}),
            ReqID	
        end,
    [#tr{label="Rename",
         map=true,
         desc="Rename macro",
         prop=[{entry, name, "New name: "}],
         back=true,
         func=Rename,
         image="rename.jpg"}];
tr_buttons(#tag{class=module, node=Mod}) ->
    FileQuery = ui({graph_query, ?Mod, file, []}),
    FileNode = hd(ui({graph_query, ?Query, exec, [Mod, FileQuery]})),
    FileName = ui({graph_query, ?File, path, [FileNode]}),
	ModName = ui({graph_query, ?Mod, name, [Mod]}),
    Rename =
        fun(Args) ->
            Name = proplists:get_value(name, Args),
            ReqID = ?UI:getid(),
            request(ReqID,{transform, rename_mod,
                               [{ask_missing, false}, {file, FileName}, 
                                {module, ModName}, {name, Name}]}),
            ReqID
        end,
    Drop =
        fun(_Args) -> 
            ReqID = ?UI:getid(),
            request(ReqID, {drop, FileName}),
            ReqID
        end,
    [#tr{label="Rename",
         map=true,
         desc="Rename Module",
         prop=[{entry, name, "New name: "}],
         back=true,
         func=Rename,
         image="rename.jpg"},
     #tr{label="Drop",
         map=false,
         back=true,
         func=Drop,
         image="remove.jpg"}];
tr_buttons(#tag{class=expr, kind=implicit_fun, node=Expr}) ->
    ExprClauseQuery = ui({graph_query, ?Expr, clause, []}),
    ClauseFormQuery = ui({graph_query, ?Clause, form, []}),
    FormFileQuery = ui({graph_query, ?Form, file, []}),
    Seq1 = ui({graph_query, ?Query, seq, [[ExprClauseQuery, ClauseFormQuery]]}),
    Seq2 = ui({graph_query, ?Query, seq, [[Seq1, FormFileQuery]]}),
    [File] = ui({graph_query, ?Query, exec, [Expr, Seq2]}),
    FileName = ui({graph_query, ?File, path, [File]}),
    Token = hd(ui({graph_query, ?Query, exec, [Expr, [elex]]})),
    {Pos, _} = ui({graph_query, ?Token, pos, [Token]}),
    Expand =
        fun(_) ->
            ReqID = ?UI:getid(),
            request(ReqID,{transform, expand_funexpr,
                               [{ask_missing, false}, {file, FileName}, 
                                {position, Pos}]}),
            ReqID
        end,
    [#tr{label="Expand",
         func=Expand,
         image="expand.jpg"}];
tr_buttons(#tag{class=expr, kind=application, node=Expr}) ->
    ExprClauseQuery = ui({graph_query, ?Expr, clause, []}),
    ClauseFormQuery = ui({graph_query, ?Clause, form, []}),
    FormFileQuery = ui({graph_query, ?Form, file, []}),
    Seq1 = ui({graph_query, ?Query, seq, [[ExprClauseQuery, ClauseFormQuery]]}),
    Seq2 = ui({graph_query, ?Query, seq, [[Seq1, FormFileQuery]]}),
    [File] = ui({graph_query, ?Query, exec, [Expr, Seq2]}),
    ExprChldrnQuery = ui({graph_query, ?Expr, children, []}),
    FileName = ui({graph_query, ?File, path, [File]}),
    Token = hd(ui({graph_query, ?Query, exec, [Expr, {seq, ExprChldrnQuery, [elex]}]})),
    {Pos, _} = ui({graph_query, ?Token, pos, [Token]}),
    Inline =
        fun(_) ->
            ReqID = ?UI:getid(),
            request(ReqID,{transform, inline_fun,
                               [{ask_missing, false}, {file, FileName}, 
                                {position, Pos}]}),
            ReqID
        end,
    [#tr{label="Inline",
         back=true,
         func=Inline,
         image="inline.jpg"}];
tr_buttons(_) ->
    [].
    
    
%% UI
ui(NameArgs)->
    ReqID = ?UI:getid(),
    Fun = fun() -> ui_loop(ReqID) end,
    request(ReqID,NameArgs,Fun).
    
ui_loop(ReqID)->
    receive
        {ReqID, reply, R} -> 
             case R of
                {ok, Res} -> Res;
                X         -> X
             end;
         {ReqID, _, _} ->
             ui_loop(ReqID)       
%        {ReqID, query_id, QueryId}->
%            ui_loop(ReqID);       
%        {ReqID, progress, {add, _File, _, _Max}} ->
%            ui_loop(ReqID);
%        {ReqID, progress, {drop, _File, _, _Max}} ->
%            ui_loop(ReqID, Args);
%        {ReqID, progress, {add, File, Percent, FormCount, FormMax, KBps}} ->
%            ui_loop(ReqID);
%        {ReqID, progress, {drop, File, _Percent, FormCount, FormMax, KBps}} ->
%            ui_loop(ReqID)
    end.

request(ReqID,NameArgs) ->  
    request(ReqID,NameArgs,fun() -> ok end).  
request(ReqID,NameArgs,FunToRun) ->
    case ?UI:request(ReqID,NameArgs) of
        ok -> FunToRun();
        deny -> throw(request_denied)
    end.
