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

track.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: track.c 121 2008-01-10 00:17:40Z olivier $
 */

#include "gui/common.h"

struct gui_track_button_t
{
  GtkWidget * widget;
  GtkWidget * mask_button;
  int         track;
  int         beat;
  gui_t *     gui;
};

struct gui_track_sample_t
{
  gui_t * gui;
  int     track;
  char *  sample_fname;
};

static void
gui_track_sample_file_selected (gui_t *gui, char * filename, void * data)
{
  gui_track_button_t *tb = (gui_track_button_t *) data;
  gui_file_do_load_sample (gui, tb->track, filename);

  char dir[256];
  sprintf (dir, "%s/", dirname (filename));
  DIR *dir_stream;
  if ((dir_stream = opendir(dirname(filename)))) {
    closedir(dir_stream);
    DEBUG ("Setting sample_wdir to : %s", dir);
    strcpy (gui->rc->sample_wdir, dir);
  } else {
    DEBUG ("Not setting sample_wdir to : %s", dir);
  }
  g_free (filename);
}

static void
gui_track_load_sample_dialog (GtkWidget * widget, gui_track_button_t * tb)
{
  gui_t *gui = tb->gui;  
  DEBUG ("Using sample_wdir : %s", gui->rc->sample_wdir);
  gui_file_show_selector (gui, "Load sample", gui->rc->sample_wdir, gui_track_sample_file_selected, (void *) tb, 0);
}

static void
gui_track_load_sample_from_history (GtkWidget * menu_item, gui_track_sample_t * ts)
{
  gui_t *gui = ts->gui;
  char *filename = strdup (ts->sample_fname);
  gui_file_do_load_sample (gui, ts->track, filename);
  free (filename);
}

static void
gui_track_toggle_smoothing (GtkWidget * menu_item, gui_track_button_t * tb)
{
  gui_t *gui = tb->gui;
  if (!gui->refreshing)
   {
    int status = (GTK_CHECK_MENU_ITEM (menu_item)->active) ? 1 : 0;
    sequence_set_smoothing (gui->sequence, tb->track, status);
    gui_set_modified (gui, 1);
   }
}

char *
gui_track_ask_name (gui_t *gui, char *current_name, int is_name_doublon, int allow_cancel)
{  
  GtkWidget * dialog;
  char str[128];
  char *new_name = NULL;

  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);

  if (allow_cancel) 
   {
    dialog = gtk_dialog_new_with_buttons (
                "Rename track",
                GTK_WINDOW (gui->window),
                GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
                GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
                NULL);
   }
  else
    dialog = gtk_dialog_new_with_buttons (
                "Rename track",
                GTK_WINDOW (gui->window),
                GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
                GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                NULL);
    g_signal_connect (G_OBJECT (dialog), "delete_event", 
                      G_CALLBACK (gui_no_delete), NULL);
  
  if (is_name_doublon)
   {
    sprintf (str, "A track named \"%s\" already exists, please provide another name :",
             current_name);
             
    GtkWidget *label = gtk_label_new (str);
    gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 15);
   }
  
  GtkWidget *entry = gtk_entry_new();
  gtk_entry_set_max_length (GTK_ENTRY (entry), 127);
  gtk_entry_set_text (GTK_ENTRY (entry), current_name);
  gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
  gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 15);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE,
                      0);
  gtk_widget_show_all (dialog);
  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) 
   {
    char *buf = (char *)gtk_entry_get_text (GTK_ENTRY (entry));
    if (strlen (buf)) {
      new_name = malloc (128);
      strcpy (new_name, buf);
      g_strstrip (new_name);
    }
   }
  gtk_widget_destroy (dialog);
  return new_name;
}

