Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add busy cursors to history paste/compress/discard #17301

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ FILE(GLOB SOURCE_FILES
"dtgtk/gradientslider.c"
"dtgtk/icon.c"
"dtgtk/paint.c"
"dtgtk/progress.c"
"dtgtk/range.c"
"dtgtk/resetlabel.c"
"dtgtk/sidepanel.c"
Expand Down
41 changes: 37 additions & 4 deletions src/common/history.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include "develop/blend.h"
#include "develop/develop.h"
#include "develop/masks.h"
#include "dtgtk/progress.h"
#include "gui/gtk.h"
#include "gui/hist_dialog.h"
#include "imageio/imageio_common.h"

Expand Down Expand Up @@ -1369,8 +1371,14 @@ void dt_history_truncate_on_image(const dt_imgid_t imgid,

int dt_history_compress_on_list(const GList *imgs)
{
if(!imgs)
return 0;
int uncompressed=0;

unsigned num_images = g_list_length((GList*)imgs);
dt_progressbar_params_t *progbar = dt_progressbar_create(_("compress history for %d images"),
NULL, num_images, TRUE);
dt_progressbar_start(progbar);
// Get the list of selected images
for(const GList *l = imgs; l; l = g_list_next(l))
{
Expand Down Expand Up @@ -1444,8 +1452,12 @@ int dt_history_compress_on_list(const GList *imgs)

dt_unlock_image(imgid);
dt_history_hash_write_from_history(imgid, DT_HISTORY_HASH_CURRENT);
if(!dt_progressbar_step(progbar))
break;
}

dt_progressbar_done(progbar);
dt_progressbar_destroy(progbar);
return uncompressed;
}

Expand Down Expand Up @@ -1904,6 +1916,10 @@ gboolean dt_history_paste_on_list(const GList *list,
if(undo)
dt_undo_start_group(darktable.undo, DT_UNDO_LT_HISTORY);

unsigned num_images = g_list_length((GList*)list);
dt_progressbar_params_t *progbar = dt_progressbar_create(_("paste history to %d images"),
NULL, num_images, TRUE);
dt_progressbar_start(progbar);
for(GList *l = (GList *)list; l; l = g_list_next(l))
{
const int dest = GPOINTER_TO_INT(l->data);
Expand All @@ -1912,6 +1928,8 @@ gboolean dt_history_paste_on_list(const GList *list,
darktable.view_manager->copy_paste.selops,
darktable.view_manager->copy_paste.copy_iop_order,
darktable.view_manager->copy_paste.full_copy);
if(!dt_progressbar_step(progbar))
break;
}

if(undo)
Expand All @@ -1923,7 +1941,8 @@ gboolean dt_history_paste_on_list(const GList *list,
{
dt_dev_pixelpipe_rebuild(darktable.develop);
}

dt_progressbar_done(progbar);
dt_progressbar_destroy(progbar);
return TRUE;
}

Expand Down Expand Up @@ -1955,6 +1974,10 @@ gboolean dt_history_paste_parts_on_list(const GList *list,
return FALSE;
}

unsigned num_images = g_list_length((GList*)l_copy);
dt_progressbar_params_t *progbar = dt_progressbar_create(_("paste history to %d images"),
NULL, num_images, TRUE);
dt_progressbar_start(progbar);
if(undo)
dt_undo_start_group(darktable.undo, DT_UNDO_LT_HISTORY);

Expand All @@ -1966,6 +1989,8 @@ gboolean dt_history_paste_parts_on_list(const GList *list,
darktable.view_manager->copy_paste.selops,
darktable.view_manager->copy_paste.copy_iop_order,
darktable.view_manager->copy_paste.full_copy);
if(!dt_progressbar_step(progbar))
break;
}

if(undo)
Expand All @@ -1980,7 +2005,8 @@ gboolean dt_history_paste_parts_on_list(const GList *list,
{
dt_dev_pixelpipe_rebuild(darktable.develop);
}

dt_progressbar_done(progbar);
dt_progressbar_destroy(progbar);
return TRUE;
}

Expand All @@ -1990,8 +2016,12 @@ gboolean dt_history_delete_on_list(const GList *list,
if(!list) // do we have any images on which to operate?
return FALSE;

dt_gui_cursor_set_busy();
unsigned num_images = g_list_length((GList*)list);
dt_progressbar_params_t *progbar = dt_progressbar_create(_("discard history for %d images"),
NULL, num_images, TRUE);
dt_progressbar_start(progbar);
if(undo) dt_undo_start_group(darktable.undo, DT_UNDO_LT_HISTORY);
dt_gui_process_events();

for(GList *l = (GList *)list; l; l = g_list_next(l))
{
Expand All @@ -2014,12 +2044,15 @@ gboolean dt_history_delete_on_list(const GList *list,
when the mimpap will be recreated */
if(darktable.collection->params.sorts[DT_COLLECTION_SORT_ASPECT_RATIO])
dt_image_set_aspect_ratio(imgid, FALSE);
if(!dt_progressbar_step(progbar))
break;
}

DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_TAG_CHANGED);

if(undo) dt_undo_end_group(darktable.undo);
dt_gui_cursor_clear_busy();
dt_progressbar_done(progbar);
dt_progressbar_destroy(progbar);
return TRUE;
}

Expand Down
140 changes: 140 additions & 0 deletions src/dtgtk/progress.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
This file is part of darktable,
Copyright (C) 2024 darktable developers.

darktable 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.

darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
*/

#include "dtgtk/progress.h"
#include "gui/gtk.h"

dt_progressbar_params_t *dt_progressbar_create(const char *title, const char *message,
unsigned total_items, gboolean can_cancel)
{
dt_progressbar_params_t *params = calloc(sizeof(dt_progressbar_params_t),1);
if(params)
{
params->title = g_strdup(title);
params->message = g_strdup(message);
params->total_items = total_items;
params->can_cancel = can_cancel;
params->min_for_dialog = 10;
}
return params;
}

static void _progress_callback(dt_progressbar_params_t *params)
{
params->cancelled = TRUE;
}

gboolean dt_progressbar_start(dt_progressbar_params_t *prog)
{
if(!prog)
{
dt_gui_cursor_set_busy();
return FALSE;
}
prog->processed_items = 0;
prog->cancelled = FALSE;
if(prog->total_items >= prog->min_for_dialog || prog->total_items == 0)
{
// create a modal dialog with a progress bar
GtkWindow *main_window = NULL; //FIXME
GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR;
gchar *title = g_strdup_printf(prog->title,prog->total_items);
prog->dialog = gtk_dialog_new_with_buttons(title,
main_window,
flags,
prog->can_cancel ? _("cancel") : NULL,
GTK_RESPONSE_CANCEL,
NULL);
g_free(title);
gtk_widget_set_name(prog->dialog, "progressmeter");
prog->progress_bar = gtk_progress_bar_new();
GtkProgressBar *progress_bar = GTK_PROGRESS_BAR(prog->progress_bar);
gtk_progress_bar_set_show_text(progress_bar, TRUE);
GtkBox *content = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(prog->dialog)));
gtk_box_pack_end(content, prog->progress_bar, FALSE, FALSE, 0);
if(prog->message)
gtk_progress_bar_set_text(progress_bar, prog->message);
gtk_window_set_keep_above(GTK_WINDOW(prog->dialog), TRUE);
gtk_window_set_modal(GTK_WINDOW(prog->dialog),TRUE);
g_signal_connect_swapped(G_OBJECT(prog->dialog), "response", G_CALLBACK(_progress_callback), prog);
gtk_widget_show_all(prog->dialog);
// give Gtk a chance to update the screen
dt_gui_process_events();
}
else
{
dt_gui_cursor_set_busy();
}
return TRUE;
}

