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