403 lines
11 KiB
C
403 lines
11 KiB
C
/* dvdisaster: Additional error correction for optical media.
|
|
* Copyright (C) 2004-2009 Carsten Gnoerlich.
|
|
* Project home page: http://www.dvdisaster.com
|
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
|
* or direct your browser at http://www.gnu.org.
|
|
*/
|
|
|
|
#include "dvdisaster.h"
|
|
|
|
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_DARWIN) || defined(SYS_NETBSD) || defined(SYS_SOLARIS)
|
|
#include <sys/wait.h>
|
|
#endif
|
|
|
|
#ifdef SYS_MINGW
|
|
#include "windows.h"
|
|
#include "shellapi.h"
|
|
#endif
|
|
|
|
/***
|
|
*** Ask user to specify his browser
|
|
***/
|
|
|
|
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_DARWIN) || defined(SYS_NETBSD) || defined(SYS_SOLARIS)
|
|
|
|
#define SEARCH_BUTTON 1
|
|
|
|
typedef struct
|
|
{ GtkWidget *dialog;
|
|
GtkWidget *entry;
|
|
GtkWidget *search;
|
|
GtkWidget *filesel;
|
|
GtkWidget *fileok;
|
|
GtkWidget *filecancel;
|
|
char *url;
|
|
} browser_dialog_info;
|
|
|
|
static void response_cb(GtkWidget *widget, int response, gpointer data)
|
|
{ browser_dialog_info *bdi = (browser_dialog_info*)data;
|
|
|
|
switch(response)
|
|
{ case GTK_RESPONSE_ACCEPT:
|
|
if(Closure->browser) g_free(Closure->browser);
|
|
Closure->browser = g_strdup(gtk_entry_get_text(GTK_ENTRY(bdi->entry)));
|
|
ShowHTML(bdi->url);
|
|
break;
|
|
|
|
case GTK_RESPONSE_REJECT:
|
|
if(bdi->url) g_free(bdi->url);
|
|
break;
|
|
}
|
|
gtk_widget_destroy(widget);
|
|
if(bdi->filesel)
|
|
gtk_widget_destroy(bdi->filesel);
|
|
g_free(bdi);
|
|
}
|
|
|
|
static void search_cb(GtkWidget *widget, gpointer data)
|
|
{ browser_dialog_info *bdi = (browser_dialog_info*)data;
|
|
|
|
if(widget == bdi->search)
|
|
{ bdi->filesel = gtk_file_selection_new(_utf("windowtitle|Choose a browser"));
|
|
bdi->fileok = GTK_FILE_SELECTION(bdi->filesel)->ok_button;
|
|
bdi->filecancel = GTK_FILE_SELECTION(bdi->filesel)->cancel_button;
|
|
ReverseCancelOK(GTK_DIALOG(bdi->filesel));
|
|
gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(bdi->filesel));
|
|
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->ok_button), "clicked",
|
|
G_CALLBACK(search_cb), bdi);
|
|
|
|
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->cancel_button), "clicked",
|
|
G_CALLBACK(search_cb), bdi);
|
|
|
|
gtk_widget_show(bdi->filesel);
|
|
}
|
|
|
|
if(widget == bdi->fileok)
|
|
{
|
|
if(Closure->browser) g_free(Closure->browser);
|
|
Closure->browser = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(bdi->filesel)));
|
|
ShowHTML(bdi->url);
|
|
gtk_widget_destroy(bdi->filesel);
|
|
gtk_widget_destroy(bdi->dialog);
|
|
g_free(bdi);
|
|
return;
|
|
}
|
|
|
|
if(widget == bdi->filecancel)
|
|
{ gtk_widget_destroy(bdi->filesel);
|
|
bdi->filesel = NULL;
|
|
}
|
|
}
|
|
|
|
static void browser_dialog(char *url)
|
|
{ GtkWidget *dialog, *vbox, *hbox, *label, *entry, *button;
|
|
browser_dialog_info *bdi = g_malloc0(sizeof(browser_dialog_info));
|
|
|
|
/* Create the dialog */
|
|
|
|
dialog = gtk_dialog_new_with_buttons(_utf("windowtitle|Browser required"),
|
|
Closure->window, GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
|
|
bdi->dialog = dialog;
|
|
if(url)
|
|
{ bdi->url = g_strdup(url);
|
|
}
|
|
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, FALSE, FALSE, 0);
|
|
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
|
|
|
|
/* Insert the contents */
|
|
|
|
label = gtk_label_new(NULL);
|
|
gtk_label_set_markup(GTK_LABEL(label), _utf("<b>Could not find a suitable browser.</b>\n\n"
|
|
"Which browser would you like to use\n"
|
|
"for reading the online documentation?\n\n"
|
|
"Please enter its name (e.g. mozilla) or\n"
|
|
"use the \"Search\" button for a file dialog.\n")),
|
|
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 10);
|
|
|
|
hbox = gtk_hbox_new(FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 10);
|
|
|
|
bdi->entry = entry = gtk_entry_new();
|
|
gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 10);
|
|
|
|
bdi->search = button = gtk_button_new_with_label(_utf("Search"));
|
|
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(search_cb), bdi);
|
|
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 10);
|
|
|
|
/* Show it */
|
|
|
|
g_signal_connect(dialog, "response", G_CALLBACK(response_cb), bdi);
|
|
|
|
gtk_widget_show_all(dialog);
|
|
}
|
|
#endif /* SYS_ unix-like */
|
|
|
|
/***
|
|
*** Show the manual in an external browser
|
|
***/
|
|
|
|
/*
|
|
* Check the child processes exit status
|
|
* to find whether the browser could be invoked.
|
|
*/
|
|
|
|
typedef struct
|
|
{ pid_t pid;
|
|
char *url;
|
|
GtkWidget *msg;
|
|
int seconds;
|
|
} browser_info;
|
|
|
|
|
|
static void msg_destroy_cb(GtkWidget *widget, gpointer data)
|
|
{ browser_info *bi = (browser_info*)data;
|
|
|
|
bi->msg = NULL;
|
|
}
|
|
|
|
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_DARWIN) || defined(SYS_NETBSD) || defined(SYS_SOLARIS)
|
|
|
|
/*
|
|
* The following list of browsers and html wrappers
|
|
* will be tried one at a time until one entry succeeds by:
|
|
* - returning zero
|
|
* - not returning within 60 seconds
|
|
*/
|
|
|
|
static int browser_index;
|
|
static void try_browser(browser_info*);
|
|
|
|
static char *browsers[] =
|
|
{ "user-selection",
|
|
"xdg-open",
|
|
"gnome-open",
|
|
"htmlview",
|
|
"firefox",
|
|
"mozilla",
|
|
"konqueror",
|
|
"epiphany",
|
|
"opera",
|
|
"/Applications/Safari.app/Contents/MacOS/Safari", /* better way to do this? */
|
|
NULL
|
|
};
|
|
|
|
static gboolean browser_timeout_func(gpointer data)
|
|
{ browser_info *bi = (browser_info*)data;
|
|
int status;
|
|
|
|
waitpid(bi->pid, &status, WNOHANG);
|
|
|
|
/* At least mozilla returns random values under FreeBSD on success,
|
|
so we can't rely on the return value exept our own 110 one. */
|
|
|
|
if(WIFEXITED(status))
|
|
{
|
|
switch(WEXITSTATUS(status))
|
|
{ case 110: /* browser did not execute */
|
|
browser_index++;
|
|
if(!browsers[browser_index]) /* all browsers from the list failed */
|
|
{ browser_dialog(bi->url);
|
|
|
|
if(bi->msg)
|
|
gtk_widget_destroy(bi->msg);
|
|
if(bi->url)
|
|
g_free(bi->url);
|
|
g_free(bi);
|
|
}
|
|
else /* try next browser from list */
|
|
{ bi->seconds = 0;
|
|
try_browser(bi);
|
|
}
|
|
return FALSE;
|
|
|
|
case 0: /* browser assumed to be successful */
|
|
default:
|
|
if(bi->msg)
|
|
gtk_widget_destroy(bi->msg);
|
|
if(bi->url)
|
|
g_free(bi->url);
|
|
g_free(bi);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bi->seconds++;
|
|
if(bi->seconds == 10 && bi->msg)
|
|
{ gtk_widget_destroy(bi->msg);
|
|
bi->msg = NULL;
|
|
}
|
|
|
|
return bi->seconds > 60 ? FALSE : TRUE;
|
|
}
|
|
#endif /* SYS_ unix-like */
|
|
|
|
#ifdef SYS_MINGW
|
|
static gboolean browser_timeout_func(gpointer data)
|
|
{ browser_info *bi = (browser_info*)data;
|
|
|
|
bi->seconds++;
|
|
|
|
if(bi->seconds >= 10)
|
|
{ if(bi->msg)
|
|
{ gtk_widget_destroy(bi->msg);
|
|
bi->msg = NULL;
|
|
}
|
|
if(bi->url) g_free(bi->url);
|
|
g_free(bi);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif /* SYS_MINGW */
|
|
|
|
/*
|
|
* Invoke the browser
|
|
*/
|
|
|
|
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_DARWIN) || defined(SYS_NETBSD) || defined(SYS_SOLARIS)
|
|
static void try_browser(browser_info *bi)
|
|
{ pid_t pid;
|
|
|
|
bi->pid = pid = fork();
|
|
|
|
if(pid == -1)
|
|
{ printf("fork failed\n");
|
|
return;
|
|
}
|
|
|
|
/* make the parent remember and wait() for the browser */
|
|
|
|
if(pid > 0)
|
|
{ g_timeout_add(1000, browser_timeout_func, (gpointer)bi);
|
|
|
|
if(browser_index)
|
|
{ g_free(Closure->browser);
|
|
Closure->browser = g_strdup(browsers[browser_index]);
|
|
}
|
|
}
|
|
|
|
/* try calling the browser */
|
|
|
|
if(pid == 0)
|
|
{ char *argv[10];
|
|
int argc = 0;
|
|
|
|
argv[argc++] = browser_index ? browsers[browser_index] : Closure->browser;
|
|
argv[argc++] = bi->url;
|
|
argv[argc++] = NULL;
|
|
execvp(argv[0], argv);
|
|
|
|
_exit(110); /* couldn't execute */
|
|
}
|
|
}
|
|
#endif /* SYS_ unix-like */
|
|
|
|
|
|
void ShowHTML(char *target)
|
|
{ browser_info *bi = g_malloc0(sizeof(browser_info));
|
|
gint64 ignore;
|
|
const char *lang;
|
|
char *path = NULL;
|
|
int http_url;
|
|
|
|
/* If no target is given, select between translations of the manual. */
|
|
|
|
if(!target) target = g_strdup("index.html");
|
|
|
|
http_url = strlen(target) > 4 && !strncmp(target, "http", 4);
|
|
|
|
if(!http_url && !strchr(target, '/')) /* create full path */
|
|
{
|
|
if(!Closure->docDir)
|
|
{
|
|
CreateMessage(_("Documentation not installed."), GTK_MESSAGE_ERROR);
|
|
g_free(bi);
|
|
return;
|
|
}
|
|
|
|
lang = g_getenv("LANG");
|
|
|
|
if(lang)
|
|
{ if(!strncmp(lang, "ru", 2))
|
|
#ifdef SYS_MINGW
|
|
path = g_strdup_printf("%s\\ru\\%s",Closure->docDir,target);
|
|
#else
|
|
path = g_strdup_printf("%s/ru/%s",Closure->docDir,target);
|
|
#endif
|
|
else if(!strncmp(lang, "de", 2))
|
|
#ifdef SYS_MINGW
|
|
path = g_strdup_printf("%s\\de\\%s",Closure->docDir,target);
|
|
#else
|
|
path = g_strdup_printf("%s/de/%s",Closure->docDir,target);
|
|
#endif
|
|
}
|
|
|
|
if(!path)
|
|
{
|
|
#ifdef SYS_MINGW
|
|
path = g_strdup_printf("%s\\en\\%s",Closure->docDir,target);
|
|
#else
|
|
path = g_strdup_printf("%s/en/%s",Closure->docDir,target);
|
|
#endif
|
|
}
|
|
|
|
#ifdef SYS_MINGW
|
|
if(!LargeStat(path, &ignore))
|
|
{
|
|
g_free(path); /* the local dir is Windows specific */
|
|
path = g_strdup_printf("%s\\local\\%s",Closure->docDir,target);
|
|
}
|
|
#endif
|
|
g_free(target);
|
|
bi->url = path;
|
|
}
|
|
else bi->url = target;
|
|
|
|
if(!http_url && !LargeStat(bi->url, &ignore))
|
|
{
|
|
CreateMessage(_("Documentation file\n%s\nnot found.\n"), GTK_MESSAGE_ERROR, bi->url);
|
|
g_free(bi);
|
|
g_free(bi->url);
|
|
return;
|
|
}
|
|
|
|
/* Lock the help button and show a message for 10 seconds. */
|
|
|
|
TimedInsensitive(Closure->helpButton, 10000);
|
|
bi->msg = CreateMessage(_("Please hang on until the browser comes up!"), GTK_MESSAGE_INFO);
|
|
g_signal_connect(G_OBJECT(bi->msg), "destroy", G_CALLBACK(msg_destroy_cb), bi);
|
|
|
|
#ifdef SYS_MINGW
|
|
/* Okay, Billy wins big time here ;-) */
|
|
|
|
ShellExecute(NULL, "open", bi->url, NULL, NULL, SW_SHOWNORMAL);
|
|
g_timeout_add(1000, browser_timeout_func, (gpointer)bi);
|
|
#endif
|
|
|
|
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_DARWIN) || defined(SYS_NETBSD) || defined(SYS_SOLARIS)
|
|
/* Try the first browser */
|
|
|
|
browser_index = 0;
|
|
try_browser(bi);
|
|
#endif
|
|
}
|