app: new error dialog to backtrace and encourage people to report bugs.
GIMP will now try to get a backtrace (on Unix machines only for now, using g_on_error_stack_trace(); for Windows, we will likely have to look into DrMinGW). This is now applied to CRITICAL errors only, which usually means major bugs but are currently mostly hidden unless you run GIMP in terminal. We limit to 3 backtraces, because many CRITICAL typically get into domino effect and cause more CRITICALs (for instance when a g_return*_if_fail() returns too early).
This commit is contained in:
@ -159,9 +159,10 @@ gimp_show_message (Gimp *gimp,
|
||||
GObject *handler,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message)
|
||||
const gchar *message,
|
||||
const gchar *trace)
|
||||
{
|
||||
const gchar *desc = "Message";
|
||||
const gchar *desc = trace ? "Error" : "Message";
|
||||
|
||||
g_return_if_fail (GIMP_IS_GIMP (gimp));
|
||||
g_return_if_fail (handler == NULL || G_IS_OBJECT (handler));
|
||||
@ -174,8 +175,8 @@ gimp_show_message (Gimp *gimp,
|
||||
{
|
||||
if (gimp->gui.show_message)
|
||||
{
|
||||
gimp->gui.show_message (gimp, handler,
|
||||
severity, domain, message);
|
||||
gimp->gui.show_message (gimp, handler, severity,
|
||||
domain, message, trace);
|
||||
return;
|
||||
}
|
||||
else if (GIMP_IS_PROGRESS (handler) &&
|
||||
@ -190,6 +191,8 @@ gimp_show_message (Gimp *gimp,
|
||||
gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity,
|
||||
NULL, NULL, &desc, NULL);
|
||||
g_printerr ("%s-%s: %s\n\n", domain, desc, message);
|
||||
if (trace)
|
||||
g_printerr ("Back trace:\n%s\n\n", trace);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -35,7 +35,8 @@ struct _GimpGui
|
||||
GObject *handler,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message);
|
||||
const gchar *message,
|
||||
const gchar *trace);
|
||||
void (* help) (Gimp *gimp,
|
||||
GimpProgress *progress,
|
||||
const gchar *help_domain,
|
||||
@ -144,7 +145,8 @@ void gimp_show_message (Gimp *gimp,
|
||||
GObject *handler,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message);
|
||||
const gchar *message,
|
||||
const gchar *trace);
|
||||
void gimp_help (Gimp *gimp,
|
||||
GimpProgress *progress,
|
||||
const gchar *help_domain,
|
||||
|
@ -1123,7 +1123,7 @@ gimp_message_valist (Gimp *gimp,
|
||||
|
||||
message = g_strdup_vprintf (format, args);
|
||||
|
||||
gimp_show_message (gimp, handler, severity, NULL, message);
|
||||
gimp_show_message (gimp, handler, severity, NULL, message, NULL);
|
||||
|
||||
g_free (message);
|
||||
}
|
||||
@ -1138,7 +1138,7 @@ gimp_message_literal (Gimp *gimp,
|
||||
g_return_if_fail (handler == NULL || G_IS_OBJECT (handler));
|
||||
g_return_if_fail (message != NULL);
|
||||
|
||||
gimp_show_message (gimp, handler, severity, NULL, message);
|
||||
gimp_show_message (gimp, handler, severity, NULL, message, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "widgets/gimpchanneltreeview.h"
|
||||
#include "widgets/gimpcoloreditor.h"
|
||||
#include "widgets/gimpcolormapeditor.h"
|
||||
#include "widgets/gimpcriticaldialog.h"
|
||||
#include "widgets/gimpdashboard.h"
|
||||
#include "widgets/gimpdevicestatus.h"
|
||||
#include "widgets/gimpdialogfactory.h"
|
||||
@ -215,6 +216,15 @@ dialogs_error_get (GimpDialogFactory *factory,
|
||||
return gimp_error_dialog_new (_("GIMP Message"));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
dialogs_critical_get (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
gint view_size)
|
||||
{
|
||||
return gimp_critical_dialog_new (_("GIMP critical error"));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
dialogs_close_all_get (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
|
@ -77,6 +77,10 @@ GtkWidget * dialogs_error_get (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
gint view_size);
|
||||
GtkWidget * dialogs_critical_get (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
gint view_size);
|
||||
GtkWidget * dialogs_close_all_get (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
|
@ -286,6 +286,8 @@ static const GimpDialogFactoryEntry entries[] =
|
||||
dialogs_action_search_get, TRUE, TRUE, TRUE),
|
||||
TOPLEVEL ("gimp-error-dialog",
|
||||
dialogs_error_get, TRUE, FALSE, FALSE),
|
||||
TOPLEVEL ("gimp-critical-dialog",
|
||||
dialogs_critical_get, TRUE, FALSE, FALSE),
|
||||
TOPLEVEL ("gimp-close-all-dialog",
|
||||
dialogs_close_all_get, TRUE, FALSE, FALSE),
|
||||
TOPLEVEL ("gimp-quit-dialog",
|
||||
|
159
app/errors.c
159
app/errors.c
@ -21,8 +21,11 @@
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@ -41,6 +44,7 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define MAX_TRACES 3
|
||||
|
||||
/* private variables */
|
||||
|
||||
@ -52,23 +56,24 @@ static gchar *full_prog_name = NULL;
|
||||
|
||||
/* local function prototypes */
|
||||
|
||||
static G_GNUC_NORETURN void gimp_eek (const gchar *reason,
|
||||
const gchar *message,
|
||||
gboolean use_handler);
|
||||
static void gimp_third_party_message_log_func (const gchar *log_domain,
|
||||
GLogLevelFlags flags,
|
||||
const gchar *message,
|
||||
gpointer data);
|
||||
static void gimp_message_log_func (const gchar *log_domain,
|
||||
GLogLevelFlags flags,
|
||||
const gchar *message,
|
||||
gpointer data);
|
||||
static void gimp_error_log_func (const gchar *domain,
|
||||
GLogLevelFlags flags,
|
||||
const gchar *message,
|
||||
gpointer data) G_GNUC_NORETURN;
|
||||
|
||||
static void gimp_third_party_message_log_func (const gchar *log_domain,
|
||||
GLogLevelFlags flags,
|
||||
const gchar *message,
|
||||
gpointer data);
|
||||
static void gimp_message_log_func (const gchar *log_domain,
|
||||
GLogLevelFlags flags,
|
||||
const gchar *message,
|
||||
gpointer data);
|
||||
static void gimp_error_log_func (const gchar *domain,
|
||||
GLogLevelFlags flags,
|
||||
const gchar *message,
|
||||
gpointer data) G_GNUC_NORETURN;
|
||||
static G_GNUC_NORETURN void gimp_eek (const gchar *reason,
|
||||
const gchar *message,
|
||||
gboolean use_handler);
|
||||
|
||||
static gchar * gimp_get_stack_trace (void);
|
||||
|
||||
|
||||
/* public functions */
|
||||
@ -129,7 +134,7 @@ errors_init (Gimp *gimp,
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (log_domains); i++)
|
||||
g_log_set_handler (log_domains[i],
|
||||
G_LOG_LEVEL_MESSAGE,
|
||||
G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_CRITICAL,
|
||||
gimp_message_log_func, gimp);
|
||||
|
||||
g_log_set_handler ("GEGL",
|
||||
@ -176,7 +181,7 @@ gimp_third_party_message_log_func (const gchar *log_domain,
|
||||
* messages.
|
||||
*/
|
||||
gimp_show_message (gimp, NULL, GIMP_MESSAGE_WARNING,
|
||||
log_domain, message);
|
||||
log_domain, message, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -190,17 +195,45 @@ gimp_message_log_func (const gchar *log_domain,
|
||||
const gchar *message,
|
||||
gpointer data)
|
||||
{
|
||||
Gimp *gimp = data;
|
||||
static gint n_traces;
|
||||
GimpMessageSeverity severity = GIMP_MESSAGE_WARNING;
|
||||
Gimp *gimp = data;
|
||||
gchar *trace = NULL;
|
||||
|
||||
if (flags & G_LOG_LEVEL_CRITICAL)
|
||||
{
|
||||
severity = GIMP_MESSAGE_ERROR;
|
||||
|
||||
if (n_traces < MAX_TRACES)
|
||||
{
|
||||
/* Getting debug traces is time-expensive, and worse, some
|
||||
* critical errors have the bad habit to create more errors
|
||||
* (the first ones are therefore usually the most useful).
|
||||
* This is why we keep track of how many times we made traces
|
||||
* and stop doing them after a while.
|
||||
* Hence when this happens, critical errors are simply processed as
|
||||
* lower level errors.
|
||||
*/
|
||||
trace = gimp_get_stack_trace ();
|
||||
n_traces++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gimp)
|
||||
{
|
||||
gimp_show_message (gimp, NULL, GIMP_MESSAGE_WARNING, NULL, message);
|
||||
gimp_show_message (gimp, NULL, severity,
|
||||
NULL, message, trace);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s: %s\n\n",
|
||||
gimp_filename_to_utf8 (full_prog_name), message);
|
||||
if (trace)
|
||||
g_printerr ("Back trace:\n%s\n\n", trace);
|
||||
}
|
||||
|
||||
if (trace)
|
||||
g_free (trace);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -268,3 +301,91 @@ gimp_eek (const gchar *reason,
|
||||
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
gimp_get_stack_trace (void)
|
||||
{
|
||||
gchar *trace = NULL;
|
||||
#if defined(G_OS_UNIX)
|
||||
GString *gtrace = NULL;
|
||||
gchar buffer[256];
|
||||
ssize_t read_n;
|
||||
pid_t pid;
|
||||
int status;
|
||||
int out_fd[2];
|
||||
#endif
|
||||
|
||||
/* Though we should theoretically ask with GIMP_STACK_TRACE_QUERY, we
|
||||
* just assume yes right now. TODO: improve this!
|
||||
*/
|
||||
if (stack_trace_mode == GIMP_STACK_TRACE_NEVER)
|
||||
return NULL;
|
||||
|
||||
/* This works only on UNIX systems. On Windows, we'll have to find
|
||||
* another method, probably with DrMingW.
|
||||
*/
|
||||
#if defined(G_OS_UNIX)
|
||||
if (pipe (out_fd) == -1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This is a trick to get the stack trace inside a string.
|
||||
* GLib's g_on_error_stack_trace() unfortunately writes directly to
|
||||
* the standard output, which is a very unfortunate implementation.
|
||||
*/
|
||||
pid = fork ();
|
||||
if (pid == 0)
|
||||
{
|
||||
/* Child process. */
|
||||
|
||||
/* XXX I just don't understand why, but somehow the parent process
|
||||
* doesn't get the output if I don't print something first. I just
|
||||
* leave this very dirty hack until I figure out what's going on.
|
||||
*/
|
||||
printf(" ");
|
||||
|
||||
/* Redirect the debugger output. */
|
||||
dup2 (out_fd[1], STDOUT_FILENO);
|
||||
close (out_fd[0]);
|
||||
close (out_fd[1]);
|
||||
g_on_error_stack_trace (full_prog_name);
|
||||
_exit (0);
|
||||
}
|
||||
else if (pid > 0)
|
||||
{
|
||||
/* Main process. */
|
||||
waitpid (pid, &status, 0);
|
||||
}
|
||||
else if (pid == (pid_t) -1)
|
||||
{
|
||||
/* No trace can be done. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gtrace = g_string_new ("");
|
||||
|
||||
/* It is important to close the writing side of the pipe, otherwise
|
||||
* the read() will wait forever without getting the information that
|
||||
* writing is finished.
|
||||
*/
|
||||
close (out_fd[1]);
|
||||
|
||||
while ((read_n = read (out_fd[0], buffer, 256)) > 0)
|
||||
{
|
||||
g_string_append_len (gtrace, buffer, read_n);
|
||||
}
|
||||
close (out_fd[0]);
|
||||
|
||||
if (gtrace)
|
||||
trace = g_string_free (gtrace, FALSE);
|
||||
if (trace && strlen (g_strstrip (trace)) == 0)
|
||||
{
|
||||
/* Empty strings are the same as no strings. */
|
||||
g_free (trace);
|
||||
trace = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include "plug-in/gimpplugin.h"
|
||||
|
||||
#include "widgets/gimpcriticaldialog.h"
|
||||
#include "widgets/gimpdialogfactory.h"
|
||||
#include "widgets/gimpdockable.h"
|
||||
#include "widgets/gimperrorconsole.h"
|
||||
@ -55,6 +56,7 @@ typedef struct
|
||||
Gimp *gimp;
|
||||
gchar *domain;
|
||||
gchar *message;
|
||||
gchar *trace;
|
||||
GObject *handler;
|
||||
GimpMessageSeverity severity;
|
||||
} GimpLogMessageData;
|
||||
@ -64,13 +66,19 @@ static gboolean gui_message_idle (gpointer user_data);
|
||||
static gboolean gui_message_error_console (Gimp *gimp,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message);
|
||||
const gchar *message,
|
||||
const gchar *trace);
|
||||
static gboolean gui_message_error_dialog (Gimp *gimp,
|
||||
GObject *handler,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message);
|
||||
const gchar *message,
|
||||
const gchar *trace);
|
||||
static void gui_message_console (GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message,
|
||||
const gchar *trace);
|
||||
static gchar * gui_message_format (GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message);
|
||||
|
||||
@ -80,12 +88,13 @@ gui_message (Gimp *gimp,
|
||||
GObject *handler,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message)
|
||||
const gchar *message,
|
||||
const gchar *trace)
|
||||
{
|
||||
switch (gimp->message_handler)
|
||||
{
|
||||
case GIMP_ERROR_CONSOLE:
|
||||
if (gui_message_error_console (gimp, severity, domain, message))
|
||||
if (gui_message_error_console (gimp, severity, domain, message, trace))
|
||||
return;
|
||||
|
||||
gimp->message_handler = GIMP_MESSAGE_BOX;
|
||||
@ -104,6 +113,7 @@ gui_message (Gimp *gimp,
|
||||
data->gimp = gimp;
|
||||
data->domain = g_strdup (domain);
|
||||
data->message = g_strdup (message);
|
||||
data->trace = trace ? g_strdup (trace) : NULL;
|
||||
data->handler = handler? g_object_ref (handler) : NULL;
|
||||
data->severity = severity;
|
||||
|
||||
@ -112,14 +122,15 @@ gui_message (Gimp *gimp,
|
||||
data, g_free);
|
||||
return;
|
||||
}
|
||||
if (gui_message_error_dialog (gimp, handler, severity, domain, message))
|
||||
if (gui_message_error_dialog (gimp, handler, severity,
|
||||
domain, message, trace))
|
||||
return;
|
||||
|
||||
gimp->message_handler = GIMP_CONSOLE;
|
||||
/* fallthru */
|
||||
|
||||
case GIMP_CONSOLE:
|
||||
gui_message_console (severity, domain, message);
|
||||
gui_message_console (severity, domain, message, trace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -133,14 +144,18 @@ gui_message_idle (gpointer user_data)
|
||||
data->handler,
|
||||
data->severity,
|
||||
data->domain,
|
||||
data->message))
|
||||
data->message,
|
||||
data->trace))
|
||||
{
|
||||
gui_message_console (data->severity,
|
||||
data->domain,
|
||||
data->message);
|
||||
data->message,
|
||||
data->trace);
|
||||
}
|
||||
g_free (data->domain);
|
||||
g_free (data->message);
|
||||
if (data->trace)
|
||||
g_free (data->trace);
|
||||
if (data->handler)
|
||||
g_object_unref (data->handler);
|
||||
|
||||
@ -151,10 +166,13 @@ static gboolean
|
||||
gui_message_error_console (Gimp *gimp,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message)
|
||||
const gchar *message,
|
||||
const gchar *trace)
|
||||
{
|
||||
GtkWidget *dockable;
|
||||
|
||||
/* TODO: right now the error console does nothing with the trace. */
|
||||
|
||||
dockable = gimp_dialog_factory_find_widget (gimp_dialog_factory_get_singleton (),
|
||||
"gimp-error-console");
|
||||
|
||||
@ -255,16 +273,59 @@ global_error_dialog (void)
|
||||
FALSE);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
global_critical_dialog (void)
|
||||
{
|
||||
GdkScreen *screen;
|
||||
gint monitor;
|
||||
|
||||
monitor = gimp_get_monitor_at_pointer (&screen);
|
||||
|
||||
return gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (),
|
||||
screen, monitor,
|
||||
NULL /*ui_manager*/,
|
||||
"gimp-critical-dialog", -1,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gui_message_error_dialog (Gimp *gimp,
|
||||
GObject *handler,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message)
|
||||
const gchar *message,
|
||||
const gchar *trace)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *dialog;
|
||||
GtkMessageType type = GTK_MESSAGE_ERROR;
|
||||
|
||||
if (GIMP_IS_PROGRESS (handler))
|
||||
switch (severity)
|
||||
{
|
||||
case GIMP_MESSAGE_INFO: type = GTK_MESSAGE_INFO; break;
|
||||
case GIMP_MESSAGE_WARNING: type = GTK_MESSAGE_WARNING; break;
|
||||
case GIMP_MESSAGE_ERROR: type = GTK_MESSAGE_ERROR; break;
|
||||
}
|
||||
|
||||
if (trace)
|
||||
{
|
||||
/* Process differently when traces are included.
|
||||
* The reason is that this will take significant place, and cannot
|
||||
* be processed as a progress message or in the global dialog. It
|
||||
* will require its own dedicated dialog which will encourage
|
||||
* people to report the bug.
|
||||
*/
|
||||
gchar *text;
|
||||
|
||||
dialog = global_critical_dialog ();
|
||||
text = gui_message_format (severity, domain, message);
|
||||
gimp_critical_dialog_add (GIMP_CRITICAL_DIALOG (dialog),
|
||||
text, trace);
|
||||
g_free (text);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else if (GIMP_IS_PROGRESS (handler))
|
||||
{
|
||||
/* If there's already an error dialog associated with this
|
||||
* progress, then continue without trying gimp_progress_message().
|
||||
@ -278,15 +339,7 @@ gui_message_error_dialog (Gimp *gimp,
|
||||
}
|
||||
else if (GTK_IS_WIDGET (handler))
|
||||
{
|
||||
GtkWidget *parent = GTK_WIDGET (handler);
|
||||
GtkMessageType type = GTK_MESSAGE_ERROR;
|
||||
|
||||
switch (severity)
|
||||
{
|
||||
case GIMP_MESSAGE_INFO: type = GTK_MESSAGE_INFO; break;
|
||||
case GIMP_MESSAGE_WARNING: type = GTK_MESSAGE_WARNING; break;
|
||||
case GIMP_MESSAGE_ERROR: type = GTK_MESSAGE_ERROR; break;
|
||||
}
|
||||
GtkWidget *parent = GTK_WIDGET (handler);
|
||||
|
||||
dialog =
|
||||
gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (parent)),
|
||||
@ -324,11 +377,31 @@ gui_message_error_dialog (Gimp *gimp,
|
||||
static void
|
||||
gui_message_console (GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message)
|
||||
const gchar *message,
|
||||
const gchar *trace)
|
||||
{
|
||||
gchar *formatted_message;
|
||||
|
||||
formatted_message = gui_message_format (severity, domain, message);
|
||||
g_printerr ("%s\n\n", formatted_message);
|
||||
g_free (formatted_message);
|
||||
|
||||
if (trace)
|
||||
g_printerr ("Stack trace:\n%s\n\n", trace);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
gui_message_format (GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message)
|
||||
{
|
||||
const gchar *desc = "Message";
|
||||
gchar *formatted_message;
|
||||
|
||||
gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity,
|
||||
NULL, NULL, &desc, NULL);
|
||||
g_printerr ("%s-%s: %s\n\n", domain, desc, message);
|
||||
|
||||
formatted_message = g_strdup_printf ("%s-%s: %s", domain, desc, message);
|
||||
|
||||
return formatted_message;
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ void gui_message (Gimp *gimp,
|
||||
GObject *handler,
|
||||
GimpMessageSeverity severity,
|
||||
const gchar *domain,
|
||||
const gchar *message);
|
||||
const gchar *message,
|
||||
const gchar *trace);
|
||||
|
||||
|
||||
#endif /* __GUI_VTABLE_H__ */
|
||||
|
@ -61,7 +61,7 @@ message_invoker (GimpProcedure *procedure,
|
||||
if (gimp->plug_in_manager->current_plug_in)
|
||||
domain = gimp_plug_in_get_undo_desc (gimp->plug_in_manager->current_plug_in);
|
||||
gimp_show_message (gimp, G_OBJECT (progress), GIMP_MESSAGE_WARNING,
|
||||
domain, message);
|
||||
domain, message, NULL);
|
||||
}
|
||||
|
||||
return gimp_procedure_get_return_values (procedure, success,
|
||||
|
201
app/version.c
201
app/version.c
@ -38,15 +38,17 @@
|
||||
#include "gimp-intl.h"
|
||||
|
||||
|
||||
static void
|
||||
gimp_show_library_version (const gchar *package,
|
||||
gint build_time_major,
|
||||
gint build_time_minor,
|
||||
gint build_time_micro,
|
||||
gint run_time_major,
|
||||
gint run_time_minor,
|
||||
gint run_time_micro)
|
||||
static gchar *
|
||||
gimp_library_version (const gchar *package,
|
||||
gint build_time_major,
|
||||
gint build_time_minor,
|
||||
gint build_time_micro,
|
||||
gint run_time_major,
|
||||
gint run_time_minor,
|
||||
gint run_time_micro,
|
||||
gboolean localized)
|
||||
{
|
||||
gchar *lib_version;
|
||||
gchar *build_time_version;
|
||||
gchar *run_time_version;
|
||||
|
||||
@ -60,91 +62,156 @@ gimp_show_library_version (const gchar *package,
|
||||
run_time_micro);
|
||||
|
||||
/* show versions of libraries used by GIMP */
|
||||
g_print (_("using %s version %s (compiled against version %s)"),
|
||||
package, run_time_version, build_time_version);
|
||||
g_print ("\n");
|
||||
|
||||
lib_version = g_strdup_printf (localized ?
|
||||
_("using %s version %s (compiled against version %s)") :
|
||||
"using %s version %s (compiled against version %s)",
|
||||
package, run_time_version, build_time_version);
|
||||
g_free (run_time_version);
|
||||
g_free (build_time_version);
|
||||
|
||||
return lib_version;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_show_library_versions (void)
|
||||
static gchar *
|
||||
gimp_library_versions (gboolean localized)
|
||||
{
|
||||
gint gegl_major_version;
|
||||
gint gegl_minor_version;
|
||||
gint gegl_micro_version;
|
||||
gchar *lib_versions;
|
||||
gchar *lib_version;
|
||||
gchar *temp;
|
||||
gint gegl_major_version;
|
||||
gint gegl_minor_version;
|
||||
gint gegl_micro_version;
|
||||
|
||||
gegl_get_version (&gegl_major_version,
|
||||
&gegl_minor_version,
|
||||
&gegl_micro_version);
|
||||
|
||||
gimp_show_library_version ("GEGL",
|
||||
GEGL_MAJOR_VERSION,
|
||||
GEGL_MINOR_VERSION,
|
||||
GEGL_MICRO_VERSION,
|
||||
gegl_major_version,
|
||||
gegl_minor_version,
|
||||
gegl_micro_version);
|
||||
lib_versions = gimp_library_version ("GEGL",
|
||||
GEGL_MAJOR_VERSION,
|
||||
GEGL_MINOR_VERSION,
|
||||
GEGL_MICRO_VERSION,
|
||||
gegl_major_version,
|
||||
gegl_minor_version,
|
||||
gegl_micro_version,
|
||||
localized);
|
||||
|
||||
gimp_show_library_version ("GLib",
|
||||
GLIB_MAJOR_VERSION,
|
||||
GLIB_MINOR_VERSION,
|
||||
GLIB_MICRO_VERSION,
|
||||
glib_major_version,
|
||||
glib_minor_version,
|
||||
glib_micro_version);
|
||||
lib_version = gimp_library_version ("GLib",
|
||||
GLIB_MAJOR_VERSION,
|
||||
GLIB_MINOR_VERSION,
|
||||
GLIB_MICRO_VERSION,
|
||||
glib_major_version,
|
||||
glib_minor_version,
|
||||
glib_micro_version,
|
||||
localized);
|
||||
temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
|
||||
g_free (lib_versions);
|
||||
g_free (lib_version);
|
||||
lib_versions = temp;
|
||||
|
||||
gimp_show_library_version ("GdkPixbuf",
|
||||
GDK_PIXBUF_MAJOR,
|
||||
GDK_PIXBUF_MINOR,
|
||||
GDK_PIXBUF_MICRO,
|
||||
gdk_pixbuf_major_version,
|
||||
gdk_pixbuf_minor_version,
|
||||
gdk_pixbuf_micro_version);
|
||||
lib_version = gimp_library_version ("GdkPixbuf",
|
||||
GDK_PIXBUF_MAJOR,
|
||||
GDK_PIXBUF_MINOR,
|
||||
GDK_PIXBUF_MICRO,
|
||||
gdk_pixbuf_major_version,
|
||||
gdk_pixbuf_minor_version,
|
||||
gdk_pixbuf_micro_version,
|
||||
localized);
|
||||
temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
|
||||
g_free (lib_versions);
|
||||
g_free (lib_version);
|
||||
lib_versions = temp;
|
||||
|
||||
#ifndef GIMP_CONSOLE_COMPILATION
|
||||
gimp_show_library_version ("GTK+",
|
||||
GTK_MAJOR_VERSION,
|
||||
GTK_MINOR_VERSION,
|
||||
GTK_MICRO_VERSION,
|
||||
gtk_major_version,
|
||||
gtk_minor_version,
|
||||
gtk_micro_version);
|
||||
lib_version = gimp_library_version ("GTK+",
|
||||
GTK_MAJOR_VERSION,
|
||||
GTK_MINOR_VERSION,
|
||||
GTK_MICRO_VERSION,
|
||||
gtk_major_version,
|
||||
gtk_minor_version,
|
||||
gtk_micro_version,
|
||||
localized);
|
||||
temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
|
||||
g_free (lib_versions);
|
||||
g_free (lib_version);
|
||||
lib_versions = temp;
|
||||
#endif
|
||||
|
||||
gimp_show_library_version ("Pango",
|
||||
PANGO_VERSION_MAJOR,
|
||||
PANGO_VERSION_MINOR,
|
||||
PANGO_VERSION_MICRO,
|
||||
pango_version () / 100 / 100,
|
||||
pango_version () / 100 % 100,
|
||||
pango_version () % 100);
|
||||
lib_version = gimp_library_version ("Pango",
|
||||
PANGO_VERSION_MAJOR,
|
||||
PANGO_VERSION_MINOR,
|
||||
PANGO_VERSION_MICRO,
|
||||
pango_version () / 100 / 100,
|
||||
pango_version () / 100 % 100,
|
||||
pango_version () % 100,
|
||||
localized);
|
||||
temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
|
||||
g_free (lib_versions);
|
||||
g_free (lib_version);
|
||||
lib_versions = temp;
|
||||
|
||||
gimp_show_library_version ("Fontconfig",
|
||||
FC_MAJOR, FC_MINOR, FC_REVISION,
|
||||
FcGetVersion () / 100 / 100,
|
||||
FcGetVersion () / 100 % 100,
|
||||
FcGetVersion () % 100);
|
||||
lib_version = gimp_library_version ("Fontconfig",
|
||||
FC_MAJOR, FC_MINOR, FC_REVISION,
|
||||
FcGetVersion () / 100 / 100,
|
||||
FcGetVersion () / 100 % 100,
|
||||
FcGetVersion () % 100,
|
||||
localized);
|
||||
temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
|
||||
g_free (lib_versions);
|
||||
g_free (lib_version);
|
||||
lib_versions = temp;
|
||||
|
||||
g_print (_("using %s version %s (compiled against version %s)"),
|
||||
"Cairo", cairo_version_string (), CAIRO_VERSION_STRING);
|
||||
g_print ("\n");
|
||||
lib_version = g_strdup_printf (localized ?
|
||||
_("using %s version %s (compiled against version %s)") :
|
||||
"using %s version %s (compiled against version %s)",
|
||||
"Cairo", cairo_version_string (), CAIRO_VERSION_STRING);
|
||||
temp = g_strdup_printf ("%s\n%s\n", lib_versions, lib_version);
|
||||
g_free (lib_versions);
|
||||
g_free (lib_version);
|
||||
lib_versions = temp;
|
||||
|
||||
return lib_versions;
|
||||
}
|
||||
|
||||
void
|
||||
gimp_version_show (gboolean be_verbose)
|
||||
{
|
||||
g_print (_("%s version %s"), GIMP_NAME, GIMP_VERSION);
|
||||
g_print ("\n");
|
||||
gchar *version = gimp_version (be_verbose, TRUE);
|
||||
|
||||
g_print ("%s", version);
|
||||
|
||||
g_free (version);
|
||||
}
|
||||
|
||||
gchar *
|
||||
gimp_version (gboolean be_verbose,
|
||||
gboolean localized)
|
||||
{
|
||||
gchar *version;
|
||||
gchar *temp;
|
||||
|
||||
version = g_strdup_printf (localized ? _("%s version %s") : "%s version %s",
|
||||
GIMP_NAME, GIMP_VERSION);;
|
||||
temp = g_strconcat (version, "\n", NULL);
|
||||
g_free (version);
|
||||
version = temp;
|
||||
|
||||
if (be_verbose)
|
||||
{
|
||||
g_print ("git-describe: %s", GIMP_GIT_VERSION);
|
||||
g_print ("\n");
|
||||
g_print ("C compiler:\n%s", CC_VERSION);
|
||||
gchar *verbose_info;
|
||||
gchar *lib_versions;
|
||||
|
||||
g_print ("\n");
|
||||
gimp_show_library_versions ();
|
||||
lib_versions = gimp_library_versions (localized);
|
||||
verbose_info = g_strdup_printf ("git-describe: %s\n"
|
||||
"C compiler:\n%s\n%s",
|
||||
GIMP_GIT_VERSION, CC_VERSION,
|
||||
lib_versions);
|
||||
g_free (lib_versions);
|
||||
|
||||
temp = g_strconcat (version, verbose_info, NULL);
|
||||
g_free (version);
|
||||
g_free (verbose_info);
|
||||
version = temp;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
@ -19,12 +19,10 @@
|
||||
#define __VERSION_H__
|
||||
|
||||
|
||||
#ifndef GIMP_APP_GLUE_COMPILATION
|
||||
#error You must not #include "version.h" from an app/ subdir
|
||||
#endif
|
||||
|
||||
|
||||
void gimp_version_show (gboolean be_verbose);
|
||||
void gimp_version_show (gboolean be_verbose);
|
||||
gchar * gimp_version (gboolean be_verbose,
|
||||
gboolean localized);
|
||||
|
||||
|
||||
#endif /* __VERSION_H__ */
|
||||
|
@ -116,6 +116,8 @@ libappwidgets_a_sources = \
|
||||
gimpcontrollermouse.h \
|
||||
gimpcontrollerwheel.c \
|
||||
gimpcontrollerwheel.h \
|
||||
gimpcriticaldialog.c \
|
||||
gimpcriticaldialog.h \
|
||||
gimpcursor.c \
|
||||
gimpcursor.h \
|
||||
gimpcurveview.c \
|
||||
|
262
app/widgets/gimpcriticaldialog.c
Normal file
262
app/widgets/gimpcriticaldialog.c
Normal file
@ -0,0 +1,262 @@
|
||||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpcriticaldialog.c
|
||||
* Copyright (C) 2018 Jehan <jehan@gimp.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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gegl.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "libgimpbase/gimpbase.h"
|
||||
#include "libgimpwidgets/gimpwidgets.h"
|
||||
|
||||
#include "widgets-types.h"
|
||||
|
||||
#include "gimpcriticaldialog.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "gimp-intl.h"
|
||||
|
||||
#define GIMP_CRITICAL_RESPONSE_CLIPBOARD 1
|
||||
#define GIMP_CRITICAL_RESPONSE_URL 2
|
||||
|
||||
|
||||
static void gimp_critical_dialog_response (GtkDialog *dialog,
|
||||
gint response_id);
|
||||
|
||||
G_DEFINE_TYPE (GimpCriticalDialog, gimp_critical_dialog, GIMP_TYPE_DIALOG)
|
||||
|
||||
#define parent_class gimp_critical_dialog_parent_class
|
||||
|
||||
|
||||
static void
|
||||
gimp_critical_dialog_class_init (GimpCriticalDialogClass *klass)
|
||||
{
|
||||
GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
|
||||
|
||||
dialog_class->response = gimp_critical_dialog_response;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_critical_dialog_init (GimpCriticalDialog *dialog)
|
||||
{
|
||||
PangoAttrList *attrs;
|
||||
PangoAttribute *attr;
|
||||
gchar *text;
|
||||
gchar *version;
|
||||
GtkWidget *widget;
|
||||
GtkTextBuffer *buffer;
|
||||
const gchar *button1 = _("Copy bug information");
|
||||
const gchar *button2 = _("Open bug tracker");
|
||||
|
||||
gtk_window_set_role (GTK_WINDOW (dialog), "gimp-critical");
|
||||
|
||||
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
|
||||
button1, GIMP_CRITICAL_RESPONSE_CLIPBOARD,
|
||||
button2, GIMP_CRITICAL_RESPONSE_URL,
|
||||
_("_Close"), GTK_RESPONSE_CLOSE,
|
||||
NULL);
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
|
||||
gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
|
||||
|
||||
dialog->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||||
dialog->vbox, TRUE, TRUE, 0);
|
||||
gtk_widget_show (dialog->vbox);
|
||||
|
||||
/* The error label. */
|
||||
dialog->label = gtk_label_new (NULL);
|
||||
gtk_label_set_justify (GTK_LABEL (dialog->label), GTK_JUSTIFY_CENTER);
|
||||
gtk_label_set_selectable (GTK_LABEL (dialog->label), TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (dialog->vbox),
|
||||
dialog->label, FALSE, FALSE, 0);
|
||||
|
||||
attrs = pango_attr_list_new ();
|
||||
attr = pango_attr_weight_new (PANGO_WEIGHT_SEMIBOLD);
|
||||
pango_attr_list_insert (attrs, attr);
|
||||
gtk_label_set_attributes (GTK_LABEL (dialog->label), attrs);
|
||||
pango_attr_list_unref (attrs);
|
||||
|
||||
gtk_widget_show (dialog->label);
|
||||
|
||||
/* Generic "report a bug" instructions. */
|
||||
text = g_strdup_printf ("%s\n"
|
||||
" \xe2\x80\xa2 %s %s\n"
|
||||
" \xe2\x80\xa2 %s %s\n"
|
||||
" \xe2\x80\xa2 %s \n"
|
||||
" \xe2\x80\xa2 %s \n"
|
||||
" \xe2\x80\xa2 %s \n"
|
||||
" \xe2\x80\xa2 %s\n\n"
|
||||
"%s",
|
||||
_("To help us improve GIMP, you can report the bug with "
|
||||
"these simple steps:"),
|
||||
_("Copy the bug information to clipboard by clicking: "),
|
||||
button1,
|
||||
_("Open our bug tracker in browser by clicking: "),
|
||||
button2,
|
||||
_("Create a login if you don't have one yet."),
|
||||
_("Paste the clipboard text in a new bug report."),
|
||||
_("Add relevant information in English in the bug report "
|
||||
"explaining what you were doing when this error occurred."),
|
||||
_("This error may have left GIMP in an inconsistent state. "
|
||||
"It is advised to save your work and restart GIMP."),
|
||||
_("Note: you can also close the dialog directly but reporting "
|
||||
"bugs is the best way to make your software awesome."));
|
||||
widget = gtk_label_new (text);
|
||||
g_free (text);
|
||||
gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (dialog->vbox), widget, FALSE, FALSE, 0);
|
||||
gtk_widget_show (widget);
|
||||
|
||||
/* Bug details for developers. */
|
||||
widget = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_box_pack_start (GTK_BOX (dialog->vbox), widget, TRUE, TRUE, 0);
|
||||
gtk_widget_show (widget);
|
||||
|
||||
buffer = gtk_text_buffer_new (NULL);
|
||||
version = gimp_version (TRUE, FALSE);
|
||||
gtk_text_buffer_set_text (buffer, version, -1);
|
||||
g_free (version);
|
||||
|
||||
dialog->details = gtk_text_view_new_with_buffer (buffer);
|
||||
g_object_unref (buffer);
|
||||
gtk_text_view_set_editable (GTK_TEXT_VIEW (dialog->details), FALSE);
|
||||
gtk_widget_show (dialog->details);
|
||||
gtk_container_add (GTK_CONTAINER (widget), dialog->details);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_critical_dialog_response (GtkDialog *dialog,
|
||||
gint response_id)
|
||||
{
|
||||
GimpCriticalDialog *critical = GIMP_CRITICAL_DIALOG (dialog);
|
||||
|
||||
switch (response_id)
|
||||
{
|
||||
case GIMP_CRITICAL_RESPONSE_CLIPBOARD:
|
||||
{
|
||||
GtkClipboard *clipboard;
|
||||
|
||||
clipboard = gtk_clipboard_get_for_display (gdk_display_get_default (),
|
||||
GDK_SELECTION_CLIPBOARD);
|
||||
if (clipboard)
|
||||
{
|
||||
GtkTextBuffer *buffer;
|
||||
gchar *text;
|
||||
GtkTextIter start;
|
||||
GtkTextIter end;
|
||||
|
||||
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (critical->details));
|
||||
gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
|
||||
gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
|
||||
text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
|
||||
gtk_clipboard_set_text (clipboard, text, -1);
|
||||
g_free (text);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GIMP_CRITICAL_RESPONSE_URL:
|
||||
{
|
||||
const gchar *url;
|
||||
|
||||
/* XXX Ideally I'd find a way to prefill the bug report
|
||||
* through the URL or with POST data. But I could not find
|
||||
* any. Anyway since we may soon ditch bugzilla to follow
|
||||
* GNOME infrastructure changes, I don't want to waste too
|
||||
* much time digging into it.
|
||||
*/
|
||||
url = "https://bugzilla.gnome.org/enter_bug.cgi?product=GIMP";
|
||||
gtk_show_uri (gdk_screen_get_default (),
|
||||
url,
|
||||
gtk_get_current_event_time(),
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
case GTK_RESPONSE_DELETE_EVENT:
|
||||
case GTK_RESPONSE_CLOSE:
|
||||
default:
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* public functions */
|
||||
|
||||
GtkWidget *
|
||||
gimp_critical_dialog_new (const gchar *title)
|
||||
{
|
||||
g_return_val_if_fail (title != NULL, NULL);
|
||||
|
||||
return g_object_new (GIMP_TYPE_CRITICAL_DIALOG,
|
||||
"title", title,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gimp_critical_dialog_add (GimpCriticalDialog *dialog,
|
||||
const gchar *message,
|
||||
const gchar *trace)
|
||||
{
|
||||
GtkTextBuffer *buffer;
|
||||
GtkTextIter end;
|
||||
gchar *text;
|
||||
|
||||
if (! GIMP_IS_CRITICAL_DIALOG (dialog) || ! message || ! trace)
|
||||
{
|
||||
/* This is a bit hackish. We usually should use
|
||||
* g_return_if_fail(). But I don't want to end up in a critical
|
||||
* recursing loop if our code had bugs. We would crash GIMP with
|
||||
* a CRITICAL which would otherwise not have necessarily ended up
|
||||
* in a crash.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* The user text, which should be localized. */
|
||||
if (! gtk_label_get_text (GTK_LABEL (dialog->label)) ||
|
||||
strlen (gtk_label_get_text (GTK_LABEL (dialog->label))) == 0)
|
||||
{
|
||||
/* First critical error. Let's just display it. */
|
||||
text = g_strdup_printf (_("GIMP encountered a critical error: %s"),
|
||||
message);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Let's not display all errors. They will be in the bug report
|
||||
* part anyway.
|
||||
*/
|
||||
text = g_strdup_printf (_("GIMP encountered several critical errors!"));
|
||||
}
|
||||
gtk_label_set_text (GTK_LABEL (dialog->label),
|
||||
text);
|
||||
g_free (text);
|
||||
|
||||
/* The details text is untranslated on purpose. This is the message
|
||||
* meant to go to clipboard for the bug report. It has to be in
|
||||
* English.
|
||||
*/
|
||||
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dialog->details));
|
||||
gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
|
||||
text = g_strdup_printf ("\n\n> %s\n\nStack trace:\n%s", message, trace);
|
||||
gtk_text_buffer_insert (buffer, &end, text, -1);
|
||||
g_free (text);
|
||||
}
|
64
app/widgets/gimpcriticaldialog.h
Normal file
64
app/widgets/gimpcriticaldialog.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpcriticaldialog.h
|
||||
* Copyright (C) 2018 Jehan <jehan@gimp.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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GIMP_CRITICAL_DIALOG_H__
|
||||
#define __GIMP_CRITICAL_DIALOG_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
#define GIMP_TYPE_CRITICAL_DIALOG (gimp_critical_dialog_get_type ())
|
||||
#define GIMP_CRITICAL_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CRITICAL_DIALOG, GimpCriticalDialog))
|
||||
#define GIMP_CRITICAL_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CRITICAL_DIALOG, GimpCriticalDialogClass))
|
||||
#define GIMP_IS_CRITICAL_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CRITICAL_DIALOG))
|
||||
#define GIMP_IS_CRITICAL_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CRITICAL_DIALOG))
|
||||
#define GIMP_CRITICAL_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CRITICAL_DIALOG, GimpCriticalDialogClass))
|
||||
|
||||
|
||||
typedef struct _GimpCriticalDialogClass GimpCriticalDialogClass;
|
||||
|
||||
struct _GimpCriticalDialog
|
||||
{
|
||||
GimpDialog parent_instance;
|
||||
|
||||
GtkWidget *vbox;
|
||||
|
||||
GtkWidget *label;
|
||||
GtkWidget *details;
|
||||
};
|
||||
|
||||
struct _GimpCriticalDialogClass
|
||||
{
|
||||
GimpDialogClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType gimp_critical_dialog_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gimp_critical_dialog_new (const gchar *title);
|
||||
void gimp_critical_dialog_add (GimpCriticalDialog *dialog,
|
||||
const gchar *message,
|
||||
const gchar *trace);
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GIMP_CRITICAL_DIALOG_H__ */
|
@ -145,6 +145,7 @@ typedef struct _GimpSaveDialog GimpSaveDialog;
|
||||
/* misc dialogs */
|
||||
|
||||
typedef struct _GimpColorDialog GimpColorDialog;
|
||||
typedef struct _GimpCriticalDialog GimpCriticalDialog;
|
||||
typedef struct _GimpErrorDialog GimpErrorDialog;
|
||||
typedef struct _GimpMessageDialog GimpMessageDialog;
|
||||
typedef struct _GimpProgressDialog GimpProgressDialog;
|
||||
|
Reference in New Issue
Block a user