static void
gui_track_rename (GtkWidget * menu_item, gui_track_button_t * tb)
{
  gui_t *gui = tb->gui;
  char *new_name;
  char *old_name = sequence_get_track_name (gui->sequence, tb->track);
  new_name = gui_track_ask_name (gui, old_name, 0, 1); 
  while (new_name 
         && sequence_track_name_exists (gui->sequence, new_name) 
         && strcmp (new_name, old_name))
    new_name = gui_track_ask_name (gui, new_name, 1, 1); 
  if (new_name)
   {
    sequence_set_track_name (gui->sequence, tb->track, new_name);
    free (new_name);
    gui_refresh (gui);
    gui_set_modified (gui, 1);
   }
}

static void
gui_track_remove (GtkWidget * menu_item, gui_track_button_t * tb)
{
  gui_t *gui = tb->gui;
  sequence_remove_track (gui->sequence, tb->track);
  song_cleanup_unused_samples (gui->song);
  gui_refresh (gui);
  gui_set_modified (gui, 1);
}

static void
gui_track_mute (GtkWidget *toggle, gui_track_button_t * tb)
{
  gui_t *gui = tb->gui;
 
  if (!gui->refreshing) 
   {
    int active = 
      (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)) == TRUE) 
        ? 1 : 0;

    sequence_mute_track (gui->sequence, active, tb->track);
    gui_set_modified (gui, 1);
  }
}

static void
gui_track_pitch_changed (GtkWidget * adj, gui_track_button_t * tb)
{
  gui_t *gui = tb->gui;  
  if (!gui->refreshing) 
   {
    GtkWidget *spinner = g_object_get_data (G_OBJECT (adj), "user-data");
    sequence_set_pitch (gui->sequence, 
                        tb->track,
                        gtk_spin_button_get_value (GTK_SPIN_BUTTON (spinner)));

    gui_set_modified (gui, 1);
   }
}


static void
gui_track_size_allocate (GtkWidget * menu_item, GtkAllocation * allocation ,gui_t * gui)
{
  int height = allocation->height - 2;
  grid_set_cell_size (gui->grid, height * 3 / 4, height * 3 / 4, height, height);
}
  
static void
gui_track_header_size_allocate (GtkWidget * menu_item, GtkAllocation * allocation ,gui_t * gui)
{
  DEBUG ("header height/y : %d/%d", allocation->height, allocation->y);
  grid_set_header_label_ypos (gui->grid, allocation->y - 1);
}

static void
gui_track_volume_changed (GtkWidget * adj, gui_track_button_t * tb)
{
  gui_t *gui = tb->gui;  
  GtkWidget *spinner = g_object_get_data (G_OBJECT (adj), "user-data");
  if (!gui->refreshing) 
   {
    double value;
#ifdef USE_PHAT    
#ifdef USE_KNOB
    value = phat_knob_get_value (PHAT_KNOB (spinner)) / 100; 
#else
    value = phat_fan_slider_get_value (PHAT_FAN_SLIDER (spinner)) / 100; 
#endif
#else
    value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spinner)) / 100; 
#endif
    sequence_set_volume (gui->sequence, tb->track, value); 
   }

  gui_set_modified (gui, 1);
}

static GtkWidget *
gui_track_add_menu_item (GtkWidget *menu, char *type, char *label, GCallback callback, gpointer data)
{
  GtkWidget *item ;
  if (!strcmp(type,"#separator")) item = gtk_separator_menu_item_new();  
  else if (!strcmp (type, "#toggle")) {
    item = gtk_check_menu_item_new_with_label (label);
    if (callback) g_signal_connect (G_OBJECT (item), "toggled", callback, data);
  } else if (!strcmp (type, "#stock")) {
    item = gtk_image_menu_item_new_from_stock (label, NULL);
    if (callback) g_signal_connect (G_OBJECT (item), "activate", callback, data);
  } else {
    item = gtk_menu_item_new_with_label (label);
    if (callback) g_signal_connect (G_OBJECT (item), "activate", callback, data);
    if (!strcmp (type, "#ghost"))
      gtk_widget_set_sensitive (item, FALSE);
  }
  gtk_menu_shell_append (GTK_MENU_SHELL (menu),item);
  gtk_widget_show (item);
  return item;
}

