~geoubuntu/gnome-radio/trunk

« back to all changes in this revision

Viewing changes to src/record.c

  • Committer: Pojar Gheorghe–Ioan
  • Date: 2021-11-20 05:23:36 UTC
  • Revision ID: geoubuntu@gmail.com-20211120052336-r88illfotfxoimlq
* Initial release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2021  Pojar Gheorghe–Ioan  <geoubuntu@gmail.com>
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the tedecay of the GNU General Public License as
 
6
 * published by the Free Software Foundation; either version 2 of
 
7
 * the License, or (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 
16
 */
 
17
 
 
18
#include <config.h>
 
19
 
 
20
#include <glib/gi18n.h>
 
21
#include <gtk/gtk.h>
 
22
 
 
23
#include "builder_utils.h"
 
24
#include "rec_tech.h"
 
25
#include "record.h"
 
26
 
 
27
#define REDRAW_TIMEOUT 1
 
28
 
 
29
#define NOTIFICATION_TIMEOUT 5
 
30
 
 
31
static GtkWidget *window;
 
32
static GtkWidget *record_notification_revealer;
 
33
static GtkWidget *drawing_area;
 
34
static GtkWidget *name_entry;
 
35
static GtkWidget *size_label;
 
36
static GtkWidget *duration_label;
 
37
 
 
38
static guint notification_timeout_id;
 
39
 
 
40
static gboolean
 
41
cancel_idle (Recording *recording)
 
42
{
 
43
    recording_window_data_free (recording);
 
44
    recording_stop (recording);
 
45
 
 
46
    return G_SOURCE_REMOVE;
 
47
}
 
48
 
 
49
static gboolean
 
50
redraw_recording_window (Recording *recording)
 
51
{
 
52
    gchar *text;
 
53
 
 
54
    if (recording->error != NULL)
 
55
    {
 
56
        RecordingWindowData *data = (RecordingWindowData *) recording->window_data;
 
57
 
 
58
        data->redraw_recording_window_timeout_id = 0;
 
59
        return G_SOURCE_REMOVE;
 
60
    }
 
61
 
 
62
    text = g_format_size_full (recording->file_size, G_FORMAT_SIZE_LONG_FORMAT);
 
63
    gtk_label_set_text (GTK_LABEL (size_label), text);
 
64
    g_free (text);
 
65
 
 
66
    text = recording_time_to_string (recording->secs);
 
67
    gtk_label_set_text (GTK_LABEL (duration_label), text);
 
68
    g_free (text);
 
69
 
 
70
    return G_SOURCE_CONTINUE;
 
71
}
 
72
 
 
73
static gboolean
 
74
notification_close (gpointer user_data)
 
75
{
 
76
    g_clear_handle_id (&notification_timeout_id, g_source_remove);
 
77
    gtk_revealer_set_reveal_child (GTK_REVEALER (record_notification_revealer), FALSE);
 
78
 
 
79
    return G_SOURCE_REMOVE;
 
80
}
 
81
 
 
82
gboolean
 
83
on_record_notification_enter_notify_event (GtkWidget *widget,
 
84
                                           GdkEvent  *event,
 
85
                                           gpointer   user_data)
 
86
{
 
87
    g_clear_handle_id (&notification_timeout_id, g_source_remove);
 
88
 
 
89
    return GDK_EVENT_PROPAGATE;
 
90
}
 
91
 
 
92
gboolean
 
93
on_record_notification_leave_notify_event (GtkWidget *widget,
 
94
                                           GdkEvent  *event,
 
95
                                           gpointer   user_data)
 
96
{
 
97
    g_clear_handle_id (&notification_timeout_id, g_source_remove);
 
98
    notification_timeout_id = g_timeout_add_seconds (NOTIFICATION_TIMEOUT, G_SOURCE_FUNC (notification_close), NULL);
 
99
 
 
100
    return GDK_EVENT_PROPAGATE;
 
101
}
 
102
 
 
103
void
 
104
on_record_notification_details_button_clicked (GtkButton *button,
 
105
                                               gpointer   user_data)
 
106
{
 
107
    GtkWidget *dialog;
 
108
 
 
109
    g_clear_handle_id (&notification_timeout_id, g_source_remove);
 
110
    gtk_revealer_set_reveal_child (GTK_REVEALER (record_notification_revealer), FALSE);
 
111
 
 
112
    dialog = gtk_message_dialog_new (GTK_WINDOW (window),
 
113
                                     GTK_DIALOG_MODAL |
 
114
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
 
115
                                     GTK_MESSAGE_INFO,
 
116
                                     GTK_BUTTONS_OK,
 
117
                                     _("Encoding profile is missing"));
 
118
 
 
119
    gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
 
120
                                              "%s", _("An encoding profile is missing, the incoming data is captured as a raw audio file, but the file may take up a lot of disk space."));
 
121
 
 
122
    gtk_dialog_run (GTK_DIALOG (dialog));
 
123
    gtk_widget_destroy (dialog);
 
124
}
 
