Logo Search packages:      
Sourcecode: jackbeat version File versions  Download package

gui.c

/*
 *   Jackbeat - JACK sequencer
 *    
 *   Copyright (c) 2004-2008 Olivier Guilyardi <olivier {at} samalyse {dot} com>
 *    
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *   SVN:$Id: gui.c 124 2008-01-10 19:10:45Z olivier $
 */

#include "gui/common.h"

static int gui_instance_counter = 0;
static int gui_instance_num = 0;

#define GUI_ANIMATION_INTERVAL 30 // miliseconds

static void gui_clear_sequence (gui_t * gui, guint action, GtkWidget * w);
static void gui_auto_connect (gui_t *gui,
                              guint callback_action, GtkWidget * menu_item);
static void gui_set_transport (gui_t *gui,
                               guint action, GtkWidget * menu_item);
static void gui_new_instance (gui_t * gui, guint action, GtkWidget * w);
static void gui_close_from_menu (gui_t * gui, guint action, GtkWidget * w);
static void gui_exit (gui_t * gui, guint action, GtkWidget * w);
static void gui_duplicate_sequence (gui_t * gui, guint action, GtkWidget * w);
static void gui_set_resampler_type (gui_t *gui,
                                    guint type, GtkWidget *menu_item);
static void gui_transpose_volumes_dialog (gui_t *gui, guint action, GtkWidget *widget);

static GtkItemFactoryEntry gui_menu_items[] = {
  /* File menu */
  {"/_File", NULL, NULL, 0, "<Branch>"},
  {"/File/New", "<control>N", gui_new_instance, 1, "<StockItem>",
   GTK_STOCK_NEW},
  {"/File/Open", "<control>O", gui_file_load_sequence, 1, "<StockItem>",
   GTK_STOCK_OPEN},
  {"/File/Save", "<control>S", gui_file_save_sequence, 1, "<StockItem>",
   GTK_STOCK_SAVE},
  {"/File/Save as", NULL, gui_file_save_as_sequence, 1, "<StockItem>",
   GTK_STOCK_SAVE_AS},
  {"/File/Export waveform", NULL, gui_file_export_sequence, 1, "<Item>"},
  {"/File/Close", "<Control><Shift>W", gui_close_from_menu, 1, "<StockItem>",
   GTK_STOCK_CLOSE},
  {"/File/separator", NULL, NULL, 0, "<Separator>"},
  {"/File/Quit", "<control>Q", gui_exit, 1, "<StockItem>",
   GTK_STOCK_QUIT},

  /* Edit menu */ 
  {"/_Edit", NULL, NULL, 0, "<Branch>"},
  {"/Edit/Clear", NULL, gui_clear_sequence, 1, "<StockItem>", GTK_STOCK_DELETE},
  {"/Edit/Double", NULL, gui_duplicate_sequence, 1, "<Item>"},
  {"/Edit/Transpose volumes", NULL, gui_transpose_volumes_dialog, 1, "<Item>"},

  /* Options menu */
  {"/_Options", NULL, NULL, 0, "<Branch>"},
  {"/Options/Auto-connect", NULL, gui_auto_connect, 1, "<CheckItem>"},
  {"/Options/Transport control", NULL, NULL, 0, "<Branch>"},
  {"/Options/Transport control/Respond", NULL, gui_set_transport, 2,
   "<RadioItem>"},
  {"/Options/Transport control/Respond and query", NULL, gui_set_transport, 3,
   "/Options/Transport control/Respond"},
  {"/Options/Resampling quality", NULL, NULL, 0, "<Branch>"},
  {"/Options/Resampling quality/Hi-fi : sinc algorithm - more CPU", NULL, gui_set_resampler_type, 
   SEQUENCE_SINC, "<RadioItem>"},
  {"/Options/Resampling quality/Lo-fi : linear - lightweight", NULL, gui_set_resampler_type, 
   SEQUENCE_LINEAR, "/Options/Resampling quality/Hi-fi : sinc algorithm - more CPU"},
};

static gint gui_menu_nitems =
  sizeof (gui_menu_items) / sizeof (gui_menu_items[0]);

