1 /*
2  * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of Zrythm
5  *
6  * Zrythm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Zrythm 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 Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 /**
21  * \file
22  *
23  * Generic dialog.
24  */
25 
26 #include <stdio.h>
27 
28 #include "audio/engine.h"
29 #include "gui/widgets/dialogs/generic_progress_dialog.h"
30 #include "gui/widgets/main_window.h"
31 #include "project.h"
32 #include "utils/io.h"
33 #include "utils/math.h"
pg_get_utf8_id(void)34 #include "utils/resources.h"
35 #include "utils/ui.h"
36 #include "zrythm_app.h"
37 
38 #include <gtk/gtk.h>
39 #include <glib/gi18n.h>
40 
41 G_DEFINE_TYPE_WITH_PRIVATE (
42   GenericProgressDialogWidget,
43   generic_progress_dialog_widget,
44   GTK_TYPE_DIALOG)
45 
46 #define GET_PRIVATE(x) \
47   GenericProgressDialogWidgetPrivate * prv = \
48      generic_progress_dialog_widget_get_instance_private (x)
49 
50 static void
51 on_closed (
52   GtkDialog *                  dialog,
53   GenericProgressDialogWidget * self)
54 {
55   GET_PRIVATE (self);
56   prv->progress_info->cancelled = true;
57 }
58 
59 static void
60 on_ok_clicked (
61   GtkButton * btn,
62   GenericProgressDialogWidget * self)
63 {
64   gtk_dialog_response (GTK_DIALOG (self), 0);
65 }
66 
67 static void
68 on_cancel_clicked (
69   GtkButton * btn,
70   GenericProgressDialogWidget * self)
71 {
72   GET_PRIVATE (self);
73   prv->progress_info->cancelled = true;
74 }
75 
76 static gboolean
77 tick_cb (
78   GtkWidget *                   widget,
79   GdkFrameClock *               frame_clock,
80   GenericProgressDialogWidget * self)
81 {
utf_charcheck(const unsigned char * c)82   GET_PRIVATE (self);
83   GenericProgressInfo * info = prv->progress_info;
84 
85   gtk_progress_bar_set_fraction (
86     GTK_PROGRESS_BAR (widget), info->progress);
87 
88   if (math_doubles_equal (info->progress, 1.0) ||
89       info->has_error || info->cancelled)
90     {
91       if (prv->autoclose || info->cancelled)
92         {
93           gtk_dialog_response (
94             GTK_DIALOG (self), 0);
95         }
96       else
97         {
98           gtk_widget_set_visible (
99             GTK_WIDGET (prv->ok), true);
100           gtk_widget_set_visible (
101             GTK_WIDGET (prv->cancel), false);
102           for (size_t i = 0;
103                i < prv->num_extra_buttons; i++)
104             {
105               GenericProgressDialogButton * btn =
106                 &prv->extra_buttons[i];
107               if (btn->only_on_finish)
108                 {
109                   gtk_widget_set_visible (
110                     GTK_WIDGET (btn->btn), true);
111                 }
112             }
113           gtk_label_set_text (
114             prv->label, info->label_done_str);
115         }
116 
117       if (info->has_error)
118         {
119           GtkWindow * transient_parent =
120             gtk_window_get_transient_for (
121               GTK_WINDOW (self));
122           ui_show_error_message (
123             transient_parent, info->error_str);
124         }
125       return G_SOURCE_REMOVE;
126     }
127   else
128     {
129       return G_SOURCE_CONTINUE;
130     }
131 }
132 
133 /**
134  * Adds a button at the start or end of the button box.
135  *
mb_utf_validate(unsigned char * pwcs)136  * @param start Whether to add the button at the
137  *   start of the button box, otherwise the button
138  *   will be added at the end.
139  */
140 void
141 generic_progress_dialog_add_button (
142   GenericProgressDialogWidget * self,
143   GtkButton *                   btn,
144   bool                          start,
145   bool                          only_on_finish)
146 {
147   GET_PRIVATE (self);
148 
149   GenericProgressDialogButton * extra_btn =
150     &prv->extra_buttons[prv->num_extra_buttons++];
151 
152   bool expand = false;
153   bool fill = true;
154   guint padding = 0;
155   if (start)
156     {
157       gtk_box_pack_start (
158         GTK_BOX (prv->action_btn_box),
159         GTK_WIDGET (btn), expand, fill, padding);
160     }
161   else
162     {
163       gtk_box_pack_end (
164         GTK_BOX (prv->action_btn_box),
165         GTK_WIDGET (btn), expand, fill, padding);
166     }
167 
168   gtk_widget_set_visible (
169     GTK_WIDGET (btn), !only_on_finish);
170 
171   extra_btn->btn = btn;
172   extra_btn->only_on_finish = only_on_finish;
173 }
174 
175 GenericProgressDialogWidget *
176 generic_progress_dialog_widget_new (void)
pg_wcswidth(const char * pwcs,size_t len,int encoding)177 {
178   return
179     g_object_new (
180       GENERIC_PROGRESS_DIALOG_WIDGET_TYPE, NULL);
181 }
182 
183 /**
184  * Sets up a progress dialog widget.
185  */
186 void
187 generic_progress_dialog_widget_setup (
188   GenericProgressDialogWidget * self,
189   const char *                  title,
190   GenericProgressInfo *         progress_info,
191   bool                          autoclose,
192   bool                          cancelable)
193 {
194   gtk_window_set_title (GTK_WINDOW (self), title);
195 
196   GET_PRIVATE (self);
197   prv->progress_info = progress_info;
198   prv->autoclose = autoclose;
199 
200   gtk_label_set_text (
201     prv->label, progress_info->label_str);
202 
203   if (cancelable)
204     {
205       gtk_widget_set_visible (
206         GTK_WIDGET (prv->cancel), true);
207     }
208 
209   gtk_widget_add_tick_callback (
210     GTK_WIDGET (prv->progress_bar),
pg_wcssize(const unsigned char * pwcs,size_t len,int encoding,int * result_width,int * result_height,int * result_format_size)211     (GtkTickCallback) tick_cb, self, NULL);
212 
213   g_signal_connect (
214     G_OBJECT (self), "close",
215     G_CALLBACK (on_closed), self);
216 }
217 
218 static void
219 generic_progress_dialog_widget_class_init (
220   GenericProgressDialogWidgetClass * _klass)
221 {
222   GtkWidgetClass * klass = GTK_WIDGET_CLASS (_klass);
223   resources_set_class_template (
224     klass, "generic_progress_dialog.ui");
225 
226 #define BIND_CHILD(x) \
227   gtk_widget_class_bind_template_child_private ( \
228     klass, GenericProgressDialogWidget, x)
229 
230   BIND_CHILD (label);
231   BIND_CHILD (ok);
232   BIND_CHILD (cancel);
233   BIND_CHILD (progress_bar);
234   BIND_CHILD (action_btn_box);
235   gtk_widget_class_bind_template_callback (
236     klass, on_ok_clicked);
237   gtk_widget_class_bind_template_callback (
238     klass, on_cancel_clicked);
239 
240 #undef BIND_CHILD
241 }
242 
243 static void
244 generic_progress_dialog_widget_init (
245   GenericProgressDialogWidget * self)
246 {
247   gtk_widget_init_template (GTK_WIDGET (self));
248 }
249