125
 
 
126
void
 
127
on_record_notification_close_button_clicked (GtkButton *button,
 
128
                                             gpointer   user_data)
 
129
{
 
130
    g_clear_handle_id (&notification_timeout_id, g_source_remove);
 
131
    gtk_revealer_set_reveal_child (GTK_REVEALER (record_notification_revealer), FALSE);
 
132
}
 
133
 
 
134
void
 
135
on_record_notification_revealer_unrealize (GtkWidget *widget,
 
136
                                           gpointer   user_data)
 
137
{
 
138
    g_clear_handle_id (&notification_timeout_id, g_source_remove);
 
139
}
 
140
 
 
141
void
 
142
run_recording_window (Recording *recording)
 
143
{
 
144
    RecordingWindowData *data = g_new0 (RecordingWindowData, 1);
 
145
 
 
146
    recording->window_data = data;
 
147
 
 
148
    if (recording->error == NULL)
 
149
    {
 
150
        g_autofree gchar *markup = NULL;
 
151
 
 
152
        data->window = window;
 
153
        data->drawing_area = drawing_area;
 
154
        data->name_entry = name_entry;
 
155
        data->redraw_recording_window_timeout_id = g_timeout_add_seconds (REDRAW_TIMEOUT,
 
156
                                                                          G_SOURCE_FUNC (redraw_recording_window),
 
157
                                                                          recording);
 
158
 
 
159
        markup = g_strdup_printf (_("Recording from station %s"), recording->station);
 
160
        data->inhibit_cookie = gtk_application_inhibit (GTK_APPLICATION (g_application_get_default ()),
 
161
                                                        GTK_WINDOW (data->window),
 
162
                                                        GTK_APPLICATION_INHIBIT_IDLE,
 
163
                                                        markup);
 
164
 
 
165
        gtk_widget_show (data->window);
 
166
 
 
167
        if (recording->profile == NULL)
 
168
        {
 
169
            gtk_revealer_set_reveal_child (GTK_REVEALER (record_notification_revealer), TRUE);
 
170
            notification_timeout_id = g_timeout_add_seconds (NOTIFICATION_TIMEOUT, G_SOURCE_FUNC (notification_close), NULL);
 
171
        }
 
172
    }
 
173
    else
 
174
    {
 
175
        if (g_error_matches (recording->error, RECORDING_ERROR, RECORDING_ERROR_MISSING_PLUGIN))
 
176
        {
 
177
            g_idle_add (G_SOURCE_FUNC (cancel_idle), recording);
 
178
        }
 
179
    }
 
180
}
 
181
 
 
182
void
 
183
on_stop_recording_button_clicked (GtkButton *button,
 
184
                                  gpointer   user_data)
 
185
{
 
186
    Recording *recording = (Recording *) user_data;
 
187
 
 
188
    g_idle_add (G_SOURCE_FUNC (cancel_idle), recording);
 
189
}
 
190
 
 
191
gboolean
 
192
on_recording_drawing_area_draw (GtkWidget *widget,
 
193
                                cairo_t   *cr,
 
194
                                gpointer   user_data)
 