gboolean dt_progressbar_step(dt_progressbar_params_t *prog)
{
if(!prog)
return TRUE; // if no progress bar requested, user should continue until items exhausted
prog->processed_items++;
if(prog->total_items >= prog->min_for_dialog)
{
// update the dialog
GtkProgressBar *progress_bar = GTK_PROGRESS_BAR(prog->progress_bar);
if(prog->total_items == 0)
gtk_progress_bar_pulse(progress_bar);
else
gtk_progress_bar_set_fraction(progress_bar, prog->processed_items / (double)prog->total_items);
dt_gui_process_events();
}
// indicate whether user should continue processing
gboolean more = prog->total_items == 0 || prog->processed_items < prog->total_items;
return more && !prog->cancelled;
}

gboolean dt_progressbar_done(dt_progressbar_params_t *prog)
{
if(prog && (prog->total_items >= prog->min_for_dialog || prog->total_items == 0))
{
// close the dialog window
g_signal_handlers_disconnect_by_func(G_OBJECT(prog->dialog), G_CALLBACK(_progress_callback), prog);
gtk_widget_destroy(prog->dialog);
prog->dialog = NULL;
dt_gui_process_events();
}
else
{
// revert to non-busy cursor
dt_gui_cursor_clear_busy();
}
return TRUE;
}

void dt_progressbar_destroy(dt_progressbar_params_t *params)
{
if(params)
{
g_free(params->title);
g_free(params->message);
if(params->dialog)
{
g_signal_handlers_disconnect_by_func(G_OBJECT(params->dialog), G_CALLBACK(_progress_callback), params);
gtk_widget_destroy(params->dialog);
params->dialog = NULL;
}
}
free(params);
}


52 changes: 52 additions & 0 deletions src/dtgtk/progress.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
This file is part of darktable,
Copyright (C) 2024 darktable developers.

darktable 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.

darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
*/

#include <gtk/gtk.h>

typedef struct {
// items set up by caller before starting
char *title; // dialog title
char *message; // optional message in content area (NULL if none)
unsigned total_items; // the total number of items we'll be processsing
unsigned min_for_dialog; // only actually show the progress bar if at least this many
gboolean can_cancel; // is user allowed to cancel the processing?
// items which get updated as we progress; user should treat these as read-only
unsigned processed_items;
gboolean cancelled;
// internal use only
GtkWidget *dialog;
GtkWidget *progress_bar;
} dt_progressbar_params_t;


// allocate/free a parameter block for the progress bar dialog
dt_progressbar_params_t *dt_progressbar_create(const char *title,
const char *message,
unsigned total_items,
gboolean can_cancel);
void dt_progressbar_destroy(dt_progressbar_params_t *params);

// initialize the progress bar and put up a modal dialog if total_items > min_for_dialog
// if the number of items is not large enough to warrant a dialog, turn on the busy cursor
gboolean dt_progressbar_start(dt_progressbar_params_t *prog);
// we have processed one item, so update the progress bar if it is being displayed
// returns TRUE if iteration should continue, FALSE if the user cancelled
gboolean dt_progressbar_step(dt_progressbar_params_t *prog);
// clean up: remove dialog or unset busy cursor, as appropriate
gboolean dt_progressbar_done(dt_progressbar_params_t *prog);

Loading
Loading