Files
dvdisaster/src/main-window.c
Stéphane Lesimple db4d3af31d sync from upstream up to 22:14a375563e2b
This includes the following commits:

changeset:   22:14a375563e2b
summary:     chang "while" command to "type -P" in bash based configure

changeset:   21:c4bf7c0f33d2
summary:     updated codec paper for RS01 life cycle

changeset:   20:dbcaad8128aa
summary:     replaced build count with mercurial changeset

changeset:   19:fba258a6acfa
summary:     Added tag 0.79.10 for changeset 49950be5a2ef

changeset:   18:49950be5a2ef
summary:     merged some debian patches

changeset:   17:815be3929c41
summary:     merged "easy" patches from Stéphane Lesimple's version

changeset:   16:7d15f8a958cb
summary:     Made printf format strings 32/64bit safe as suggested by Stéphane;

changeset:   15:1055a53b8d6d
summary:     reorganized code for --with-gui=[yes|no] option

changeset:   14:fbe2ae12a32c
summary:     Added tag 0.79.9 for changeset f2fdd6d3a1f5

changeset:   13:f2fdd6d3a1f5
summary:     updated TODO and CHANGELOG

And other changes that were needed to resolve the (many) conflicts.
2021-10-09 15:22:57 +02:00

522 lines
16 KiB
C