195
{
 
196
    Recording *recording = (Recording *) user_data;
 
197
    GtkStyleContext *context;
 
198
    GdkRGBA color;
 
199
    int width, height, middle;
 
200
    gdouble scl;
 
201
    gdouble peak0, peak1, decay0, decay1;
 
202
 
 
203
    if (!gtk_widget_get_realized (widget))
 
204
    {
 
205
        return FALSE;
 
206
    }
 
207
 
 
208
    context = gtk_widget_get_style_context (widget);
 
209
    width = gtk_widget_get_allocated_width (widget);
 
210
    height = gtk_widget_get_allocated_height (widget);
 
211
 
 
212
    gtk_render_background (context, cr, 0, 0, width, height);
 
213
 
 
214
    middle = width >> 1;
 
215
    scl = middle / (-MIN_LEVEL_VALUE);
 
216
 
 
217
    peak0 = recording->peak[0] * scl;
 
218
    peak1 = recording->peak[1] * scl;
 
219
    decay0 = recording->decay[0] * scl;
 
220
    decay1 = recording->decay[1] * scl;
 
221
 
 
222
    gtk_style_context_get_color (context, gtk_style_context_get_state (context), &color);
 
223
 
 
224
    cairo_set_source_rgb (cr, color.red, color.green, color.blue);
 
225
    cairo_rectangle (cr, middle - decay0, 0, 2, height);
 
226
    cairo_fill (cr);
 
227
    cairo_rectangle (cr, (middle - 1) + decay1, 0, 2, height);
 
228
    cairo_fill (cr);
 
229
 
 
230
    cairo_set_source_rgba (cr, color.red, color.green, color.blue, 0.2);
 
231
    cairo_rectangle (cr, middle - peak0, 0, peak0 + peak1, height);
 
232
    cairo_fill (cr);
 
233
 
 
234
    return FALSE;
 
235
}
 
236
 
 
237
gboolean
 
238
on_recording_window_delete_event (GtkWidget *widget,
 
239
                                  GdkEvent  *event,
 
240
                                  gpointer   user_data)
 
241
{
 
242
    Recording *recording = (Recording *) user_data;
 
243
 
 
244
    g_idle_add (G_SOURCE_FUNC (cancel_idle), recording);
 
245
 
 
246
    return GDK_EVENT_STOP;
 
247
}
 
248
 
 
249
void
 
250
on_recording_details_expander_activate (GtkExpander *expander,
 
251
                                        gpointer     user_data)
 
252
{
 
253
    Recording *recording = (Recording *) user_data;
 
254
    guint text_length;
 
255
 
 
256
    gtk_entry_set_text (GTK_ENTRY ((name_entry)), recording->display_name);
 
257
    text_length = (guint) gtk_entry_get_text_length (GTK_ENTRY ((name_entry)));
 
258
    if (text_length == 0)
 
259
    {
 
260
        return;
 
261
    }
 
262
 
 
263
    gtk_editable_select_region (GTK_EDITABLE (name_entry), 0, -1);
 
264
 
 
265
    gtk_entry_grab_focus_without_selecting (GTK_ENTRY ((name_entry)));
 
266
}
 
267
 
 
268
static void
 
269
recording_name_entry_restore_original_name (GtkWidget *widget,
 
270
                                            gpointer   user_data)
 
271
{
 
272
    Recording *recording = (Recording *) user_data;
 
273
    g_autofree char *displayed_name = NULL;
 
274
 
 
275
    displayed_name = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
 
276
 
 
277
    if (strcmp (displayed_name, recording->display_name) != 0)
 
278
    {
 
279
        gtk_entry_set_text (GTK_ENTRY (widget), recording->display_name);
 
280
    }
 
281
 
 
282
    gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
 
283
}
 
284
 
 
285
void
 
286
on_recording_name_entry_activate (GtkEntry *entry,
 
287
                                  gpointer  user_data)
 
288
{
 
289
    Recording *recording = (Recording *) user_data;
 
290
    g_autofree char *new_name = NULL;
 
291
 
 
292
    new_name = g_strstrip (gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1));
 
293
    /* Special case: silently revert text if new text is empty */
 
294
    if (strlen (new_name) == 0)
 
295
    {
 
296
        recording_name_entry_restore_original_name (GTK_WIDGET (entry), recording);
 
297
    }
 
298
    else
 