static void
gui_update_window_title (gui_t * gui)
{
  char s[128];
  sprintf (s, "%s%s - %s_%d", (gui->sequence_is_modified) ? "*" : "",
           basename (gui->filename), gui->name_prefix, gui->instance_index+1);
  gtk_window_set_title (GTK_WINDOW (gui->window), s);
}

void
gui_set_modified (gui_t * gui, int status)
{
  gui->sequence_is_modified = status;
  gui_update_window_title (gui);
}

static void
gui_auto_connect (gui_t *gui,
                  guint callback_action, GtkWidget * menu_item)
{
  sequence_set_auto_connect (gui->sequence, (GTK_CHECK_MENU_ITEM (menu_item))->active);
  gui->rc->auto_connect = (GTK_CHECK_MENU_ITEM (menu_item))->active ? 1 : 0;
}

static void
gui_set_transport (gui_t *gui,
                   guint action, GtkWidget * menu_item)
{
  switch (action)
    {
    case 1:
      sequence_set_transport (gui->sequence, 0, 0);
      break;
    case 2:
      if (gui->rewind) gtk_widget_set_sensitive (gui->rewind, FALSE);
      sequence_set_transport (gui->sequence, 1, 0);
      gui->rc->transport_aware = 1;
      gui->rc->transport_query = 0;
      break;
    case 3:
      if (gui->rewind) gtk_widget_set_sensitive (gui->rewind, TRUE);
      sequence_set_transport (gui->sequence, 1, 1);
      gui->rc->transport_aware = 1;
      gui->rc->transport_query = 1;
      break;
    }
}

static void gui_set_resampler_type (gui_t *gui, guint type, 
                                    GtkWidget *menu_item)
{
  if (type != sequence_get_resampler_type(gui->sequence))
   {
    sequence_set_resampler_type (gui->sequence, type);
    gui->rc->default_resampler_type = type;
   }
}


void
gui_display_error (gui_t * gui, char *text)
{
  GtkWidget *dialog;

  dialog =
    gtk_message_dialog_new (gui->window ? GTK_WINDOW (gui->window) : NULL,
                            GTK_DIALOG_DESTROY_WITH_PARENT |
                            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
                            GTK_BUTTONS_OK, "%s", text);

  gtk_window_set_title (GTK_WINDOW (dialog), "Jackbeat");
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

int
gui_ask_confirmation (gui_t * gui, char *text)
{
  GtkWidget *dialog;
  gint response;

  dialog =
    gtk_message_dialog_new (gui->window ? GTK_WINDOW (gui->window) : NULL,
                            GTK_DIALOG_DESTROY_WITH_PARENT |
                            GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
                            GTK_BUTTONS_OK_CANCEL, "%s", text);
  gtk_window_set_title (GTK_WINDOW (dialog), "Jackbeat");

  response = gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);

  if (response == GTK_RESPONSE_OK)
    return 1;

  return 0;
}

gboolean 
gui_no_delete (GtkWidget *widget, GdkEvent  *event, gpointer data)
{
  return TRUE;
}

static void
gui_transpose_volumes (GtkWidget *adj, gui_t *gui)
{
  GtkWidget *master = g_object_get_data (G_OBJECT (adj), "user-data");
  double *orig_volumes = g_object_get_data (G_OBJECT (master), "user-data");
#ifdef USE_PHAT
#ifdef USE_KNOB
  double mvol = (double) phat_fan_slider_get_value (PHAT_FAN_SLIDER (master));
#else
  double mvol = (double) phat_knob_get_value (PHAT_KNOB (master));
#endif
#else
  double mvol = (double) gtk_spin_button_get_value (GTK_SPIN_BUTTON (master));
#endif  
  int ntracks = sequence_get_tracks_num (gui->sequence);
  int i;
  for (i=0; i < ntracks; i++) 
   {
    double tvol = orig_volumes[i] * mvol / 100;
    if (gui->transpose_volumes_round) tvol =  floor (tvol);
    
#ifdef USE_PHAT
#ifdef USE_KNOB
    phat_knob_set_value (PHAT_KNOB (gui->volume_spinners[i]), tvol);
#else
    phat_fan_slider_set_value (PHAT_FAN_SLIDER (gui->volume_spinners[i]),
                               tvol);
#endif                               
#else
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (gui->volume_spinners[i]),
                               tvol);
#endif
   }
}
    