/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2017 Carsten Gnoerlich.
* Copyright (C) 2019-2021 The dvdisaster development team.
*
* Email: support@dvdisaster.org
*
* This file is part of dvdisaster.
*
* dvdisaster 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 3 of the License, or
* (at your option) any later version.
*
* dvdisaster 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 dvdisaster. If not, see <http://www.gnu.org/licenses/>.
*/
/*** src type: only GUI code ***/
#ifdef WITH_GUI_YES
#include "dvdisaster.h"
/***
*** Global callbacks
***/
static void destroy_cb(GtkWidget *widget, gpointer data)
{
/* If an action is currently running with spawned threads,
give them time to terminate cleanly. */
if(Closure->subThread)
{ Closure->stopActions = STOP_SHUTDOWN_ALL;
g_thread_join(Closure->subThread);
}
gtk_main_quit();
}
static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{
return FALSE;
}
/***
*** The right-side action buttons
***/
/*
* Callback for the action buttons
*/
static void action_cb(GtkWidget *widget, gpointer data)
{ gint action = GPOINTER_TO_INT(data);
Method *method = NULL;
if(action != ACTION_STOP)
{
/* Clear the log buffer, request new log file time stamp */
if(action != ACTION_CREATE_CONT)
{ g_mutex_lock(Closure->logLock);
g_string_truncate(Closure->logString, 0);
g_string_printf(Closure->logString, _("log: %s\n"), Closure->versionString);
g_mutex_unlock(Closure->logLock);
Closure->logFileStamped = FALSE;
}
/* Make sure we're using the current file selections */
g_free(Closure->imageName);
Closure->imageName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->imageEntry)));
if(Closure->autoSuffix)
{ Closure->imageName = ApplyAutoSuffix(Closure->imageName, "iso");
gtk_entry_set_text(GTK_ENTRY(Closure->imageEntry), Closure->imageName);
}
g_free(Closure->eccName);
Closure->eccName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->eccEntry)));
if(Closure->autoSuffix)
{ Closure->eccName = ApplyAutoSuffix(Closure->eccName, "ecc");
gtk_entry_set_text(GTK_ENTRY(Closure->eccEntry), Closure->eccName);
}
/* The ecc file may not be labeled as an .iso image */
if(Closure->eccName)
{ int len = strlen(Closure->eccName);
if(!strcmp(Closure->eccName, Closure->imageName))
{ GuiCreateMessage(_("The .iso image and error correction file\n"
"must not be the same file!\n\n"
"If you intended to create or use an .iso image\n"
"which is augmented with error correction data,\n"
"please leave the error correction file name blank."),
GTK_MESSAGE_ERROR);
return;
}
if(!strcmp(Closure->eccName+len-4, ".iso"))
{ GuiCreateMessage(_("The error correction file type must not be \".iso\".\n\n"
"If you intended to create or use an .iso image\n"
"which is augmented with error correction data,\n"
"please leave the error correction file name blank."),
GTK_MESSAGE_ERROR);
return;
}
}
/* Reset warnings which may be temporarily disabled during an action */
Closure->noMissingWarnings = FALSE;
/* Do not queue up stop actions */
Closure->stopActions = FALSE;
}
/* Dispatch action */
switch(action)
{ case ACTION_STOP:
Closure->stopActions = STOP_CURRENT_ACTION;
break;
case ACTION_READ:
GuiAllowActions(FALSE);
if(Closure->adaptiveRead)
{ gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), 2);
GuiResetAdaptiveReadWindow();
CreateGThread((GThreadFunc)ReadMediumAdaptive, (gpointer)0);
}
else
{ gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), 1);
Closure->additionalSpiralColor = 1;
GuiResetLinearReadWindow();
CreateGThread((GThreadFunc)ReadMediumLinear, (gpointer)0);
}
break;
case ACTION_CREATE:
case ACTION_CREATE_CONT:
method = FindMethod(Closure->methodName);
if(!method)
{ GuiCreateMessage(_("\nMethod %s not available.\n"
"Use -m without parameters for a method list.\n"),
GTK_MESSAGE_ERROR, Closure->methodName);
break;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), method->tabWindowIndex);
method->resetCreateWindow(method);
GuiAllowActions(FALSE);
CreateGThread((GThreadFunc)method->create, (gpointer)method);
break;
case ACTION_FIX:
{ Image *image;
image = OpenImageFromFile(Closure->imageName, O_RDWR, IMG_PERMS);
image = OpenEccFileForImage(image, Closure->eccName, O_RDWR, IMG_PERMS);
if(ReportImageEccInconsistencies(image)) /* abort if no method found */
return;
/* Determine method. Ecc files win over augmented ecc. */
if(image && image->eccFileMethod) method = image->eccFileMethod;
else if(image && image->eccMethod) method = image->eccMethod;
else { GuiCreateMessage(_("Internal error: No suitable method for repairing image."),
GTK_MESSAGE_ERROR);
return;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), method->tabWindowIndex+1);
method->resetFixWindow(method);
GuiAllowActions(FALSE);
CreateGThread((GThreadFunc)method->fix, (gpointer)image);
}
break;
case ACTION_SCAN:
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), 1);
Closure->additionalSpiralColor = -1;
GuiResetLinearReadWindow();
GuiAllowActions(FALSE);
CreateGThread((GThreadFunc)ReadMediumLinear, (gpointer)1);
break;
case ACTION_STRIP:
GuiAllowActions(FALSE);
CreateGThread((GThreadFunc)StripECCFromImageFile, (gpointer)0);
break;
case ACTION_VERIFY:
/* If something is wrong with the .iso or .ecc files
we fall back to the RS01 method for verifying since it is robust
against missing files. */
{ Image *image;
image = OpenImageFromFile(Closure->imageName, O_RDONLY, IMG_PERMS);
image = OpenEccFileForImage(image, Closure->eccName, O_RDONLY, IMG_PERMS);
/* Determine method. Ecc files win over augmented ecc. */
if(image && image->eccFileMethod) method = image->eccFileMethod;
else if(image && image->eccMethod) method = image->eccMethod;
else if(!(method = FindMethod("RS01")))
{ GuiCreateMessage(_("RS01 method not available for comparing files."),
GTK_MESSAGE_ERROR);
return;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), method->tabWindowIndex+2);
method->resetVerifyWindow(method);
GuiAllowActions(FALSE);
CreateGThread((GThreadFunc)method->verify, (gpointer)image);
break;
}
}
}
/*
* Start an action from another thread
*/
static gboolean action_idle_func(gpointer action)
{
Closure->enableCurveSwitch = TRUE;
action_cb(NULL, action);
return FALSE;
}
void GuiContinueWithAction(int action)
{
g_idle_add(action_idle_func, GINT_TO_POINTER(action));
}
/*
* Create the action buttons and the associated notebook pages
*/
static GtkWidget *create_button(char *label, char *icon)
{ GtkWidget *button,*box,*image,*lab;
char *utf_label = g_locale_to_utf8(label, -1, NULL, NULL, NULL);
button = gtk_button_new();
box = gtk_vbox_new(FALSE, 0);
image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_LARGE_TOOLBAR);
lab = gtk_label_new(utf_label);
g_free(utf_label);
gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), lab, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(button), box);
// gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
return button;
}
static GtkWidget* create_action_bar(GtkNotebook *notebook)
{ GtkWidget *vbox, *outer_vbox, *wid, *content, *ignore;
int window_number = FIRST_CREATE_WINDOW;
unsigned int i;
outer_vbox = gtk_vbox_new(TRUE, 0);
vbox = gtk_vbox_new(FALSE, 0); /* needed for vertical spacing */
gtk_box_pack_start(GTK_BOX(outer_vbox), vbox, TRUE, TRUE, 3);
/*** Read */
Closure->readButton = wid = create_button(_("button|Read"), "dvdisaster-read");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_READ);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
GuiAttachTooltip(wid, _("tooltip|Read Image"),
_("Reads an optical disc image into a file (or tries to complete an existing image file)."));
content = gtk_vbox_new(FALSE, 0); /* read linear window */
ignore = gtk_label_new("read_tab_l");
gtk_notebook_append_page(notebook, content, ignore);
GuiCreateLinearReadWindow(content);
content = gtk_vbox_new(FALSE, 0); /* read adaptive window */
ignore = gtk_label_new("read_tab_a");
gtk_notebook_append_page(notebook, content, ignore);
GuiCreateAdaptiveReadWindow(content);
/*** Create */
Closure->createButton = wid = create_button(_("button|Create"), "dvdisaster-create");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_CREATE);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
GuiAttachTooltip(wid, _("tooltip|Create error correction data"),
_("Creates error correction data. Requires an image file."));
/*** Scan */
Closure->scanButton = wid = create_button(_("button|Scan"), "dvdisaster-scan");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_SCAN);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
GuiAttachTooltip(wid, _("tooltip|Scan medium"),
_("Scans medium for unreadable sectors."));
/*** Fix */
Closure->fixButton = wid = create_button(_("button|Fix"), "dvdisaster-fix");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_FIX);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
GuiAttachTooltip(wid, _("tooltip|Repair image"),
_("Repairs an image. Requires an image file and error correction data."));
/*** Verify */
Closure->testButton = wid = create_button(_("button|Verify"), "dvdisaster-verify");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_VERIFY);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
GuiAttachTooltip(wid, _("tooltip|Consistency check"),
_("Tests consistency of error correction data and image file."));
/*** Strip */
Closure->stripButton = wid = create_button(_("button|Strip"), "dvdisaster-strip");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_STRIP);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
GuiAttachTooltip(wid, _("tooltip|Strip ECC"),
_("Strip ECC data from an augmented image."));
/*** Stop */
wid = create_button(_("button|Stop"), "dvdisaster-gtk-stop");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_STOP);
gtk_box_pack_end(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
GuiAttachTooltip(wid, _("tooltip|Abort action"),
_("Aborts an ongoing action."));
/*** Block drive related actions if no drives were found */
if(!Closure->deviceNodes->len)
{ gtk_widget_set_sensitive(Closure->readButton, FALSE);
gtk_widget_set_sensitive(Closure->scanButton, FALSE);
}
/*** Create notebook windows for the methods */
for(i=0; i<Closure->methodList->len; i++)
{ Method *method = g_ptr_array_index(Closure->methodList, i);
method->tabWindowIndex = window_number;
window_number += 3;
/* Create window */
content = gtk_vbox_new(FALSE, 0);
ignore = gtk_label_new("create_tab");
gtk_notebook_append_page(notebook, content, ignore);
method->createCreateWindow(method, content);
/* Fix window */
content = gtk_vbox_new(FALSE, 0);
ignore = gtk_label_new("fix_tab");
gtk_notebook_append_page(notebook, content, ignore);
method->createFixWindow(method, content);
/* Verify window */
content = gtk_vbox_new(FALSE, 0);
ignore = gtk_label_new("verify_tab");
gtk_notebook_append_page(notebook, content, ignore);
method->createVerifyWindow(method, content);
}
return outer_vbox;
}
/***
*** Create the main window
***/
/*
* Show the log data
*/
static void log_cb(GtkWidget *widget, gpointer data)
{ GuiShowLog();
}
void GuiCreateMainWindow(int *argc, char ***argv)
{ GtkWidget *window,*wid,*outer_box,*middle_box,*status_box,*sep;
GtkWidget *box, *icon, *button;
char title[80];
int sig_okay = TRUE;
/*** Initialize GTK+ */
gtk_init(argc, argv);
/*** Some style tinkering */
gtk_rc_parse_string("style \"dvdisaster-style\"\n"
"{ GtkMenuBar::shadow_type = none\n"
"}\n"
"class \"GtkMenuBar\" style \"dvdisaster-style\"\n");
/*** Create our icons */
GuiCreateIconFactory();
/*** Open the main window */
g_snprintf(title, 80, "dvdisaster-%s", Closure->cookedVersion);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), title);
if(sig_okay)
gtk_window_set_default_size(GTK_WINDOW(window), -1, 550);
gtk_window_set_icon(GTK_WINDOW(window), Closure->windowIcon);
Closure->window = GTK_WINDOW(window);
/* Connect with the close button from the window manager */
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_cb), NULL);
/* and with destroy events */
g_signal_connect(window, "destroy", G_CALLBACK(destroy_cb), NULL);
/*** Initialize the tooltips struct */
Closure->tooltips = gtk_tooltips_new();
/*** Create the sub parts of the GUI */
outer_box = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), outer_box);
/* Menu and tool bar */
wid = GuiCreateMenuBar(outer_box);
gtk_box_pack_start(GTK_BOX(outer_box), wid, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(outer_box), sep, FALSE, FALSE, 0);
wid = GuiCreateToolBar(outer_box);
gtk_box_pack_start(GTK_BOX(outer_box), wid, FALSE, FALSE, 3);
/* Middle part */
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(outer_box), sep, FALSE, FALSE, 0);
middle_box = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(outer_box), middle_box, TRUE, TRUE, 0);
wid = Closure->notebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(wid), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(wid), FALSE);
gtk_box_pack_start(GTK_BOX(middle_box), wid, TRUE, TRUE, 0);
GuiCreateWelcomePage(GTK_NOTEBOOK(Closure->notebook));
wid = create_action_bar((GTK_NOTEBOOK(Closure->notebook)));
gtk_box_pack_end(GTK_BOX(middle_box), wid, FALSE, FALSE, 3);
sep = gtk_vseparator_new();
gtk_box_pack_end(GTK_BOX(middle_box), sep, FALSE, FALSE, 0);
/* Status bar enclosure */
status_box = gtk_hbox_new(FALSE, 0);
gtk_box_pack_end(GTK_BOX(outer_box), status_box, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_end(GTK_BOX(outer_box), sep, FALSE, FALSE, 0);
/* Status bar contents. */
Closure->status = gtk_label_new(NULL);
gtk_label_set_ellipsize(GTK_LABEL(Closure->status), PANGO_ELLIPSIZE_END);
gtk_misc_set_alignment(GTK_MISC(Closure->status), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(status_box), GTK_WIDGET(Closure->status), TRUE, TRUE, 5);
button = gtk_button_new();
gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
gtk_box_pack_end(GTK_BOX(status_box), button, FALSE, FALSE, 5);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(log_cb), NULL);
GuiAttachTooltip(button,
_("tooltip|Protocol for current action"),
_("Displays additional information created during the current or last action."));
box = gtk_hbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(button), box);
icon = gtk_image_new_from_stock("dvdisaster-gtk-index", GTK_ICON_SIZE_SMALL_TOOLBAR);
gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 2);
wid = gtk_label_new(_utf("View log"));
gtk_box_pack_start(GTK_BOX(box), wid, FALSE, FALSE, 0);
/* And enter the main loop */
gtk_widget_show_all(window);
gtk_main();
}
#endif /* WITH_GUI_YES */