static
GtkWidget *
gui_track_make_menu (gui_t *gui, char *name, int track)
{
  GtkWidget *menu, *submenu;
  GtkWidget *item;
  gui_track_button_t *tb;
  int i;
  gui_track_sample_t *ts;
  
  tb = gui->tracks_buttons + track * sequence_get_beats_num (gui->sequence);

  menu = gtk_menu_new ();

  gui_track_add_menu_item (menu, "#item", "Load sample...", G_CALLBACK (gui_track_load_sample_dialog), (gpointer) tb);
  submenu = gtk_menu_new ();
  if (gui->rc->sample_history_num == 0) {
    item = gui_track_add_menu_item (submenu, "#ghost", "<No sample>", NULL, NULL);
  } else {
    for (i=0; i < gui->rc->sample_history_num; i++) 
     {
      ts = gui->track_samples + track * gui->rc->sample_history_num + i;
      ts->gui = gui;
      ts->track = track;
      ts->sample_fname = gui->rc->sample_history[i];
      char *hist = strdup (gui->rc->sample_history[i]);
      char *dirname1 = strdup (dirname (hist));
      free (hist);
      char *_dirname1 = strdup (dirname1);
      char *dirname2 = strdup (dirname (_dirname1));
      free (_dirname1);
      _dirname1 = strdup (dirname1);
      char *dir_basename = strdup (basename (_dirname1));
      free (_dirname1);
      hist = strdup (gui->rc->sample_history[i]);
      char *basename1 = strdup (basename (hist));
      free (hist);
      hist = strdup (gui->rc->sample_history[i]);
     
      char display[256];
      
      if ((strcmp (dirname1, "/") == 0) || (strcmp (dirname2, "/") == 0)) strcpy (display, hist);
      else sprintf (display, "/.../%s/%s", dir_basename, basename1);

      free (dirname1);
      free (dirname2);
      free (dir_basename);
      free (basename1);
      free (hist);
      
      gui_track_add_menu_item (submenu, "#item", display, G_CALLBACK (gui_track_load_sample_from_history), (gpointer) ts);
     }
  }

  item = gui_track_add_menu_item (menu, "#item", "Recent",NULL,NULL);
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);

  gui_track_add_menu_item (menu, "#separator", NULL, NULL, NULL);

  item = gui_track_add_menu_item (menu, "#toggle", "Smoothing", NULL, NULL);
  if (sequence_get_smoothing (gui->sequence, track))
      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
  g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (gui_track_toggle_smoothing), (gpointer) tb);
  
  gui_track_add_menu_item (menu, "#separator", NULL, NULL, NULL);

  item = gui_track_add_menu_item (menu, "#item", "Rename...", G_CALLBACK
                            (gui_track_rename), (gpointer) tb);

  item = gui_track_add_menu_item (menu, "#stock", GTK_STOCK_REMOVE, G_CALLBACK
                            (gui_track_remove), (gpointer) tb);

  if (sequence_get_tracks_num (gui->sequence) == 1)
    gtk_widget_set_sensitive (item, FALSE);

  item = gtk_menu_item_new_with_label (name);
  gtk_widget_show(item);
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
    
  return item;
}

