1 /* -*- c-basic-offset: 4 -*- */
2 /* wavbreaker - A tool to split a wave file up into multiple waves.
3  * Copyright (C) 2002-2006 Timothy Robinson
4  * Copyright (C) 2006-2007 Thomas Perl
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #include <config.h>
22 
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <math.h>
27 #include <limits.h>
28 #include <sys/stat.h>
29 #include <gtk/gtk.h>
30 #include <string.h>
31 #include <libgen.h>
32 #include <errno.h>
33 #include <time.h>
34 #include <signal.h>
35 
36 #include "wavbreaker.h"
37 #include "sample.h"
38 #include "about.h"
39 #include "appconfig.h"
40 #include "autosplit.h"
41 #include "saveas.h"
42 #include "popupmessage.h"
43 #include "overwritedialog.h"
44 #include "toc.h"
45 #include "cue.h"
46 #include "reallyquit.h"
47 #include "guimerge.h"
48 #include "moodbar.h"
49 #include "draw.h"
50 
51 #include <locale.h>
52 #include "gettext.h"
53 
54 #define APPNAME "wavbreaker"
55 
56 #define SILENCE_MIN_LENGTH 4
57 
58 static struct WaveformSurface *sample_surface;
59 static struct WaveformSurface *summary_surface;
60 
61 static GtkWidget *main_window;
62 static GtkWidget *header_bar;
63 static GtkWidget *header_bar_save_button;
64 static GtkWidget *vpane1, *vpane2;
65 static GtkWidget *scrollbar;
66 static GtkAdjustment *adj;
67 static GtkWidget *draw;
68 static GtkWidget *draw_summary;
69 static GtkWidget *play_button;
70 static GtkWidget *jump_to_popover;
71 static GtkWidget *autosplit_popover;
72 static GtkWidget *menu_popover;
73 
74 static GtkWidget *cursor_marker_spinner;
75 static GtkWidget *cursor_marker_min_spinner;
76 static GtkWidget *cursor_marker_sec_spinner;
77 static GtkWidget *cursor_marker_subsec_spinner;
78 static GtkWidget *button_seek_backward;
79 static GtkWidget *button_jump_to_time;
80 static GtkWidget *button_seek_forward;
81 static GtkWidget *button_auto_split;
82 static GtkWidget *button_add_break;
83 static GtkWidget *button_remove_break;
84 
85 static GtkAdjustment *cursor_marker_spinner_adj;
86 static GtkAdjustment *cursor_marker_min_spinner_adj;
87 static GtkAdjustment *cursor_marker_sec_spinner_adj;
88 static GtkAdjustment *cursor_marker_subsec_spinner_adj;
89 
90 static GraphData graphData;
91 
92 static MoodbarData *moodbarData;
93 
94 static gulong cursor_marker;
95 static gulong play_marker;
96 static int pixmap_offset;
97 char *sample_filename = NULL;
98 struct stat sample_stat;
99 static gboolean overwrite_track_names = FALSE;
100 
101 // one-shot idle_add-style event sources
102 static guint open_file_source_id;
103 static guint redraw_source_id;
104 
105 // timeout-based (periodic) progress UI update event sources
106 static guint file_open_progress_source_id;
107 static guint play_progress_source_id;
108 static guint file_write_progress_source_id;
109 
110 static gdouble progress_pct;
111 static WriteInfo write_info;
112 
113 typedef struct CursorData_ CursorData;
114 
115 struct CursorData_ {
116     gulong marker;
117     gboolean is_equal;
118 };
119 
120 enum {
121     COLUMN_WRITE,
122     COLUMN_FILENAME,
123     COLUMN_TIME,
124     COLUMN_DURATION,
125     COLUMN_OFFSET,
126     COLUMN_EDITABLE,
127     NUM_COLUMNS
128 };
129 
130 static GList *track_break_list = NULL;
131 static GtkListStore *store = NULL;
132 GtkWidget *treeview;
133 
134 /*
135  *-------------------------------------------------------------------------
136  * Function Prototypes
137  *-------------------------------------------------------------------------
138  */
139 
140 /* Track Break Functions */
141 static gboolean track_break_button_press(GtkWidget *widget,
142     GdkEventButton *event, gpointer user_data);
143 void track_break_write_toggled(GtkWidget *widget, gchar *path_str,
144     gpointer data);
145 void track_break_filename_edited(GtkCellRendererText *cell,
146     const gchar *path_str, const gchar *new_text, gpointer user_data);
147 void track_break_start_time_edited(GtkCellRendererText *cell,
148     const gchar *path_str, const gchar *new_text, gpointer user_data);
149 guint track_break_find_offset();
150 void track_break_delete_entry();
151 void track_break_setup_filename(gpointer data, gpointer user_data);
152 void track_break_rename( gboolean overwrite);
153 void track_break_add_to_model(gpointer data, gpointer user_data);
154 void track_break_add_entry();
155 void track_break_set_durations();
156 void track_break_set_duration(gpointer data, gpointer user_data);
157 
158 int track_breaks_export_to_file( char* filename);
159 int track_breaks_load_from_file( gchar const *filename);
160 void track_break_write_text( gpointer data, gpointer user_data);
161 void track_break_write_cue( gpointer data, gpointer user_data);
162 
163 /* File Functions */
164 void set_sample_filename(const char *f);
165 static void open_file();
166 static void set_title( char* title);
167 
168 /* Sample and Summary Display Functions */
169 static void force_redraw();
170 static void redraw();
171 static gboolean redraw_later( gpointer data);
172 
173 static void reset_sample_display(guint);
174 
175 static gboolean
176 configure_event(GtkWidget *widget,
177                 GdkEventConfigure *event,
178                 gpointer data);
179 
180 static gboolean
181 draw_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
182 
183 static gboolean
184 draw_summary_configure_event(GtkWidget *widget,
185                              GdkEventConfigure *event,
186                              gpointer user_data);
187 
188 static gboolean
189 draw_summary_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
190 
191 static gboolean
192 draw_summary_button_release(GtkWidget *widget,
193                             GdkEventButton *event,
194                             gpointer user_data);
195 
196 /* Menu Functions */
197 static void
198 menu_open_file(GSimpleAction *action, GVariant *parameter, gpointer user_data);
199 
200 static void
201 menu_menu(GSimpleAction *action, GVariant *parameter, gpointer user_data);
202 
203 static void
204 menu_delete_track_break(GSimpleAction *action, GVariant *parameter, gpointer user_data);
205 
206 static void
207 menu_import(GSimpleAction *action, GVariant *parameter, gpointer user_data);
208 
209 static void
210 menu_save(GSimpleAction *action, GVariant *parameter, gpointer user_data);
211 
212 static void
213 menu_save_as(GSimpleAction *action, GVariant *parameter, gpointer user_data);
214 
215 #if defined(WANT_MOODBAR)
216 static void
217 menu_view_moodbar(GSimpleAction *action, GVariant *parameter, gpointer user_data);
218 
219 static void
220 menu_moodbar(GSimpleAction *action, GVariant *parameter, gpointer user_data);
221 #endif
222 
223 static void
224 menu_about(GSimpleAction *action, GVariant *parameter, gpointer user_data);
225 
226 static void
227 menu_config(GSimpleAction *action, GVariant *parameter, gpointer user_data);
228 
229 static void
230 menu_merge(GSimpleAction *action, GVariant *parameter, gpointer user_data);
231 
232 static void
233 menu_export(GSimpleAction *action, GVariant *parameter, gpointer user_data);
234 
235 static void
236 menu_autosplit(gpointer callback_data, guint callback_action, GtkWidget *widget);
237 
238 static void
239 menu_rename(GSimpleAction *action, GVariant *parameter, gpointer user_data);
240 
241 static void
242 menu_play(GtkWidget *widget, gpointer user_data);
243 
244 static void
245 menu_stop(GtkWidget *widget, gpointer user_data);
246 
247 static void
248 menu_next_silence( GtkWidget* widget, gpointer user_data);
249 
250 static void
251 menu_jump_to(GtkWidget *widget, gpointer user_data);
252 
253 static void
254 menu_prev_silence( GtkWidget* widget, gpointer user_data);
255 
256 void
257 menu_add_track_break(GSimpleAction *action, GVariant *parameter, gpointer user_data);
258 
259 static void set_stop_icon();
260 static void set_play_icon();
261 
262 static void save_window_sizes();
263 static void check_really_quit();
264 
265 static void
266 offset_to_time(guint, gchar *, gboolean);
267 
268 static guint
269 time_to_offset(gint min, gint sec, gint subsec);
270 
271 static void
272 offset_to_duration(guint, guint, gchar *);
273 
274 static void
275 update_status(gboolean);
276 
277 /*
278 static char *status_message = NULL;
279 
280 char *get_status_message()
281 {
282     return status_message;
283 }
284 
285 void set_status_message(const char *val)
286 {
287     if (status_message != NULL) {
288         g_free(status_message);
289     }
290     status_message = g_strdup(val);
291 }
292 */
293 
parts_check_cb(GtkWidget * widget,gpointer data)294 void parts_check_cb(GtkWidget *widget, gpointer data) {
295 
296     TrackBreak *track_break;
297     guint list_pos;
298     gpointer list_data;
299     gint i;
300     GtkTreeIter iter;
301 
302     i = 0;
303 
304     while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, i++)) {
305 
306         list_pos = i - 1;
307         list_data = g_list_nth_data(track_break_list, list_pos);
308         track_break = (TrackBreak *)list_data;
309 
310         switch ((glong)data) {
311 
312             case CHECK_ALL:
313                 track_break->write = TRUE;
314                 break;
315             case CHECK_NONE:
316                 track_break->write = FALSE;
317                 break;
318             case CHECK_INVERT:
319                 track_break->write = !track_break->write;
320                 break;
321         }
322 
323         gtk_list_store_set(GTK_LIST_STORE(store), &iter, COLUMN_WRITE,
324                            track_break->write, -1);
325     }
326 
327     force_redraw();
328 }
329 
jump_to_cursor_marker(GSimpleAction * action,GVariant * parameter,gpointer user_data)330 void jump_to_cursor_marker(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
331     reset_sample_display(cursor_marker);
332     redraw();
333 }
334 
jump_to_track_break(GSimpleAction * action,GVariant * parameter,gpointer user_data)335 void jump_to_track_break(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
336     guint n = 0;
337 
338     n = track_break_find_offset();
339     if (n <= graphData.numSamples) {
340         reset_sample_display(n);
341     }
342 
343     redraw();
344 }
345 
wavbreaker_autosplit(long x)346 void wavbreaker_autosplit(long x) {
347     long n = x;
348 
349     gulong orig_cursor_marker = cursor_marker;
350 
351     while (n <= graphData.numSamples) {
352         cursor_marker = n;
353         track_break_add_entry();
354         n += x;
355     }
356 
357     cursor_marker = orig_cursor_marker;
358     force_redraw();
359 }
360 
361 /*
362  *-------------------------------------------------------------------------
363  * Track Break
364  *-------------------------------------------------------------------------
365  */
366 
367 /* TODO */
368 /*
369 static void
370 cell_data_func_gpa (GtkTreeViewColumn *col,
371                     GtkCellRenderer   *cell,
372                     GtkTreeModel      *model,
373                     GtkTreeIter       *iter,
374                     gpointer           data)
375 {
376 	gchar   buf[32];
377 	GValue  val = {0, };
378 
379 	gtk_tree_model_get_value(model, iter, COLUMN_TIME, &val);
380 
381 	g_snprintf(buf, sizeof(buf), "%s", g_value_get_string(&val));
382 
383     g_printf("text: %s\n", buf);
384 //	g_object_set(cell, "text", buf, NULL);
385 }
386 */
387 
388 static void
set_action_enabled(const char * action,gboolean enabled)389 set_action_enabled(const char *action, gboolean enabled)
390 {
391     g_object_set(G_OBJECT(g_action_map_lookup_action(G_ACTION_MAP(main_window), action)),
392             "enabled", enabled,
393             NULL);
394 }
395 
396 static void
on_tree_selection_changed(GtkTreeSelection * selection,gpointer user_data)397 on_tree_selection_changed(GtkTreeSelection *selection, gpointer user_data)
398 {
399     gboolean can_remove = TRUE;
400 
401     GList *list = gtk_tree_selection_get_selected_rows(selection, NULL);
402     GList *cur = list;
403     while (cur) {
404         GtkTreePath *path = cur->data;
405         if (gtk_tree_path_get_indices(path)[0] == 0) {
406             can_remove = FALSE;
407             break;
408         }
409         cur = cur->next;
410     }
411     g_list_free(list);
412 
413     set_action_enabled("remove_break", can_remove);
414 }
415 
416 GtkWidget *
track_break_create_list_gui()417 track_break_create_list_gui()
418 {
419     GtkTreeViewColumn *column;
420     GtkCellRenderer *renderer;
421     GtkWidget *sw;
422 
423 /* create the scrolled window for the list */
424     sw = gtk_scrolled_window_new(NULL, NULL);
425     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
426     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
427                                     GTK_POLICY_AUTOMATIC,
428                                     GTK_POLICY_AUTOMATIC);
429 
430     /* create the data store */
431     store = gtk_list_store_new(NUM_COLUMNS,
432                                G_TYPE_BOOLEAN,
433                                G_TYPE_STRING,
434                                G_TYPE_STRING,
435                                G_TYPE_STRING,
436                                G_TYPE_UINT,
437                                G_TYPE_BOOLEAN);
438 
439     /* create the treeview */
440     treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
441     gtk_container_add(GTK_CONTAINER(sw), treeview);
442 
443     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
444     g_signal_connect(G_OBJECT(selection), "changed",
445             G_CALLBACK(on_tree_selection_changed), NULL);
446 
447     /* connect/add the right-click signal */
448     gtk_widget_add_events(draw_summary, GDK_BUTTON_RELEASE_MASK);
449     gtk_widget_add_events(draw_summary, GDK_BUTTON_PRESS_MASK);
450     g_signal_connect(G_OBJECT(treeview), "button_press_event",
451                      G_CALLBACK(track_break_button_press), NULL);
452 
453     gtk_widget_show(treeview);
454 
455     /* create the columns */
456     /* Write Toggle Column */
457     column = gtk_tree_view_column_new();
458     renderer = gtk_cell_renderer_toggle_new();
459     g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(track_break_write_toggled), store);
460     gtk_tree_view_column_set_title(column, _("Write"));
461     gtk_tree_view_column_pack_start(column, renderer, FALSE);
462     gtk_tree_view_column_add_attribute(column, renderer, "active", COLUMN_WRITE);
463     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
464     gtk_tree_view_column_set_fixed_width(column, 50);
465     gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
466 
467     /* File Name Column */
468     column = gtk_tree_view_column_new();
469     renderer = gtk_cell_renderer_text_new();
470     g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(track_break_filename_edited), store);
471     gtk_tree_view_column_set_title(column, _("File Name"));
472     gtk_tree_view_column_pack_start(column, renderer, TRUE);
473     gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_FILENAME);
474     gtk_tree_view_column_add_attribute(column, renderer, "editable", COLUMN_EDITABLE);
475     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
476     //gtk_tree_view_column_set_fixed_width(column, 200);
477     gtk_tree_view_column_set_expand(column, TRUE);
478     gtk_tree_view_column_set_resizable(column, TRUE);
479     gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
480 
481     /* File Time Start Column */
482     column = gtk_tree_view_column_new();
483     renderer = gtk_cell_renderer_text_new();
484     /* TODO
485     renderer = gui_cell_renderer_spin_new(0.0, 5.0, 0.1, 1.0, 1.0, 0.1, 0);
486     g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(track_break_start_time_edited), store);
487     gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func_gpa, NULL, NULL);
488     */
489     gtk_tree_view_column_set_title(column, _("Time"));
490     gtk_tree_view_column_pack_start(column, renderer, FALSE);
491     gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_TIME);
492     //gtk_tree_view_column_add_attribute(column, renderer, "editable", COLUMN_EDITABLE);
493     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
494     gtk_tree_view_column_set_resizable(column, TRUE);
495     gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
496 
497     /* File Duration Column */
498     column = gtk_tree_view_column_new();
499     renderer = gtk_cell_renderer_text_new();
500     gtk_tree_view_column_set_title(column, _("Duration"));
501     gtk_tree_view_column_pack_start(column, renderer, FALSE);
502     gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_DURATION);
503     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
504     gtk_tree_view_column_set_resizable(column, TRUE);
505     gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
506 
507     /* File Offset Column */
508     column = gtk_tree_view_column_new();
509     renderer = gtk_cell_renderer_text_new();
510     gtk_tree_view_column_set_title(column, _("Offset"));
511     gtk_tree_view_column_pack_start(column, renderer, FALSE);
512     gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_OFFSET);
513     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
514     gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
515 
516     return sw;
517 }
518 
519 gint
track_break_sort(gconstpointer a,gconstpointer b)520 track_break_sort(gconstpointer a, gconstpointer b)
521 {
522     TrackBreak *x = (TrackBreak *)a;
523     TrackBreak *y = (TrackBreak *)b;
524 
525     if (x->offset < y->offset) {
526         return -1;
527     } else if (x->offset > y->offset) {
528         return 1;
529     } else {
530         return 0;
531     }
532 }
533 
534 static gboolean
track_break_button_press(GtkWidget * widget,GdkEventButton * event,gpointer user_data)535 track_break_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
536 {
537     if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) {
538 
539         cursor_marker = track_break_find_offset();
540         gtk_spin_button_set_value (GTK_SPIN_BUTTON (cursor_marker_spinner), cursor_marker);
541         jump_to_cursor_marker(NULL, NULL, NULL);
542         return FALSE;
543 
544     } else if (event->button != 3) {
545         return FALSE;
546     }
547 
548     GMenu *menu_model = g_menu_new();
549 
550     GMenu *check_model = g_menu_new();
551     g_menu_append(check_model, _("Check all"), "win.check_all");
552     g_menu_append(check_model, _("Check none"), "win.check_none");
553     g_menu_append(check_model, _("Invert check"), "win.check_invert");
554     g_menu_append_section(menu_model, NULL, G_MENU_MODEL(check_model));
555 
556     GMenu *all_model = g_menu_new();
557     g_menu_append(all_model, _("Auto-rename track breaks"), "win.auto_rename");
558     g_menu_append_section(menu_model, NULL, G_MENU_MODEL(all_model));
559 
560     GMenu *break_model = g_menu_new();
561     g_menu_append(break_model, _("Remove track break"), "win.remove_break");
562     g_menu_append(break_model, _("Jump to track break"), "win.jump_break");
563     g_menu_append_section(menu_model, NULL, G_MENU_MODEL(break_model));
564 
565     GtkMenu *menu = GTK_MENU(gtk_menu_new_from_model(G_MENU_MODEL(menu_model)));
566     gtk_menu_attach_to_widget(menu, main_window, NULL);
567     gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
568 
569     return FALSE;
570 }
571 
572 /* DEBUG FUNCTION START */
track_break_print_element(gpointer data,gpointer user_data)573 void track_break_print_element(gpointer data, gpointer user_data)
574 {
575     TrackBreak *breakup;
576 
577     breakup = (TrackBreak *)data;
578 
579     printf("filename: %s", breakup->filename);
580     printf("\ttime: %s", breakup->time);
581     printf("\tduration: %s", breakup->duration);
582     printf("\toffset: %lu\n", breakup->offset);
583 }
584 /* DEBUG FUNCTION END */
585 
586 void
track_break_free_element(gpointer data,gpointer user_data)587 track_break_free_element(gpointer data, gpointer user_data)
588 {
589     TrackBreak *track_break;
590 
591     track_break = (TrackBreak *)data;
592 
593     g_free(track_break->filename);
594     g_free(track_break);
595 }
596 
track_break_compare_cursor_marker(gpointer data,gpointer user_data)597 void track_break_compare_cursor_marker(gpointer data, gpointer user_data)
598 {
599     TrackBreak *track_break = (TrackBreak *) data;
600     CursorData *cd = (CursorData *) user_data;
601 
602     if (cd->marker == track_break->offset) {
603         cd->is_equal = TRUE;
604     }
605 }
606 
track_break_set_duration(gpointer data,gpointer user_data)607 void track_break_set_duration(gpointer data, gpointer user_data)
608 {
609     TrackBreak *track_break = (TrackBreak *) data;
610     TrackBreak *next_track_break;
611     guint index;
612 
613     index = g_list_index(track_break_list, track_break);
614     index++;
615     /*
616     printf("index: %d\n", index);
617     printf("cursor_marker: %d\n", cursor_marker);
618     printf("numSamples: %d\n", graphData.numSamples);
619     */
620     next_track_break = (TrackBreak *) g_list_nth_data(track_break_list, index);
621 
622     if (next_track_break != NULL) {
623         // Take the offset of the next track as the end of the duration.
624         offset_to_duration(track_break->offset, next_track_break->offset,
625             track_break->duration);
626     } else {
627         // There is no next track.
628         // Take the end of the sample as the end of the duration.
629         offset_to_duration(track_break->offset, graphData.numSamples,
630             track_break->duration);
631     }
632     //printf("\n");
633 }
634 
track_break_clear_list()635 void track_break_clear_list()
636 {
637     gtk_list_store_clear(store);
638     g_list_foreach(track_break_list, track_break_free_element, NULL);
639     g_list_free(track_break_list);
640     track_break_list = NULL;
641 }
642 
track_break_selection(gpointer data,gpointer user_data)643 void track_break_selection(gpointer data, gpointer user_data)
644 {
645     GtkTreePath *path = (GtkTreePath*)data;
646     TrackBreak *track_break;
647     guint list_pos;
648     gpointer list_data;
649     GtkTreeModel *model;
650     GtkTreeIter iter;
651 
652     list_pos = gtk_tree_path_get_indices(path)[0];
653     if (list_pos == 0) {
654         // Do not allow first break to be deleted
655         return;
656     }
657 
658     list_data = g_list_nth_data(track_break_list, list_pos);
659     track_break = (TrackBreak *)list_data;
660     track_break_list = g_list_remove(track_break_list, track_break);
661     track_break_free_element(track_break, NULL);
662 
663     model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
664     gtk_tree_model_get_iter(model, &iter, path);
665     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
666 
667 /* DEBUG CODE START */
668 /*
669     g_list_foreach(track_break_list, track_break_print_element, NULL);
670     g_print("\n");
671 */
672 /* DEBUG CODE END */
673 
674     gtk_tree_path_free(path);
675 }
676 
677 void
track_break_delete_entry()678 track_break_delete_entry()
679 {
680     GtkTreeSelection *selection;
681     GtkTreeModel *model;
682     GList *list;
683 
684     if (sample_filename == NULL) {
685         return;
686     }
687 
688     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
689 
690     list = gtk_tree_selection_get_selected_rows(selection, &model);
691     g_list_foreach(list, track_break_selection, NULL);
692     g_list_free(list);
693 
694     track_break_set_durations();
695     track_break_rename( FALSE);
696 
697     force_redraw();
698 }
699 
700 void
track_break_setup_filename(gpointer data,gpointer user_data)701 track_break_setup_filename(gpointer data, gpointer user_data)
702 {
703     TrackBreak *track_break = (TrackBreak *)data;
704     gchar *orig_filename = (gchar *)user_data;
705 
706     gchar fn[128];
707     gchar buf[128];
708     int index;
709     int disc_num;
710     static int prev_disc_num;
711     static int track_num = 1;
712 
713     /* when not overwriting track names, only update not-yet-set names */
714     if( overwrite_track_names == FALSE) {
715 
716         // try and determine if the user has modified the filename
717 
718         if (track_break->filename != NULL) {
719             size_t cut = 0;
720             gboolean remove_from_start = FALSE;
721             if (appconfig_get_use_etree_filename_suffix()) {
722                 // remove the dXtXX from the end
723                 cut = 5;
724             } else {
725                 // remove the XX- from the beginning or -XX from the end
726                 cut = 3;
727                 remove_from_start = appconfig_get_prepend_file_number();
728             }
729 
730             int length = strlen(track_break->filename);
731             int orig_length = strlen(orig_filename);
732 
733             if (length != orig_length + cut) {
734                 return;
735             }
736 
737             if (memcmp(orig_filename, track_break->filename + (remove_from_start ? cut : 0), orig_length) != 0) {
738                 return;
739             }
740         }
741     }
742 
743     index = g_list_index(track_break_list, track_break);
744     index++;
745     if (appconfig_get_use_etree_filename_suffix()) {
746         int cd_length = atoi(appconfig_get_etree_cd_length());
747         disc_num = (track_break->offset / CD_BLOCKS_PER_SEC / 60) / cd_length;
748         disc_num++;
749         if (index == 1) {
750             prev_disc_num = 0;
751         }
752         if (prev_disc_num != disc_num) {
753             track_num = 1;
754         } else {
755             track_num++;
756         }
757         prev_disc_num = disc_num;
758         sprintf(buf, "d%dt%02d", disc_num, track_num);
759     } else {
760         if (appconfig_get_prepend_file_number()) {
761             sprintf(buf, "%02d%s", index, appconfig_get_etree_filename_suffix());
762         } else {
763             sprintf(buf, "%s%02d", appconfig_get_etree_filename_suffix(), index);
764         }
765     }
766 
767     fn[0] = '\0';
768 
769     if (!appconfig_get_use_etree_filename_suffix() && appconfig_get_prepend_file_number()) {
770         strcat(fn, buf);
771         strcat(fn, orig_filename);
772     } else {
773         strcat(fn, orig_filename);
774         strcat(fn, buf);
775     }
776 
777     g_free(track_break->filename);
778     track_break->filename = g_strdup(fn);
779 }
780 
781 void
track_break_add_to_model(gpointer data,gpointer user_data)782 track_break_add_to_model(gpointer data, gpointer user_data)
783 {
784     GtkTreeIter iter;
785     GtkTreePath *path;
786     gchar path_str[8];
787     TrackBreak *track_break = (TrackBreak *)data;
788     int index = g_list_index(track_break_list, track_break);
789 
790     sprintf(path_str, "%d", index);
791     path = gtk_tree_path_new_from_string(path_str);
792 
793 /* DEBUG CODE START */
794 /*
795     g_print("gtktreepath: %s\n", path_str);
796     printf("list contents:\n");
797     g_list_foreach(track_break_list, print_element, NULL);
798 */
799 /* DEBUG CODE END */
800 
801     gtk_list_store_append(store, &iter);
802 /*
803     if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &sibling, path)) {
804         gtk_list_store_insert_before(store, &iter, &sibling);
805     } else {
806         gtk_list_store_append(store, &iter);
807     }
808 */
809     gtk_list_store_set(store, &iter, COLUMN_WRITE, track_break->write,
810                                      COLUMN_FILENAME, track_break->filename,
811                                      COLUMN_TIME, track_break->time,
812                                      COLUMN_DURATION, track_break->duration,
813                                      COLUMN_OFFSET, track_break->offset,
814                                      COLUMN_EDITABLE, track_break->editable,
815                                      -1);
816 
817     gtk_tree_path_free(path);
818 }
819 
track_break_find_offset()820 guint track_break_find_offset()
821 {
822     GtkTreeSelection *selection;
823     GtkTreeIter iter;
824     GtkTreeModel *model;
825     guint offset = 0;
826     gchar *time;
827     gchar *duration;
828     gchar *filename;
829 
830     if (sample_filename == NULL) {
831         return 0;
832     }
833 
834     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
835     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
836         gtk_tree_model_get(model, &iter,
837             COLUMN_FILENAME, &filename,
838             COLUMN_TIME, &time,
839             COLUMN_DURATION, &duration,
840             COLUMN_OFFSET, &offset, -1);
841         g_free(time);
842         g_free(duration);
843         g_free(filename);
844     }
845 
846     return offset;
847 /*
848     *tb = (TrackBreak *) gtk_tree_selection_get_user_data(selection);
849 
850     cursor_data->is_equal = FALSE;
851     cursor_data->marker = cursor_marker;
852 
853     printf("tb->offset: %s\n", tb->offset);
854 */
855 }
856 
857 static void
select_and_show_track_break(int index)858 select_and_show_track_break(int index)
859 {
860     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
861     gtk_tree_selection_unselect_all(selection);
862     GtkTreePath *path = gtk_tree_path_new_from_indices(index, -1);
863     gtk_tree_selection_select_path(selection, path);
864     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), path, NULL, FALSE, 0.f, 0.f);
865     gtk_tree_path_free(path);
866 }
867 
track_break_add_entry()868 void track_break_add_entry()
869 {
870     TrackBreak *track_break = NULL;
871     CursorData cursor_data;
872 
873     if (sample_filename == NULL) {
874         return;
875     }
876 
877     // check for duplicate track breaks
878     cursor_data.is_equal = FALSE;
879     cursor_data.marker = cursor_marker;
880     g_list_foreach(track_break_list, track_break_compare_cursor_marker,
881                    &cursor_data);
882     if (cursor_data.is_equal == TRUE) {
883         return;
884     }
885 
886     if (! (track_break = (TrackBreak *)g_malloc(sizeof(TrackBreak)))) {
887         printf("couldn't malloc enough memory for track_break\n");
888         exit(1);
889     }
890 
891     track_break->write = 1;
892     track_break->offset = cursor_marker;
893     track_break->editable = TRUE;
894     offset_to_time(cursor_marker, track_break->time, TRUE);
895     track_break->filename = NULL;
896 
897     track_break_list = g_list_insert_sorted(track_break_list, track_break,
898                                             track_break_sort);
899 
900     track_break_set_durations();
901     track_break_rename( FALSE);
902 
903     select_and_show_track_break(g_list_index(track_break_list, track_break));
904 
905     force_redraw();
906 }
907 
track_break_add_offset(char * filename,gulong offset)908 void track_break_add_offset( char* filename, gulong offset)
909 {
910     TrackBreak *track_break = NULL;
911 
912     if (sample_filename == NULL) {
913         return;
914     }
915 
916     if( offset > graphData.numSamples) {
917         if( filename != NULL) {
918             printf( "Offset for %s is too big, skipping.\n", filename);
919         }
920         return;
921     }
922 
923     if( !(track_break = (TrackBreak *)g_malloc(sizeof(TrackBreak)))) {
924         printf("couldn't malloc enough memory for track_break\n");
925         return;
926     }
927 
928     track_break->editable = TRUE;
929     track_break->offset = offset;
930     offset_to_time( track_break->offset, track_break->time, FALSE);
931 
932     if( filename == NULL) {
933         track_break->write = 0;
934         track_break->filename = NULL;
935     }
936     else {
937 	if( filename == (void *)-1 ) {
938 	    track_break->write = 1;
939 	    track_break->filename = NULL;
940 	}
941 	else {
942 	    track_break->write = 1;
943 	    track_break->filename = g_strdup( filename);
944 	}
945     }
946 
947     track_break_list = g_list_insert_sorted( track_break_list, track_break,
948                                              track_break_sort);
949 
950     track_break_set_durations();
951     track_break_rename( FALSE);
952 }
953 
track_break_set_durations()954 void track_break_set_durations() {
955     g_list_foreach(track_break_list, track_break_set_duration, NULL);
956     gtk_list_store_clear(store);
957     g_list_foreach(track_break_list, track_break_add_to_model, NULL);
958 }
959 
track_break_rename(gboolean overwrite)960 void track_break_rename( gboolean overwrite) {
961     if (sample_filename == NULL) {
962         return;
963     }
964 
965     /* do we have to overwrite already-set names or just NULL names? */
966     overwrite_track_names = overwrite;
967 
968     /* setup the filename */
969     gchar *str_ptr = g_path_get_basename(sample_filename);
970     gchar *end = strrchr(str_ptr, '.');
971     if (end) {
972         *end = '\0';
973     }
974 
975     g_list_foreach(track_break_list, track_break_setup_filename, str_ptr);
976     gtk_list_store_clear(store);
977     g_list_foreach(track_break_list, track_break_add_to_model, NULL);
978     g_free(str_ptr);
979 
980     redraw();
981 
982 /* DEBUG CODE START */
983 /*
984     g_list_foreach(track_break_list, track_break_print_element, NULL);
985     g_print("\n");
986 */
987 /* DEBUG CODE END */
988 }
989 
track_break_write_toggled(GtkWidget * widget,gchar * path_str,gpointer user_data)990 void track_break_write_toggled(GtkWidget *widget,
991                                gchar *path_str,
992                                gpointer user_data)
993 {
994     GtkTreeModel *model = (GtkTreeModel *)user_data;
995     GtkTreeIter iter;
996     GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
997     TrackBreak *track_break;
998     guint list_pos;
999     gpointer list_data;
1000 
1001     list_pos = atoi(path_str);
1002     list_data = g_list_nth_data(track_break_list, list_pos);
1003     track_break = (TrackBreak *)list_data;
1004     track_break->write = !track_break->write;
1005 
1006 /* DEBUG CODE START */
1007 /*
1008     g_print("gtktreepath: %s\n", path_str);
1009     g_print("list_pos: %d\n", list_pos);
1010     g_print("track_break->offset: %d\n", track_break->offset);
1011     g_print("track_break->write: %d\n\n", track_break->write);
1012 */
1013 /* DEBUG CODE END */
1014 
1015     gtk_tree_model_get_iter(model, &iter, path);
1016     gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_WRITE,
1017                        track_break->write, -1);
1018 
1019     gtk_tree_path_free(path);
1020     force_redraw();
1021 }
1022 
track_break_filename_edited(GtkCellRendererText * cell,const gchar * path_str,const gchar * new_text,gpointer user_data)1023 void track_break_filename_edited(GtkCellRendererText *cell,
1024                                  const gchar *path_str,
1025                                  const gchar *new_text,
1026                                  gpointer user_data)
1027 {
1028     GtkTreeModel *model = GTK_TREE_MODEL(user_data);
1029     GtkTreeIter iter;
1030     GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1031     TrackBreak *track_break;
1032     guint list_pos;
1033     gpointer list_data;
1034 
1035     list_pos = atoi(path_str);
1036     list_data = g_list_nth_data(track_break_list, list_pos);
1037     track_break = (TrackBreak *)list_data;
1038     track_break->filename = g_strdup(new_text);
1039 
1040     gtk_tree_model_get_iter(model, &iter, path);
1041     gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_FILENAME,
1042                        track_break->filename, -1);
1043 
1044     gtk_tree_path_free(path);
1045 
1046     force_redraw();
1047 }
1048 
track_break_start_time_edited(GtkCellRendererText * cell,const gchar * path_str,const gchar * new_text,gpointer user_data)1049 void track_break_start_time_edited(GtkCellRendererText *cell,
1050                                    const gchar *path_str,
1051                                    const gchar *new_text,
1052                                    gpointer user_data)
1053 {
1054     GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1055     TrackBreak *track_break;
1056     guint list_pos;
1057     gpointer list_data;
1058 
1059     list_pos = atoi(path_str);
1060     list_data = g_list_nth_data(track_break_list, list_pos);
1061     track_break = (TrackBreak *)list_data;
1062     printf("new time: %s\n", new_text);
1063     printf("old time: %s\n", track_break->time);
1064     /*
1065     track_break->filename = g_strdup(new_text);
1066 
1067     gtk_tree_model_get_iter(model, &iter, path);
1068     gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_FILENAME,
1069                        track_break->filename, -1);
1070                        */
1071 
1072     gtk_tree_path_free(path);
1073 
1074     force_redraw();
1075 }
1076 
1077 /*
1078  *-------------------------------------------------------------------------
1079  * File Save Dialog Stuff
1080  *-------------------------------------------------------------------------
1081  */
1082 gboolean
file_write_progress_idle_func(gpointer data)1083 file_write_progress_idle_func(gpointer data) {
1084     static GtkWidget *window;
1085     static GtkWidget *pbar;
1086     static GtkWidget *vbox;
1087     static GtkWidget *label;
1088     static GtkWidget *status_label;
1089     char *str_ptr;
1090     static int cur_file_displayed = 0;
1091     static double fraction;
1092 
1093     if (write_info.check_file_exists) {
1094         if (window != NULL) {
1095             gtk_widget_destroy(window);
1096             window = NULL;
1097         }
1098         write_info.sync_check_file_overwrite_to_write_progress = 1;
1099         write_info.check_file_exists = 0;
1100         overwritedialog_show( wavbreaker_get_main_window(), &write_info);
1101 
1102         return TRUE;
1103     }
1104 
1105     if (write_info.sync_check_file_overwrite_to_write_progress) {
1106         return TRUE;
1107     }
1108 
1109     if (window == NULL) {
1110         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1111         gtk_widget_realize(window);
1112         gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
1113         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1114         gtk_window_set_transient_for(GTK_WINDOW(window),
1115                 GTK_WINDOW(main_window));
1116         gtk_window_set_type_hint(GTK_WINDOW(window),
1117                 GDK_WINDOW_TYPE_HINT_DIALOG);
1118         gtk_window_set_position(GTK_WINDOW(window),
1119                 GTK_WIN_POS_CENTER_ON_PARENT);
1120         gdk_window_set_functions(GDK_WINDOW(gtk_widget_get_window(window)), GDK_FUNC_MOVE);
1121 
1122         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1123         gtk_container_add(GTK_CONTAINER(window), vbox);
1124         gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
1125 
1126         gtk_window_set_title( GTK_WINDOW(window), _("Splitting wave file"));
1127 
1128         gchar *tmp_str = g_markup_printf_escaped("<span size=\"larger\" weight=\"bold\">%s</span>",
1129                 gtk_window_get_title(GTK_WINDOW(window)));
1130 
1131         label = gtk_label_new( NULL);
1132         gtk_label_set_markup( GTK_LABEL(label), tmp_str);
1133         g_free(tmp_str);
1134         g_object_set(G_OBJECT(label), "xalign", 0.0f, "yalign", 0.5f, NULL);
1135         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 5);
1136 
1137         label = gtk_label_new( _("The selected track breaks are now written to disk. This can take some time."));
1138         gtk_label_set_line_wrap( GTK_LABEL(label), TRUE);
1139         g_object_set(G_OBJECT(label), "xalign", 0.0f, "yalign", 0.5f, NULL);
1140         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 5);
1141 
1142         pbar = gtk_progress_bar_new();
1143         gtk_box_pack_start(GTK_BOX(vbox), pbar, FALSE, TRUE, 5);
1144 
1145         status_label = gtk_label_new( NULL);
1146         g_object_set(G_OBJECT(status_label), "xalign", 0.0f, "yalign", 0.5f, NULL);
1147         gtk_label_set_ellipsize( GTK_LABEL(status_label), PANGO_ELLIPSIZE_MIDDLE);
1148         gtk_box_pack_start(GTK_BOX(vbox), status_label, FALSE, TRUE, 5);
1149 
1150         gtk_widget_show_all(GTK_WIDGET(window));
1151         cur_file_displayed = -1;
1152     }
1153 
1154     if (write_info.sync) {
1155         write_info.sync = 0;
1156         gtk_widget_destroy(window);
1157         window = NULL;
1158 
1159         gchar *tmp_str;
1160         if( write_info.num_files > 1) {
1161             tmp_str = g_strdup_printf(_("The file %s has been split into %d parts."), basename( sample_filename), write_info.num_files);
1162         } else {
1163             tmp_str = g_strdup_printf(_("The file %s has been split into one part."), basename( sample_filename));
1164         }
1165         popupmessage_show( NULL, _("Operation successful"), tmp_str);
1166         g_free(tmp_str);
1167 
1168         file_write_progress_source_id = 0;
1169         return FALSE;
1170     }
1171 
1172     if (cur_file_displayed != write_info.cur_file) {
1173         str_ptr = basename( write_info.cur_filename);
1174 
1175         if( str_ptr == NULL) {
1176             str_ptr = write_info.cur_filename;
1177         }
1178 
1179         gchar *fn = g_markup_escape_text(str_ptr, -1);
1180         gchar *tmp = g_strdup_printf(_("Writing %s"), fn);
1181         g_free(fn);
1182         gchar *msg = g_markup_printf_escaped("<i>%s</i>", tmp);
1183         g_free(tmp);
1184 
1185         gtk_label_set_markup(GTK_LABEL(status_label), msg);
1186         g_free(msg);
1187 
1188         cur_file_displayed = write_info.cur_file;
1189     }
1190 
1191     fraction = 1.00*(write_info.cur_file-1+write_info.pct_done)/write_info.num_files;
1192     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pbar), fraction);
1193 
1194     gchar *tmp_str;
1195     // FIXME: i18n plural forms
1196     if( write_info.num_files > 1) {
1197         tmp_str = g_strdup_printf(_("%d of %d parts written"), write_info.cur_file-1, write_info.num_files);
1198     } else {
1199         tmp_str = g_strdup_printf(_("%d of 1 part written"), write_info.cur_file-1);
1200     }
1201     gtk_progress_bar_set_text( GTK_PROGRESS_BAR(pbar), tmp_str);
1202     g_free(tmp_str);
1203 
1204     return TRUE;
1205 }
1206 
1207 gboolean
file_play_progress_idle_func(gpointer data)1208 file_play_progress_idle_func(gpointer data) {
1209     GtkAllocation allocation;
1210     gtk_widget_get_allocation(draw, &allocation);
1211     gint half_width = allocation.width / 2;
1212     gint offset = allocation.width * (1.0/PLAY_MARKER_SCROLL);
1213 
1214     gint x = play_marker - half_width;
1215     gint y = play_marker - pixmap_offset;
1216     gint z = allocation.width * (1.0 - 1.0/PLAY_MARKER_SCROLL);
1217 
1218     if (y > z && x > 0) {
1219         reset_sample_display(play_marker - offset + half_width);
1220     } else if (pixmap_offset > play_marker) {
1221         reset_sample_display(play_marker);
1222     }
1223 
1224     gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), pixmap_offset);
1225     gtk_widget_queue_draw(scrollbar);
1226 
1227     redraw();
1228     update_status(FALSE);
1229 
1230     if (sample_is_playing()) {
1231         return TRUE;
1232     } else {
1233         set_play_icon();
1234         play_progress_source_id = 0;
1235         return FALSE;
1236     }
1237 }
1238 
1239 /*
1240  *-------------------------------------------------------------------------
1241  * File Open Dialog Stuff
1242  *-------------------------------------------------------------------------
1243  */
1244 
1245 gboolean
file_open_progress_idle_func(gpointer data)1246 file_open_progress_idle_func(gpointer data) {
1247     static GtkWidget *window;
1248     static GtkWidget *pbar;
1249     static GtkWidget *vbox;
1250     static GtkWidget *label;
1251     static char tmp_str[6144];
1252     static char tmp_str2[6144];
1253     static int current, size;
1254 
1255     if (window == NULL) {
1256         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1257         gtk_widget_realize(window);
1258         gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
1259         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1260         gtk_window_set_transient_for(GTK_WINDOW(window),
1261                 GTK_WINDOW(main_window));
1262         gtk_window_set_type_hint(GTK_WINDOW(window),
1263                 GDK_WINDOW_TYPE_HINT_DIALOG);
1264         gtk_window_set_position(GTK_WINDOW(window),
1265                 GTK_WIN_POS_CENTER_ON_PARENT);
1266         gdk_window_set_functions(GDK_WINDOW(gtk_widget_get_window(window)), GDK_FUNC_MOVE);
1267 
1268         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1269         gtk_container_add(GTK_CONTAINER(window), vbox);
1270         gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
1271 
1272         gtk_window_set_title( GTK_WINDOW(window), _("Analyzing waveform"));
1273 
1274         tmp_str[0] = '\0';
1275         strcat( tmp_str, "<span size=\"larger\" weight=\"bold\">");
1276         strcat( tmp_str, gtk_window_get_title( GTK_WINDOW( window)));
1277         strcat( tmp_str, "</span>");
1278 
1279         label = gtk_label_new( NULL);
1280         gtk_label_set_markup( GTK_LABEL(label), tmp_str);
1281         g_object_set(G_OBJECT(label), "xalign", 0.0f, "yalign", 0.5f, NULL);
1282         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 5);
1283 
1284         label = gtk_label_new( _("The waveform data of the selected file is being analyzed and processed. This can take some time."));
1285         gtk_label_set_line_wrap( GTK_LABEL(label), TRUE);
1286         g_object_set(G_OBJECT(label), "xalign", 0.0f, "yalign", 0.5f, NULL);
1287         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 5);
1288 
1289         pbar = gtk_progress_bar_new();
1290         gtk_box_pack_start(GTK_BOX(vbox), pbar, FALSE, TRUE, 5);
1291 
1292         sprintf( tmp_str, _("Analyzing %s"), basename( sample_filename));
1293         gchar *str = g_markup_escape_text(tmp_str, -1);
1294         sprintf( tmp_str2, "<i>%s</i>", str);
1295         g_free(str);
1296 
1297         label = gtk_label_new( NULL);
1298         gtk_label_set_markup( GTK_LABEL(label), tmp_str2);
1299         g_object_set(G_OBJECT(label), "xalign", 0.0f, "yalign", 0.5f, NULL);
1300         gtk_label_set_ellipsize( GTK_LABEL(label), PANGO_ELLIPSIZE_END);
1301         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 5);
1302 
1303         gtk_widget_show_all(GTK_WIDGET(window));
1304     }
1305 
1306     if (progress_pct >= 1.0) {
1307         progress_pct = 0;
1308 
1309         gtk_widget_destroy(window);
1310         window = NULL;
1311 
1312         /* --------------------------------------------------- */
1313         /* Reset things because we have a new file             */
1314         /* --------------------------------------------------- */
1315 
1316         gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), 0);
1317         gtk_adjustment_set_value(GTK_ADJUSTMENT(cursor_marker_spinner_adj), 0);
1318         gtk_adjustment_set_value(GTK_ADJUSTMENT(cursor_marker_min_spinner_adj), 0);
1319         gtk_adjustment_set_value(GTK_ADJUSTMENT(cursor_marker_sec_spinner_adj), 0);
1320         gtk_adjustment_set_value(GTK_ADJUSTMENT(cursor_marker_subsec_spinner_adj), 0);
1321 
1322         gtk_widget_queue_draw(scrollbar);
1323 
1324         /* TODO: Remove FIX !!!!!!!!!!! */
1325         configure_event(draw, NULL, NULL);
1326 
1327 #if defined(WANT_MOODBAR)
1328         if (moodbarData) {
1329             moodbar_free(moodbarData);
1330         }
1331         moodbarData = moodbar_open(sample_filename);
1332         set_action_enabled("display_moodbar", moodbarData != NULL);
1333         set_action_enabled("generate_moodbar", moodbarData == NULL);
1334 #endif
1335 
1336         redraw();
1337 
1338         /* --------------------------------------------------- */
1339 
1340         file_open_progress_source_id = 0;
1341         return FALSE;
1342 
1343     } else {
1344         size = sample_stat.st_size/(1024*1024);
1345         current = size*progress_pct;
1346         sprintf( tmp_str, _("%d of %d MB analyzed"), current, size);
1347         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pbar), progress_pct);
1348         gtk_progress_bar_set_text( GTK_PROGRESS_BAR(pbar), tmp_str);
1349 
1350         return TRUE;
1351     }
1352 }
1353 
open_file()1354 static void open_file() {
1355     if (sample_open_file(sample_filename, &graphData, &progress_pct) != 0) {
1356         char *message = sample_get_error_message();
1357         popupmessage_show(main_window, _("Error opening file"), message);
1358         sample_filename = NULL;
1359         g_free(message);
1360         return;
1361     }
1362 
1363     set_action_enabled("add_break", TRUE);
1364     set_action_enabled("jump_cursor", TRUE);
1365 
1366     set_action_enabled("check_all", TRUE);
1367     set_action_enabled("check_none", TRUE);
1368     set_action_enabled("check_invert", TRUE);
1369 
1370     set_action_enabled("auto_rename", TRUE);
1371     set_action_enabled("remove_break", TRUE);
1372     set_action_enabled("jump_break", TRUE);
1373 
1374     set_action_enabled("export", TRUE);
1375     set_action_enabled("import", TRUE);
1376 
1377 #if defined(WANT_MOODBAR)
1378     set_action_enabled("display_moodbar", moodbarData != NULL);
1379     set_action_enabled("generate_moodbar", moodbarData == NULL);
1380 #endif
1381     gtk_widget_set_sensitive( play_button, TRUE);
1382     gtk_widget_set_sensitive( header_bar_save_button, TRUE);
1383 
1384     gtk_widget_set_sensitive( cursor_marker_spinner, TRUE);
1385     gtk_widget_set_sensitive( cursor_marker_min_spinner, TRUE);
1386     gtk_widget_set_sensitive( cursor_marker_sec_spinner, TRUE);
1387     gtk_widget_set_sensitive( cursor_marker_subsec_spinner, TRUE);
1388 
1389     gtk_widget_set_sensitive(button_seek_backward, TRUE);
1390     gtk_widget_set_sensitive(button_jump_to_time, TRUE);
1391     gtk_widget_set_sensitive(button_seek_forward, TRUE);
1392     gtk_widget_set_sensitive(button_auto_split, TRUE);
1393     gtk_widget_set_sensitive(button_add_break, TRUE);
1394     gtk_widget_set_sensitive(button_remove_break, TRUE);
1395 
1396     menu_stop(NULL, NULL);
1397 
1398     cursor_marker = 0;
1399     track_break_clear_list();
1400     track_break_add_entry();
1401 
1402     if (file_open_progress_source_id) {
1403         g_source_remove(file_open_progress_source_id);
1404     }
1405     file_open_progress_source_id = g_timeout_add(100, file_open_progress_idle_func, NULL);
1406     set_title( basename( sample_filename));
1407 }
1408 
1409 static gboolean
open_file_arg(gpointer data)1410 open_file_arg(gpointer data)
1411 {
1412     if (data) {
1413         set_sample_filename((char *)data);
1414         g_free(data);
1415         open_file();
1416     }
1417 
1418     /* do not call this function again = return FALSE */
1419     open_file_source_id = 0;
1420     return FALSE;
1421 }
1422 
set_title(char * title)1423 static void set_title( char* title)
1424 {
1425   char buf[1024];
1426 
1427   if( title == NULL) {
1428     gtk_window_set_title( (GtkWindow*)main_window, APPNAME);
1429     gtk_header_bar_set_title(GTK_HEADER_BAR(header_bar), APPNAME);
1430     return;
1431   }
1432 
1433   sprintf( buf, "%s (%s)", APPNAME, title);
1434   gtk_window_set_title( (GtkWindow*)main_window, buf);
1435   gtk_header_bar_set_title(GTK_HEADER_BAR(header_bar), buf);
1436 }
1437 
open_select_file()1438 static void open_select_file() {
1439     GtkWidget *dialog;
1440 
1441     GtkFileFilter *filter_all;
1442     GtkFileFilter *filter_supported;
1443 
1444     filter_all = gtk_file_filter_new();
1445     gtk_file_filter_set_name( filter_all, _("All files"));
1446     gtk_file_filter_add_pattern( filter_all, "*");
1447 
1448     filter_supported = gtk_file_filter_new();
1449     gtk_file_filter_set_name( filter_supported, _("Supported files"));
1450     gtk_file_filter_add_pattern( filter_supported, "*.wav");
1451 #if defined(HAVE_MPG123)
1452     gtk_file_filter_add_pattern( filter_supported, "*.mp3");
1453 #endif
1454     gtk_file_filter_add_pattern( filter_supported, "*.dat");
1455     gtk_file_filter_add_pattern( filter_supported, "*.raw");
1456 
1457     dialog = gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_window),
1458         GTK_FILE_CHOOSER_ACTION_OPEN,
1459         _("_Cancel"), GTK_RESPONSE_CANCEL,
1460         _("_Open"), GTK_RESPONSE_ACCEPT,
1461         NULL);
1462 
1463     gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter_all);
1464     gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter_supported);
1465     gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(dialog), filter_supported);
1466 
1467     if (sample_filename != NULL) {
1468         char* filename = g_strdup(sample_filename);
1469         char* dir = dirname(filename);
1470 
1471         if (dir != NULL && dir[0] != '.') {
1472             gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir);
1473         }
1474 
1475         g_free(filename);
1476     }
1477 
1478     if (gtk_dialog_run( GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1479         char *filename;
1480         char *dirname;
1481 
1482         filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1483         set_sample_filename(filename);
1484         g_free(filename);
1485         dirname = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
1486         saveas_set_dirname(dirname);
1487         g_free(dirname);
1488         gtk_widget_destroy(dialog);
1489         open_file();
1490     } else {
1491         gtk_widget_destroy(dialog);
1492     }
1493 }
1494 
set_sample_filename(const char * f)1495 void set_sample_filename(const char *f) {
1496     if (sample_filename != NULL) {
1497         g_free(sample_filename);
1498         sample_close_file();
1499     }
1500     sample_filename = g_strdup(f);
1501     stat( sample_filename, &sample_stat);
1502 }
1503 
1504 /*
1505  *-------------------------------------------------------------------------
1506  * Sample Drawing Area Stuff
1507  *-------------------------------------------------------------------------
1508  */
1509 
blit_cairo_surface(cairo_t * cr,cairo_surface_t * surface,int width,int height)1510 static inline void blit_cairo_surface(cairo_t *cr, cairo_surface_t *surface, int width, int height)
1511 {
1512     if (!surface) {
1513         return;
1514     }
1515 
1516     cairo_set_source_surface(cr, surface, 0.f, 0.f);
1517     cairo_rectangle(cr, 0.f, 0.f, (float)width, (float)height);
1518     cairo_fill(cr);
1519 }
1520 
force_redraw()1521 static void force_redraw()
1522 {
1523     waveform_surface_invalidate(sample_surface);
1524     waveform_surface_invalidate(summary_surface);
1525 
1526     redraw();
1527 }
1528 
redraw()1529 static void redraw()
1530 {
1531     static int redraw_done = 1;
1532 
1533     if( redraw_done) {
1534         /* Only redraw if the last operation finished already. */
1535         redraw_done = 0;
1536         if (redraw_source_id) {
1537             g_source_remove(redraw_source_id);
1538         }
1539         redraw_source_id = g_idle_add(redraw_later, &redraw_done);
1540     }
1541 }
1542 
redraw_later(gpointer data)1543 static gboolean redraw_later( gpointer data)
1544 {
1545     int *redraw_done = (int*)data;
1546 
1547     struct WaveformSurfaceDrawContext ctx = {
1548         .widget = draw,
1549         .pixmap_offset = pixmap_offset,
1550         .track_break_list = track_break_list,
1551         .graphData = &graphData,
1552         .moodbarData = appconfig_get_show_moodbar() ? moodbarData : NULL,
1553     };
1554     waveform_surface_draw(sample_surface, &ctx);
1555     gtk_widget_queue_draw(draw);
1556 
1557     ctx.widget = draw_summary;
1558     waveform_surface_draw(summary_surface, &ctx);
1559     gtk_widget_queue_draw(draw_summary);
1560 
1561     *redraw_done = 1;
1562 
1563     redraw_source_id = 0;
1564     return FALSE;
1565 }
1566 
configure_event(GtkWidget * widget,GdkEventConfigure * event,gpointer data)1567 static gboolean configure_event(GtkWidget *widget,
1568     GdkEventConfigure *event, gpointer data)
1569 {
1570     GtkAllocation allocation;
1571     gtk_widget_get_allocation(widget, &allocation);
1572     int width = allocation.width;
1573 
1574     if (graphData.numSamples == 0) {
1575         pixmap_offset = 0;
1576         gtk_adjustment_set_page_size(adj, 1);
1577         gtk_adjustment_set_upper(adj, 1);
1578         gtk_adjustment_set_page_increment(adj, 1);
1579     } else if (width > graphData.numSamples) {
1580         pixmap_offset = 0;
1581         gtk_adjustment_set_page_size(adj, graphData.numSamples);
1582         gtk_adjustment_set_upper(adj, graphData.numSamples);
1583         gtk_adjustment_set_page_increment(adj, width / 2);
1584     } else {
1585         if (pixmap_offset + width > graphData.numSamples) {
1586             pixmap_offset = graphData.numSamples - width;
1587         }
1588         gtk_adjustment_set_page_size(adj, width);
1589         gtk_adjustment_set_upper(adj, graphData.numSamples);
1590         gtk_adjustment_set_page_increment(adj, width / 2);
1591     }
1592 
1593     gtk_adjustment_set_step_increment(adj, 10);
1594     gtk_adjustment_set_value(adj, pixmap_offset);
1595     gtk_adjustment_set_upper(cursor_marker_spinner_adj, graphData.numSamples - 1);
1596 
1597     struct WaveformSurfaceDrawContext ctx = {
1598         .widget = widget,
1599         .pixmap_offset = pixmap_offset,
1600         .track_break_list = track_break_list,
1601         .graphData = &graphData,
1602         .moodbarData = appconfig_get_show_moodbar() ? moodbarData : NULL,
1603     };
1604     waveform_surface_draw(sample_surface, &ctx);
1605 
1606     return TRUE;
1607 }
1608 
draw_draw_event(GtkWidget * widget,cairo_t * cr,gpointer data)1609 static gboolean draw_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
1610 {
1611     GList *tbl;
1612     TrackBreak *tb_cur = NULL, *tb_first = NULL;
1613     TrackBreak **tbs;
1614     const int border = 3;
1615     int tbc = 0, i, border_left, border_right, text_height = 0, ellipsis_width = 0;
1616     gboolean label_truncated = FALSE;
1617     char tmp[1024];
1618 
1619     cairo_text_extents_t te;
1620 
1621     GtkAllocation allocation;
1622     gtk_widget_get_allocation(widget, &allocation);
1623 
1624     guint width = allocation.width,
1625           height = allocation.height;
1626 
1627     blit_cairo_surface(cr, sample_surface->surface, width, height);
1628 
1629     cairo_set_line_width( cr, 1);
1630     if( cursor_marker >= pixmap_offset && cursor_marker <= pixmap_offset + width) {
1631         /**
1632          * Draw RED cursor marker
1633          **/
1634         float x = cursor_marker - pixmap_offset + 0.5f;
1635 
1636         cairo_set_source_rgba(cr, 1.f, 0.f, 0.f, 0.9f);
1637         cairo_move_to(cr, x, 0.f);
1638         cairo_line_to(cr, x, height);
1639         cairo_stroke(cr);
1640 
1641         static const float TRIANGLE_SIDE = 3.f;
1642 
1643         cairo_move_to(cr, x-TRIANGLE_SIDE, 0.f);
1644         cairo_line_to(cr, x+TRIANGLE_SIDE, 0.f);
1645         cairo_line_to(cr, x, 2.f*TRIANGLE_SIDE);
1646 
1647         cairo_move_to(cr, x-TRIANGLE_SIDE, height);
1648         cairo_line_to(cr, x+TRIANGLE_SIDE, height);
1649         cairo_line_to(cr, x, height-2.f*TRIANGLE_SIDE);
1650 
1651         cairo_fill(cr);
1652     }
1653 
1654     if (sample_is_playing()) {
1655         /**
1656          * Draw GREEN play marker
1657          **/
1658         float x = play_marker - pixmap_offset + 0.5f;
1659 
1660         cairo_set_source_rgba(cr, 0.f, 0.7f, 0.f, 0.9f);
1661         cairo_move_to(cr, x, 0.f);
1662         cairo_line_to(cr, x, height);
1663         cairo_stroke(cr);
1664     }
1665 
1666     /**
1667      * Prepare text output (filename labels)
1668      **/
1669 
1670     cairo_select_font_face( cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
1671     cairo_set_font_size( cr, 10);
1672 
1673     cairo_text_extents( cr, "...", &te);
1674     ellipsis_width = te.width;
1675 
1676     /**
1677      * Find track breaks for which we need to draw labels
1678      **/
1679 
1680     tb_cur = NULL;
1681     tbs = NULL;;
1682     for( tbl = track_break_list; tbl != NULL; tbl = g_list_next( tbl)) {
1683         tb_cur = g_list_nth_data( track_break_list, g_list_position( track_break_list, tbl));
1684         if (tb_cur != NULL) {
1685             cairo_text_extents( cr, tb_cur->filename, &te);
1686 
1687             if( pixmap_offset <= tb_cur->offset) {
1688                 if( tbs == NULL && tb_first != NULL) {
1689                     tbs = (TrackBreak**)malloc( sizeof( TrackBreak*));
1690                     tbs[tbc] = tb_first;
1691                     tbc++;
1692                 }
1693                 tbs = (TrackBreak**)realloc( tbs, (tbc+1)*sizeof( TrackBreak*));
1694                 tbs[tbc] = tb_cur;
1695                 tbc++;
1696                 if( te.height > text_height) {
1697                     text_height = te.height;
1698                 }
1699             }
1700 
1701             tb_first = tb_cur;
1702         }
1703     }
1704 
1705     if( tbs == NULL && tb_first != NULL) {
1706         tbs = (TrackBreak**)malloc( sizeof( TrackBreak*));
1707         tbs[tbc] = tb_first;
1708         tbc++;
1709         text_height = te.height;
1710     }
1711 
1712     /**
1713      * Calculate label size (and if we need to draw it) and
1714      * finally draw the label with the right size and position
1715      **/
1716 
1717     for( i=0; i<tbc; i++) {
1718         if( !(tbs[i]->write)) {
1719             continue;
1720         }
1721         border_left = (tbs[i]->offset > pixmap_offset)?(tbs[i]->offset - pixmap_offset):(0);
1722         border_right = (i+1 == tbc)?(width+100):(tbs[i+1]->offset - pixmap_offset);
1723         strcpy( tmp, tbs[i]->filename);
1724         cairo_text_extents( cr, tmp, &te);
1725         label_truncated = FALSE;
1726         while( border_left + te.width + ellipsis_width + border*2 > border_right - border*2 && strlen( tmp) > 1) {
1727             tmp[strlen( tmp)-1] = '\0';
1728             cairo_text_extents( cr, tmp, &te);
1729             label_truncated = TRUE;
1730         }
1731         if( label_truncated) {
1732             strcat( tmp, "...");
1733             cairo_text_extents( cr, tmp, &te);
1734             if( border_left + te.width + border*2 > border_right - border*2) {
1735                 border_left -= (border_left + te.width + border*2) - (border_right - border*2);
1736             }
1737         }
1738 
1739         cairo_set_source_rgba(cr, 1.f, 1.f, 1.f, 0.8f);
1740         cairo_rectangle(cr, border_left, height - text_height - border*2, te.width + border*2, text_height + border*2);
1741         cairo_fill(cr);
1742 
1743         cairo_set_source_rgb(cr, 0.f, 0.f, 0.f);
1744         cairo_move_to(cr, border_left + border, height - (text_height+1)/2);
1745         cairo_show_text(cr, tmp);
1746     }
1747 
1748     if( tbs != NULL) {
1749         free( tbs);
1750     }
1751 
1752     return FALSE;
1753 }
1754 
1755 static gboolean
draw_summary_configure_event(GtkWidget * widget,GdkEventConfigure * event,gpointer user_data)1756 draw_summary_configure_event(GtkWidget *widget,
1757                              GdkEventConfigure *event,
1758                              gpointer user_data)
1759 {
1760     struct WaveformSurfaceDrawContext ctx = {
1761         .widget = widget,
1762         .pixmap_offset = pixmap_offset,
1763         .track_break_list = track_break_list,
1764         .graphData = &graphData,
1765         .moodbarData = appconfig_get_show_moodbar() ? moodbarData : NULL,
1766     };
1767     waveform_surface_draw(summary_surface, &ctx);
1768 
1769     return TRUE;
1770 }
1771 
1772 static gboolean
draw_summary_draw_event(GtkWidget * widget,cairo_t * cr,gpointer user_data)1773 draw_summary_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1774 {
1775     GtkAllocation allocation;
1776     gtk_widget_get_allocation(widget, &allocation);
1777     guint width = allocation.width,
1778           height = allocation.height;
1779 
1780     gfloat summary_scale;
1781 
1782     summary_scale = (float)(graphData.numSamples) / (float)(width);
1783 
1784     /**
1785      * Draw shadow in summary pixmap to show current view
1786      **/
1787 
1788     blit_cairo_surface(cr, summary_surface->surface, width, height);
1789 
1790     cairo_set_source_rgba( cr, 0, 0, 0, 0.3);
1791     cairo_rectangle( cr, 0, 0, pixmap_offset / summary_scale, height);
1792     cairo_fill( cr);
1793     cairo_rectangle( cr, (pixmap_offset+width) / summary_scale, 0, width - (pixmap_offset+width) / summary_scale, height);
1794     cairo_fill( cr);
1795 
1796     cairo_set_source_rgba( cr, 1, 1, 1, 0.6);
1797     cairo_set_line_width( cr, 1);
1798     cairo_move_to( cr, (int)(pixmap_offset / summary_scale) + 0.5, 0);
1799     cairo_line_to( cr, (int)(pixmap_offset / summary_scale) + 0.5, height);
1800     cairo_move_to( cr, (int)((pixmap_offset+width) / summary_scale) + 0.5, 0);
1801     cairo_line_to( cr, (int)((pixmap_offset+width) / summary_scale) + 0.5, height);
1802     cairo_stroke( cr);
1803 
1804     return FALSE;
1805 }
1806 
draw_summary_button_release(GtkWidget * widget,GdkEventButton * event,gpointer user_data)1807 static gboolean draw_summary_button_release(GtkWidget *widget,
1808     GdkEventButton *event, gpointer user_data)
1809 {
1810     guint midpoint, width;
1811     int x_scale, x_scale_leftover, x_scale_mod;
1812     int leftover_count;
1813 
1814     if (sample_is_playing()) {
1815         return TRUE;
1816     }
1817 
1818     if (graphData.numSamples == 0) {
1819         return TRUE;
1820     }
1821 
1822     GtkAllocation allocation;
1823     gtk_widget_get_allocation(widget, &allocation);
1824     width = allocation.width;
1825     x_scale = graphData.numSamples / width;
1826     x_scale_leftover = graphData.numSamples % width;
1827     if (x_scale_leftover > 0) {
1828         x_scale_mod =  width / x_scale_leftover;
1829         leftover_count = event->x / x_scale_mod;
1830     } else {
1831         x_scale_mod =  0;
1832         leftover_count = 0;
1833     }
1834 
1835     if (event->x < 0) {
1836         return TRUE;
1837     }
1838 
1839     midpoint = event->x * x_scale + leftover_count;
1840     reset_sample_display(midpoint);
1841     redraw();
1842 
1843     return TRUE;
1844 }
1845 
reset_sample_display(guint midpoint)1846 void reset_sample_display(guint midpoint)
1847 {
1848     GtkAllocation allocation;
1849     gtk_widget_get_allocation(draw, &allocation);
1850     int width = allocation.width;
1851     int start = midpoint - width / 2;
1852 
1853     if (graphData.numSamples == 0) {
1854         pixmap_offset = 0;
1855     } else if (width > graphData.numSamples) {
1856         pixmap_offset = 0;
1857     } else if (start + width > graphData.numSamples) {
1858         pixmap_offset = graphData.numSamples - width;
1859     } else {
1860         pixmap_offset = start;
1861     }
1862 
1863     if (pixmap_offset < 0) {
1864         pixmap_offset = 0;
1865     }
1866 
1867     gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), pixmap_offset);
1868     gtk_widget_queue_draw(scrollbar);
1869 }
1870 
1871 /*
1872  *-------------------------------------------------------------------------
1873  * Scrollbar and Buttons
1874  *-------------------------------------------------------------------------
1875  */
1876 
adj_value_changed(GtkAdjustment * adj,gpointer data)1877 static gboolean adj_value_changed(GtkAdjustment *adj, gpointer data)
1878 {
1879     if (sample_is_playing()) {
1880         return FALSE;
1881     }
1882 
1883     pixmap_offset = gtk_adjustment_get_value(adj);
1884 
1885     redraw();
1886 
1887     return TRUE;
1888 }
1889 
cursor_marker_time_spinners_changed(GtkAdjustment * adj,gpointer data)1890 static void cursor_marker_time_spinners_changed(GtkAdjustment *adj, gpointer data)
1891 {
1892     gint min, sec, subsec;
1893 
1894     if (sample_is_playing()) {
1895         return;
1896     }
1897 
1898     min = gtk_adjustment_get_value(cursor_marker_min_spinner_adj);
1899     sec = gtk_adjustment_get_value(cursor_marker_sec_spinner_adj);
1900     subsec = gtk_adjustment_get_value(cursor_marker_subsec_spinner_adj);
1901 
1902     cursor_marker = time_to_offset (min, sec, subsec);
1903     gtk_spin_button_set_value (GTK_SPIN_BUTTON (cursor_marker_spinner), cursor_marker);
1904 
1905     if (gtk_widget_get_visible(jump_to_popover)) {
1906         reset_sample_display(cursor_marker);
1907     }
1908 
1909     redraw();
1910     update_status(FALSE);
1911 }
1912 
cursor_marker_spinner_changed(GtkAdjustment * adj,gpointer data)1913 static void cursor_marker_spinner_changed(GtkAdjustment *adj, gpointer data)
1914 {
1915     if (sample_is_playing()) {
1916         return;
1917     }
1918     cursor_marker = gtk_adjustment_get_value(adj);
1919     /*
1920     printf("adj->value: %lu\n", adj->value);
1921     printf("cursor_marker: %lu\n", cursor_marker);
1922     printf("pixmap_offset: %lu\n", pixmap_offset);
1923     */
1924 
1925     update_status(TRUE);
1926     redraw();
1927 }
1928 
scroll_event(GtkWidget * widget,GdkEventScroll * event,gpointer data)1929 static gboolean scroll_event( GtkWidget *widget, GdkEventScroll *event, gpointer data)
1930 {
1931     long step, upper, size;
1932 
1933     step = gtk_adjustment_get_page_increment(adj);
1934     upper = gtk_adjustment_get_upper(adj);
1935     size = gtk_adjustment_get_page_size(adj);
1936 
1937     if( widget == draw) {
1938         /* Scroll in more detail on the zoomed view */
1939         step /= 2;
1940     }
1941 
1942     if( event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT) {
1943         if( pixmap_offset >= step) {
1944             pixmap_offset -= step;
1945         } else {
1946             pixmap_offset = 0;
1947         }
1948     }
1949     if( event->direction == GDK_SCROLL_DOWN || event->direction == GDK_SCROLL_RIGHT) {
1950         if( pixmap_offset <= upper-step-size) {
1951             pixmap_offset += step;
1952         } else {
1953             pixmap_offset = upper-size;
1954         }
1955     }
1956 
1957     gtk_adjustment_set_value( GTK_ADJUSTMENT(adj), pixmap_offset);
1958     gtk_widget_queue_draw( scrollbar);
1959 
1960     redraw();
1961     update_status(FALSE);
1962 
1963     return TRUE;
1964 }
1965 
button_release(GtkWidget * widget,GdkEventButton * event,gpointer data)1966 static gboolean button_release(GtkWidget *widget, GdkEventButton *event,
1967     gpointer data)
1968 {
1969     gtk_widget_grab_focus(play_button);
1970 
1971     if (event->x + pixmap_offset > graphData.numSamples) {
1972         return TRUE;
1973     }
1974 
1975     if (sample_is_playing()) {
1976         return TRUE;
1977     }
1978 
1979     int w = gtk_widget_get_allocated_width(widget);
1980 
1981     int center = pixmap_offset + w/2;
1982 
1983     static const int MINIMUM_SCROLL_STEP = 10;
1984     static const int MAXIMUM_SCROLL_STEP = 50;
1985 
1986     if (event->x < 0) {
1987         // scroll left
1988         int offset = event->x;
1989         if (offset > -MINIMUM_SCROLL_STEP) {
1990             offset = -MINIMUM_SCROLL_STEP;
1991         } else if (offset < -MAXIMUM_SCROLL_STEP) {
1992             offset = -MAXIMUM_SCROLL_STEP;
1993         }
1994 
1995         reset_sample_display(center + offset);
1996 
1997         cursor_marker = pixmap_offset;
1998     } else if (event->x > w-1) {
1999         // scroll right
2000         int offset = event->x - (w-1);
2001         if (offset < MINIMUM_SCROLL_STEP) {
2002             offset = MINIMUM_SCROLL_STEP;
2003         } else if (offset > MAXIMUM_SCROLL_STEP) {
2004             offset = MAXIMUM_SCROLL_STEP;
2005         }
2006 
2007         reset_sample_display(center + offset);
2008 
2009         cursor_marker = pixmap_offset + w-1;
2010     } else {
2011         cursor_marker = pixmap_offset + event->x;
2012     }
2013 
2014     if (event->type == GDK_BUTTON_RELEASE && event->button == 3) {
2015         GMenu *menu_model = g_menu_new();
2016 
2017         g_menu_append(menu_model, _("Add track break"), "win.add_break");
2018         g_menu_append(menu_model, _("Remove track break"), "win.remove_break");
2019         g_menu_append(menu_model, _("Jump to cursor marker"), "win.jump_cursor");
2020 
2021         GtkMenu *menu = GTK_MENU(gtk_menu_new_from_model(G_MENU_MODEL(menu_model)));
2022         gtk_menu_attach_to_widget(menu, main_window, NULL);
2023         gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
2024 
2025         redraw();
2026         return TRUE;
2027     }
2028 
2029     TrackBreak *nearest_track_break = NULL;
2030     int nearest_track_break_index = 0;
2031     int containing_track_break_index = 0;
2032     {
2033         GList *cur = track_break_list;
2034         int idx = 0;
2035 
2036         nearest_track_break = cur ? cur->data : NULL;
2037         while (cur) {
2038             TrackBreak *tb = cur->data;
2039             if (ABS((long)tb->offset - (long)cursor_marker) < ABS((long)nearest_track_break->offset - (long)cursor_marker)) {
2040                 nearest_track_break = tb;
2041                 nearest_track_break_index = idx;
2042             }
2043 
2044             if (tb->offset < cursor_marker) {
2045                 containing_track_break_index = idx;
2046             }
2047 
2048             cur = cur->next;
2049             ++idx;
2050         }
2051     }
2052 
2053     static const long SNAP_DISTANCE_FRAMES = 20;
2054     if (nearest_track_break && ABS((long)cursor_marker - (long)nearest_track_break->offset) < SNAP_DISTANCE_FRAMES) {
2055         // snap cursor to track break
2056         cursor_marker = nearest_track_break->offset;
2057         containing_track_break_index = nearest_track_break_index;
2058     }
2059 
2060     select_and_show_track_break(containing_track_break_index);
2061 
2062     gtk_adjustment_set_value(cursor_marker_spinner_adj, cursor_marker);
2063 
2064     /* DEBUG CODE START */
2065     /*
2066     printf("cursor_marker: %lu\n", cursor_marker);
2067     */
2068     /* DEBUG CODE END */
2069 
2070     update_status(FALSE);
2071     redraw();
2072 
2073     return TRUE;
2074 }
2075 
offset_to_duration(guint start_time,guint end_time,gchar * str)2076 static void offset_to_duration(guint start_time, guint end_time, gchar *str) {
2077     guint duration = end_time - start_time;
2078 /*
2079 printf("start time: %d\n", start_time);
2080 printf("end time: %d\n", end_time);
2081 */
2082     offset_to_time(duration, str, FALSE);
2083 }
2084 
time_to_offset(gint min,gint sec,gint subsec)2085 static guint time_to_offset(gint min, gint sec, gint subsec) {
2086     guint offset;
2087 
2088     offset = (min * CD_BLOCKS_PER_SEC * 60) + sec * CD_BLOCKS_PER_SEC + subsec;
2089 
2090     return offset;
2091 }
2092 
offset_to_time(guint time,gchar * str,gboolean time_offset_update)2093 static void offset_to_time(guint time, gchar *str, gboolean time_offset_update) {
2094 
2095     int min, sec, subsec;
2096 
2097     if (time > 0) {
2098         min = time / (CD_BLOCKS_PER_SEC * 60);
2099         sec = time % (CD_BLOCKS_PER_SEC * 60);
2100         subsec = sec % CD_BLOCKS_PER_SEC;
2101         sec = sec / CD_BLOCKS_PER_SEC;
2102     } else {
2103         min = sec = subsec = 0;
2104     }
2105 
2106     if (time_offset_update) {
2107         gtk_spin_button_set_value (GTK_SPIN_BUTTON (cursor_marker_min_spinner), min);
2108         gtk_spin_button_set_value (GTK_SPIN_BUTTON (cursor_marker_sec_spinner), sec);
2109         gtk_spin_button_set_value (GTK_SPIN_BUTTON (cursor_marker_subsec_spinner), subsec);
2110     }
2111 
2112     sprintf(str, "%d:%02d.%02d", min, sec, subsec);
2113 }
2114 
update_status(gboolean update_time_offset)2115 static void update_status(gboolean update_time_offset) {
2116     char str[1024];
2117     char strbuf[1024];
2118 
2119     sprintf( str, _("Cursor"));
2120     strcat( str, ": ");
2121     offset_to_time(cursor_marker, strbuf, update_time_offset);
2122     strcat(str, strbuf);
2123 
2124     if (sample_is_playing()) {
2125         strcat( str, "\t");
2126         strcat( str, _("Playing"));
2127         strcat( str, ": ");
2128         offset_to_time(play_marker, strbuf, update_time_offset);
2129         strcat(str, strbuf);
2130     }
2131 
2132     gtk_header_bar_set_subtitle(GTK_HEADER_BAR(header_bar), str);
2133 }
2134 
menu_play(GtkWidget * widget,gpointer user_data)2135 static void menu_play(GtkWidget *widget, gpointer user_data)
2136 {
2137     if (sample_is_playing()) {
2138         menu_stop( NULL, NULL);
2139         update_status(FALSE);
2140         set_play_icon();
2141         return;
2142     }
2143 
2144     play_marker = cursor_marker;
2145     switch (play_sample(cursor_marker, &play_marker)) {
2146         case 0:
2147             if (play_progress_source_id) {
2148                 g_source_remove(play_progress_source_id);
2149             }
2150             play_progress_source_id = g_timeout_add(10, file_play_progress_idle_func, NULL);
2151             set_stop_icon();
2152             break;
2153         case 1:
2154             printf("error in play_sample\n");
2155             break;
2156         case 2:
2157             menu_stop( NULL, NULL);
2158             update_status(FALSE);
2159             set_play_icon();
2160 //            printf("already playing\n");
2161 //            menu_stop(NULL, NULL);
2162 //            play_sample(cursor_marker, &play_marker);
2163             break;
2164         case 3:
2165 //            printf("must open sample file to play\n");
2166             break;
2167     }
2168 }
2169 
set_stop_icon()2170 static void set_stop_icon() {
2171     gtk_button_set_image(GTK_BUTTON(play_button),
2172             gtk_image_new_from_icon_name("media-playback-stop-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR));
2173 }
2174 
set_play_icon()2175 static void set_play_icon() {
2176     gtk_button_set_image(GTK_BUTTON(play_button),
2177             gtk_image_new_from_icon_name("media-playback-start-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR));
2178 }
2179 
menu_stop(GtkWidget * widget,gpointer user_data)2180 static void menu_stop(GtkWidget *widget, gpointer user_data)
2181 {
2182     stop_sample();
2183 }
2184 
2185 static void
menu_jump_to(GtkWidget * widget,gpointer user_data)2186 menu_jump_to(GtkWidget *widget, gpointer user_data)
2187 {
2188     gtk_popover_popup(GTK_POPOVER(jump_to_popover));
2189 }
2190 
menu_next_silence(GtkWidget * widget,gpointer user_data)2191 static void menu_next_silence( GtkWidget* widget, gpointer user_data)
2192 {
2193     int i, c = SILENCE_MIN_LENGTH+1, v;
2194     int amp = graphData.minSampleAmp + (graphData.maxSampleAmp-graphData.minSampleAmp)*appconfig_get_silence_percentage()/100;
2195 
2196     for( i=cursor_marker+1; i<graphData.numSamples; i++) {
2197         v = graphData.data[i].max - graphData.data[i].min;
2198         if( v < amp) {
2199             c++;
2200         } else {
2201             c = 0;
2202         }
2203 
2204         if( c==SILENCE_MIN_LENGTH) {
2205             cursor_marker = i;
2206             jump_to_cursor_marker(NULL, NULL, NULL);
2207             update_status(FALSE);
2208             return;
2209         }
2210     }
2211 }
2212 
menu_prev_silence(GtkWidget * widget,gpointer user_data)2213 static void menu_prev_silence( GtkWidget* widget, gpointer user_data)
2214 {
2215     int i, c = SILENCE_MIN_LENGTH+1, v;
2216     int amp = graphData.minSampleAmp + (graphData.maxSampleAmp-graphData.minSampleAmp)*appconfig_get_silence_percentage()/100;
2217 
2218     for( i=cursor_marker-1; i>0; i--) {
2219         v = graphData.data[i].max - graphData.data[i].min;
2220         if( v < amp) {
2221             c++;
2222         } else {
2223             c = 0;
2224         }
2225 
2226         if( c==SILENCE_MIN_LENGTH) {
2227             cursor_marker = i;
2228             jump_to_cursor_marker(NULL, NULL, NULL);
2229             update_status(FALSE);
2230             return;
2231         }
2232     }
2233 }
2234 
menu_save(GSimpleAction * action,GVariant * parameter,gpointer user_data)2235 static void menu_save(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2236 {
2237     if( sample_filename == NULL) {
2238         return;
2239     }
2240 
2241     if( appconfig_get_use_outputdir()) {
2242         wavbreaker_write_files( appconfig_get_outputdir());
2243     } else {
2244         wavbreaker_write_files( ".");
2245     }
2246 }
2247 
menu_save_as(GSimpleAction * action,GVariant * parameter,gpointer user_data)2248 static void menu_save_as(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2249 {
2250     if( sample_filename == NULL) {
2251         return;
2252     }
2253 
2254     saveas_show(main_window);
2255 }
2256 
wavbreaker_write_files(char * dirname)2257 void wavbreaker_write_files(char *dirname) {
2258     if (!sample_is_writing()) {
2259         sample_write_files(track_break_list, &write_info, dirname);
2260 
2261         file_write_progress_source_id = g_timeout_add(50, file_write_progress_idle_func, NULL);
2262     }
2263 }
2264 
filter_changed(GtkFileChooser * chooser,gpointer user_data)2265 static void filter_changed (GtkFileChooser* chooser, gpointer user_data)
2266 {
2267     gchar *old_filename;
2268     gchar *base;
2269     gchar *dot;
2270     gchar *new_filename;
2271     GtkFileFilter *filter;
2272 
2273     filter = gtk_file_chooser_get_filter (chooser);
2274     if (!filter) {
2275 	return;
2276     }
2277 
2278     old_filename = gtk_file_chooser_get_uri( chooser);
2279 
2280     base = basename( old_filename);
2281     new_filename = malloc( strlen(base) + 4);
2282     strcpy( new_filename, base);
2283     dot = g_strrstr( new_filename, ".");
2284     if ( !dot) {
2285 	dot = new_filename + strlen(new_filename);
2286     } else {
2287 	*dot = '\0';
2288     }
2289 
2290     if( strcmp( gtk_file_filter_get_name( filter), _("Text files")) == 0) {
2291 	strcat( new_filename, ".txt");
2292     } else if( strcmp( gtk_file_filter_get_name( filter), _("TOC files")) == 0) {
2293 	strcat( new_filename, ".toc");
2294     } else if( strcmp( gtk_file_filter_get_name( filter), _("CUE files")) == 0) {
2295 	strcat( new_filename, ".cue");
2296     }
2297 
2298     gtk_file_chooser_set_current_name( chooser, new_filename);
2299 
2300     g_free( old_filename);
2301     free( new_filename);
2302 }
2303 
menu_export(GSimpleAction * action,GVariant * parameter,gpointer user_data)2304 static void menu_export(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2305 {
2306     GtkWidget *dialog;
2307     GtkFileFilter *filter_text;
2308     GtkFileFilter *filter_toc;
2309     GtkFileFilter *filter_cue;
2310     gchar* filename = NULL;
2311 
2312     filename = g_strdup( sample_filename);
2313     strcpy( filename + strlen( filename) - 3, "txt");
2314 
2315     filter_text = gtk_file_filter_new();
2316     gtk_file_filter_set_name( filter_text, _("Text files"));
2317     gtk_file_filter_add_pattern( filter_text, "*.txt");
2318 
2319     filter_toc = gtk_file_filter_new();
2320     gtk_file_filter_set_name( filter_toc, _("TOC files"));
2321     gtk_file_filter_add_pattern( filter_toc, "*.toc");
2322 
2323     filter_cue = gtk_file_filter_new();
2324     gtk_file_filter_set_name( filter_cue, _("CUE files"));
2325     gtk_file_filter_add_pattern( filter_cue, "*.cue");
2326 
2327     dialog = gtk_file_chooser_dialog_new(_("Export track breaks to file"), GTK_WINDOW(main_window),
2328         GTK_FILE_CHOOSER_ACTION_SAVE,
2329         _("_Cancel"), GTK_RESPONSE_CANCEL,
2330         _("_Save"), GTK_RESPONSE_ACCEPT,
2331         NULL);
2332 
2333     gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter_text);
2334     gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter_toc);
2335     gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter_cue);
2336     gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(dialog), filter_text);
2337 
2338     gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER(dialog), basename( filename));
2339     gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), dirname( filename));
2340 
2341     g_signal_connect ( GTK_FILE_CHOOSER(dialog), "notify::filter", G_CALLBACK( filter_changed), NULL);
2342 
2343     if (gtk_dialog_run( GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2344         track_breaks_export_to_file( gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog)));
2345     }
2346 
2347     g_free( filename);
2348 
2349     gtk_widget_destroy(dialog);
2350 }
2351 
menu_delete_track_break(GSimpleAction * action,GVariant * parameter,gpointer user_data)2352 void menu_delete_track_break(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2353 {
2354     track_break_delete_entry();
2355 }
2356 
menu_import(GSimpleAction * action,GVariant * parameter,gpointer user_data)2357 void menu_import(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2358 {
2359     GtkWidget *dialog;
2360     GtkFileFilter *filter_all;
2361     GtkFileFilter *filter_supported;
2362     gchar* filename = NULL;
2363     int rc;
2364 
2365     filename = g_strdup( sample_filename);
2366     strcpy( filename + strlen( filename) - 3, "txt");
2367 
2368     filter_all = gtk_file_filter_new();
2369     gtk_file_filter_set_name( filter_all, _("All files"));
2370     gtk_file_filter_add_pattern( filter_all, "*");
2371 
2372     filter_supported = gtk_file_filter_new();
2373     gtk_file_filter_set_name( filter_supported, _("Supported files"));
2374     gtk_file_filter_add_pattern( filter_supported, "*.txt");
2375     gtk_file_filter_add_pattern( filter_supported, "*.toc");
2376     gtk_file_filter_add_pattern( filter_supported, "*.cue");
2377 
2378     dialog = gtk_file_chooser_dialog_new(_("Import track breaks from file"), GTK_WINDOW(main_window),
2379         GTK_FILE_CHOOSER_ACTION_OPEN,
2380         _("_Cancel"), GTK_RESPONSE_CANCEL,
2381         _("_Open"), GTK_RESPONSE_ACCEPT,
2382         NULL);
2383 
2384     gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter_all);
2385     gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter_supported);
2386     gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(dialog), filter_supported);
2387 
2388     gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(dialog), filename);
2389 
2390     if (gtk_dialog_run( GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2391 	gchar const *selected = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog));
2392 	if (g_str_has_suffix( selected, ".toc") || g_str_has_suffix( selected, ".TOC")) {
2393 	    rc = toc_read_file( selected, track_break_list);
2394 	    if( rc ) {
2395 		popupmessage_show( main_window, _("Import failed"), _("There has been an error importing track breaks from the TOC file."));
2396 	    }
2397 	} else if (g_str_has_suffix( selected, ".cue") || g_str_has_suffix( selected, ".CUE")) {
2398 	    rc = cue_read_file( selected, track_break_list);
2399 	    if( rc ) {
2400 		popupmessage_show( main_window, _("Import failed"), _("There has been an error importing track breaks from the CUE file."));
2401 	    }
2402 	} else {
2403 	    track_breaks_load_from_file( selected);
2404 	}
2405     }
2406 
2407     g_free( filename);
2408 
2409     gtk_widget_destroy(dialog);
2410 
2411     force_redraw();
2412 }
2413 
menu_add_track_break(GSimpleAction * action,GVariant * parameter,gpointer user_data)2414 void menu_add_track_break(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2415 {
2416     track_break_add_entry();
2417 }
2418 
2419 static void
menu_open_file(GSimpleAction * action,GVariant * parameter,gpointer user_data)2420 menu_open_file(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2421 {
2422     open_select_file();
2423 }
2424 
2425 static void
menu_menu(GSimpleAction * action,GVariant * parameter,gpointer user_data)2426 menu_menu(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2427 {
2428     gtk_popover_popup(GTK_POPOVER(menu_popover));
2429 }
2430 
2431 #if defined(WANT_MOODBAR)
2432 void
wavbreaker_update_moodbar_state()2433 wavbreaker_update_moodbar_state()
2434 {
2435     if (moodbarData) {
2436         moodbar_free(moodbarData);
2437     }
2438     moodbarData = moodbar_open(sample_filename);
2439     set_action_enabled("display_moodbar", moodbarData != NULL);
2440     set_action_enabled("generate_moodbar", moodbarData == NULL);
2441 
2442     redraw();
2443 }
2444 
2445 static void
menu_view_moodbar(GSimpleAction * action,GVariant * parameter,gpointer user_data)2446 menu_view_moodbar(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2447 {
2448     GVariant *state = g_action_get_state(G_ACTION(action));
2449     gboolean new_value = !g_variant_get_boolean(state);
2450     g_variant_unref(state);
2451 
2452     g_action_change_state(G_ACTION(action), g_variant_new("b", new_value));
2453     appconfig_set_show_moodbar(new_value);
2454 
2455     wavbreaker_update_moodbar_state();
2456 }
2457 
2458 static void
menu_moodbar(GSimpleAction * action,GVariant * parameter,gpointer user_data)2459 menu_moodbar(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2460 {
2461     moodbar_generate(main_window, sample_filename);
2462 }
2463 #endif
2464 
2465 static void
menu_about(GSimpleAction * action,GVariant * parameter,gpointer user_data)2466 menu_about(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2467 {
2468     about_show(main_window);
2469 }
2470 
2471 static void
menu_config(GSimpleAction * action,GVariant * parameter,gpointer user_data)2472 menu_config(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2473 {
2474     appconfig_show(main_window);
2475 }
2476 
2477 static void
menu_merge(GSimpleAction * action,GVariant * parameter,gpointer user_data)2478 menu_merge(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2479 {
2480     guimerge_show(main_window);
2481 }
2482 
2483 static void
menu_autosplit(gpointer callback_data,guint callback_action,GtkWidget * widget)2484 menu_autosplit(gpointer callback_data, guint callback_action, GtkWidget *widget)
2485 {
2486     gtk_popover_popup(GTK_POPOVER(autosplit_popover));
2487 }
2488 
2489 static void
menu_rename(GSimpleAction * action,GVariant * parameter,gpointer user_data)2490 menu_rename(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2491 {
2492     /* rename (AND overwrite) track breaks */
2493     track_break_rename( TRUE);
2494 }
2495 
save_window_sizes()2496 static void save_window_sizes()
2497 {
2498     gint x, y, w, h;
2499 
2500     gdk_window_get_root_origin(GDK_WINDOW(gtk_widget_get_window(main_window)), &x, &y);
2501     gtk_window_get_size(GTK_WINDOW(main_window), &w, &h);
2502     /*
2503     g_print("w: %d\n", w);
2504     g_print("h: %d\n", h);
2505     */
2506     appconfig_set_main_window_xpos(x);
2507     appconfig_set_main_window_ypos(y);
2508     appconfig_set_main_window_width(w);
2509     appconfig_set_main_window_height(h);
2510     appconfig_set_vpane1_position(gtk_paned_get_position(GTK_PANED(vpane1)));
2511     appconfig_set_vpane2_position(gtk_paned_get_position(GTK_PANED(vpane2)));
2512 }
2513 
wavbreaker_quit()2514 void wavbreaker_quit() {
2515     stop_sample();
2516 
2517     if (file_write_progress_source_id) {
2518         g_source_remove(file_write_progress_source_id);
2519         file_write_progress_source_id = 0;
2520     }
2521 
2522     if (open_file_source_id) {
2523         g_source_remove(open_file_source_id);
2524         open_file_source_id = 0;
2525     }
2526 
2527     if (file_open_progress_source_id) {
2528         g_source_remove(file_open_progress_source_id);
2529         file_open_progress_source_id = 0;
2530     }
2531 
2532     if (redraw_source_id) {
2533         g_source_remove(redraw_source_id);
2534         redraw_source_id = 0;
2535     }
2536 
2537     if (play_progress_source_id) {
2538         g_source_remove(play_progress_source_id);
2539         play_progress_source_id = 0;
2540     }
2541 
2542     save_window_sizes();
2543     gtk_widget_destroy(main_window);
2544 }
2545 
check_really_quit()2546 static void check_really_quit()
2547 {
2548     if( sample_filename != NULL && g_list_length(track_break_list) != 1) {
2549         reallyquit_show(main_window);
2550     } else {
2551         wavbreaker_quit();
2552     }
2553 }
2554 
wavbreaker_get_main_window()2555 GtkWidget *wavbreaker_get_main_window()
2556 {
2557     return main_window;
2558 }
2559 
2560 
2561 /*
2562  *-------------------------------------------------------------------------
2563  * Main Window Events
2564  *-------------------------------------------------------------------------
2565  */
2566 
2567 static gboolean
delete_event(GtkWidget * widget,GdkEventAny * event,gpointer data)2568 delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
2569 {
2570     //g_print("delete_event event occurred\n");
2571     check_really_quit();
2572     return TRUE;
2573 }
2574 
2575 static GtkWidget *
make_time_offset_widget()2576 make_time_offset_widget()
2577 {
2578     GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
2579 
2580     gtk_box_pack_start( GTK_BOX( hbox), gtk_label_new( _("Time offset:")), FALSE, FALSE, 0);
2581 
2582     cursor_marker_min_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 1000.0, 1.0, 74.0, 0);
2583     cursor_marker_min_spinner = gtk_spin_button_new(cursor_marker_min_spinner_adj, 1.0, 0);
2584     gtk_widget_set_sensitive( cursor_marker_min_spinner, FALSE);
2585     gtk_box_pack_start(GTK_BOX(hbox), cursor_marker_min_spinner, FALSE, FALSE, 0);
2586     g_signal_connect(G_OBJECT(cursor_marker_min_spinner_adj), "value-changed",
2587              G_CALLBACK(cursor_marker_time_spinners_changed), NULL);
2588 
2589     gtk_box_pack_start( GTK_BOX( hbox), gtk_label_new(":"), FALSE, FALSE, 0);
2590 
2591     cursor_marker_sec_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 59.0, 1.0, 74.0, 0);
2592     cursor_marker_sec_spinner = gtk_spin_button_new(cursor_marker_sec_spinner_adj, 1.0, 0);
2593     gtk_widget_set_sensitive( cursor_marker_sec_spinner, FALSE);
2594     gtk_box_pack_start(GTK_BOX(hbox), cursor_marker_sec_spinner, FALSE, FALSE, 0);
2595     g_signal_connect(G_OBJECT(cursor_marker_sec_spinner_adj), "value-changed",
2596              G_CALLBACK(cursor_marker_time_spinners_changed), NULL);
2597 
2598     gtk_box_pack_start( GTK_BOX( hbox), gtk_label_new("."), FALSE, FALSE, 0);
2599 
2600     cursor_marker_subsec_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, CD_BLOCKS_PER_SEC-1, 1.0, 74.0, 0);
2601     cursor_marker_subsec_spinner = gtk_spin_button_new(cursor_marker_subsec_spinner_adj, 1.0, 0);
2602     gtk_widget_set_sensitive( cursor_marker_subsec_spinner, FALSE);
2603     gtk_box_pack_start(GTK_BOX(hbox), cursor_marker_subsec_spinner, FALSE, FALSE, 0);
2604     g_signal_connect(G_OBJECT(cursor_marker_subsec_spinner_adj), "value-changed",
2605              G_CALLBACK(cursor_marker_time_spinners_changed), NULL);
2606 
2607     gtk_widget_show_all(hbox);
2608 
2609     return hbox;
2610 }
2611 
2612 static void
do_startup(GApplication * application,gpointer user_data)2613 do_startup(GApplication *application, gpointer user_data)
2614 {
2615     setlocale(LC_ALL, "");
2616     bindtextdomain(PACKAGE, LOCALEDIR);
2617     bind_textdomain_codeset(PACKAGE, "UTF-8");
2618     textdomain(PACKAGE);
2619 
2620     appconfig_init();
2621 }
2622 
2623 static void
menu_check_all(GSimpleAction * action,GVariant * parameter,gpointer user_data)2624 menu_check_all(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2625 {
2626     parts_check_cb(NULL, GINT_TO_POINTER(CHECK_ALL));
2627 }
2628 
2629 static void
menu_check_none(GSimpleAction * action,GVariant * parameter,gpointer user_data)2630 menu_check_none(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2631 {
2632     parts_check_cb(NULL, GINT_TO_POINTER(CHECK_NONE));
2633 }
2634 
2635 static void
menu_check_invert(GSimpleAction * action,GVariant * parameter,gpointer user_data)2636 menu_check_invert(GSimpleAction *action, GVariant *parameter, gpointer user_data)
2637 {
2638     parts_check_cb(NULL, GINT_TO_POINTER(CHECK_INVERT));
2639 }
2640 
2641 static void
do_activate(GApplication * app,gpointer user_data)2642 do_activate(GApplication *app, gpointer user_data)
2643 {
2644     GtkWidget *vbox;
2645     GtkWidget *tbl_widget;
2646 
2647     GtkWidget *vpane_vbox;
2648     GtkWidget *list_vbox;
2649     GtkWidget *frame;
2650 
2651     GtkWidget *hbox;
2652     GtkWidget *button;
2653 
2654     sample_surface = waveform_surface_create_sample();
2655     summary_surface = waveform_surface_create_summary();
2656 
2657     main_window = gtk_application_window_new(GTK_APPLICATION(app));
2658 
2659     GActionEntry entries[] = {
2660         { "open", menu_open_file, NULL, NULL, NULL, },
2661         { "menu", menu_menu, NULL, NULL, NULL, },
2662 
2663         // TODO: "save" is currently unused
2664         { "save", menu_save, NULL, NULL, NULL, },
2665         { "save_to_folder", menu_save_as, NULL, NULL, NULL, },
2666         { "export", menu_export, NULL, NULL, NULL, },
2667         { "import", menu_import, NULL, NULL, NULL, },
2668 
2669         { "add_break", menu_add_track_break, NULL, NULL, NULL, },
2670         { "jump_cursor", jump_to_cursor_marker, NULL, NULL, NULL, },
2671 
2672 #if defined(WANT_MOODBAR)
2673         { "display_moodbar", menu_view_moodbar, NULL, appconfig_get_show_moodbar()?"true":"false", NULL, },
2674         { "generate_moodbar", menu_moodbar, NULL, NULL, NULL, },
2675 #endif
2676 
2677         { "check_all", menu_check_all, NULL, NULL, NULL, },
2678         { "check_none", menu_check_none, NULL, NULL, NULL, },
2679         { "check_invert", menu_check_invert, NULL, NULL, NULL, },
2680 
2681         { "auto_rename", menu_rename, NULL, NULL, NULL, },
2682         { "remove_break", menu_delete_track_break, NULL, NULL, NULL, },
2683         { "jump_break", jump_to_track_break, NULL, NULL, NULL, },
2684     };
2685 
2686     g_action_map_add_action_entries(G_ACTION_MAP(main_window),
2687             entries, G_N_ELEMENTS(entries),
2688             main_window);
2689 
2690     set_action_enabled("add_break", FALSE);
2691     set_action_enabled("jump_cursor", FALSE);
2692 
2693     set_action_enabled("check_all", FALSE);
2694     set_action_enabled("check_none", FALSE);
2695     set_action_enabled("check_invert", FALSE);
2696 
2697     set_action_enabled("auto_rename", FALSE);
2698     set_action_enabled("remove_break", FALSE);
2699     set_action_enabled("jump_break", FALSE);
2700 
2701     set_action_enabled("export", FALSE);
2702     set_action_enabled("import", FALSE);
2703 
2704 #if defined(WANT_MOODBAR)
2705     set_action_enabled("display_moodbar", FALSE);
2706     set_action_enabled("generate_moodbar", FALSE);
2707 #endif
2708 
2709     gtk_window_set_default_icon_name( PACKAGE);
2710 
2711     header_bar = gtk_header_bar_new();
2712     gtk_header_bar_set_title(GTK_HEADER_BAR(header_bar), APPNAME);
2713     gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header_bar), TRUE);
2714 
2715     GtkWidget *open_button = gtk_button_new_from_icon_name("document-open-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2716     gtk_widget_set_tooltip_text(open_button, _("Open file"));
2717     gtk_actionable_set_action_name(GTK_ACTIONABLE(open_button), "win.open");
2718     gtk_header_bar_pack_start(GTK_HEADER_BAR(header_bar), open_button);
2719 
2720     GtkWidget *menu_button = gtk_button_new_from_icon_name("open-menu-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2721     gtk_widget_set_tooltip_text(menu_button, _("Open menu"));
2722     gtk_actionable_set_action_name(GTK_ACTIONABLE(menu_button), "win.menu");
2723     gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), menu_button);
2724 
2725     GMenu *top_menu = g_menu_new();
2726 
2727 #if defined(WANT_MOODBAR)
2728     GMenu *display_menu = g_menu_new();
2729     g_menu_append(display_menu, _("Display moodbar"), "win.display_moodbar");
2730     g_menu_append(display_menu, _("Generate moodbar"), "win.generate_moodbar");
2731     g_menu_append_section(top_menu, NULL, G_MENU_MODEL(display_menu));
2732 #endif
2733 
2734     GMenu *toc_menu = g_menu_new();
2735     g_menu_append(toc_menu, _("Import track breaks"), "win.import");
2736     g_menu_append(toc_menu, _("Export track breaks"), "win.export");
2737     g_menu_append_section(top_menu, NULL, G_MENU_MODEL(toc_menu));
2738 
2739     GMenu *tools_menu = g_menu_new();
2740     g_menu_append(tools_menu, _("Merge wave files"), "app.guimerge");
2741     g_menu_append_section(top_menu, NULL, G_MENU_MODEL(tools_menu));
2742 
2743     GMenu *prefs_menu = g_menu_new();
2744     g_menu_append(prefs_menu, _("Preferences"), "app.preferences");
2745     g_menu_append_section(top_menu, NULL, G_MENU_MODEL(prefs_menu));
2746 
2747     GMenu *about_menu = g_menu_new();
2748     g_menu_append(about_menu, _("About"), "app.about");
2749     g_menu_append_section(top_menu, NULL, G_MENU_MODEL(about_menu));
2750 
2751     menu_popover = gtk_popover_new_from_model(menu_button, G_MENU_MODEL(top_menu));
2752     gtk_popover_set_position(GTK_POPOVER(menu_popover), GTK_POS_BOTTOM);
2753 
2754     header_bar_save_button = GTK_WIDGET(gtk_button_new_from_icon_name("document-save-as-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR));
2755     gtk_actionable_set_action_name(GTK_ACTIONABLE(header_bar_save_button), "win.save_to_folder");
2756     gtk_widget_set_sensitive(header_bar_save_button, FALSE);
2757     gtk_widget_set_tooltip_text(header_bar_save_button, _("Save file parts"));
2758     gtk_header_bar_pack_start(GTK_HEADER_BAR(header_bar), header_bar_save_button);
2759 
2760     gtk_window_set_titlebar(GTK_WINDOW(main_window), header_bar);
2761 
2762     set_title( NULL);
2763 
2764     g_signal_connect(G_OBJECT(main_window), "delete_event",
2765              G_CALLBACK(delete_event), NULL);
2766 
2767     gtk_container_set_border_width(GTK_CONTAINER(main_window), 0);
2768 
2769     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2770     gtk_container_add(GTK_CONTAINER(main_window), vbox);
2771 
2772     /* paned view */
2773     vpane1 = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
2774     gtk_box_pack_start(GTK_BOX(vbox), vpane1, TRUE, TRUE, 0);
2775 
2776     /* vbox for the vpane */
2777     vpane_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2778     gtk_paned_pack1(GTK_PANED(vpane1), vpane_vbox, TRUE, TRUE);
2779 
2780     /* paned view */
2781     vpane2 = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
2782     gtk_box_pack_start(GTK_BOX(vpane_vbox), vpane2, TRUE, TRUE, 0);
2783 
2784     /* The summary_surface drawing area */
2785     draw_summary = gtk_drawing_area_new();
2786 
2787     g_signal_connect(G_OBJECT(draw_summary), "draw",
2788              G_CALLBACK(draw_summary_draw_event), NULL);
2789     g_signal_connect(G_OBJECT(draw_summary), "configure_event",
2790              G_CALLBACK(draw_summary_configure_event), NULL);
2791     g_signal_connect(G_OBJECT(draw_summary), "button_release_event",
2792              G_CALLBACK(draw_summary_button_release), NULL);
2793     g_signal_connect(G_OBJECT(draw_summary), "motion_notify_event",
2794              G_CALLBACK(draw_summary_button_release), NULL);
2795     g_signal_connect(G_OBJECT(draw_summary), "scroll-event",
2796              G_CALLBACK(scroll_event), NULL);
2797 
2798     gtk_widget_add_events(draw_summary, GDK_BUTTON_RELEASE_MASK);
2799     gtk_widget_add_events(draw_summary, GDK_BUTTON_PRESS_MASK);
2800     gtk_widget_add_events(draw_summary, GDK_BUTTON_MOTION_MASK);
2801 
2802     frame = gtk_frame_new(NULL);
2803     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
2804 
2805     gtk_container_add(GTK_CONTAINER(frame), draw_summary);
2806     gtk_paned_add1(GTK_PANED(vpane2), frame);
2807 
2808     /* The sample_surface drawing area */
2809     draw = gtk_drawing_area_new();
2810 
2811     g_signal_connect(G_OBJECT(draw), "draw",
2812              G_CALLBACK(draw_draw_event), NULL);
2813     g_signal_connect(G_OBJECT(draw), "configure_event",
2814              G_CALLBACK(configure_event), NULL);
2815     g_signal_connect(G_OBJECT(draw), "button_release_event",
2816              G_CALLBACK(button_release), NULL);
2817     g_signal_connect(G_OBJECT(draw), "motion_notify_event",
2818              G_CALLBACK(button_release), NULL);
2819     g_signal_connect(G_OBJECT(draw), "scroll-event",
2820              G_CALLBACK(scroll_event), NULL);
2821 
2822     gtk_widget_add_events(draw, GDK_BUTTON_RELEASE_MASK);
2823     gtk_widget_add_events(draw, GDK_BUTTON_PRESS_MASK);
2824     gtk_widget_add_events(draw, GDK_BUTTON_MOTION_MASK);
2825 
2826     frame = gtk_frame_new(NULL);
2827     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
2828 
2829     gtk_container_add(GTK_CONTAINER(frame), draw);
2830     gtk_paned_add2(GTK_PANED(vpane2), frame);
2831 //    gtk_box_pack_start(GTK_BOX(vpane_vbox), draw, TRUE, TRUE, 5);
2832 
2833 /* Add scrollbar */
2834     adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 100, 1, 10, 100));
2835     g_signal_connect(G_OBJECT(adj), "value_changed",
2836              G_CALLBACK(adj_value_changed), NULL);
2837 
2838     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT(adj));
2839     gtk_box_pack_start(GTK_BOX(vpane_vbox), scrollbar, FALSE, TRUE, 0);
2840 
2841 
2842 /* vbox for the list */
2843     list_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
2844     gtk_container_set_border_width(GTK_CONTAINER(list_vbox), 5);
2845     gtk_paned_pack2(GTK_PANED(vpane1), list_vbox, FALSE, TRUE);
2846 
2847 /* Add cursor marker spinner and track break add and delete buttons */
2848     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
2849     gtk_box_pack_start(GTK_BOX(list_vbox), hbox, FALSE, FALSE, 0);
2850 
2851     play_button = gtk_button_new_from_icon_name("media-playback-start-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2852     gtk_widget_set_sensitive(play_button, FALSE);
2853     gtk_widget_set_tooltip_text(play_button, _("Toggle playback"));
2854     gtk_box_pack_start(GTK_BOX(hbox), play_button, FALSE, FALSE, 0);
2855     g_signal_connect(G_OBJECT(play_button), "clicked", G_CALLBACK(menu_play), NULL);
2856 
2857 
2858     gtk_box_pack_start( GTK_BOX( hbox), gtk_label_new( _("Cursor position:")), FALSE, FALSE, 0);
2859 
2860     cursor_marker_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 1000.0, 1.0, 74.0, 0);
2861     cursor_marker_spinner = gtk_spin_button_new(cursor_marker_spinner_adj, 1.0, 0);
2862     gtk_widget_set_sensitive( cursor_marker_spinner, FALSE);
2863     gtk_box_pack_start(GTK_BOX(hbox), cursor_marker_spinner, FALSE, FALSE, 0);
2864     g_signal_connect(G_OBJECT(cursor_marker_spinner_adj), "value-changed",
2865              G_CALLBACK(cursor_marker_spinner_changed), NULL);
2866 
2867     //gtk_box_pack_start(GTK_BOX(hbox), make_time_offset_widget(), FALSE, FALSE, 0);
2868 
2869     // Jump Around
2870     GtkWidget *bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
2871     gtk_box_pack_start(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
2872     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_EXPAND);
2873 
2874     button = gtk_button_new_from_icon_name("media-seek-backward-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2875     gtk_widget_set_tooltip_text(button, _("Seek to previous silence"));
2876     gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
2877     g_signal_connect(G_OBJECT(button), "clicked",
2878              G_CALLBACK(menu_prev_silence), NULL);
2879     button_seek_backward = button;
2880 
2881     button = gtk_button_new_from_icon_name("preferences-system-time-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2882     gtk_widget_set_tooltip_text(button, _("Jump to time"));
2883     gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
2884     g_signal_connect(G_OBJECT(button), "clicked",
2885              G_CALLBACK(menu_jump_to), NULL);
2886 
2887     jump_to_popover = gtk_popover_new(button);
2888     gtk_popover_set_position(GTK_POPOVER(jump_to_popover), GTK_POS_BOTTOM);
2889     gtk_container_add(GTK_CONTAINER(jump_to_popover), make_time_offset_widget());
2890 
2891     button_jump_to_time = button;
2892 
2893     button = gtk_button_new_from_icon_name("media-seek-forward-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2894     gtk_widget_set_tooltip_text(button, _("Seek to next silence"));
2895     gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
2896     g_signal_connect(G_OBJECT(button), "clicked",
2897              G_CALLBACK(menu_next_silence), NULL);
2898     button_seek_forward = button;
2899 
2900     // Spacer
2901     gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
2902 
2903     button = gtk_button_new_from_icon_name("edit-cut-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2904     gtk_widget_set_tooltip_text(button, _("Auto-split by interval"));
2905     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2906     g_signal_connect(G_OBJECT(button), "clicked",
2907              G_CALLBACK(menu_autosplit), NULL);
2908     autosplit_popover = gtk_popover_new(button);
2909     gtk_popover_set_position(GTK_POPOVER(autosplit_popover), GTK_POS_BOTTOM);
2910     gtk_container_add(GTK_CONTAINER(autosplit_popover), autosplit_create(GTK_POPOVER(autosplit_popover)));
2911     button_auto_split = button;
2912 
2913     gtk_box_pack_start(GTK_BOX(hbox), gtk_separator_new(GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 0);
2914 
2915     /* add track break button */
2916     button = gtk_button_new_from_icon_name("list-add-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2917     gtk_actionable_set_action_name(GTK_ACTIONABLE(button), "win.add_break");
2918     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2919     button_add_break = button;
2920 
2921     /* delete track break button */
2922     button = gtk_button_new_from_icon_name("list-remove-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2923     gtk_actionable_set_action_name(GTK_ACTIONABLE(button), "win.remove_break");
2924     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2925     button_remove_break = button;
2926 
2927     /* Set buttons to be disabled initially */
2928     gtk_widget_set_sensitive(button_seek_forward, FALSE);
2929     gtk_widget_set_sensitive(button_jump_to_time, FALSE);
2930     gtk_widget_set_sensitive(button_seek_backward, FALSE);
2931     gtk_widget_set_sensitive(button_auto_split, FALSE);
2932     gtk_widget_set_sensitive(button_add_break, FALSE);
2933     gtk_widget_set_sensitive(button_remove_break, FALSE);
2934 
2935 /* Track Break List */
2936     tbl_widget = track_break_create_list_gui();
2937     gtk_box_pack_start(GTK_BOX(list_vbox), tbl_widget, TRUE, TRUE, 0);
2938 
2939 /* Finish up */
2940 
2941     write_info.cur_filename = NULL;
2942 
2943     sample_init();
2944 
2945     if (appconfig_get_main_window_xpos() > 0) {
2946         gtk_window_move (GTK_WINDOW (main_window),
2947                     appconfig_get_main_window_xpos(),
2948                     appconfig_get_main_window_ypos());
2949     }
2950 
2951     if( appconfig_get_main_window_width() > 0) {
2952         gtk_window_resize(GTK_WINDOW(main_window),
2953                 appconfig_get_main_window_width(),
2954                 appconfig_get_main_window_height());
2955         gtk_paned_set_position(GTK_PANED(vpane1), appconfig_get_vpane1_position());
2956         gtk_paned_set_position(GTK_PANED(vpane2), appconfig_get_vpane2_position());
2957     }
2958 
2959     gtk_widget_show_all( GTK_WIDGET(main_window));
2960 
2961     if (user_data) {
2962         open_file_source_id = g_idle_add(open_file_arg, user_data);
2963     }
2964 }
2965 
2966 static void
do_open(GApplication * application,gpointer files,gint n_files,gchar * hint,gpointer user_data)2967 do_open(GApplication *application, gpointer files, gint n_files, gchar *hint, gpointer user_data)
2968 {
2969     GFile **gfiles = (GFile **)files;
2970     for (int i=0; i<n_files; ++i) {
2971         const char *path = g_file_get_path(gfiles[i]);
2972         do_activate(application, g_strdup(path));
2973     }
2974 }
2975 
2976 static void
do_shutdown(GApplication * application,gpointer user_data)2977 do_shutdown(GApplication *application, gpointer user_data)
2978 {
2979 #if defined(WANT_MOODBAR)
2980     moodbar_abort();
2981 #endif
2982 
2983     waveform_surface_free(sample_surface);
2984     waveform_surface_free(summary_surface);
2985 
2986     appconfig_write_file();
2987 }
2988 
2989 int
main(int argc,char * argv[])2990 main(int argc, char *argv[])
2991 {
2992     GtkApplication *app;
2993     int status;
2994 
2995     app = gtk_application_new("net.sourceforge.wavbreaker", G_APPLICATION_HANDLES_OPEN);
2996     g_signal_connect(app, "startup", G_CALLBACK (do_startup), NULL);
2997     g_signal_connect(app, "activate", G_CALLBACK (do_activate), NULL);
2998     g_signal_connect(app, "open", G_CALLBACK (do_open), NULL);
2999     g_signal_connect(app, "shutdown", G_CALLBACK (do_shutdown), NULL);
3000 
3001     static const GActionEntry entries[] = {
3002         { "guimerge", menu_merge, NULL, NULL, NULL, },
3003         { "preferences", menu_config, NULL, NULL, NULL },
3004         { "about", menu_about, NULL, NULL, NULL },
3005     };
3006     g_action_map_add_action_entries(G_ACTION_MAP(app), entries, G_N_ELEMENTS(entries), app);
3007 
3008     status = g_application_run(G_APPLICATION (app), argc, argv);
3009     g_object_unref(app);
3010 
3011     return status;
3012 }
3013 
3014 struct WriteStatus {
3015     FILE* fp;
3016     int index;
3017 };
3018 
track_breaks_export_to_file(char * filename)3019 int track_breaks_export_to_file( char* filename) {
3020     FILE *fp = NULL;
3021     int write_err = -1;
3022     char *data_filename = NULL;
3023 
3024     data_filename = basename(sample_filename);
3025 
3026     if( g_str_has_suffix (filename, ".txt")) {
3027 
3028 	fp = fopen( filename, "w");
3029 	if( !fp) {
3030 	    fprintf( stderr, "Error opening %s.\n", filename);
3031 	    return -1;
3032 	}
3033 
3034 	fprintf( fp, "\n; Created by " PACKAGE " " VERSION "\n; http://thpinfo.com/2006/wavbreaker/tb-file-format.txt\n\n");
3035 
3036 	g_list_foreach( track_break_list, track_break_write_text, fp);
3037 
3038 	fprintf( fp, "\n; Total breaks: %d\n; Original file: %s\n\n", g_list_length( track_break_list), sample_filename);
3039 
3040 	fclose( fp);
3041 
3042     } else if( g_str_has_suffix (filename, ".toc")) {
3043 
3044         write_err = toc_write_file( filename, data_filename, track_break_list);
3045 
3046         if( write_err) {
3047             popupmessage_show( main_window, _("Export failed"), _("There has been an error exporting track breaks to the TOC file."));
3048 	    return -1;
3049         }
3050 
3051     } else if( g_str_has_suffix (filename, ".cue")) {
3052 
3053 	fp = fopen( filename, "w");
3054 	if( !fp) {
3055 	    fprintf( stderr, "Error opening %s.\n", filename);
3056 	    return -1;
3057 	}
3058 
3059 	fprintf( fp, "FILE \"%s\" WAVE\n", data_filename);
3060 
3061 	struct WriteStatus ws;
3062 	ws.fp = fp;
3063 	ws.index = 1;
3064 
3065 	g_list_foreach( track_break_list, track_break_write_cue, &ws);
3066 	fclose( fp);
3067 
3068     } else {
3069 	popupmessage_show( main_window, _("Export failed"), _("Unrecognised export type"));
3070 	return -1;
3071     }
3072 
3073     return 0;
3074 }
3075 
track_break_write_text(gpointer data,gpointer user_data)3076 void track_break_write_text( gpointer data, gpointer user_data) {
3077     FILE* fp = (FILE*)user_data;
3078     TrackBreak* track_break = (TrackBreak*)data;
3079 
3080     if( track_break->write) {
3081         fprintf(fp, "%lu=%s\n", track_break->offset, track_break->filename);
3082     } else {
3083         fprintf(fp, "%lu\n", track_break->offset);
3084     }
3085 }
3086 
track_break_write_cue(gpointer data,gpointer user_data)3087 void track_break_write_cue( gpointer data, gpointer user_data) {
3088     struct WriteStatus* ws = (struct WriteStatus*)user_data;
3089     TrackBreak* track_break = (TrackBreak*)data;
3090     char* time;
3091     char* p;
3092 
3093     time = g_strdup( track_break->time);
3094     p = time;
3095     while (*p != '\0') {
3096 	if (*p == '.') {
3097 	    *p = ':';
3098 	}
3099 	++p;
3100     }
3101 
3102     fprintf( ws->fp, "TRACK %02d AUDIO\n", ws->index);
3103     fprintf( ws->fp, "INDEX 01 %s\n", time);
3104     ws->index++;
3105     free( time);
3106 }
3107 
track_breaks_load_from_file(gchar const * filename)3108 int track_breaks_load_from_file( gchar const *filename) {
3109     FILE* fp;
3110     char tmp[1024];
3111     char* ptr;
3112     char* fname;
3113     int c;
3114 
3115     fp = fopen( filename, "r");
3116     if( !fp) {
3117         fprintf( stderr, "Error opening %s.\n", filename);
3118         return 1;
3119     }
3120 
3121     track_break_clear_list();
3122 
3123     ptr = tmp;
3124     while( !feof( fp)) {
3125         c = fgetc( fp);
3126         if( c == EOF)
3127             break;
3128 
3129         if( c == '\n') {
3130             *ptr = '\0';
3131             if( ptr != tmp && tmp[0] != ';') {
3132                 fname = strchr( tmp, '=');
3133                 if( fname == NULL) {
3134                     //DEBUG: printf( "Empty cut at %d\n", atoi( tmp));
3135                     track_break_add_offset(NULL, atol(tmp));
3136                 } else {
3137                     *(fname++) = '\0';
3138                     while( *fname == ' ')
3139                         fname++;
3140                     //DEBUG: printf( "Cut at %d for %s\n", atoi( tmp), fname);
3141                     track_break_add_offset(fname, atol(tmp));
3142                 }
3143             }
3144             ptr = tmp;
3145         } else {
3146             *ptr = c;
3147             ptr++;
3148             if( ptr > tmp+1024) {
3149                 fprintf( stderr, "Error parsing file.\n");
3150                 fclose( fp);
3151                 return 1;
3152             }
3153         }
3154     }
3155 
3156     fclose( fp);
3157     force_redraw();
3158     return 0;
3159 }
3160 
3161 /** @param str Time in MM:SS:FF format (where there are CD_BLOCKS_PER_SEC frames per second).
3162  *  @return offset in frames.
3163  */
3164 guint
msf_time_to_offset(gchar * str)3165 msf_time_to_offset( gchar *str )
3166 {
3167     guint   offset;
3168     int    mm = 0, ss = 0, ff = 0;
3169     int    consumed;
3170 
3171     consumed = sscanf(str, "%d:%d:%d", &mm, &ss, &ff);
3172     if (consumed != 3) {
3173 	return 0;
3174     }
3175 
3176     offset  = mm * CD_BLOCKS_PER_SEC * 60;
3177     offset += ss * CD_BLOCKS_PER_SEC;
3178     offset += ff;
3179 
3180     return offset;
3181 }
3182