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(&section_name, 0, sizeof(section_name));
171 
172       if ((sscanf(dir_entry->d_name, "README.en.%d.%255s", &order_num, &section_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