void
gui_track_draw (gui_t * gui)
{
  GtkWidget *scrolled_win;
  GtkWidget *table;
  GtkWidget *hbox;
  GtkWidget *scrolled_vbox;
  int i, j;
  GtkWidget *button;
  GtkWidget *menu;
  gui_track_button_t *tb = NULL;

  DEBUG ("Drawing sequence");

  /* Fetching dimensions */
  int ntracks = sequence_get_tracks_num(gui->sequence);
  int nbeats = sequence_get_beats_num(gui->sequence);
  int measure_len = sequence_get_measure_len(gui->sequence);
  int nmeasures = (nbeats - 1) / measure_len + 1;

  /* (Re)allocating main widgets and callback data */
  if (gui->tracks_box != NULL) gtk_widget_destroy (gui->tracks_box);

  if (gui->tracks_buttons) free (gui->tracks_buttons);
  gui->tracks_buttons = calloc (nbeats * ntracks, sizeof (gui_track_button_t));

  if (gui->track_samples) free (gui->track_samples);
  
  if ((i = gui->rc->sample_history_num))
    gui->track_samples = calloc (i * ntracks, sizeof (gui_track_sample_t));

  gui->volume_spinners = realloc (gui->volume_spinners, ntracks * sizeof (GtkWidget *));
  
  hbox = gtk_hbox_new (FALSE, 0);

  scrolled_vbox = gtk_vbox_new (FALSE, 0);
  
  /* Creating scrolled window */
  DEBUG("Creating scrolled window");
  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  /* Creating table */
  
  /* Row headers : label row */
  int nrow_headers = 1;
  
  /* Two rows for each track + headers */
  int nrows = ntracks * 2 + nrow_headers; 

  /* Column headers : menus + mute buttons + pitch control + level control */
  int ncol_headers = 4;
  
  /* headers + nbeats + separators cols (nmeasures - 1) */
  int ncols = ncol_headers + nbeats + nmeasures - 1;
  
  table = gtk_table_new (nrows, ncols, FALSE);
  DEBUG ("Created table of size : %d x %d", nrows, ncols);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  gtk_table_set_col_spacings (GTK_TABLE (table), 2);

  gtk_box_pack_start (GTK_BOX(scrolled_vbox), table, FALSE, FALSE, 0);
  
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_win),
                                         scrolled_vbox);
  gtk_box_pack_start (GTK_BOX (hbox), scrolled_win, TRUE, TRUE, 0);

  /* Drawing labels */
  DEBUG ("Drawing labels");

  /* Top-left empty label */
  button = gtk_label_new (" ");
  g_signal_connect (G_OBJECT (button), "size-allocate", 
                    G_CALLBACK (gui_track_header_size_allocate), (gpointer) gui);
  gtk_table_attach (GTK_TABLE (table), button, 0, 2, 0, 1,
                    GTK_EXPAND | GTK_FILL, 0, 1, 1);
  
  /* Pitch label */
  button = gtk_label_new ("Pitch");
  gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 1,
                    GTK_EXPAND | GTK_FILL, 0, 1, 1);
  
  /* Volume label */
  button = gtk_label_new ("Volume");
  gtk_table_attach (GTK_TABLE (table), button, 3, 4, 0, 1,
                    GTK_EXPAND | GTK_FILL, 0, 1, 1);
  
  /* Drawing menus */
  DEBUG ("Drawing menus");
  for (i=0; i < ntracks; i++)
   {
    menu = gui_track_make_menu (gui, sequence_get_track_name (gui->sequence, i), i);
    button = gtk_menu_bar_new();
    gtk_menu_bar_append (GTK_MENU_BAR (button), menu);
    gtk_table_attach (GTK_TABLE (table), button, 0, 1, i * 2 + 1, i * 2 + 3,
                      GTK_EXPAND | GTK_FILL, 0, 1, 1);
   }
 
  /* Drawing Mute buttons */
  DEBUG ("Drawing mute buttons");
  for (i=0; i < ntracks; i++)
   {
    tb = gui->tracks_buttons + i * nbeats;

    button = gtk_toggle_button_new_with_label ("M");
    if (sequence_track_is_muted (gui->sequence, i))
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
    gtk_tooltips_set_tip (gui->tooltips, button, "Mute/unmute this track", "");
    g_signal_connect (G_OBJECT (button), "toggled",
                      G_CALLBACK (gui_track_mute), (gpointer) tb);
    if (i == 0) 
      g_signal_connect (G_OBJECT (button), "size-allocate", 
                        G_CALLBACK (gui_track_size_allocate), (gpointer) gui);
  
    gtk_table_attach (GTK_TABLE (table), button, 1, 2, i * 2 + 1, i * 2 + 3,
                      0, 0, 1, 1);
    
   }

  DEBUG ("Drawing controls");
  /* Drawing pitch spin buttons */
  GtkAdjustment *adj;
  for (i=0; i < ntracks; i++)
   {
    adj = (GtkAdjustment *) gtk_adjustment_new (sequence_get_pitch (gui->sequence, i),
                                                -96, 96, 0.01, 0.0001, 0);
    button = gtk_spin_button_new (adj, 0.5, 4);
    g_object_set_data (G_OBJECT (adj), "user-data", (gpointer) button);
    gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (button),
                                       GTK_UPDATE_IF_VALID);
    tb = gui->tracks_buttons + i * nbeats;
    g_signal_connect (G_OBJECT (adj), "value_changed",
                      G_CALLBACK (gui_track_pitch_changed), (gpointer) tb);
    gtk_table_attach (GTK_TABLE (table), button, 2, 3, i * 2 + 1, i * 2 + 3,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
   }
  
  /* Drawing volume spin buttons */
  for (i=0; i < ntracks; i++)
   {
#ifdef USE_PHAT
    adj = (GtkAdjustment *) gtk_adjustment_new (sequence_get_volume (gui->sequence, i) * 100,
                                                0, 120, 1, 0.01, 0);
#ifdef USE_KNOB
    button = phat_knob_new (adj);
#else
    button = phat_hfan_slider_new (adj);
#endif    
#else
    adj = (GtkAdjustment *) gtk_adjustment_new (sequence_get_volume (gui->sequence, i) * 100,
                                                0, 999, 1, 0.01, 0);
    button = gtk_spin_button_new (adj, 0.5, 2);
    gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (button),
                                       GTK_UPDATE_IF_VALID);
