1 /*
2 * Copyright (C) 2000-2019 the xine project
3 *
4 * This file is part of xine, a unix video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 *
20 */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/keysym.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <assert.h>
35
36 #include "common.h"
37 #include "recode.h"
38
39 #if defined(HAVE_ICONV) && defined(HAVE_LANGINFO_CODESET)
40 # include <iconv.h>
41 # include <langinfo.h>
42 # define USE_CONV
43 #endif
44
45
46 #define WINDOW_WIDTH 570
47 #define WINDOW_HEIGHT 473
48
49 #define MAX_SECTIONS 50
50
51 #define MAX_DISP_ENTRIES 17
52
53 typedef struct {
54 char *name;
55 const char **content;
56 int line_num;
57 int order_num;
58 } help_section_t;
59
60 typedef struct {
61 xitk_window_t *xwin;
62 xitk_widget_list_t *widget_list;
63
64 int running;
65 int visible;
66
67 xitk_widget_t *tabs;
68 xitk_widget_t *ok;
69 xitk_widget_t *browser;
70
71 char *tab_sections[MAX_SECTIONS + 1];
72
73 help_section_t *sections[MAX_SECTIONS + 1];
74 int num_sections;
75
76 xitk_register_key_t kreg;
77
78 } _help_t;
79
80 static _help_t *help = NULL;
81
82 static int th; /* Tabs height */
83
help_change_section(xitk_widget_t * wx,void * data,int section)84 static void help_change_section(xitk_widget_t *wx, void *data, int section) {
85 if(section < help->num_sections)
86 xitk_browser_update_list(help->browser, help->sections[section]->content, NULL,
87 help->sections[section]->line_num, 0);
88 }
89
help_add_section(const char * filename,const char * doc_encoding,int order_num,char * section_name)90 static void help_add_section(const char *filename, const char *doc_encoding,
91 int order_num, char *section_name) {
92 struct stat st;
93 xitk_recode_t *xr;
94
95 /* ensure that the file is not empty */
96 if(help->num_sections < MAX_SECTIONS) {
97 if((stat(filename, &st) == 0) && (st.st_size)) {
98 int fd;
99
100 assert(doc_encoding != NULL);
101 xr = xitk_recode_init (doc_encoding, NULL, 0);
102
103 if((fd = xine_open_cloexec(filename, O_RDONLY)) >= 0) {
104 char *buf = NULL, *pbuf;
105 int bytes_read;
106
107 if((buf = (char *) malloc(st.st_size + 1))) {
108 if((bytes_read = read(fd, buf, st.st_size)) == st.st_size) {
109 char *p, **hbuf = NULL;
110 int lines = 0;
111
112 buf[st.st_size] = '\0';
113
114 pbuf = buf;
115 while((p = xine_strsep(&pbuf, "\n")) != NULL) {
116 hbuf = (char **) realloc(hbuf, sizeof(char *) * (lines + 2));
117
118 while((*(p + strlen(p) - 1) == '\n') || (*(p + strlen(p) - 1) == '\r'))
119 *(p + strlen(p) - 1) = '\0';
120
121 hbuf[lines] = xitk_recode(xr, p);
122 hbuf[++lines] = NULL;
123 }
124
125 if(lines) {
126 help_section_t *section;
127
128 section = (help_section_t *) calloc(1, sizeof(help_section_t));
129
130 section->name = strdup((section_name && strlen(section_name)) ?
131 section_name : _("Undefined"));
132 section->content = (const char **)hbuf;
133 section->line_num = lines;
134 section->order_num = order_num;
135
136 help->sections[help->num_sections++] = section;
137 help->sections[help->num_sections] = NULL;
138 }
139 }
140
141 free(buf);
142 }
143 close(fd);
144 }
145 xitk_recode_done(xr);
146 }
147 }
148 }
149
help_sections(void)150 static void help_sections(void) {
151 DIR *dir;
152 int i;
153
154 if ((dir = opendir(XINE_DOCDIR)) == NULL) {
155 xine_error("Cannot open help files: %s", strerror(errno));
156 }
157 else {
158 struct dirent *dir_entry;
159 const langs_t *lang = get_lang();
160 char locale_file[XITK_NAME_MAX + 16];
161 char locale_readme[XITK_PATH_MAX + XITK_NAME_MAX + 2];
162 char default_readme[XITK_PATH_MAX + XITK_NAME_MAX + 2];
163 char ending[XITK_NAME_MAX + 2];
164 char section_name[1024];
165
166 while ((dir_entry = readdir(dir)) != NULL) {
167 int order_num;
168
169 memset(&ending, 0, sizeof(ending));
170 memset(§ion_name, 0, sizeof(section_name));
171
172 if ((sscanf(dir_entry->d_name, "README.en.%d.%255s", &order_num, §ion_name[0])) == 2) {
173 sscanf(dir_entry->d_name, "README.en.%255s", &ending[0]);
174
175 snprintf(locale_file, sizeof(locale_file), "%s.%s.%s", "README", lang->ext, ending);
176 snprintf(locale_readme, sizeof(locale_readme), "%s/%s", XINE_DOCDIR, locale_file);
177 snprintf(default_readme, sizeof(default_readme), "%s/%s", XINE_DOCDIR, dir_entry->d_name);
178
179 if ((strcmp(locale_file, dir_entry->d_name)) && is_a_file(locale_readme)) {
180 help_add_section(locale_readme, lang->doc_encoding, order_num, section_name);
181 }
182 else {
183 help_add_section(default_readme, "ISO-8859-1", order_num, section_name);
184 }
185 }
186 }
187
188 closedir(dir);
189
190 if(help->num_sections) {
191
192 /* Sort sections */
193 for(i = 0; i < help->num_sections; i++) {
194 int j;
195 for(j = 0; j < help->num_sections; j++) {
196 if(help->sections[i]->order_num < help->sections[j]->order_num) {
197 help_section_t *hsec = help->sections[i];
198 help->sections[i] = help->sections[j];
199 help->sections[j] = hsec;
200 }
201 }
202 }
203
204 /* create tabs section names */
205 for(i = 0; i < help->num_sections; i++) {
206 char *p;
207
208 p = help->tab_sections[i] = strdup(help->sections[i]->name);
209
210 /* substitute underscores by spaces (nicer) */
211 while(*p) {
212 if(*p == '_')
213 *p = ' ';
214 p++;
215 }
216
217 help->tab_sections[i + 1] = NULL;
218 }
219 }
220 }
221 }
222
help_exit(xitk_widget_t * w,void * data)223 static void help_exit(xitk_widget_t *w, void *data) {
224
225 if(help) {
226 window_info_t wi;
227
228 help->running = 0;
229 help->visible = 0;
230
231 if((xitk_get_window_info(help->kreg, &wi))) {
232 config_update_num("gui.help_x", wi.x);
233 config_update_num("gui.help_y", wi.y);
234 WINDOW_INFO_ZERO(&wi);
235 }
236
237 xitk_unregister_event_handler(&help->kreg);
238
239 xitk_destroy_widgets(help->widget_list);
240 xitk_window_destroy_window(gGui->imlib_data, help->xwin);
241
242 help->xwin = NULL;
243 /* xitk_dlist_init (&help->widget_list->list); */
244
245 if(help->num_sections) {
246 int i;
247
248 for(i = 0; i < help->num_sections; i++) {
249 int j = 0;
250
251 free(help->tab_sections[i]);
252
253 free(help->sections[i]->name);
254
255 while(help->sections[i]->content[j]) {
256 free((char *) help->sections[i]->content[j]);
257 j++;
258 }
259
260 free(help->sections[i]->content);
261 free(help->sections[i]);
262 }
263 }
264
265 gGui->x_lock_display (gGui->display);
266 XFreeGC(gGui->display, (XITK_WIDGET_LIST_GC(help->widget_list)));
267 gGui->x_unlock_display (gGui->display);
268
269 XITK_WIDGET_LIST_FREE(help->widget_list);
270
271 SAFE_FREE(help);
272
273 try_to_set_input_focus(gGui->video_window);
274 }
275 }
276
help_handle_event(XEvent * event,void * data)277 static void help_handle_event(XEvent *event, void *data) {
278
279 switch(event->type) {
280
281 case KeyPress:
282 if(xitk_get_key_pressed(event) == XK_Escape)
283 help_exit(NULL, NULL);
284 else
285 gui_handle_event (event, gGui);
286 break;
287 }
288 }
289
help_raise_window(void)290 void help_raise_window(void) {
291 if(help != NULL)
292 raise_window(xitk_window_get_window(help->xwin), help->visible, help->running);
293 }
294
help_end(void)295 void help_end(void) {
296 help_exit(NULL, NULL);
297 }
298
help_is_running(void)299 int help_is_running(void) {
300
301 if(help != NULL)
302 return help->running;
303
304 return 0;
305 }
306
help_is_visible(void)307 int help_is_visible(void) {
308
309 if(help != NULL) {
310 if(gGui->use_root_window)
311 return xitk_is_window_visible(gGui->display, xitk_window_get_window(help->xwin));
312 else
313 return help->visible && xitk_is_window_visible(gGui->display, xitk_window_get_window(help->xwin));
314 }
315
316 return 0;
317 }
318
help_toggle_visibility(xitk_widget_t * w,void * data)319 void help_toggle_visibility (xitk_widget_t *w, void *data) {
320 if(help != NULL)
321 toggle_window(xitk_window_get_window(help->xwin), help->widget_list,
322 &help->visible, help->running);
323 }
324
help_reparent(void)325 void help_reparent(void) {
326 if(help)
327 reparent_window((xitk_window_get_window(help->xwin)));
328 }
329
help_panel(void)330 void help_panel(void) {
331 GC gc;
332 xitk_labelbutton_widget_t lb;
333 xitk_browser_widget_t br;
334 xitk_tabs_widget_t tab;
335 xitk_pixmap_t *bg;
336 int x, y;
337 xitk_widget_t *w;
338
339 help = (_help_t *) calloc(1, sizeof(_help_t));
340
341 x = xine_config_register_num (__xineui_global_xine_instance, "gui.help_x",
342 80,
343 CONFIG_NO_DESC,
344 CONFIG_NO_HELP,
345 CONFIG_LEVEL_DEB,
346 CONFIG_NO_CB,
347 CONFIG_NO_DATA);
348 y = xine_config_register_num (__xineui_global_xine_instance, "gui.help_y",
349 80,
350 CONFIG_NO_DESC,
351 CONFIG_NO_HELP,
352 CONFIG_LEVEL_DEB,
353 CONFIG_NO_CB,
354 CONFIG_NO_DATA);
355
356 /* Create window */
357 help->xwin = xitk_window_create_dialog_window(gGui->imlib_data,
358 _("Help"),
359 x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
360
361 set_window_states_start((xitk_window_get_window(help->xwin)));
362
363 gGui->x_lock_display (gGui->display);
364 gc = XCreateGC(gGui->display,
365 (xitk_window_get_window(help->xwin)), None, None);
366 gGui->x_unlock_display (gGui->display);
367
368 help->widget_list = xitk_widget_list_new();
369 xitk_widget_list_set(help->widget_list,
370 WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(help->xwin)));
371 xitk_widget_list_set(help->widget_list, WIDGET_LIST_GC, gc);
372
373 help_sections();
374
375 XITK_WIDGET_INIT(&tab, gGui->imlib_data);
376
377 if(help->num_sections) {
378 tab.num_entries = help->num_sections;
379 tab.entries = help->tab_sections;
380 }
381 else {
382 static char *no_section[] = { NULL, NULL };
383
384 no_section[0] = _("No Help Section Available");
385
386 tab.num_entries = 1;
387 tab.entries = no_section;
388 }
389 tab.skin_element_name = NULL;
390 tab.parent_wlist = help->widget_list;
391 tab.callback = help_change_section;
392 tab.userdata = NULL;
393 help->tabs = xitk_noskin_tabs_create (help->widget_list,
394 &tab, 15, 24, WINDOW_WIDTH - 30, tabsfontname);
395 xitk_add_widget (help->widget_list, help->tabs);
396 th = xitk_get_widget_height(help->tabs) - 1;
397 xitk_enable_and_show_widget(help->tabs);
398
399 bg = xitk_image_create_xitk_pixmap(gGui->imlib_data, WINDOW_WIDTH, WINDOW_HEIGHT);
400
401 gGui->x_lock_display (gGui->display);
402 XCopyArea(gGui->display, (xitk_window_get_background(help->xwin)), bg->pixmap,
403 bg->gc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
404 gGui->x_unlock_display (gGui->display);
405
406 draw_rectangular_outter_box(gGui->imlib_data, bg, 15, (24 + th),
407 (WINDOW_WIDTH - 30 - 1), (MAX_DISP_ENTRIES * 20 + 16 + 10 - 1));
408 xitk_window_change_background(gGui->imlib_data, help->xwin, bg->pixmap,
409 WINDOW_WIDTH, WINDOW_HEIGHT);
410
411 xitk_image_destroy_xitk_pixmap(bg);
412
413 XITK_WIDGET_INIT(&br, gGui->imlib_data);
414
415 br.arrow_up.skin_element_name = NULL;
416 br.slider.skin_element_name = NULL;
417 br.arrow_dn.skin_element_name = NULL;
418 br.browser.skin_element_name = NULL;
419 br.browser.max_displayed_entries = MAX_DISP_ENTRIES;
420 br.browser.num_entries = 0;
421 br.browser.entries = NULL;
422 br.callback = NULL;
423 br.dbl_click_callback = NULL;
424 br.parent_wlist = help->widget_list;
425 br.userdata = NULL;
426 help->browser = xitk_noskin_browser_create (help->widget_list, &br,
427 (XITK_WIDGET_LIST_GC(help->widget_list)), 15 + 5, (24 + th) + 5, WINDOW_WIDTH - (30 + 10 + 16), 20, 16, br_fontname);
428 xitk_add_widget (help->widget_list, help->browser);
429 xitk_enable_and_show_widget(help->browser);
430
431 xitk_browser_set_alignment(help->browser, ALIGN_LEFT);
432 help_change_section(NULL, NULL, 0);
433
434 XITK_WIDGET_INIT(&lb, gGui->imlib_data);
435
436
437 lb.button_type = CLICK_BUTTON;
438 lb.label = _("Close");
439 lb.align = ALIGN_CENTER;
440 lb.callback = help_exit;
441 lb.state_callback = NULL;
442 lb.userdata = NULL;
443 lb.skin_element_name = NULL;
444 w = xitk_noskin_labelbutton_create (help->widget_list, &lb,
445 WINDOW_WIDTH - (100 + 15), WINDOW_HEIGHT - (23 + 15), 100, 23, "Black", "Black", "White", tabsfontname);
446 xitk_add_widget (help->widget_list, w);
447 xitk_enable_and_show_widget(w);
448
449 help->kreg = xitk_register_event_handler("help",
450 (xitk_window_get_window(help->xwin)),
451 help_handle_event,
452 NULL,
453 NULL,
454 help->widget_list,
455 NULL);
456
457 help->visible = 1;
458 help->running = 1;
459 help_raise_window();
460
461 try_to_set_input_focus(xitk_window_get_window(help->xwin));
462 }
463
464