]> git.lyx.org Git - lyx.git/blob - development/tools/scgen.pl
ws change
[lyx.git] / development / tools / scgen.pl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 % Prolog
3 %
4 % Shortcut generator v1.0
5 %
6 % This program is known to work with SWI-Prolog version 2.9.5, which you
7 % can find at
8 %
9 %    http://www.swi.psy.uva.nl/usr/jan/SWI-Prolog.html
10 %
11 % Purpose: to generate shortcuts for labels in menus and dialogs that are
12 % guaranteed to be unique within a set of labels. The shortcuts are
13 % generated in a prioritized manner, such that characters at the beginning
14 % of words are preferred to characters in the middle of words.
15 %
16 % You might find this progam useful if you are translating LyX.
17 %
18 % Don't use this file in the development/tools-directory; copy it
19 % somewhere out of the LyX-distribution tree, unless you're improving
20 % the tool itself.  The input is to be hard-coded into the file and that
21 % may cause unnecessary garbage to appear in any patch you're putting
22 % together.
23 %
24 % The labels are supposed to be entered at "LABELS" below,
25 % and you get one solution (hopefully) with "go.". 
26 % If there are no solutions, the Prolog-interpretator will reply with a
27 % "No."
28 %
29 % You can get all candidate solutions with "all.", but this requires
30 % lots of memory.
31 %
32 % You can inspect the correspondance between the original strings and
33 % the prioritized ones with "inspect.".
34 %
35 % I have no idea what the big O for the algorithm is, but I suspect it's
36 % rather steep.
37 % Asger speculates that the algorithm is O(n^m), where n is the cardinality
38 % of the candidate sets, while m is the number of sets. Since we do an
39 % exhausitive search, this has to be the case.
40
41 % Predicates:
42
43 % print out one solution, i.e. a set of label/shortcut's
44 % writef/2 may be specific for SWI-prolog
45 show_one_alternative([]).
46 show_one_alternative([L/_/C|Rest]):-
47   writef('%s|#%n\n',[L,C]),
48   show_one_alternative(Rest).
49
50 % printout predicate for "all."
51 show_solutions([]).
52 show_solutions([H|T]):-
53   write('-----------\n'),
54   show_one_alternative(H),
55   show_solutions(T).
56
57 % print out correspondance between original strings and prioritized ones.
58 show_priority([]).
59 show_priority([L/P/_|Rest]):-
60   writef('%s|%n\n',[L,P]),
61   show_priority(Rest).
62
63 % character is from a set of allowed characters.
64 % "AZaz09"=[65, 90, 97, 122, 48, 57]
65 allowed_char(C):-
66   C >= 97, C =< 122. % a-z
67 allowed_char(C):-
68   C >= 65, C =< 90. % A-Z
69 allowed_char(C):-
70   C >= 48, C =< 57. % 0-9
71
72 % turn lowercase to uppercase; alt-<key> is case insensitive
73 uppercase(L,U):-
74   (L >= 97, L =< 122) -> U is L - 32;
75   U is L.
76
77 % possible_char/2: Gets all characters in label, one after one.
78 possible_char(_/Label,Char):-
79   member(Char,Label).           % the character is part of the label
80
81 % prepare_labels/2: Prepares all labels. Constructs a new list of pairs 
82 % where the original string is coupled with the prepared string.
83 prepare_labels([], []).
84 prepare_labels([H1|T1], [H1/H2|T2]):-
85         prepare_string(H1, H2),
86         prepare_labels(T1, T2).
87
88 % prepare_string/2: Prepares a string by removing duplicate characters,
89 % prioritizing initials letters, removing illegal characters and turning
90 % lowercase to uppercase characters.
91 prepare_string(Label,Result):-
92   string_to_list(Label,List1),  % make a list of the string
93   prioritize(List1, List2),     % Prioritize string
94   filter_chars(List2, List3),   % Filter out unwanted chars
95   unique(List3, Result).        % Remove duplicates
96
97 % prioritize/2: This predicate rearranges a list, such that
98 % chars at the beginning of words are put first in the list.
99 % i.e. prioritize("Foo bar", "Fboo ar")" is true.
100 prioritize(L1,L2):-
101         initial_chars(L1, I),   % Find all initial characters
102         subtract(L1, I, Rest),  % and the others
103         append(I, Rest, L2).    % and we have the result.
104
105 % initial_chars/2: Returns a list of characters that appear at the beginning
106 % of words. i.e. initial_chars("Foo bar", "Fb") is true.
107 initial_chars([],[]).
108 initial_chars([A|T1], [A|T3]):-
109         rest_after_space(T1, T2),       % Return rest of list after space
110         initial_chars(T2, T3).
111
112 % rest_after_space/2: Returns the list after the first space.
113 % i.e. "rest_after_space("Foo bar", "bar") is true.
114 rest_after_space([], []).
115 rest_after_space([32, H1|T1], [H1|T1]):- !.
116 rest_after_space([_|T1], T2):-
117         rest_after_space(T1, T2).
118
119 % filterchars/2: Filter outs non-allowed characters from list, and turns
120 % lowercase to uppercase.
121 filter_chars([], []).
122 filter_chars([H|T1], [C|T2]):-
123         allowed_char(H), !, uppercase(H, C),
124         filter_chars(T1, T2).
125 filter_chars([_|T1], T2):-
126         filter_chars(T1, T2).
127
128 % unique/2: This predicate removes duplicate characters without reordering.
129 % i.e. unique("Foo bar", "Fo bar") is true.
130 unique([], []).
131 unique([H|T1], [H|T3]):-
132         delete(T1, H, T2),      % Remove duplicates.
133         unique(T2,T3).
134
135 % working_shortcuts/2 instantiates Char in the first argument for a whole
136 % menu/dialog.
137 working_shortcuts([],_). % end of the list
138
139 working_shortcuts([Label/Char|Rest],Used):-
140   possible_char(Label,Char), % i.e. part of the label string
141   \+member(Char,Used),  % not used by any other shortcut
142   working_shortcuts(Rest,[Char|Used]). % and all the other labels have
143                                        % shortcuts
144 % The prolog motor will backtrack up and down the list of labels
145 % until it finds a set with unique characters for each label
146
147
148 % Label strings goes here.
149 % Just cut&paste the strings from the LyX-source, and remove any
150 % control sequences for menus.  We could add a couple of predicates to scan
151 % a file, but re-consulting this file takes only a fraction of a second
152 % so I didn't bother to add a fancy user interface here.  The solution is
153 % printed like <label> '|#' <key>, allthough this isn't nessecarily useful,
154 % for menus for instance.
155 %
156 % The characters are picked with priority from left to right within
157 % strings, and the labels are listed in falling priority.
158 % If you want a certain label to have a certain shortcut, try adding that
159 % character in front of the string (like 'Exit' -> 'xExit') and move it
160 % higher up in the list.
161 % If this doesn't work, replace the string with only that character
162 % ('Exit' -> 'x'). If you get a "No." then, you lose.
163 % Use "inspect." to inspect the resulting priotized strings.
164
165 % LABELS
166 lab(Labels):-
167   Strings = [
168 "New...",
169 "New from template...",
170 "Open...",
171 "Close",
172 "Save",
173 "Save As...",
174 "Revert to saved",
175 "View dvi",
176 "View PostScript",
177 "Update dvi",
178 "Update PostScript",
179 "Print...",
180 "Fax...",
181 "Export",
182 "Exit"
183 % NB, no comma on the last one. Easy to forget.
184   ],
185   prepare_labels(Strings, Str),
186   bagof(L/_,member(L,Str),Labels).
187
188 % Inspect mapping between original string and prioritized ones.
189 inspect:-
190         lab(Labels),
191         show_priority(Labels).
192
193 % Find ALL solutions (they are often legion - don't bother ;)
194 all:- % May (probably, on large dialogs) run out on memory.
195   lab(Labels),
196   setof(Labels,working_shortcuts(Labels,[]),Solutions),
197   show_solutions(Solutions).
198
199 % Find one solution
200 one:-
201   lab(Labels),
202   working_shortcuts(Labels,[]),
203   show_one_alternative(Labels).
204
205 % Entry point.
206 go:-
207   one.