static void
gui_transpose_volumes_toggle_round (GtkWidget * widget, gui_t * gui)
{
  gui->transpose_volumes_round = 
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) ? 1 : 0;
  if (gui->transpose_volumes_round) 
   {
    GtkWidget *adj = g_object_get_data (G_OBJECT (widget), "user-data");
    gui_transpose_volumes (adj, gui);
   }
}

static void
gui_transpose_volumes_dialog (gui_t *gui, guint action, GtkWidget *widget)
{ 
  GtkWidget * dialog;

  GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
  GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 15);

  int ntracks = sequence_get_tracks_num (gui->sequence);
  double *orig_volumes = calloc (ntracks, sizeof (double));
  int i;
  for (i = 0; i < ntracks; i++)
#ifdef USE_PHAT  
#ifdef USE_KNOB
    orig_volumes[i] = phat_fan_slider_get_value (PHAT_FAN_SLIDER (gui->volume_spinners[i]));
#else
    orig_volumes[i] = phat_knob_get_value (PHAT_KNOB (gui->volume_spinners[i]));
#endif
#else
    orig_volumes[i] = gtk_spin_button_get_value (GTK_SPIN_BUTTON (gui->volume_spinners[i]));
#endif
  
  char s[128];
  sprintf (s, "Transpose volumes - %s_%.2d", gui->name_prefix, gui->instance_index+1);

  dialog = gtk_dialog_new_with_buttons (
              s,
              GTK_WINDOW (gui->window),
              GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
              GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, NULL);

  sprintf (s, "[%s:%.2d] Transpose all volumes by (%%) :",  gui->name_prefix, 
           gui->instance_index+1);
  GtkWidget *label = gtk_label_new (s);
  gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 8);
 

  GtkAdjustment *adj = (GtkAdjustment *) 
                        gtk_adjustment_new (100, 0, 999, 1, 0.01, 0);
  GtkWidget *master = gtk_spin_button_new (adj, 0.5, 2);
  g_object_set_data (G_OBJECT (adj), "user-data", (gpointer) master);
  g_object_set_data (G_OBJECT (master), "user-data", (gpointer) orig_volumes);
  gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (master),
                                     GTK_UPDATE_IF_VALID);
  g_signal_connect (G_OBJECT (adj), "value_changed",
                    G_CALLBACK (gui_transpose_volumes), (gpointer) gui);

  gtk_box_pack_start (GTK_BOX (vbox), master, TRUE, TRUE, 8);

  GtkWidget *button = gtk_check_button_new_with_label ("Round values");
  g_object_set_data (G_OBJECT (button), "user-data", (gpointer) adj);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
                                gui->transpose_volumes_round ? TRUE : FALSE);
  g_signal_connect (G_OBJECT (button), "toggled",
                    G_CALLBACK (gui_transpose_volumes_toggle_round), (gpointer) gui);
  
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 8);
  
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE,
                      0);
  gtk_widget_show_all (dialog);

  gtk_dialog_run (GTK_DIALOG (dialog)); 
  gtk_widget_destroy (dialog);
  free (orig_volumes);
}


