lyx_mirror/development/tools/scgen.pl
Lars Gullik Bjønnes 27de1486ca Initial revision
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@140 a592a061-630c-0410-9148-cb99ea01b6c8
1999-09-27 18:44:28 +00:00

208 lines
6.7 KiB
Prolog

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Prolog
%
% Shortcut generator v1.0
%
% This program is known to work with SWI-Prolog version 2.9.5, which you
% can find at
%
% http://www.swi.psy.uva.nl/usr/jan/SWI-Prolog.html
%
% Purpose: to generate shortcuts for labels in menus and dialogs that are
% guaranteed to be unique within a set of labels. The shortcuts are
% generated in a prioritized manner, such that characters at the beginning
% of words are preferred to characters in the middle of words.
%
% You might find this progam useful if you are translating LyX.
%
% Don't use this file in the development/tools-directory; copy it
% somewhere out of the LyX-distribution tree, unless you're improving
% the tool itself. The input is to be hard-coded into the file and that
% may cause unnecessary garbage to appear in any patch you're putting
% together.
%
% The labels are supposed to be entered at "LABELS" below,
% and you get one solution (hopefully) with "go.".
% If there are no solutions, the Prolog-interpretator will reply with a
% "No."
%
% You can get all candidate solutions with "all.", but this requires
% lots of memory.
%
% You can inspect the correspondance between the original strings and
% the prioritized ones with "inspect.".
%
% I have no idea what the big O for the algorithm is, but I suspect it's
% rather steep.
% Asger speculates that the algorithm is O(n^m), where n is the cardinality
% of the candidate sets, while m is the number of sets. Since we do an
% exhausitive search, this has to be the case.
% Predicates:
% print out one solution, i.e. a set of label/shortcut's
% writef/2 may be specific for SWI-prolog
show_one_alternative([]).
show_one_alternative([L/_/C|Rest]):-
writef('%s|#%n\n',[L,C]),
show_one_alternative(Rest).
% printout predicate for "all."
show_solutions([]).
show_solutions([H|T]):-
write('-----------\n'),
show_one_alternative(H),
show_solutions(T).
% print out correspondance between original strings and prioritized ones.
show_priority([]).
show_priority([L/P/_|Rest]):-
writef('%s|%n\n',[L,P]),
show_priority(Rest).
% character is from a set of allowed characters.
% "AZaz09"=[65, 90, 97, 122, 48, 57]
allowed_char(C):-
C >= 97, C =< 122. % a-z
allowed_char(C):-
C >= 65, C =< 90. % A-Z
allowed_char(C):-
C >= 48, C =< 57. % 0-9
% turn lowercase to uppercase; alt-<key> is case insensitive
uppercase(L,U):-
(L >= 97, L =< 122) -> U is L - 32;
U is L.
% possible_char/2: Gets all characters in label, one after one.
possible_char(_/Label,Char):-
member(Char,Label). % the character is part of the label
% prepare_labels/2: Prepares all labels. Constructs a new list of pairs
% where the original string is coupled with the prepared string.
prepare_labels([], []).
prepare_labels([H1|T1], [H1/H2|T2]):-
prepare_string(H1, H2),
prepare_labels(T1, T2).
% prepare_string/2: Prepares a string by removing duplicate characters,
% prioritizing initials letters, removing illegal characters and turning
% lowercase to uppercase characters.
prepare_string(Label,Result):-
string_to_list(Label,List1), % make a list of the string
prioritize(List1, List2), % Prioritize string
filter_chars(List2, List3), % Filter out unwanted chars
unique(List3, Result). % Remove duplicates
% prioritize/2: This predicate rearranges a list, such that
% chars at the beginning of words are put first in the list.
% i.e. prioritize("Foo bar", "Fboo ar")" is true.
prioritize(L1,L2):-
initial_chars(L1, I), % Find all initial characters
subtract(L1, I, Rest), % and the others
append(I, Rest, L2). % and we have the result.
% initial_chars/2: Returns a list of characters that appear at the beginning
% of words. i.e. initial_chars("Foo bar", "Fb") is true.
initial_chars([],[]).
initial_chars([A|T1], [A|T3]):-
rest_after_space(T1, T2), % Return rest of list after space
initial_chars(T2, T3).
% rest_after_space/2: Returns the list after the first space.
% i.e. "rest_after_space("Foo bar", "bar") is true.
rest_after_space([], []).
rest_after_space([32, H1|T1], [H1|T1]):- !.
rest_after_space([_|T1], T2):-
rest_after_space(T1, T2).
% filterchars/2: Filter outs non-allowed characters from list, and turns
% lowercase to uppercase.
filter_chars([], []).
filter_chars([H|T1], [C|T2]):-
allowed_char(H), !, uppercase(H, C),
filter_chars(T1, T2).
filter_chars([_|T1], T2):-
filter_chars(T1, T2).
% unique/2: This predicate removes duplicate characters without reordering.
% i.e. unique("Foo bar", "Fo bar") is true.
unique([], []).
unique([H|T1], [H|T3]):-
delete(T1, H, T2), % Remove duplicates.
unique(T2,T3).
% working_shortcuts/2 instantiates Char in the first argument for a whole
% menu/dialog.
working_shortcuts([],_). % end of the list
working_shortcuts([Label/Char|Rest],Used):-
possible_char(Label,Char), % i.e. part of the label string
\+member(Char,Used), % not used by any other shortcut
working_shortcuts(Rest,[Char|Used]). % and all the other labels have
% shortcuts
% The prolog motor will backtrack up and down the list of labels
% until it finds a set with unique characters for each label
% Label strings goes here.
% Just cut&paste the strings from the LyX-source, and remove any
% control sequences for menus. We could add a couple of predicates to scan
% a file, but re-consulting this file takes only a fraction of a second
% so I didn't bother to add a fancy user interface here. The solution is
% printed like <label> '|#' <key>, allthough this isn't nessecarily useful,
% for menus for instance.
%
% The characters are picked with priority from left to right within
% strings, and the labels are listed in falling priority.
% If you want a certain label to have a certain shortcut, try adding that
% character in front of the string (like 'Exit' -> 'xExit') and move it
% higher up in the list.
% If this doesn't work, replace the string with only that character
% ('Exit' -> 'x'). If you get a "No." then, you lose.
% Use "inspect." to inspect the resulting priotized strings.
% LABELS
lab(Labels):-
Strings = [
"New...",
"New from template...",
"Open...",
"Close",
"Save",
"Save As...",
"Revert to saved",
"View dvi",
"View PostScript",
"Update dvi",
"Update PostScript",
"Print...",
"Fax...",
"Export",
"Exit"
% NB, no comma on the last one. Easy to forget.
],
prepare_labels(Strings, Str),
bagof(L/_,member(L,Str),Labels).
% Inspect mapping between original string and prioritized ones.
inspect:-
lab(Labels),
show_priority(Labels).
% Find ALL solutions (they are often legion - don't bother ;)
all:- % May (probably, on large dialogs) run out on memory.
lab(Labels),
setof(Labels,working_shortcuts(Labels,[]),Solutions),
show_solutions(Solutions).
% Find one solution
one:-
lab(Labels),
working_shortcuts(Labels,[]),
show_one_alternative(Labels).
% Entry point.
go:-
one.