1 /*
2 * Copyright (c) 2003-2004 the xdvik development team
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 */
23
24 /*
25 * Common code for Xaw and Motif menu bar creation.
26 */
27
28 #include "xdvi-config.h"
29 #include "xdvi.h"
30 #include "events.h"
31 #include "menu.h"
32 #include "util.h"
33
34 #ifdef MOTIF
35 # include <Xm/RowColumn.h>
36 # include <Xm/ToggleB.h>
37 #else
38 # include <X11/Intrinsic.h>
39 # include <X11/Xatom.h>
40 # include <X11/StringDefs.h>
41 # include <X11/Xaw/Label.h>
42 #endif
43
44
45 /* translate string argument into corresponding buttonTypeT */
46 static buttonTypeT
get_type(const char * str)47 get_type(const char *str)
48 {
49 if (strcmp(str, "PUSH") == 0)
50 return BT_PUSH;
51 else if (strcmp(str, "RADIO") == 0)
52 return BT_RADIO;
53 else if (strcmp(str, "CHECK") == 0)
54 return BT_CHECK;
55 else if (strcmp(str, "SEP") == 0)
56 return BT_SEP;
57 else
58 return BT_INVALID;
59 }
60
61 static struct button_info *m_button_info = NULL; /* toplevel node of pulldown menu structure */
62
63
64 static void
set_menu_info(void * val,XtActionProc proc,Boolean (* cmp)(),struct button_info * item)65 set_menu_info(void *val, XtActionProc proc, Boolean (*cmp)(), struct button_info *item)
66 {
67 size_t i;
68 ASSERT(item != NULL, "item in set_menu_info musn't be NULL!");
69 for (i = 0; i < item->size; i++) {
70 if ((item->elems[i].type == BT_RADIO || item->elems[i].type == BT_CHECK)
71 && item->elems[i].action != NULL
72 && item->elems[i].action->proc != NULL
73 && item->elems[i].action->proc == proc
74 && item->elems[i].action->num_params > 0
75 && item->elems[i].action->params[0] != NULL) {
76 Boolean state;
77 ASSERT(cmp != NULL, "comparison function musn't be NULL!");
78 state = cmp(val, item->elems[i].action->params[0]);
79 #ifdef MOTIF
80 ASSERT(item->elems[i].widget != 0, "Widget musn't be NULL!");
81 XmToggleButtonSetState(item->elems[i].widget, state, False);
82 #else
83 if (item->elems[i].widget == 0) {
84 XDVI_WARNING((stderr, "Widget for menu `%s' is null!", item->elems[i].title));
85 continue;
86 }
87 xaw_set_button_state(item->elems + i, state);
88 #endif
89 }
90 if (item->elems[i].submenu != NULL) { /* invoke it recursively */
91 set_menu_info(val, proc, cmp, item->elems[i].submenu);
92 }
93 }
94 }
95
96
97 /* set a menu according to val and the compare function cmp */
98 void
set_menu(void * val,XtActionProc proc,Boolean (* cmp)())99 set_menu(void *val, XtActionProc proc, Boolean (*cmp)())
100 {
101 /* removed following since cast from function pointer to void pointer is not supported by ANSI C */
102 /* TRACE_GUI((stderr, "set_menu_info: %d, %p, %p", *(int *)val, (void *)proc, (void *)cmp)); */
103 set_menu_info(val, proc, cmp, m_button_info);
104 }
105
106 static void
initialize_menus(void)107 initialize_menus(void)
108 {
109 int use_gs;
110 int shrinkval;
111
112 /* initialize tickmarks for all possible actions */
113 use_gs = resource.postscript;
114 #ifdef PS_GS
115 if (!resource.useGS)
116 use_gs = 0;
117 #endif
118
119 set_menu(&use_gs, Act_set_ps, check_int);
120 #ifdef PS_GS
121 set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle);
122 #endif
123 set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle);
124 shrinkval = resource.pixels_per_inch / mane.shrinkfactor;
125 set_menu(&shrinkval, Act_shrink_to_dpi, check_int);
126 set_menu(&mane.shrinkfactor, Act_set_shrink_factor, check_int);
127 set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle);
128 #if 0
129 set_menu((char *)resource.paper, Act_set_paper_landscape, check_paper_landscape);
130 set_menu((char *)resource.paper, Act_set_papersize, check_papersize);
131 #endif /* 0 */
132 set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
133 set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert);
134 }
135
136 static void
free_items(char ** items,size_t len)137 free_items(char **items, size_t len)
138 {
139 size_t curr = 0;
140 while(curr < len) {
141 free(items[curr++]);
142 }
143 free(items);
144 }
145
146 #if 0
147 static void
148 show_items(char *descr, char **items, size_t len)
149 {
150 size_t i;
151 for (i = 0; i < len; i++) {
152 fprintf(stderr, "%s %d: |%s|\n", descr, i, items[i]);
153 }
154 }
155 #endif
156
157 static void
add_info(struct button_info ** info,buttonTypeT bt_type,char mnemonic,const char * title,const char * accelerator,struct xdvi_action * action)158 add_info(struct button_info **info, buttonTypeT bt_type,
159 char mnemonic, const char *title,
160 const char *accelerator, struct xdvi_action *action)
161 {
162 size_t idx = (*info)->size++;
163 (*info)->elems = xrealloc((*info)->elems, (*info)->size * sizeof *((*info)->elems));
164 (*info)->elems[idx].title = xstrdup(title);
165 (*info)->elems[idx].type = bt_type;
166 if (accelerator == NULL || accelerator[0] == '\0')
167 (*info)->elems[idx].accelerator = NULL;
168 else
169 (*info)->elems[idx].accelerator = xstrdup(accelerator);
170 (*info)->elems[idx].mnemonic = mnemonic;
171 (*info)->elems[idx].action = action;
172 (*info)->elems[idx].widget = 0;
173 (*info)->elems[idx].submenu = NULL;
174 }
175
176 static void
insert_items(struct button_info ** info,char ** items,size_t num_items,const char * button_type,const char * accelerator,const char * action)177 insert_items(struct button_info **info, char **items, size_t num_items,
178 const char *button_type, const char *accelerator, const char *action)
179 {
180 const char ENTRY_SEP = '|';
181 size_t i = 0;
182 size_t entry_len;
183 size_t entry_count = 0;
184 char **entry_items = NULL;
185 size_t idx;
186
187 Boolean found = False;
188 Boolean have_error = False;
189
190 if (*items == NULL) {
191 /* should be a separator, which is treated as a special case since
192 there's no menu title: */
193 if (strcmp (button_type, "SEP") == 0)
194 add_info(info, BT_SEP, '\0', "SEP", NULL, NULL);
195 else
196 XDVI_WARNING((stderr, "Shouldn't happen: items == NULL!"));
197 return;
198 }
199
200 entry_len = strlen(items[0]);
201 entry_items = split_line(items[0], ENTRY_SEP, 0, entry_len, &entry_count);
202
203 if (entry_count < 2) {
204 XDVI_WARNING((stderr, "Missing Mnemonic in button info `%s'", items[0]));
205 entry_count++;
206 entry_items = xrealloc(entry_items, entry_count * sizeof *entry_items);
207 entry_items[1] = xstrdup("");
208 entry_items[2] = NULL;
209 }
210
211 for (i = 0; i < (*info)->size; i++) {
212 if (strcmp(entry_items[0], (*info)->elems[i].title) == 0) {
213 found = true;
214 break;
215 }
216 }
217
218 idx = i;
219
220 if (!found) { /* new item, resize info and add this item */
221 struct xdvi_action *my_action = NULL;
222 buttonTypeT my_type = BT_NONE;
223
224 /* if it's a `leaf' in the menu hierarchy, compile the action and set the button type */
225 if (num_items == 1) {
226 char *fmt = strchr(entry_items[0], '$');
227 if (fmt != NULL
228 && (fmt == entry_items[0] || (fmt > entry_items[0] && *(fmt - 1) != '\\'))
229 && (fmt[1] == '#' || fmt[1] == '%' ||fmt[1] == '_')) {
230 XDVI_WARNING((stderr, "Xdvik doesn't support format characters in button labels; "
231 "skipping button \"%s\"", items[0]));
232 have_error = True;
233 }
234
235 if (strlen(action) == 0 || (!compile_action(action, &my_action))) {
236 XDVI_WARNING((stderr, "Invalid action \"%s\" for button \"%s\" (skipping this line).",
237 action, items[0]));
238 have_error = True;
239 }
240
241 if ((my_type = get_type(button_type)) == BT_INVALID) {
242 XDVI_WARNING((stderr, "Invalid type \"%s\" for button \"%s\" (skipping this line).",
243 button_type, items[0]));
244 have_error = True;
245 }
246 }
247
248 if (!have_error) {
249 add_info(info, my_type, entry_items[1][0], entry_items[0], accelerator, my_action);
250 }
251 }
252 free_items(entry_items, entry_count);
253
254 if (num_items > 1 || (num_items == 1 && strcmp(button_type, "SEP") == 0)) { /* not a leaf, invoke recursivly for next level */
255 if ((*info)->elems[idx].submenu == NULL) { /* submenu didn't exist yet, create it */
256 struct button_info *new_submenu = xmalloc(sizeof *new_submenu);
257 new_submenu->elems = NULL;
258 new_submenu->size = 0;
259 (*info)->elems[idx].submenu = new_submenu;
260 }
261 insert_items(&((*info)->elems[idx].submenu), items + 1, num_items - 1, button_type, accelerator, action);
262 }
263 }
264
265 static void
show_button_info(int depth,struct button_info * info)266 show_button_info(int depth, struct button_info *info)
267 {
268 size_t i;
269 for (i = 0; i < info->size; i++) {
270 TRACE_GUI((stderr, "%*c-->%s; type=%d; mnemonic=%c; accel=%s; submenu=%p; w=%lu; action: %p",
271 depth, ' ',
272 info->elems[i].title,
273 info->elems[i].type,
274 info->elems[i].mnemonic,
275 info->elems[i].accelerator ? info->elems[i].accelerator : "<NULL>",
276 (void *)info->elems[i].submenu,
277 (unsigned long)info->elems[i].widget,
278 (void *)info->elems[i].action));
279 if (info->elems[i].submenu != NULL) {
280 show_button_info(depth + 3, info->elems[i].submenu);
281 }
282 }
283 }
284
285 static void
parse_button_translations(struct button_info ** info)286 parse_button_translations(struct button_info **info)
287 {
288 const char *curr_p, *end_p;
289
290 const char LINE_SEP = ':';
291 const char MENU_SEP = '>';
292
293 for (curr_p = resource.menu_translations;
294 curr_p != NULL && *curr_p != '\0';
295 curr_p = end_p + 1) {
296 end_p = strchr(curr_p, '\n');
297 if (end_p != NULL) {
298 size_t line_len = end_p - curr_p;
299 size_t line_count = 0;
300 char **line_items;
301 line_items = split_line(curr_p, LINE_SEP, 0, line_len, &line_count);
302 /* fprintf(stderr, "length of line: %d; %d items\n", line_len, line_count); */
303 /* show_items("LINE", line_items, line_count); */
304 if (line_count != 4) { /* error */
305 XDVI_WARNING((stderr, "Wrong number of items (%lu) in translations line:\n\"%.*s\" "
306 "(skipping this line).\n",
307 (unsigned long)line_count, (int)line_len, curr_p));
308 free_items(line_items, line_count);
309 continue;
310 }
311 else { /* split first elem into menu description */
312 size_t menu_len = strlen(line_items[0]);
313 size_t menu_count = 0;
314 char **menu_items;
315 if (menu_len == 0) { /* error */
316 XDVI_WARNING((stderr, "Menu description (first item) mustn't be empty:\n\"%.*s\" "
317 "(skipping this line).\n",
318 (int)line_len, curr_p));
319 free_items(line_items, line_count);
320 continue;
321 }
322 menu_items = split_line(line_items[0], MENU_SEP, 0, menu_len, &menu_count);
323 /* show_items(" MENU", menu_items, menu_count); */
324
325 insert_items(info, menu_items, menu_count, line_items[1], line_items[2], line_items[3]);
326 free_items(menu_items, menu_count);
327 }
328 free_items(line_items, line_count);
329 }
330 }
331 show_button_info(0, *info);
332 }
333
334 /*
335 Top-level routine: creates the pulldown menu buttons for Motif and Xaw.
336 For Motif, sets `*menu_bar' to the address of the new widget created,
337 for Xaw, sets *width to the width of the button panel created.
338 */
339 void
create_menu_buttons(Widget parent,Widget * menu_bar)340 create_menu_buttons(Widget parent,
341 #ifdef MOTIF
342 Widget *menu_bar
343 #else
344 int *width
345 #endif
346 )
347 {
348 #ifdef MOTIF
349 Widget menu = 0;
350 size_t i;
351 #else /* MOTIF */
352 Widget panel = 0;
353 #endif
354
355 m_button_info = xmalloc(sizeof *m_button_info);
356 m_button_info->elems = NULL;
357 m_button_info->size = 0;
358
359 #ifdef MOTIF
360 *menu_bar = XmCreateMenuBar(parent, "menuBar", NULL, 0);
361 parse_button_translations(&m_button_info);
362
363 for (i = 0; i < m_button_info->size; i++) {
364 menu = xm_create_menu(*menu_bar,
365 m_button_info->elems[i].title,
366 m_button_info->elems[i].mnemonic,
367 m_button_info->elems[i].submenu);
368 }
369 if (menu != 0) {
370 XtVaSetValues(*menu_bar, XmNmenuHelpWidget, menu, NULL);
371 }
372 #else /* MOTIF */
373 xaw_initialize_menu_bitmaps();
374 panel = xaw_create_menu_widgets(parent);
375 parse_button_translations(&m_button_info);
376 xaw_create_menu(m_button_info, panel, width);
377 #endif /* MOTIF */
378 initialize_menus();
379 }
380
381