void
gui_show_progress (gui_t *gui, char *title, char *text)
{
  gui->progress_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  if (gui->window != NULL)
   {
    gtk_window_set_modal (GTK_WINDOW (gui->progress_window), TRUE);
    gtk_window_set_transient_for (GTK_WINDOW (gui->progress_window), 
                                  GTK_WINDOW (gui->window));
    gtk_window_set_destroy_with_parent (GTK_WINDOW (gui->progress_window), TRUE);
   }
  gtk_window_set_default_size (GTK_WINDOW (gui->progress_window), 350,50);
  gtk_window_set_position (GTK_WINDOW (gui->progress_window), GTK_WIN_POS_CENTER_ON_PARENT);
  gtk_window_set_title (GTK_WINDOW (gui->progress_window), title);
  g_signal_connect (G_OBJECT (gui->progress_window), "delete_event", 
                    G_CALLBACK (gui_no_delete), NULL);

  
  GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (gui->progress_window), vbox);
  
  GtkWidget *hbox_top = gtk_hbox_new (FALSE, 0);
  
  GtkWidget *spacer = gtk_label_new (" ");
  gtk_box_pack_start (GTK_BOX (vbox), spacer, TRUE, TRUE, 0);
  
  gtk_box_pack_start (GTK_BOX (vbox), hbox_top, TRUE, TRUE, 0);

  GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, 
                                              GTK_ICON_SIZE_DIALOG);
  gtk_box_pack_start (GTK_BOX (hbox_top), icon, FALSE, FALSE, 15);
  
  GtkWidget *label = gtk_label_new (text);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.2);
  gtk_box_pack_start (GTK_BOX (hbox_top), label, FALSE, FALSE, 0);

  spacer = gtk_label_new (" ");
  gtk_box_pack_start (GTK_BOX (vbox), spacer, TRUE, TRUE, 0);

  GtkWidget *hbox_bot = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox_bot, TRUE, TRUE, 0);
  
  gui->progress_bar = gtk_progress_bar_new();
  gtk_box_pack_start (GTK_BOX (hbox_bot), gui->progress_bar, TRUE, TRUE, 15);

  spacer = gtk_label_new (" ");
  gtk_box_pack_start (GTK_BOX (vbox), spacer, TRUE, TRUE, 0);

  gtk_widget_show_all (vbox);

}

void 
gui_progress_callback (char * status, double fraction, void * data)
{
  gui_t *gui = (gui_t *) data;
  if (!GTK_WIDGET_VISIBLE(gui->progress_window))
    gtk_widget_show (gui->progress_window);
  gtk_progress_bar_set_text (GTK_PROGRESS_BAR (gui->progress_bar), status);
  if (fraction > 1)
   {
    DEBUG("Warning: progress fraction is greater than 1. Correcting..");
    fraction = 1;
   }
  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (gui->progress_bar), fraction);
  while (g_main_context_iteration (NULL, FALSE));
}

void
gui_hide_progress (gui_t * gui)
{
  gtk_widget_destroy (gui->progress_window);
}

static void
gui_clear_sequence (gui_t * gui, guint action, GtkWidget * w)
{
  if (!gui->sequence_is_modified || gui_ask_confirmation
      (gui, "This will clear the whole pattern and associated samples. Are you sure ?"))
    {
      int rtype = sequence_get_resampler_type (gui->sequence);
      char *name = strdup (sequence_get_name (gui->sequence));
      int error;
      sequence_t *new_sequence = sequence_new (name, &error);
      if (!new_sequence) {
        char *s = error_to_string (error);
        gui_display_error (gui, s);
        return;
      }

      song_unregister_sequence (gui->song, gui->sequence);
      song_cleanup_unused_samples (gui->song);
      sequence_destroy (gui->sequence);
      gui->sequence = new_sequence; 
      free (name);
      sequence_set_resampler_type (gui->sequence, rtype);
      sequence_set_transport (gui->sequence, gui->rc->transport_aware,
                              gui->rc->transport_query);
      song_register_sequence (gui->song, gui->sequence);
      gui_refresh (gui);
      gui_set_modified (gui, 0);
    }
}

static GtkWidget *
gui_make_menubar (gui_t * gui, GtkItemFactoryEntry * items, gint nitems)
{
  GtkItemFactory *item_factory;
  GtkAccelGroup *accel_group;

  accel_group = gtk_accel_group_new ();
  item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
                                       accel_group);
  gtk_item_factory_create_items (item_factory, nitems, items, (gpointer) gui);
  gtk_window_add_accel_group (GTK_WINDOW (gui->window), accel_group);

  if (gui->rc->transport_query)
    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM
                                    (gtk_item_factory_get_item
                                     (item_factory, "/Options/Transport control/Respond and query")),
                                    TRUE);
  else
    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM
                                    (gtk_item_factory_get_item
                                     (item_factory, "/Options/Transport control/Respond")),
                                    TRUE);

  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM
                                  (gtk_item_factory_get_item
                                   (item_factory, "/Options/Auto-connect")),
                                  (sequence_is_auto_connecting(gui->sequence)) ? TRUE : FALSE);

  GtkWidget *item = NULL;
  switch (sequence_get_resampler_type (gui->sequence)) {
    case SEQUENCE_SINC: 
     item =  gtk_item_factory_get_item (item_factory, 
              "/Options/Resampling quality/Hi-fi : sinc algorithm - more CPU");
     break;
    case SEQUENCE_LINEAR:
     item =  gtk_item_factory_get_item (item_factory, 
              "/Options/Resampling quality/Lo-fi : linear - lightweight");
     break;
  }
  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);

  return gtk_item_factory_get_widget (item_factory, "<main>");
}