299
    {
 
300
        /* Do not rename if not changed since we read the display name.
 
301
         *  This is needed so that we do not save the display name to the
 
302
         *  file when nothing is changed */
 
303
        if (strcmp (new_name, recording->display_name) != 0)
 
304
        {
 
305
            g_free (recording->display_name);
 
306
            recording->display_name = g_strdup (new_name);
 
307
 
 
308
            recording_output_file_rename (recording);
 
309
        }
 
310
    }
 
311
 
 
312
    gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
 
313
}
 
314
 
 
315
static gboolean
 
316
on_recording_name_entry_undo (GtkWidget *widget,
 
317
                              gpointer   user_data)
 
318
{
 
319
    Recording *recording = (Recording *) user_data;
 
320
 
 
321
    recording_name_entry_restore_original_name (widget, recording);
 
322
 
 
323
    return GDK_EVENT_STOP;
 
324
}
 
325
 
 
326
gboolean
 
327
on_recording_name_entry_event (GtkWidget *widget,
 
328
                               GdkEvent  *event,
 
329
                               gpointer   user_data)
 
330
{
 
331
    Recording *recording = (Recording *) user_data;
 
332
    guint keyval;
 
333
    GdkModifierType state;
 
334
 
 
335
    if (gdk_event_get_event_type (event) != GDK_KEY_PRESS)
 
336
    {
 
337
        return GDK_EVENT_PROPAGATE;
 
338
    }
 
339
 
 
340
    if (G_UNLIKELY (!gdk_event_get_keyval (event, &keyval)))
 
341
    {
 
342
        g_return_val_if_reached (GDK_EVENT_PROPAGATE);
 
343
    }
 
344
    if (G_UNLIKELY (!gdk_event_get_state (event, &state)))
 
345
    {
 
346
        g_return_val_if_reached (GDK_EVENT_PROPAGATE);
 
347
    }
 
348
 
 
349
    if (keyval == GDK_KEY_z && (state & GDK_CONTROL_MASK) != 0)
 
350
    {
 
351
        return on_recording_name_entry_undo (widget, recording);
 
352
    }
 
353
 
 
354
    return GDK_EVENT_PROPAGATE;
 
355
}
 
356
 
 
357
void
 
358
on_recording_location_label_realize (GtkWidget *widget,
 
359
                                     gpointer   user_data)
 
360
{
 
361
    Recording *recording = (Recording *) user_data;
 
362
    g_autoptr (GFile) parent = NULL;
 
363
    g_autofree char *name = NULL;
 
364
 
 
365
    if (recording->file == NULL)
 
366
    {
 
367
        return;
 
368
    }
 
369
 
 
370
    parent = g_file_get_parent (recording->file);
 
371
    name = g_file_get_parse_name (parent);
 
372
 
 
373
    gtk_label_set_text (GTK_LABEL (widget), name);
 
374
}
 
375
 
 
376
GtkWidget *
 
377
recording_window (Recording *recording)
 
378
{
 
379
    g_autoptr (GtkBuilder) builder = NULL;
 
380
    GtkWidget *widget;
 
381
    g_autofree gchar *markup = NULL;
 
382
 
 
383
    builder = builder_load (GNOME_RADIO_UI_RESOURCE_PATH "record.ui", recording);
 
384
 
 
385
    window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
 
386
 
 
387
    widget = GTK_WIDGET (gtk_builder_get_object (builder, "title_label"));
 
388
 
 
389
    markup = g_markup_printf_escaped (_("Recording from station %s"), recording->station);
 
390
    gtk_label_set_markup (GTK_LABEL (widget), markup);
 
391
 
 
392
    record_notification_revealer = GTK_WIDGET (gtk_builder_get_object (builder, "record_notification_revealer"));
 
393
 
 
394
    drawing_area = GTK_WIDGET (gtk_builder_get_object (builder, "drawing_area"));
 
395
 
 
396
    name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry"));
 
397
 
 
398
    size_label = GTK_WIDGET (gtk_builder_get_object (builder, "size_label"));
 
399
 
 
400
    duration_label = GTK_WIDGET (gtk_builder_get_object (builder, "duration_label"));
 
401
 
 
402
    return window;
 
403
}