#endif                                       
    g_object_set_data (G_OBJECT (adj), "user-data", (gpointer) button);
    tb = gui->tracks_buttons + i * nbeats;
    g_signal_connect (G_OBJECT (adj), "value_changed",
                      G_CALLBACK (gui_track_volume_changed), (gpointer) tb);
    gtk_table_attach (GTK_TABLE (table), button, 3, 4, i * 2 + 1, i * 2 + 3,
                      GTK_EXPAND | GTK_FILL, 0, 1, 1);
    gui->volume_spinners[i] = button;
   }
  
  /* Setting up grid */
  DEBUG ("Setting up grid");
  grid_resize (gui->grid, nbeats, ntracks);
  grid_set_column_group_size (gui->grid, measure_len);
  for (j=0; j < ntracks; j++)
   {
    int handle_mask = sequence_is_enabled_mask (gui->sequence, j);
    for (i=0; i < nbeats; i++)
     {
      tb = gui->tracks_buttons + j * nbeats + i;
      tb->gui = gui;
      tb->track = j;
      tb->beat = i;
      
      grid_set_value (gui->grid, i, j, sequence_get_beat(gui->sequence, j, i));
      if (handle_mask)
        grid_set_mask (gui->grid, i, j, sequence_get_mask_beat(gui->sequence, j, i));
     }
   }

  gtk_table_attach (GTK_TABLE (table), grid_get_widget (gui->grid), 
                    ncol_headers, ncols, 0, nrows, 
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
  
  /* Finalizing */
  gui->tracks_box = hbox;
  gtk_box_pack_start (GTK_BOX (gui->main_vbox), gui->tracks_box, TRUE, TRUE, 0);
  gtk_widget_show_all (gui->tracks_box);
  DEBUG ("Finished drawing sequence");
}


Generated by  Doxygen 1.6.0   Back to index