static void
gui_sequence_modified (void *data, int col, int row, int value)
{
  gui_t *gui = (gui_t *)data;
  sequence_set_beat (gui->sequence, row, col, value);
  gui_set_modified (gui, 1);
}

static void
gui_mask_modified (void *data, int col, int row, int mask)
{
  gui_t *gui = (gui_t *)data;
  sequence_set_mask_beat (gui->sequence, row, col, mask);
  gui_set_modified (gui, 1);
}

void
gui_refresh (gui_t * gui)
{
  if (!gui->refreshing) 
   {
    DEBUG ("Refreshing GUI");
    gui->refreshing = 1;
    gui_update_window_title (gui);
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (gui->tracks_num),
                               sequence_get_tracks_num(gui->sequence));
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (gui->beats_num),
                               sequence_get_beats_num(gui->sequence));
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (gui->measure_len),
                               sequence_get_measure_len(gui->sequence));
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (gui->bpm), sequence_get_bpm(gui->sequence));
    if (sequence_is_looping(gui->sequence))
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui->loop), TRUE);
    else
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui->loop), FALSE);
    gui_track_draw (gui);
    gui->refreshing = 0;
   }
}

void
gui_do_exit (gui_t *gui)
{
  DEBUG("Writing rc settings");  
  rc_write (gui->rc);
  DEBUG ("Exiting GTK");
  gtk_main_quit ();
  DEBUG ("Shutting down JACK client");
  DEBUG ("Bye");
}

static gboolean
gui_close (GtkWidget * widget, GdkEvent  *event, gui_t * gui)
{
  if (!gui->sequence_is_modified || gui_ask_confirmation
      (gui, "Current changes will be lost if you continue. Are your sure ?"))
    {
      song_unregister_sequence (gui->song, gui->sequence);
      song_cleanup_unused_samples (gui->song);
      sequence_destroy (gui->sequence);
      g_source_remove (gui->timeout_tag);
      gtk_object_destroy (GTK_OBJECT (gui->tooltips));
      gtk_widget_destroy (gui->window);
      if (--gui_instance_num == 0) 
       {  
        gui_do_exit (gui);
       } 
      free (gui);
      return FALSE;
    }
  return TRUE;
}

gboolean
gui_timeout (gpointer data)
{
  gui_t *gui = (gui_t *) data;
  int i;
  int tracks_num = sequence_get_tracks_num (gui->sequence);
  if (sequence_is_playing (gui->sequence))
    for (i=0; i < tracks_num; i++) 
     {
      grid_highlight_cell (gui->grid, 
                           sequence_get_active_beat (gui->sequence, i), 
                           i, 
                           sequence_get_level (gui->sequence, i));

     }
  else
    for (i=0; i < tracks_num; i++) 
       {
        grid_highlight_cell (gui->grid, -1, i, 0);
       }
  return TRUE;
}

static void
gui_duplicate_sequence (gui_t * gui, guint action, GtkWidget * w)
{
  if (!gui->refreshing)
    {
      DEBUG ("Pattern resized");
      gint tracks_num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (gui->tracks_num));
      gint beats_num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (gui->beats_num));
      gint measure_len = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (gui->measure_len));

      g_source_remove (gui->timeout_tag);

      if (!sequence_resize (gui->sequence, tracks_num, beats_num * 2, measure_len, 1)) 
        gui_display_error (gui, error_to_string (sequence_get_error(gui->sequence)));
              
      song_cleanup_unused_samples (gui->song);
      
      gui_refresh (gui);
      gui->timeout_tag = g_timeout_add (GUI_ANIMATION_INTERVAL, gui_timeout, (gpointer) gui);
      gui_set_modified (gui, 1);
    }
}

