mp3splt-gtk
main_window.c
Go to the documentation of this file.
1 /**********************************************************
2  *
3  * mp3splt-gtk -- utility based on mp3splt,
4  * for mp3/ogg splitting without decoding
5  *
6  * Copyright: (C) 2005-2012 Alexandru Munteanu
7  * Contact: m@ioalex.net
8  *
9  * http://mp3splt.sourceforge.net/
10  *
11  *********************************************************/
12 /**********************************************************
13  *
14  * This program is free software; you can redistribute it and/or
15 
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
28  * USA.
29  *
30  *********************************************************/
31 
32 /*!********************************************************
33  * \file
34  * The main window
35  *
36  * main file that initialises the menubar, the toolbar,
37  * the tabs, about window, status error messages
38  *
39  *********************************************************/
40 
41 #include "main_window.h"
42 
44 void set_input_filename(const gchar *filename, ui_state *ui)
45 {
46  if (filename == NULL)
47  {
48  return;
49  }
50 
51  lock_mutex(&ui->variables_mutex);
52  if (ui->gui->input_filename != NULL)
53  {
54  g_string_free(ui->gui->input_filename,TRUE);
55  }
56  ui->gui->input_filename = g_string_new(filename);
57 
58  if (ui->gui->open_file_chooser_button != NULL && !ui->status->file_selection_changed)
59  {
60  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(ui->gui->open_file_chooser_button), filename);
61  }
62 
63  update_example_output_dir_for_single_file_split(ui);
64 
65  export_cue_file_in_configuration_directory(ui);
66 
67  unlock_mutex(&ui->variables_mutex);
68 }
69 
77 {
78  if (gui->input_filename != NULL)
79  {
80  return gui->input_filename->str;
81  }
82 
83  return "";
84 }
85 
86 static gboolean configure_window_callback(GtkWindow *window, GdkEvent *event, ui_state *ui)
87 {
88  ui_set_main_win_position(ui, event->configure.x, event->configure.y);
89  ui_set_main_win_size(ui, event->configure.width, event->configure.height);
90 
91  refresh_drawing_area(ui->gui);
92  refresh_preview_drawing_areas(ui->gui);
93 
94  clear_previous_distances(ui);
95 
96  return FALSE;
97 }
98 
99 static void initialize_window(ui_state *ui)
100 {
101  GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
102  ui->gui->window = window;
103 
104  g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(configure_window_callback), ui);
105 
106  gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME" "VERSION);
107  gtk_container_set_border_width(GTK_CONTAINER(window), 0);
108 
109  g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(exit_application), ui);
110 
111  GString *imagefile = g_string_new("");
112  build_path(imagefile, PIXMAP_PATH, "mp3splt-gtk_ico"ICON_EXT);
113  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(imagefile->str, NULL);
114  gtk_window_set_default_icon(pixbuf);
115  g_string_free(imagefile, TRUE);
116 }
117 
118 static void activate_url(GtkAboutDialog *about, const gchar *link, ui_state *ui)
119 {
120 #ifdef __WIN32__
121  char default_browser[512] = { '\0' };
122  DWORD dwType, dwSize = sizeof(default_browser) - 1;
123 
124  SHGetValue(HKEY_CURRENT_USER,
125  TEXT("Software\\Clients\\StartMenuInternet"),
126  TEXT(""),
127  &dwType,
128  default_browser,
129  &dwSize);
130 
131  if (default_browser[0] != '\0')
132  {
133  SHGetValue(HKEY_LOCAL_MACHINE,
134  TEXT("SOFTWARE\\Clients\\StartMenuInternet"),
135  TEXT(""),
136  &dwType,
137  default_browser,
138  &dwSize);
139  }
140 
141  if (default_browser[0] != '\0')
142  {
143  char browser_exe[2048] = { '\0' };
144  dwSize = sizeof(browser_exe) - 1;
145 
146  char browser_exe_registry[1024] = { '\0' };
147  snprintf(browser_exe_registry, 1024,
148  "SOFTWARE\\Clients\\StartMenuInternet\\%s\\shell\\open\\command\\",
149  default_browser);
150 
151  SHGetValue(HKEY_LOCAL_MACHINE,
152  TEXT(browser_exe_registry), TEXT(""),
153  &dwType, browser_exe, &dwSize);
154 
155  if (browser_exe[0] != '\0')
156  {
157  gint browser_command_size = strlen(browser_exe) + strlen(link) + 2;
158  char *browser_command = g_malloc(sizeof(char) * browser_command_size);
159  if (browser_command)
160  {
161  snprintf(browser_command, browser_command_size, "%s %s",
162  browser_exe, link);
163 
164  STARTUPINFO si;
165  PROCESS_INFORMATION pinf;
166  ZeroMemory(&si, sizeof(si));
167  si.cb = sizeof(si);
168  ZeroMemory(&pinf, sizeof(pinf));
169 
170  if (! CreateProcess(NULL, browser_command,
171  NULL, NULL, FALSE, 0, NULL, NULL, &si, &pinf))
172  {
173  put_status_message(_("Error launching external command"), ui);
174  }
175 
176  CloseHandle(pinf.hProcess);
177  CloseHandle(pinf.hThread);
178 
179  g_free(browser_command);
180  browser_command = NULL;
181  }
182  }
183  }
184 #endif
185 }
186 
187 static void about_window(GtkWidget *widget, ui_state *ui)
188 {
189  GtkWidget *dialog = gtk_about_dialog_new();
190 
191  GString *imagefile = g_string_new("");
192  build_path(imagefile, PIXMAP_PATH, "mp3splt-gtk.png");
193  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(imagefile->str, NULL);
194  gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
195  g_string_free(imagefile, TRUE);
196 
197  gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(dialog), (gchar *)PACKAGE_NAME);
198  gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), VERSION);
199  gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog),
200  PACKAGE_NAME" : Copyright © 2005-2012 Alexandru"
201  " Munteanu \n mp3splt : Copyright © 2002-2005 Matteo Trotta");
202 
203  gchar b3[100] = { '\0' };
204  gchar *b1 = _("using");
205 
206  char *library_version = mp3splt_get_version();
207  g_snprintf(b3, 100, "-%s-\n%s libmp3splt %s",
208  _("release of "MP3SPLT_GTK_DATE), b1, library_version);
209  free(library_version);
210 
211  gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), b3);
212 
213  gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(dialog),
214  "\n"
215  "This program is free software; you can "
216  "redistribute it and/or \n"
217  "modify it under the terms of the GNU General Public License\n"
218  "as published by the Free Software "
219  "Foundation; either version 2\n"
220  "of the License, or (at your option) "
221  "any later version.\n\n"
222  "This program is distributed in the "
223  "hope that it will be useful,\n"
224  "but WITHOUT ANY WARRANTY; without even "
225  "the implied warranty of\n"
226  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
227  "GNU General Public License for more details.\n\n"
228  "You should have received a copy of the GNU General Public License\n"
229  "along with this program; if not, write "
230  "to the Free Software\n"
231  "Foundation, Inc., "
232  "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.");
233 
234  g_signal_connect(G_OBJECT(dialog), "activate-link", G_CALLBACK(activate_url), ui);
235 
236  gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog),
237  "http://mp3splt.sourceforge.net");
238  gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),
239  "http://mp3splt.sourceforge.net");
240 
241  gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog),
242  "Mario Blättermann <mariobl@gnome.org>");
243 
244  gtk_dialog_run(GTK_DIALOG(dialog));
245  gtk_widget_destroy(dialog);
246 }
247 
253 {
254  guint status_id = gtk_statusbar_get_context_id(gui->status_bar, "mess");
255  gtk_statusbar_pop(gui->status_bar, status_id);
256 }
257 
264 void put_status_message(const gchar *text, ui_state *ui)
265 {
267 }
268 
278 void put_status_message_with_type(const gchar *text, splt_message_type mess_type, ui_state *ui)
279 {
280  gui_state *gui = ui->gui;
281 
282  if (mess_type == SPLT_MESSAGE_INFO)
283  {
284  guint status_id = gtk_statusbar_get_context_id(gui->status_bar, "mess");
285  gtk_statusbar_pop(gui->status_bar, status_id);
286  gtk_statusbar_push(gui->status_bar, status_id, text);
287  }
288 
289  put_message_in_history(text, mess_type, ui);
290 }
291 
292 void set_stop_split_safe(gboolean value, ui_state *ui)
293 {
294  lock_mutex(&ui->variables_mutex);
295  ui->status->stop_split = value;
296  unlock_mutex(&ui->variables_mutex);
297 }
298 
300 void cancel_button_event(GtkWidget *widget, ui_state *ui)
301 {
302  lmanager_stop_split(ui);
303 
304  set_stop_split_safe(TRUE, ui);
305 
306  if (widget != NULL)
307  {
308  gtk_widget_set_sensitive(widget, FALSE);
309  }
310 
311  put_status_message(_(" info: stopping the split process.. please wait"), ui);
312 }
313 
314 static void show_preferences_window(GtkWidget *widget, ui_state *ui)
315 {
316  if (ui->gui->preferences_window == NULL)
317  {
318  ui->gui->preferences_window =
319  wh_create_window_with_close_button(_("Preferences"), 600, 450, GTK_WIN_POS_CENTER,
320  GTK_WINDOW(ui->gui->window), ui->gui->preferences_widget, NULL);
321  }
322 
323  wh_show_window(ui->gui->preferences_window);
324 }
325 
326 static void show_tracktype_window(GtkWidget *widget, ui_state *ui)
327 {
328  if (ui->gui->freedb_window == NULL)
329  {
330  ui->gui->freedb_window =
331  wh_create_window_with_close_button(_("TrackType"), 500, 300, GTK_WIN_POS_CENTER,
332  GTK_WINDOW(ui->gui->window),
333  ui->gui->freedb_widget,
334  ui->gui->freedb_add_button, NULL);
335  }
336 
337  wh_show_window(ui->gui->freedb_window);
338  hide_freedb_spinner(ui->gui);
339 }
340 
341 static void show_split_files_window(GtkWidget *widget, ui_state *ui)
342 {
343  if (ui->gui->split_files_window == NULL)
344  {
345  ui->gui->split_files_window =
346  wh_create_window_with_close_button(_("Split files"), 500, 300, GTK_WIN_POS_CENTER,
347  GTK_WINDOW(ui->gui->window),
348  ui->gui->split_files_widget,
349  ui->gui->queue_files_button, ui->gui->remove_file_button,
350  ui->gui->remove_all_files_button, NULL);
351  }
352 
353  wh_show_window(ui->gui->split_files_window);
354 }
355 
356 static void show_splitpoints_window(GtkWidget *widget, ui_state *ui)
357 {
358  if (ui->gui->splitpoints_window == NULL)
359  {
360  ui->gui->splitpoints_window =
361  wh_create_window_with_close_button(_("Splitpoints"), 500, 300, GTK_WIN_POS_CENTER,
362  GTK_WINDOW(ui->gui->window),
363  ui->gui->splitpoints_widget,
364  ui->gui->scan_trim_silence_button, ui->gui->scan_silence_button, NULL);
365  }
366 
367  wh_show_window(ui->gui->splitpoints_window);
368 }
369 
370 void set_is_splitting_safe(gboolean value, ui_state *ui)
371 {
372  lock_mutex(&ui->variables_mutex);
373  ui->status->splitting = value;
374  unlock_mutex(&ui->variables_mutex);
375 }
376 
377 gint get_is_splitting_safe(ui_state *ui)
378 {
379  lock_mutex(&ui->variables_mutex);
380  gint is_splitting = ui->status->splitting;
381  unlock_mutex(&ui->variables_mutex);
382  return is_splitting;
383 }
384 
385 static void _set_process_in_progress_safe(gboolean value, ui_state *ui)
386 {
387  lock_mutex(&ui->variables_mutex);
388  if (value)
389  {
390  ui->status->process_in_progress++;
391  }
392  else
393  {
394  ui->status->process_in_progress--;
395  }
396  unlock_mutex(&ui->variables_mutex);
397 }
398 
399 void set_process_in_progress_and_wait_safe(gboolean value, ui_state *ui)
400 {
401  if (value == TRUE)
402  {
403  while (get_process_in_progress_safe(ui))
404  {
405  g_usleep(G_USEC_PER_SEC / 4);
406  }
407  }
408 
409  _set_process_in_progress_safe(value, ui);
410 }
411 
412 void set_process_in_progress_safe(gboolean value, ui_state *ui)
413 {
414 #ifdef __WIN32__
415  _set_process_in_progress_safe(value, ui);
416 #endif
417 }
418 
419 gint get_process_in_progress_safe(ui_state *ui)
420 {
421  lock_mutex(&ui->variables_mutex);
422  gint process_in_progress = ui->status->process_in_progress;
423  unlock_mutex(&ui->variables_mutex);
424  return process_in_progress > 0;
425 }
426 
428 void split_button_event(GtkWidget *widget, ui_state *ui)
429 {
430  if (get_is_splitting_safe(ui))
431  {
432  put_status_message(_(" error: split in progress..."), ui);
433  return;
434  }
435 
436  if (get_output_directory(ui) != NULL)
437  {
438  split_action(ui);
439  }
440  else
441  {
442  put_status_message(_(" error: no path of split selected"), ui);
443  }
444 }
445 
446 void set_split_file_mode_safe(gint file_mode, ui_state *ui)
447 {
448  lock_mutex(&ui->variables_mutex);
449  ui->infos->split_file_mode = file_mode;
450  unlock_mutex(&ui->variables_mutex);
451 }
452 
453 gint get_split_file_mode_safe(ui_state *ui)
454 {
455  lock_mutex(&ui->variables_mutex);
456  gint file_mode = ui->infos->split_file_mode;
457  unlock_mutex(&ui->variables_mutex);
458 
459  return file_mode;
460 }
461 
462 static void single_file_mode_split_button_event(GtkWidget *widget, ui_state *ui)
463 {
464  set_split_file_mode_safe(FILE_MODE_SINGLE, ui);
465  split_button_event(widget, ui);
466 }
467 
469 static void show_messages_history_window(GtkWidget *widget, ui_state *ui)
470 {
471  wh_show_window(ui->gui->mess_history_window);
472 }
473 
474 #ifndef NO_GNOME
475 static void ShowHelp(GtkWidget *widget, ui_state *ui)
476 {
477  GError* gerror = NULL;
478  gtk_show_uri(gdk_screen_get_default(), "ghelp:mp3splt-gtk", gtk_get_current_event_time(), &gerror);
479 }
480 #endif
481 
482 static gchar *my_dgettext(const gchar *key, const gchar *domain)
483 {
484  return dgettext("mp3splt-gtk", key);
485 }
486 
487 static void player_pause_action(GtkWidget *widget, ui_state *ui)
488 {
489  pause_event(ui->gui->pause_button, ui);
490 }
491 
492 static void player_seek_forward_action(GtkWidget *widget, ui_state *ui)
493 {
494  gfloat total_time = ui->infos->total_time;
495  gfloat new_time = ui->infos->current_time * 10 + 2./100. * total_time * 10;
496  if (new_time > total_time * 10) { new_time = total_time * 10; }
497  player_seek(new_time, ui);
498 }
499 
500 static void player_seek_backward_action(GtkWidget *widget, ui_state *ui)
501 {
502  gfloat total_time = ui->infos->total_time;
503  gfloat new_time = ui->infos->current_time * 10 - 2./100. * total_time * 10;
504  if (new_time <= 0) { new_time = 0; }
505  player_seek(new_time, ui);
506 }
507 
508 static void player_big_seek_forward_action(GtkWidget *widget, ui_state *ui)
509 {
510  gfloat total_time = ui->infos->total_time;
511  gfloat new_time = ui->infos->current_time * 10 + 15./100. * total_time * 10;
512  if (new_time > total_time * 10) { new_time = total_time * 10; }
513  player_seek(new_time, ui);
514 }
515 
516 static void player_big_seek_backward_action(GtkWidget *widget, ui_state *ui)
517 {
518  gfloat total_time = ui->infos->total_time;
519  gfloat new_time = ui->infos->current_time * 10 - 15./100. * total_time * 10;
520  if (new_time <= 0) { new_time = 0; }
521  player_seek(new_time, ui);
522 }
523 
524 static void player_small_seek_forward_action(GtkWidget *widget, ui_state *ui)
525 {
526  gfloat total_time = ui->infos->total_time;
527  gfloat new_time = ui->infos->current_time * 10 + 100 * 3 * 10;
528  if (new_time > total_time * 10) { new_time = total_time * 10; }
529  player_seek(new_time, ui);
530 }
531 
532 static void player_small_seek_backward_action(GtkWidget *widget, ui_state *ui)
533 {
534  gfloat new_time = ui->infos->current_time * 10 - 100 * 3 * 10;
535  if (new_time <= 0) { new_time = 0; }
536  player_seek(new_time, ui);
537 }
538 
539 static void player_seek_to_next_splitpoint_action(GtkWidget *widget, ui_state *ui)
540 {
541  gint time_left = -1;
542  gint time_right = -1;
543  get_current_splitpoints_time_left_right(&time_left, &time_right, NULL, ui);
544 
545  if (time_right != -1)
546  {
547  player_seek(time_right * 10, ui);
548  }
549 }
550 
551 static void player_seek_to_previous_splitpoint_action(GtkWidget *widget, ui_state *ui)
552 {
553  gint time_left = -1;
554  gint time_right = -1;
555  get_current_splitpoints_time_left_right(&time_left, &time_right, NULL, ui);
556 
557  if (time_left != -1)
558  {
559  player_seek(time_left * 10, ui);
560  }
561 }
562 
563 static void delete_closest_splitpoint(GtkWidget *widget, ui_state *ui)
564 {
565  gint left_index_point = -1;
566  gint right_index_point = -1;
567 
568  gint i = 0;
569  for (i = 0; i < ui->infos->splitnumber; i++ )
570  {
571  gint current_point_hundr_secs = get_splitpoint_time(i, ui);
572  if (current_point_hundr_secs <= ui->infos->current_time)
573  {
574  left_index_point = i;
575  continue;
576  }
577 
578  if (current_point_hundr_secs > ui->infos->current_time + (DELTA * 2))
579  {
580  right_index_point = i;
581  break;
582  }
583  }
584 
585  if (left_index_point == -1 && right_index_point == -1)
586  {
587  return;
588  }
589 
590  gint time_to_left = INT_MAX;
591  if (left_index_point != -1)
592  {
593  time_to_left = ui->infos->current_time - get_splitpoint_time(left_index_point, ui);
594  }
595 
596  gint time_to_right = INT_MAX;
597  if (right_index_point != -1)
598  {
599  time_to_right = get_splitpoint_time(right_index_point, ui) - ui->infos->current_time;
600  }
601 
602  if (time_to_right > time_to_left)
603  {
604  remove_splitpoint(left_index_point, TRUE, ui);
605  }
606  else
607  {
608  remove_splitpoint(right_index_point, TRUE, ui);
609  }
610 }
611 
612 static void zoom_in(GtkWidget *widget, ui_state *ui)
613 {
614  gdouble fraction = 40./100. * ui->infos->zoom_coeff;
615  ui->infos->zoom_coeff += fraction;
616  adjust_zoom_coeff(ui->infos);
617  refresh_drawing_area(ui->gui);
618 }
619 
620 static void zoom_out(GtkWidget *widget, ui_state *ui)
621 {
622  gdouble fraction = 40./100. * ui->infos->zoom_coeff;
623  ui->infos->zoom_coeff -= fraction;
624  adjust_zoom_coeff(ui->infos);
625  refresh_drawing_area(ui->gui);
626 }
627 
628 static gboolean window_key_press_event(GtkWidget *window, GdkEventKey *event, ui_state *ui)
629 {
630  if (event->type != GDK_KEY_PRESS) { return FALSE; }
631 
632  if (event->state != 0)
633  {
634  return FALSE;
635  }
636 
637  switch (event->keyval)
638  {
639  case GDK_Left:
640  player_seek_backward_action(NULL, ui);
641  return TRUE;
642  case GDK_Right:
643  player_seek_forward_action(NULL, ui);
644  return TRUE;
645  default:
646  return FALSE;
647  }
648 }
649 
650 void add_filters_to_file_chooser(GtkWidget *file_chooser)
651 {
652  GtkFileFilter *our_filter = gtk_file_filter_new();
653  gtk_file_filter_set_name(our_filter, _("mp3 and ogg files (*.mp3 *.ogg)"));
654  gtk_file_filter_add_pattern(our_filter, "*.mp3");
655  gtk_file_filter_add_pattern(our_filter, "*.ogg");
656  gtk_file_filter_add_pattern(our_filter, "*.MP3");
657  gtk_file_filter_add_pattern(our_filter, "*.OGG");
658  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), our_filter);
659 
660  our_filter = gtk_file_filter_new();
661  gtk_file_filter_set_name (our_filter, _("mp3 files (*.mp3)"));
662  gtk_file_filter_add_pattern(our_filter, "*.mp3");
663  gtk_file_filter_add_pattern(our_filter, "*.MP3");
664  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), our_filter);
665 
666  our_filter = gtk_file_filter_new();
667  gtk_file_filter_set_name (our_filter, _("ogg files (*.ogg)"));
668  gtk_file_filter_add_pattern(our_filter, "*.ogg");
669  gtk_file_filter_add_pattern(our_filter, "*.OGG");
670  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), our_filter);
671 }
672 
677 static void open_file_button_event(GtkWidget *widget, ui_state *ui)
678 {
679  GtkWidget *file_chooser = gtk_file_chooser_dialog_new(_("Choose File"), NULL,
680  GTK_FILE_CHOOSER_ACTION_OPEN,
681  GTK_STOCK_CANCEL,
682  GTK_RESPONSE_CANCEL,
683  GTK_STOCK_OPEN,
684  GTK_RESPONSE_ACCEPT, NULL);
685 
686  add_filters_to_file_chooser(file_chooser);
687  wh_set_browser_directory_handler(ui, file_chooser);
688 
689  if (gtk_dialog_run(GTK_DIALOG(file_chooser)) == GTK_RESPONSE_ACCEPT)
690  {
691  gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser));
692  file_chooser_ok_event(filename, ui);
693  if (filename)
694  {
695  g_free(filename);
696  filename = NULL;
697  }
698  }
699 
700  gtk_widget_destroy(file_chooser);
701  remove_status_message(ui->gui);
702 }
703 
705 static GtkWidget *create_menu_bar(ui_state *ui)
706 {
707  static const GtkActionEntry entries[] = {
708  //name, stock id, label, accelerator, tooltip, callback
709  { "FileMenu", NULL, N_("_File"), NULL, NULL, NULL },
710  { "ViewMenu", NULL, N_("_View"), NULL, NULL, NULL },
711  { "PlayerMenu", NULL, N_("_Player"), NULL, NULL, NULL },
712  { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
713 
714  { "Open", GTK_STOCK_OPEN, N_("_Open single file..."), "<Ctrl>O", N_("Open"),
715  G_CALLBACK(open_file_button_event) },
716 
717  { "AddFilesToBatch", GTK_STOCK_DIRECTORY,
718  N_("_Add files or directories to batch ..."), "<Ctrl>D",
719  N_("Add files or directories to batch"),
720  G_CALLBACK(multiple_files_add_button_event) },
721 
722  { "Import", GTK_STOCK_FILE, N_("_Import splitpoints from file..."), "<Ctrl>I",
723  N_("Import splitpoints from file..."), G_CALLBACK(import_event) },
724 
725  { "ImportFromTrackType", GTK_STOCK_FIND, N_("Import splitpoints from _TrackType.org..."), "<Ctrl>T",
726  N_("Import splitpoints from TrackType.org..."),
727  G_CALLBACK(show_tracktype_window) },
728 
729  { "Export", GTK_STOCK_SAVE_AS, N_("_Export splitpoints..."), "<Ctrl>E",
730  N_("Export splitpoints"), G_CALLBACK(export_cue_file_event) },
731 
732  { "Preferences", GTK_STOCK_PREFERENCES, N_("_Preferences"), "<Ctrl>P", N_("Preferences"),
733  G_CALLBACK(show_preferences_window) },
734 
735  { "SplitFiles", NULL, N_("Split _files"), "<Ctrl>F", N_("Split files"),
736  G_CALLBACK(show_split_files_window) },
737 
738  { "Splitpoints", GTK_STOCK_EDIT, N_("_Splitpoints"), "<Ctrl>L", N_("Splitpoints"),
739  G_CALLBACK(show_splitpoints_window) },
740 
741  { "Split", GTK_STOCK_APPLY, N_("_Split !"), "<Ctrl>S", N_("Split !"),
742  G_CALLBACK(single_file_mode_split_button_event) },
743 
744  { "BatchSplit", GTK_STOCK_EXECUTE, N_("_Batch split !"), "<Ctrl>B", N_("Batch split !"),
745  G_CALLBACK(batch_file_mode_split_button_event) },
746 
747  { "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<Ctrl>Q", N_("Quit"),
748  G_CALLBACK(exit_application_bis) },
749 
750 #ifndef NO_GNOME
751  { "Contents", GTK_STOCK_HELP, N_("_Contents"), "F1", N_("Contents"),
752  G_CALLBACK(ShowHelp)},
753 #endif
754 
755  { "Messages history", GTK_STOCK_INFO, N_("Messages _history"), "<Ctrl>H", N_("Messages history"),
756  G_CALLBACK(show_messages_history_window) },
757 
758  { "About", GTK_STOCK_ABOUT, N_("_About"), "<Ctrl>A", N_("About"),
759  G_CALLBACK(about_window)},
760 
761  //player key bindings
762  { "Player_pause", NULL, N_("P_ause / Play"), "space", N_("Pause/Play"),
763  G_CALLBACK(player_pause_action)},
764 
765  { "Player_forward", GTK_STOCK_MEDIA_FORWARD, N_("Seek _forward"), "Right", N_("Seek forward"),
766  G_CALLBACK(player_seek_forward_action)},
767  { "Player_backward", GTK_STOCK_MEDIA_REWIND, N_("Seek _backward"), "Left", N_("Seek backward"),
768  G_CALLBACK(player_seek_backward_action)},
769 
770  { "Player_small_forward", NULL, N_("Small seek f_orward"), "<Alt>Right", N_("Small seek forward"),
771  G_CALLBACK(player_small_seek_forward_action)},
772  { "Player_small_backward", NULL, N_("Small seek back_ward"), "<Alt>Left", N_("Small seek backward"),
773  G_CALLBACK(player_small_seek_backward_action)},
774 
775  { "Player_big_forward", NULL, N_("Big seek fo_rward"), "<Shift>Right", N_("Big seek forward"),
776  G_CALLBACK(player_big_seek_forward_action)},
777  { "Player_big_backward", NULL, N_("Big seek bac_kward"), "<Shift>Left", N_("Big seek backward"),
778  G_CALLBACK(player_big_seek_backward_action)},
779 
780  { "Player_next_splitpoint", GTK_STOCK_MEDIA_NEXT, N_("Seek to _next splitpoint"), "<Ctrl>Right",
781  N_("Seek to next splitpoint"), G_CALLBACK(player_seek_to_next_splitpoint_action)},
782  { "Player_previous_splitpoint", GTK_STOCK_MEDIA_PREVIOUS, N_("Seek to _previous splitpoint"), "<Ctrl>Left",
783  N_("Seek to previous splitpoint"), G_CALLBACK(player_seek_to_previous_splitpoint_action)},
784 
785  { "Add_splitpoint", GTK_STOCK_ADD, N_("Add _splitpoint"), "s",
786  N_("Add splitpoint"), G_CALLBACK(add_splitpoint_from_player)},
787 
788  { "Delete_closest_splitpoint", GTK_STOCK_REMOVE, N_("_Delete closest splitpoint"), "d",
789  N_("Delete closest splitpoint"), G_CALLBACK(delete_closest_splitpoint)},
790 
791  { "Zoom_in", GTK_STOCK_ZOOM_IN, N_("Zoom _in"), "<Ctrl>plus", N_("Zoom in"), G_CALLBACK(zoom_in)},
792  { "Zoom_out", GTK_STOCK_ZOOM_OUT, N_("Zoom _out"), "<Ctrl>minus", N_("Zoom out"), G_CALLBACK(zoom_out)},
793  };
794 
795  static const gchar *ui_info =
796  "<ui>"
797  " <menubar name='MenuBar'>"
798  " <menu action='FileMenu'>"
799  " <menuitem action='Open'/>"
800  " <menuitem action='AddFilesToBatch'/>"
801  " <separator/>"
802  " <menuitem action='Import'/>"
803  " <menuitem action='ImportFromTrackType'/>"
804  " <menuitem action='Export'/>"
805  " <separator/>"
806  " <menuitem action='Preferences'/>"
807  " <separator/>"
808  " <menuitem action='Split'/>"
809  " <menuitem action='BatchSplit'/>"
810  " <separator/>"
811  " <menuitem action='Quit'/>"
812  " </menu>"
813  " <menu action='ViewMenu'>"
814  " <menuitem action='Splitpoints'/>"
815  " <menuitem action='SplitFiles'/>"
816  " </menu>"
817  " <menu action='PlayerMenu'>"
818  " <menuitem action='Player_pause'/>"
819  " <separator/>"
820  " <menuitem action='Player_forward'/>"
821  " <menuitem action='Player_backward'/>"
822  " <menuitem action='Player_small_forward'/>"
823  " <menuitem action='Player_small_backward'/>"
824  " <menuitem action='Player_big_forward'/>"
825  " <menuitem action='Player_big_backward'/>"
826  " <menuitem action='Player_next_splitpoint'/>"
827  " <menuitem action='Player_previous_splitpoint'/>"
828  " <separator/>"
829  " <menuitem action='Add_splitpoint'/>"
830  " <menuitem action='Delete_closest_splitpoint'/>"
831  " <separator/>"
832  " <menuitem action='Zoom_in'/>"
833  " <menuitem action='Zoom_out'/>"
834  " </menu>"
835  " <menu action='HelpMenu'>"
836 #ifndef NO_GNOME
837  " <menuitem action='Contents'/>"
838  " <separator/>"
839 #endif
840  " <menuitem action='Messages history'/>"
841  " <separator/>"
842  " <menuitem action='About'/>"
843  " </menu>"
844  " </menubar>"
845  "</ui>";
846 
847  GtkActionGroup *action_group = gtk_action_group_new("Actions");
848  ui->gui->action_group = action_group;
849 
850  gtk_action_group_set_translation_domain(action_group, "mp3splt-gtk");
851  gtk_action_group_set_translate_func(action_group,
852  (GtkTranslateFunc)my_dgettext, NULL, NULL);
853 
854  gtk_action_group_add_actions(action_group, entries, G_N_ELEMENTS(entries), ui);
855  GtkUIManager *uim = gtk_ui_manager_new();
856  gtk_ui_manager_insert_action_group(uim, action_group, 0);
857 
858  g_signal_connect(G_OBJECT(ui->gui->window), "key_press_event",
859  G_CALLBACK(window_key_press_event), ui);
860 
861  gtk_window_add_accel_group(GTK_WINDOW(ui->gui->window), gtk_ui_manager_get_accel_group(uim));
862  gtk_ui_manager_add_ui_from_string(uim, ui_info, -1, NULL);
863 
864  GtkWidget *menu_box = wh_hbox_new();
865  gtk_box_pack_start(GTK_BOX(menu_box), gtk_ui_manager_get_widget(uim, "/MenuBar"), FALSE, FALSE, 0);
866 
867  player_key_actions_set_sensitivity(FALSE, ui->gui);
868 
869  return menu_box;
870 }
871 
872 static void file_selection_changed(GtkFileChooser *open_file_chooser, ui_state *ui)
873 {
874  gchar *filename = gtk_file_chooser_get_filename(open_file_chooser);
875  gchar *previous_fname = get_input_filename(ui->gui);
876 
877  if (previous_fname != NULL && filename != NULL &&
878  strcmp(filename, previous_fname) == 0)
879  {
880  return;
881  }
882 
883  if (filename != NULL)
884  {
885  ui->status->file_selection_changed = TRUE;
886  file_chooser_ok_event(filename, ui);
887  ui->status->file_selection_changed = FALSE;
888 
889  g_free(filename);
890  filename = NULL;
891  return;
892  }
893 }
894 
895 static void file_set_event(GtkFileChooserButton *open_file_chooser_button, ui_state *ui)
896 {
897  file_selection_changed(GTK_FILE_CHOOSER(open_file_chooser_button), ui);
898 }
899 
900 static GtkWidget *create_choose_file_frame(ui_state *ui)
901 {
902  GtkWidget *open_file_chooser_button = gtk_file_chooser_button_new(_("Open file ..."), GTK_FILE_CHOOSER_ACTION_OPEN);
903  dnd_add_drag_data_received_to_widget(open_file_chooser_button, DND_SINGLE_MODE_AUDIO_FILE, ui);
904 
905  ui->gui->open_file_chooser_button = open_file_chooser_button;
906  add_filters_to_file_chooser(open_file_chooser_button);
907  wh_set_browser_directory_handler(ui, open_file_chooser_button);
908 
909  g_signal_connect(G_OBJECT(open_file_chooser_button), "file-set", G_CALLBACK(file_set_event), ui);
910  g_signal_connect(G_OBJECT(open_file_chooser_button), "selection-changed",
911  G_CALLBACK(file_selection_changed), ui);
912 
913  gchar *fname = get_input_filename(ui->gui);
914  if (fname != NULL && strlen(fname) != 0)
915  {
916  gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(open_file_chooser_button), get_input_filename(ui->gui));
917  }
918 
919  return open_file_chooser_button;
920 }
921 
923 static GtkWidget *create_main_vbox(ui_state *ui)
924 {
925  GtkWidget *main_vbox = wh_vbox_new();
926  gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 0);
927 
928  /* tabbed notebook */
929  GtkWidget *notebook = gtk_notebook_new();
930 
931  gtk_box_pack_start(GTK_BOX(main_vbox), notebook, TRUE, TRUE, 0);
932  gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), TRUE);
933  gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
934  gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
935 
936  /* player page */
937  GtkWidget *player_vbox = wh_vbox_new();
938 
939  //file & split button hbox
940  GtkWidget *top_hbox = wh_hbox_new();
941  gtk_box_pack_start(GTK_BOX(player_vbox), top_hbox, FALSE, FALSE, 0);
942 
943  //choose file
944  gtk_box_pack_start(GTK_BOX(top_hbox), create_choose_file_frame(ui), TRUE, TRUE, 0);
945 
946  //single mode split button
947  GtkWidget *split_button = wh_create_cool_button(GTK_STOCK_APPLY,_("Split !"), FALSE);
948  g_signal_connect(G_OBJECT(split_button), "clicked",
949  G_CALLBACK(single_file_mode_split_button_event), ui);
950  gtk_widget_set_tooltip_text(split_button, _("Split the current file"));
951  gtk_box_pack_start(GTK_BOX(top_hbox), split_button, FALSE, FALSE, 4);
952 
953  ui->gui->player_box = create_player_control_frame(ui);
954  gtk_box_pack_start(GTK_BOX(player_vbox), ui->gui->player_box, FALSE, FALSE, 0);
955 
956  ui->gui->playlist_box = create_player_playlist_frame(ui);
957  gtk_box_pack_start(GTK_BOX(player_vbox), ui->gui->playlist_box, TRUE, TRUE, 0);
958 
959  GtkWidget *notebook_label = wh_create_cool_label(GTK_STOCK_APPLY, _("Manual single file split"));
960  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), player_vbox, notebook_label);
961 
962  /* splitpoints page */
963  ui->gui->splitpoints_widget = create_splitpoints_frame(ui);
964 
965  /* split files page */
966  ui->gui->split_files_widget = create_split_files_frame(ui);
967 
968  /* freedb page */
969  ui->gui->freedb_widget = create_freedb_frame(ui);
970 
971  /* special split page */
972  GtkWidget *special_split_vbox = wh_vbox_new();
973  gtk_container_set_border_width(GTK_CONTAINER(special_split_vbox), 0);
974  GtkWidget *frame = create_special_split_page(ui);
975  gtk_box_pack_start(GTK_BOX(special_split_vbox), frame, TRUE, TRUE, 0);
976 
977  notebook_label = wh_create_cool_label(GTK_STOCK_EXECUTE, _("Batch & automatic split"));
978  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), special_split_vbox, notebook_label);
979 
980  /* preferences widget */
981  ui->gui->preferences_widget = create_choose_preferences(ui);
982 
983  /* progress bar */
984  GtkProgressBar *percent_progress_bar = GTK_PROGRESS_BAR(gtk_progress_bar_new());
985  ui->gui->percent_progress_bar = percent_progress_bar;
986  gtk_progress_bar_set_fraction(percent_progress_bar, 0.0);
987  gtk_progress_bar_set_text(percent_progress_bar, "");
988 
989 #if GTK_MAJOR_VERSION >= 3
990  gtk_progress_bar_set_show_text(percent_progress_bar, TRUE);
991 #endif
992 
993  GtkWidget *hbox = wh_hbox_new();
994  gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(percent_progress_bar), TRUE, TRUE, 0);
995 
996  //stop button
997  GtkWidget *cancel_button = wh_create_cool_button(GTK_STOCK_STOP, _("S_top"), FALSE);
998  ui->gui->cancel_button = cancel_button;
999  g_signal_connect(G_OBJECT(cancel_button), "clicked", G_CALLBACK(cancel_button_event), ui);
1000  gtk_box_pack_start(GTK_BOX(hbox), cancel_button, FALSE, TRUE, 3);
1001  gtk_widget_set_sensitive(cancel_button, FALSE);
1002 
1003  gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 2);
1004 
1005  /* show messages history dialog */
1007 
1008  /* statusbar */
1009  GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_statusbar_new());
1010  ui->gui->status_bar = status_bar;
1011 
1012  gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(status_bar), FALSE, FALSE, 0);
1013 
1014  return main_vbox;
1015 }
1016 
1017 static void move_and_resize_main_window(ui_state *ui)
1018 {
1019  const ui_main_window *main_win = ui_get_main_window_infos(ui);
1020 
1021  gint x = main_win->root_x_pos;
1022  gint y = main_win->root_y_pos;
1023 
1024  if (x != 0 && y != 0)
1025  {
1026  gtk_window_move(GTK_WINDOW(ui->gui->window), x, y);
1027  }
1028  else
1029  {
1030  gtk_window_set_position(GTK_WINDOW(ui->gui->window), GTK_WIN_POS_CENTER);
1031  }
1032 
1033  gtk_window_resize(GTK_WINDOW(ui->gui->window), main_win->width, main_win->height);
1034 }
1035 
1036 void create_application(ui_state *ui)
1037 {
1038  initialize_window(ui);
1039 
1040  GtkWidget *window_vbox = wh_vbox_new();
1041  gtk_container_add(GTK_CONTAINER(ui->gui->window), window_vbox);
1042 
1043  gtk_box_pack_start(GTK_BOX(window_vbox), create_menu_bar(ui), FALSE, FALSE, 0);
1044  gtk_box_pack_start(GTK_BOX(window_vbox), create_main_vbox(ui), TRUE, TRUE, 0);
1045 
1046  ui_load_preferences(ui);
1047 
1048  move_and_resize_main_window(ui);
1049 
1050  gtk_widget_show_all(ui->gui->window);
1051 
1052  if (ui->infos->selected_player != PLAYER_GSTREAMER)
1053  {
1054  gtk_widget_hide(ui->gui->playlist_box);
1055  }
1056 
1057  hide_freedb_spinner(ui->gui);
1058 }
1059 
1065 {
1066  char *error_from_library = mp3splt_get_strerror(ui->mp3splt_state, error);
1067  if (error_from_library == NULL) { return; }
1068 
1069  put_status_message(error_from_library, ui);
1070  free(error_from_library);
1071  error_from_library = NULL;
1072 }
1073 
1074 static gboolean print_status_bar_confirmation_idle(ui_with_err *ui_err)
1075 {
1076  print_status_bar_confirmation(ui_err->err, ui_err->ui);
1077  g_free(ui_err);
1078  return FALSE;
1079 }
1080 
1081 void print_status_bar_confirmation_in_idle(gint error, ui_state *ui)
1082 {
1083  ui_with_err *ui_err = g_malloc0(sizeof(ui_with_err));
1084  ui_err->err = error;
1085  ui_err->ui = ui;
1086 
1087  gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE,
1088  (GSourceFunc)print_status_bar_confirmation_idle, ui_err, NULL);
1089 }
1090