static gboolean
gui_sequence_do_resize (gpointer data)
{
    gui_t *gui = (gui_t *) data;
    gint tracks_num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (gui->tracks_num));
    gint beats_num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (gui->beats_num));
    gint measure_len = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (gui->measure_len));

    g_source_remove (gui->timeout_tag);

    if (!sequence_resize (gui->sequence, tracks_num, beats_num, measure_len, 0)) 
      gui_display_error (gui, error_to_string (sequence_get_error(gui->sequence)));

    song_cleanup_unused_samples (gui->song);
    
    gui_refresh (gui);
    gui->timeout_tag = g_timeout_add (GUI_ANIMATION_INTERVAL, gui_timeout, (gpointer) gui);
    gui_set_modified (gui, 1);
    return FALSE;
}


static void
gui_sequence_resized (GtkWidget * widget, gui_t * gui)
{
  if (!gui->refreshing)
    {
      DEBUG ("Pattern resized");
      g_idle_add (gui_sequence_do_resize, (gpointer) gui);
    }
}

static void
gui_bpm_changed (GtkWidget * widget, gui_t * gui)
{
  sequence_set_bpm (gui->sequence, gtk_spin_button_get_value (GTK_SPIN_BUTTON (gui->bpm)));
  gui_set_modified (gui, 1);
}

static void
gui_play_clicked (GtkWidget * widget, gui_t * gui)
{
  sequence_start (gui->sequence);
}

static void
gui_pause_clicked (GtkWidget * widget, gui_t * gui)
{
  sequence_stop (gui->sequence);
}

static gboolean
gui_playback_toggle_pressed (GtkWidget * widget, GdkEventKey *event, gui_t * gui)
{
  if (event->keyval == GDK_space) {
    if (sequence_is_playing (gui->sequence)) sequence_stop (gui->sequence);
    else sequence_start (gui->sequence);

    return TRUE;
  }
  return FALSE;
}

static void
gui_rewind_clicked (GtkWidget * widget, gui_t * gui)
{
  sequence_rewind (gui->sequence);
}

static void
gui_loop_toggled (GtkWidget * widget, gui_t * gui)
{
  if (!gui->refreshing)
    {
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
          sequence_set_looping (gui->sequence);
      else
          sequence_unset_looping (gui->sequence);
    }
}

static GtkWidget *
gui_make_toolbar_spin_button (gui_t *gui, GtkWidget *box, const char *label, int digits, GtkAdjustment *adj, GCallback callback) 
{
  GtkWidget *label_wid = gtk_label_new (label);
  gtk_box_pack_start (GTK_BOX (box), label_wid, TRUE, FALSE, 0);
  GtkWidget *spin_button = gtk_spin_button_new (adj, 0.5, digits);
  gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin_button), GTK_UPDATE_IF_VALID);
  g_signal_connect (G_OBJECT (adj), "value_changed",
                    G_CALLBACK (callback), (gpointer) gui);
  gtk_box_pack_start (GTK_BOX (box), spin_button, TRUE, FALSE, 0);

  return spin_button;
}

static GtkWidget *
gui_make_toolbar_button (gui_t *gui, GtkWidget *box, char *stock_id, GCallback callback)
{
  GtkWidget *button = gtk_button_new ();
  GtkWidget *icon = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER (button), icon);
  g_signal_connect (G_OBJECT (button), "clicked", callback, (gpointer) gui);
  gtk_box_pack_start (GTK_BOX (box), button, TRUE, FALSE, 0);

  return button;
}

static void
gui_init (gui_t * gui)
{
  GtkWidget *button;
  GtkWidget *hbox;

  DEBUG ("Initializing GUI");
  
  gui->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (gui->window), "delete_event",
                    G_CALLBACK (gui_close), (gpointer) gui);
  gui_update_window_title (gui);
  gtk_container_set_border_width (GTK_CONTAINER (gui->window), 0);
  gtk_window_set_default_size (GTK_WINDOW (gui->window), 400, 200);

  gtk_container_set_border_width (GTK_CONTAINER (gui->window), 1);
  gui->main_vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (gui->window), gui->main_vbox);

  GtkWidget *menubar = gui_make_menubar (gui, gui_menu_items,
                                         gui_menu_nitems);

  gtk_box_pack_start (GTK_BOX (gui->main_vbox), menubar, FALSE, TRUE, 0);

  hbox = gtk_hbox_new (FALSE, 0);

  GtkAdjustment *adj;

  adj = (GtkAdjustment *) gtk_adjustment_new (sequence_get_tracks_num (gui->sequence), 1, 1024, 1, 1, 0);
  gui->tracks_num = gui_make_toolbar_spin_button (gui, hbox, " Tracks : ", 0, adj,  G_CALLBACK (gui_sequence_resized));

  adj = (GtkAdjustment *) gtk_adjustment_new (sequence_get_beats_num(gui->sequence), 1, 4096, 1, 1, 0);
  gui->beats_num = gui_make_toolbar_spin_button (gui, hbox, " Beats : ", 0, adj,  G_CALLBACK (gui_sequence_resized));

  adj = (GtkAdjustment *) gtk_adjustment_new (sequence_get_measure_len(gui->sequence), 1, 64, 1, 1, 0);
  gui->measure_len = gui_make_toolbar_spin_button (gui, hbox, " Measure : ", 0, adj,  G_CALLBACK (gui_sequence_resized));

  adj = (GtkAdjustment *) gtk_adjustment_new (sequence_get_bpm (gui->sequence), 0.1, 1000, 0.5, 0.5, 0);
  gui->bpm = gui_make_toolbar_spin_button (gui, hbox, " Bpm : ", 2, adj,  G_CALLBACK (gui_bpm_changed));

  button = gtk_vseparator_new ();
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, FALSE, 0);

  gui->loop = gtk_check_button_new_with_label ("Loop");
  if (sequence_is_looping(gui->sequence))
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui->loop), TRUE);
  g_signal_connect (G_OBJECT (gui->loop), "toggled",
                    G_CALLBACK (gui_loop_toggled), (gpointer) gui);
  gtk_box_pack_start (GTK_BOX (hbox), gui->loop, TRUE, FALSE, 0);

  gui->rewind = gui_make_toolbar_button (gui, hbox, GTK_STOCK_MEDIA_PREVIOUS, G_CALLBACK (gui_rewind_clicked));
  gtk_widget_set_sensitive (gui->rewind, gui->rc->transport_query ? TRUE : FALSE);
  gui_make_toolbar_button (gui, hbox, GTK_STOCK_MEDIA_PAUSE, G_CALLBACK (gui_pause_clicked));
  gui_make_toolbar_button (gui, hbox, GTK_STOCK_MEDIA_PLAY, G_CALLBACK (gui_play_clicked));

  g_signal_connect (G_OBJECT (gui->window), "key_press_event",
                    G_CALLBACK (gui_playback_toggle_pressed), (gpointer) gui);

  GtkWidget *handle_box = gtk_handle_box_new ();
  gtk_container_add (GTK_CONTAINER (handle_box), hbox);
  gtk_box_pack_start (GTK_BOX (gui->main_vbox), handle_box, FALSE, TRUE, 0);

  button = gtk_hseparator_new ();
  gtk_box_pack_start (GTK_BOX (gui->main_vbox), button, FALSE, TRUE, 0);

  gui->grid = grid_new ();
  grid_set_value_changed_callback (gui->grid, gui_sequence_modified, gui);
  grid_set_mask_changed_callback (gui->grid, gui_mask_modified, gui);
  
  gui_track_draw (gui);

  gtk_widget_show_all (gui->main_vbox);
}

void
gui_new (rc_t *rc,  arg_t *arg, song_t *song)
{
  gui_new_child (rc, arg, NULL, song, NULL, NULL);
}

static char *
gui_get_sequence_name (gui_t *gui, int instance_index)
{
    char *name = malloc(128);
    sprintf(name, "%s_%d", gui->name_prefix, instance_index);
    return name;
}
char *
gui_get_next_sequence_name (gui_t *gui)
{
    return gui_get_sequence_name (gui, gui_instance_counter + 1);
}

void
gui_new_child (rc_t *rc, arg_t *arg, gui_t *parent, song_t *song, sequence_t *sequence, char *filename)
{
  gui_t *gui;
  gui = calloc (1, sizeof (gui_t));
  DEBUG("Creating new GUI (PID: %d)", getpid());  
  gui->tracks_box = NULL;
  gui->sequence_is_modified = 0;
  gui->rewind = NULL;
  gui->refreshing = 0;
  gui->tracks_buttons = NULL;
  gui->track_samples = NULL;
  gui->song = song;
  gui->progress_window = NULL;
  gui->window = NULL;
  gui->volume_spinners = NULL;
  gui->transpose_volumes_round = 0;
  gui->rc = rc;
  gui->arg = arg;
  gui->last_export_framerate = 0;
  gui->last_export_sustain_type = 0;
  gui->last_export_wdir[0] = '\0';
 
  if ((parent == NULL))
    {
      gtk_rc_parse ("jackbeat.gtk.rc");
      gtk_init (&(arg->argc), &(arg->argv));
      strcpy (gui->name_prefix, arg->port_prefix);
      if (arg->filename != NULL)
       {
        if (!(sequence = gui_file_do_load_sequence (gui, arg->filename))) exit(1);
        filename = arg->filename;
       }
    }
  else
   {
    strcpy (gui->name_prefix, parent->name_prefix);
   }

  gui->instance_index = gui_instance_counter++;

  if (sequence) gui->sequence = sequence;
  else 
   {
    char *name = gui_get_sequence_name (gui, gui_instance_counter);
    int error;
    gui->sequence = sequence_new(name, &error); 
    free (name);
    if ((gui->sequence == NULL) && (error == ERR_SEQUENCE_JACK_CONNECT))
      {
        sprintf (gui->name_prefix, "%s_%d", gui->arg->port_prefix, getpid());
        name = gui_get_sequence_name (gui, gui_instance_counter);
        gui->sequence = sequence_new(name, &error); 
        free(name);
      }

    if (gui->sequence == NULL)
      {
        char *s = error_to_string (error);
        gui_display_error (gui, s);
        free (s);
        free (gui);
        return;
      }

    sequence_set_transport (gui->sequence, rc->transport_aware, rc->transport_query);
    sequence_set_auto_connect (gui->sequence, rc->auto_connect);

    if (gui->sequence && (rc->default_resampler_type != -1))
      sequence_set_resampler_type (gui->sequence, rc->default_resampler_type);

    song_register_sequence (gui->song, gui->sequence);
   }
 
  if (filename) 
   {
    strcpy (gui->filename, filename);
    gui->filename_is_set = 1;
   }
  else 
   {
    strcpy (gui->filename, "untitled.jab");
    gui->filename_is_set = 0;
   }

  gui->tooltips = gtk_tooltips_new();
  gui_init (gui);  
  
  DEBUG ("Starting GUI"); 
  gui->timeout_tag = g_timeout_add (GUI_ANIMATION_INTERVAL, gui_timeout, (gpointer) gui);
  gtk_widget_show (gui->window);
  gui_instance_num++;
  if (!parent) {
      gtk_main ();
  }
}

static void
gui_new_instance (gui_t * gui, guint action, GtkWidget * w)
{
  gui_new_child (gui->rc, gui->arg, gui, gui->song, NULL, NULL);
  gui_refresh (gui);
}
    
static void
gui_close_from_menu (gui_t * gui, guint action, GtkWidget * w)
{
  gui_close (NULL, NULL, gui);
}
    
static void
gui_exit (gui_t * gui, guint action, GtkWidget * w)
{
  // FIXME: not checking if there's any unsaved sequences
  if (gui_ask_confirmation (gui, "Are you sure that you want to close all "
                                 "windows and quit Jackbeat ?"))
    gui_do_exit (gui);
}
    

Generated by  Doxygen 1.6.0   Back to index