Files seq24-rev48 adding win32/src/0004.s24 and seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/0004.s24 differ
diff -rupN seq24-rev48 adding win32/src/config.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/config.h
--- seq24-rev48 adding win32/src/config.h	2009-05-20 20:54:39.640625000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/config.h	2009-05-20 20:54:47.156250000 -0500
@@ -36,7 +36,7 @@
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "0.9.0-win32-beta-1"
+#define VERSION "0.9.0-win32-beta-4"
 
 /* gnu source */
 #define _GNU_SOURCE 1
diff -rupN seq24-rev48 adding win32/src/config_win32.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/config_win32.h
--- seq24-rev48 adding win32/src/config_win32.h	2009-05-20 20:54:57.171875000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/config_win32.h	2009-05-20 20:30:54.484375000 -0500
@@ -36,7 +36,7 @@
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "0.9.0-win32-beta-1"
+#define VERSION "0.9.0-win32-beta-4"
 
 /* gnu source */
 #define _GNU_SOURCE 1
diff -rupN seq24-rev48 adding win32/src/globals.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/globals.h
--- seq24-rev48 adding win32/src/globals.h	2009-05-20 20:30:46.046875000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/globals.h	2009-05-20 20:30:54.656250000 -0500
@@ -26,6 +26,9 @@
 #include <string>
 #include <gtkmm/main.h>
 #include <gtkmm/drawingarea.h>
+//For keys
+#include <gtkmm/accelkey.h>
+
 
 using namespace std;
 
@@ -33,6 +36,7 @@ using namespace std;
 const int c_mainwnd_rows = 4;
 const int c_mainwnd_cols = 8;
 const int c_seqs_in_set = c_mainwnd_rows * c_mainwnd_cols;
+const int c_gmute_tracks = c_seqs_in_set * c_seqs_in_set;
 const int c_max_sets = 32;
 const int c_total_seqs = c_seqs_in_set * c_max_sets;
 
@@ -127,8 +131,11 @@ const int c_normal = 1;
 const int c_paste  = 2;
 
 /* redraw when recording ms */
+#ifdef __WIN32__
+const int c_redraw_ms = 20;
+#else
 const int c_redraw_ms = 40;
-
+#endif
 
 /* consts for perform editor */
 const int c_names_x = 6 * 24;
@@ -296,5 +303,27 @@ enum mouse_action_e 
     e_action_grow
 };
 
+enum interaction_method_e
+{
+    e_seq24_interaction,
+    e_fruity_interaction,
+    e_number_of_interactions // keep this one last...
+};
+const char* const c_interaction_method_names[] =
+{
+    "seq24",
+    "fruity",
+    NULL
+};
+const char* const c_interaction_method_descs[] =
+{
+    "original seq24 method",
+    "similar to a certain fruity sequencer we like",
+    NULL
+};
+extern interaction_method_e global_interactionmethod;
 
+// makes printable text from the given key
+// see seq24.cpp for implementation
+char* key2text( unsigned long int val );
 #endif
diff -rupN seq24-rev48 adding win32/src/learn.xpm seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/learn.xpm
--- seq24-rev48 adding win32/src/learn.xpm	1969-12-31 18:00:00.000000000 -0600
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/learn.xpm	2009-05-15 00:59:02.878612000 -0500
@@ -0,0 +1,14 @@
+/* XPM */
+static const char * learn_xpm[] = {
+"21 6 5 1",
+" 	c None",
+".	c #000000",
+"+	c #FFC1C1",
+"@	c #AD6666",
+"#	c #724B4B",
+"     ..              ",
+"     ..              ",
+"     ..              ",
+"     ..              ",
+"     ............    ",
+"     ............    "};
diff -rupN seq24-rev48 adding win32/src/learn2.xpm seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/learn2.xpm
--- seq24-rev48 adding win32/src/learn2.xpm	1969-12-31 18:00:00.000000000 -0600
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/learn2.xpm	2009-05-15 00:59:02.878612000 -0500
@@ -0,0 +1,14 @@
+/* XPM */
+static const char * learn2_xpm[] = {
+"21 6 5 1",
+". 	c None",
+"   c #000000",
+"+	c #FFC1C1",
+"@	c #AD6666",
+"#	c #724B4B",
+"     ..              ",
+"     ..              ",
+"     ..              ",
+"     ..              ",
+"     ............    ",
+"     ............    "};
diff -rupN seq24-rev48 adding win32/src/mainwid.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/mainwid.cpp
--- seq24-rev48 adding win32/src/mainwid.cpp	2009-05-20 20:30:46.156250000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/mainwid.cpp	2009-05-20 20:30:54.781250000 -0500
@@ -210,12 +210,23 @@ mainwid::draw_sequence_on_pixmap( int a_
 		/*char key =  m_seq_to_char[local_seq];*/
 
 	    char str[20];
-	    sprintf( str, 
+
+        if (m_mainperf->show_ui_sequence_key())
+        {
+    	    sprintf( str, "%c", (char)m_mainperf->lookup_keyevent_key( a_seq ) );
+
+    	    p_font_renderer->render_string_on_drawable(m_gc,
+                                                       base_x + c_seqarea_x - 7,
+                                                       base_y + c_text_y * 4 - 2,
+                                                       m_pixmap, str, col );
+        }
+
+	    sprintf( str,
 		     "%d-%d %ld/%ld",
 		     seq->get_midi_bus(), 
 		     seq->get_midi_channel()+1,
 		     seq->get_bpm(), seq->get_bw() );
-        
+
 	    p_font_renderer->render_string_on_drawable(m_gc,
                                                    base_x + c_text_x, 
                                                    base_y + c_text_y * 4 - 2,
diff -rupN seq24-rev48 adding win32/src/mainwnd.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/mainwnd.cpp
--- seq24-rev48 adding win32/src/mainwnd.cpp	2009-05-20 20:30:46.234375000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/mainwnd.cpp	2009-05-20 20:30:54.859375000 -0500
@@ -28,22 +28,37 @@
 
 #include "play2.xpm"
 #include "stop.xpm"
+#include "learn.xpm"
+#include "learn2.xpm"
 #include "perfedit.xpm"
 #include "seq24.xpm"
 #include "seq24_32.xpm"
 
 bool is_pattern_playing = false;
 
+// tooltip helper, for old vs new gtk...
+#if GTK_MINOR_VERSION >= 12
+#   define add_tooltip( obj, text ) obj->set_tooltip_text( text);
+#else
+#   define add_tooltip( obj, text ) m_tooltips->set_tip( *obj, text );
+#endif
+
 mainwnd::mainwnd(perform *a_p)
 {
     set_icon(Gdk::Pixbuf::create_from_xpm_data(seq24_32_xpm));
 
     /* set the performance */
     m_mainperf = a_p;
+    
+    /* register for notification */
+    m_mainperf->m_notify.push_back( this );
 
     /* main window */
     update_window_title();
 
+#if GTK_MINOR_VERSION < 12
+    m_tooltips = manage( new Tooltips() );
+#endif
     m_main_wid = manage( new mainwid( m_mainperf ));
     m_main_time = manage( new maintime( ));
 
@@ -99,9 +114,7 @@ mainwnd::mainwnd(perform *a_p)
                     Gdk::Pixbuf::create_from_xpm_data( stop_xpm ))));
     m_button_stop->signal_clicked().connect(
             mem_fun(*this, &mainwnd::stop_playing));
-#if GTK_MINOR_VERSION >= 12
-    m_button_stop->set_tooltip_text("Stop playing MIDI sequence");
-#endif
+    add_tooltip( m_button_stop, "Stop playing MIDI sequence" );
     hbox->pack_start(*m_button_stop, false, false);
 
     /* play button */
@@ -110,9 +123,7 @@ mainwnd::mainwnd(perform *a_p)
                     Gdk::Pixbuf::create_from_xpm_data( play2_xpm ))));
     m_button_play->signal_clicked().connect(
             mem_fun( *this, &mainwnd::start_playing));
-#if GTK_MINOR_VERSION >= 12
-    m_button_play->set_tooltip_text("Play MIDI sequence");
-#endif
+    add_tooltip( m_button_play, "Play MIDI sequence" );
     hbox->pack_start(*m_button_play, false, false);
 
     /* song edit button */
@@ -121,9 +132,7 @@ mainwnd::mainwnd(perform *a_p)
                     Gdk::Pixbuf::create_from_xpm_data( perfedit_xpm  ))));
     m_button_perfedit->signal_clicked().connect(
             mem_fun( *this, &mainwnd::open_performance_edit ));
-#if GTK_MINOR_VERSION >= 12
-    m_button_perfedit->set_tooltip_text("Show or hide song editor window");
-#endif
+    add_tooltip( m_button_perfedit, "Show or hide song editor window" );
     hbox->pack_end(*m_button_perfedit, false, false, 4);
 
     /* bpm spin button */
@@ -132,9 +141,7 @@ mainwnd::mainwnd(perform *a_p)
     m_spinbutton_bpm->set_editable( false );
     m_adjust_bpm->signal_value_changed().connect(
             mem_fun(*this, &mainwnd::adj_callback_bpm ));
-#if GTK_MINOR_VERSION >= 12
-    m_spinbutton_bpm->set_tooltip_text("Adjust beats per minute (BPM) value");
-#endif
+    add_tooltip( m_spinbutton_bpm, "Adjust beats per minute (BPM) value" );
     hbox->pack_start(*(manage( new Label( "  bpm " ))), false, false, 4);
     hbox->pack_start(*m_spinbutton_bpm, false, false );
   
@@ -145,9 +152,7 @@ mainwnd::mainwnd(perform *a_p)
     m_spinbutton_ss->set_wrap( true );
     m_adjust_ss->signal_value_changed().connect(
             mem_fun(*this, &mainwnd::adj_callback_ss ));
-#if GTK_MINOR_VERSION >= 12
-    m_spinbutton_ss->set_tooltip_text("Select sreen set");
-#endif
+    add_tooltip( m_spinbutton_ss, "Select sreen set" );
     hbox->pack_end(*m_spinbutton_ss, false, false );
     hbox->pack_end(*(manage( new Label( "  set " ))), false, false, 4);
  
@@ -157,9 +162,7 @@ mainwnd::mainwnd(perform *a_p)
             mem_fun(*this, &mainwnd::edit_callback_notepad));
     m_entry_notes->set_text(*m_mainperf->get_screen_set_notepad(
                 m_mainperf->get_screenset())); 
-#if GTK_MINOR_VERSION >= 12
-    m_entry_notes->set_tooltip_text("Enter screen set name");
-#endif
+    add_tooltip( m_entry_notes, "Enter screen set name" );
     hbox->pack_start( *m_entry_notes, true, true );
 
 
@@ -168,7 +171,34 @@ mainwnd::mainwnd(perform *a_p)
     hbox2->pack_start(*manage(new Image(
                     Gdk::Pixbuf::create_from_xpm_data(seq24_xpm))),
             false, false);
-    hbox2->pack_end( *m_main_time, false, false );
+
+    // adjust placement...
+    VBox *vbox_b = manage( new VBox() );
+    HBox *hbox3 = manage( new HBox( false, 0 ) );
+    vbox_b->pack_start( *hbox3, false, false );
+    hbox2->pack_end( *vbox_b, false, false );
+    hbox3->set_spacing( 10 );
+
+    /* timeline */
+    hbox3->pack_start( *m_main_time, false, false );
+
+    /* group learn button */
+    m_button_learn = manage( new Button( ));
+    m_button_learn->set_focus_on_click( false );
+    m_button_learn->set_flags( m_button_learn->get_flags() & ~Gtk::CAN_FOCUS );
+    m_button_learn->set_image(*manage(new Image(
+                    Gdk::Pixbuf::create_from_xpm_data( learn_xpm ))));
+    m_button_learn->signal_clicked().connect(
+            mem_fun(*this, &mainwnd::learn_toggle));
+    add_tooltip( m_button_learn, "Mute Group Learn\n\n"
+            "Click 'L' then press a mutegroup key to store the mute state of "
+            "the sequences in that key.\n\n"
+            "(see File/Options/Keyboard for available mutegroup keys "
+            "and the corresponding hotkey for the 'L' button)" );
+    hbox3->pack_end( *m_button_learn, false, false );
+
+    Button w;
+    hbox3->set_focus_child( w ); // clear the focus, don't want to trigger L via keys
 
     /* set up a vbox, put the menu in it, and add it */
     VBox *vbox = new VBox();
@@ -278,6 +308,28 @@ mainwnd::stop_playing( void )
     is_pattern_playing = false;
 }
 
+void
+mainwnd::on_grouplearnchange(bool state)
+{
+    /* respond to learn mode change from m_mainperf */
+    m_button_learn->set_image(*manage(new Image(
+        Gdk::Pixbuf::create_from_xpm_data( state ? learn2_xpm : learn_xpm))));
+}
+
+void
+mainwnd::learn_toggle()
+{
+    if (m_mainperf->is_group_learning())
+    {
+        m_mainperf->unset_mode_group_learn();
+    }
+    else
+    {
+        m_mainperf->set_mode_group_learn();
+    }
+}
+
+
 
 /* callback function */
 void mainwnd::file_new()
@@ -631,7 +683,8 @@ mainwnd::about_dialog( void )
     dialog.set_transient_for(*this);
     dialog.set_name(PACKAGE_NAME);
     dialog.set_version(PACKAGE_VERSION);
-    dialog.set_comments("Interactive MIDI Sequencer");
+    dialog.set_comments("Interactive MIDI Sequencer\n"
+                        VERSION);
 
     dialog.set_copyright(
             "(C) 2002 - 2006 Rob C. Buse\n"
@@ -649,6 +702,7 @@ mainwnd::about_dialog( void )
     list_authors.push_back("Peter Leigh <pete.leigh@gmail.com>");
     list_authors.push_back("Anthony Green <green@redhat.com>");
     list_authors.push_back("Daniel Ellis <mail@danellis.co.uk>");
+    list_authors.push_back("Kevin Meinert <kevin@subatomicglue.com>");
     dialog.set_authors(list_authors);
 
     std::list<Glib::ustring> list_documenters;
@@ -691,6 +745,9 @@ mainwnd::on_key_release_event(GdkEventKe
     if ( a_ev->keyval == m_mainperf->m_key_snapshot_1 ||
             a_ev->keyval == m_mainperf->m_key_snapshot_2 )
         m_mainperf->unset_sequence_control_status( c_status_snapshot );
+    if ( a_ev->keyval == m_mainperf->m_key_group_learn ){
+        m_mainperf->unset_mode_group_learn();
+    }
 
     return false;
 }
@@ -711,10 +768,10 @@ mainwnd::on_key_press_event(GdkEventKey*
 {
     // control and modifier key combinations matching
     // menu items have first priority
-    if (Gtk::Window::on_key_press_event(a_ev))
-        return true;
+    /*if (*/Gtk::Window::on_key_press_event(a_ev);
+        //return true;  // on win32, it'd always return true here (i.e. for SPACE bar)... ?
 
-    else if ( m_entry_notes->has_focus()) {
+    /*else */if ( m_entry_notes->has_focus()) {
         m_entry_notes->event( (GdkEvent*) a_ev );
         return false;
     }
@@ -722,7 +779,8 @@ mainwnd::on_key_press_event(GdkEventKey*
         if ( a_ev->type == GDK_KEY_PRESS ){
 
             if ( global_print_keys ){
-                printf( "key_press[%d]\n", a_ev->keyval ); 
+                printf( "key_press[%d]\n", a_ev->keyval );
+                fflush( stdout );
             }
             
             if ( a_ev->keyval == m_mainperf->m_key_bpm_dn ){
@@ -740,7 +798,8 @@ mainwnd::on_key_press_event(GdkEventKey*
                 m_mainperf->set_sequence_control_status( c_status_replace );
             }
             
-            if (a_ev->keyval ==  m_mainperf->m_key_queue )
+              if ((a_ev->keyval ==  m_mainperf->m_key_queue )
+             || (a_ev->keyval == m_mainperf->m_key_keep_queue ))
             {
                 m_mainperf->set_sequence_control_status( c_status_queue );
             }
@@ -766,23 +825,79 @@ mainwnd::on_key_press_event(GdkEventKey*
                 m_adjust_ss->set_value( m_mainperf->get_screenset()  );	
                 m_entry_notes->set_text( * m_mainperf->get_screen_set_notepad(m_mainperf->get_screenset()  )); 
             }
-            
-            if ( a_ev->keyval == m_mainperf->m_key_start )
+             if ( a_ev->keyval == m_mainperf->m_key_set_playing_screenset ){
+                m_mainperf->set_playing_screenset();
+            }
+           
+            if ( a_ev->keyval == m_mainperf->m_key_group_on ){
+                m_mainperf->set_mode_group_mute();
+            }
+            if ( a_ev->keyval == m_mainperf->m_key_group_off ){
+                m_mainperf->unset_mode_group_mute();
+            }
+           
+            if ( a_ev->keyval == m_mainperf->m_key_group_learn ){
+                m_mainperf->set_mode_group_learn();
+            }
+
+            // activate mute group key
+            if( m_mainperf->get_key_groups()->count( a_ev->keyval ) != 0 )
             {
-                if (is_pattern_playing)
-                    stop_playing();
-                else
-                    start_playing();
+                m_mainperf->select_and_mute_group( m_mainperf->lookup_keygroup_group( a_ev->keyval ) );
             }
 
+            // mute group learn
+            if (m_mainperf->is_learn_mode() &&
+                a_ev->keyval != m_mainperf->m_key_group_learn)
+            {
+                if( m_mainperf->get_key_groups()->count( a_ev->keyval ) != 0 )
+                {
+                    char buf[512];
+                    sprintf( buf, "Midi Group Learn: Learned! key:%s (code:%d) "
+                                  "mapped", key2text( a_ev->keyval ), a_ev->keyval );
+                    Gtk::MessageDialog dialog(*this,
+                           buf, false,
+                           Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK, true);
+                    dialog.run();
+                    
+                    // we miss the keyup msg for learn, force set it off
+                    m_mainperf->unset_mode_group_learn();
+                }
+                else
+                {
+                    char buf[512];
+                    sprintf( buf, "Midi Group Learn: key:%s (code:%d) not "
+                                  "one of the configured mute-group keys, to change "
+                                  "see file/options menu or .seq24rc",
+                                  key2text( a_ev->keyval ), a_ev->keyval );
+                    Gtk::MessageDialog errdialog(*this, buf, false,
+                           Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
+                    errdialog.run();
+                    // we miss the keyup msg for learn, force set it
+                    m_mainperf->unset_mode_group_learn();
+                }
+            }
+
+            // the start/end key may be the same key (i.e. SPACE)
+            // allow toggling when the same key is mapped to both triggers (i.e. SPACEBAR)
+            bool dont_toggle = m_mainperf->m_key_start != m_mainperf->m_key_stop;
+            if ( a_ev->keyval == m_mainperf->m_key_start && (dont_toggle || !is_pattern_playing))
+            {
+                start_playing();
+            }
+            else if ( a_ev->keyval == m_mainperf->m_key_stop && (dont_toggle || is_pattern_playing))
+            {
+                stop_playing();
+            }
 
+            /* toggle sequence mute/unmute using keyboard keys... */
             if( m_mainperf->get_key_events()->count( a_ev->keyval) != 0 ){
-                
-                sequence_key(  (*m_mainperf->get_key_events())[a_ev->keyval] );
+
+                sequence_key( m_mainperf->lookup_keyevent_seq( a_ev->keyval ) );
             }
         }
     }
-    
+
     return false;
 }
 
diff -rupN seq24-rev48 adding win32/src/mainwnd.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/mainwnd.h
--- seq24-rev48 adding win32/src/mainwnd.h	2009-05-20 20:30:46.250000000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/mainwnd.h	2009-05-20 20:30:54.875000000 -0500
@@ -47,13 +47,18 @@ using namespace Gtk;
 using namespace Menu_Helpers;
 
 
-class mainwnd : public Gtk::Window
+class mainwnd : public Gtk::Window, public performcallback
 {
-   
+    /* notification handler for learn mode toggle */
+    virtual void on_grouplearnchange(bool state);
+
  private:
 
     bool      m_modified;
     
+#if GTK_MINOR_VERSION < 12
+    Tooltips *m_tooltips;
+#endif
     MenuBar  *m_menubar;
     Menu     *m_menu_file;
     Menu     *m_menu_view;
@@ -69,6 +74,8 @@ class mainwnd : public Gtk::Window
 
     Gdk::Cursor   m_main_cursor;
     
+    Button      *m_button_learn;
+
     Button      *m_button_stop;
     Button      *m_button_play;
     Button      *m_button_perfedit;
@@ -97,6 +104,7 @@ class mainwnd : public Gtk::Window
 
     void start_playing();
     void stop_playing();
+    void learn_toggle();
     void open_performance_edit( );
     void sequence_key( int a_seq );
     void update_window_title();
diff -rupN seq24-rev48 adding win32/src/options.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/options.cpp
--- seq24-rev48 adding win32/src/options.cpp	2009-05-20 20:30:46.656250000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/options.cpp	2009-05-20 20:30:55.265625000 -0500
@@ -21,12 +21,77 @@
 #include "options.h"
 #include <sstream>
 
+// tooltip helper, for old vs new gtk...
+#if GTK_MINOR_VERSION >= 12
+#   define add_tooltip( obj, text ) obj->set_tooltip_text( text);
+#else
+#   define add_tooltip( obj, text ) tooltips->set_tip( *obj, text );
+#endif
 
 const int c_status = 0;
 const int c_status_inv = 1;
 const int c_d1 = 2;
 const int c_d2 = 3;
 const int c_d3 = 4;
+enum
+{
+    e_keylabelsonsequence = 9999
+};
+
+
+// GTK text edit widget for getting keyboard button values (for binding keys)
+// put cursor in text box, hit a key, something like  'a' (42)  appears...
+// each keypress replaces the previous text.
+// also supports keyevent and keygroup maps in the perform class
+class KeyBindEntry : public Entry
+{
+public:
+    enum type { location, events, groups };
+    KeyBindEntry(   type t,
+                    unsigned int* location_to_write = NULL,
+                    perform* p = NULL,
+                    long s = 0 ) :  Entry(),
+                                    m_key( location_to_write ),
+                                    m_type( t ),
+                                    m_perf( p ),
+                                    m_slot( s )
+    {
+        switch (m_type)
+        {
+            case location: if (m_key) set( *m_key ); break;
+            case events: set( m_perf->lookup_keyevent_key( m_slot ) ); break;
+            case groups: set( m_perf->lookup_keygroup_key( m_slot ) ); break;
+        }
+    }
+    void set( unsigned int val )
+    {
+        char buf[256] = "";
+        char* special = key2text( val );
+        if (special)
+            sprintf( &buf[strlen(buf)], "%s ", special );
+        else
+            sprintf( &buf[strlen(buf)], "'%c' ", (char)val );
+        sprintf( &buf[strlen(buf)], "(%u)", val );
+        set_text( buf );
+        set_width_chars( strlen(buf)-3 );
+    }
+    virtual bool on_key_press_event(GdkEventKey* event)
+    {
+        Entry::on_key_press_event( event );
+        set( event->keyval );
+        switch (m_type)
+        {
+            case location: if (m_key) *m_key = event->keyval; break;
+            case events: m_perf->set_key_event( event->keyval, m_slot ); break;
+            case groups: m_perf->set_key_group( event->keyval, m_slot ); break;
+        }
+    }
+    unsigned int* m_key;
+    type m_type;
+    perform* m_perf;
+    long m_slot;
+};
+
 
 options::options (Gtk::Window & parent, perform * a_p):
     Gtk::Dialog ("Options", parent, true, true)
@@ -72,16 +137,14 @@ options::options (Gtk::Window & parent, 
         
         
         Gtk::RadioButton * rb_off = manage (new RadioButton ("Off"));
-        tooltips->set_tip (*rb_off,
-                "Midi Clock will be disabled.");
-
+        add_tooltip( rb_off, "Midi Clock will be disabled.");
         
         Gtk::RadioButton * rb_on = manage (new RadioButton ("On (Pos)"));
-        tooltips->set_tip (*rb_on,
+        add_tooltip( rb_on,
                 "Midi Clock will be sent. Midi Song Position and Midi Continue will be sent if starting greater than tick 0 in song mode, otherwise Midi Start is sent.");
 
         Gtk::RadioButton * rb_mod = manage (new RadioButton ("On (Mod)"));
-        tooltips->set_tip (*rb_mod, "Midi Clock will be sent.  Midi Start will be sent and clocking will begin once the song position has reached the modulo of the specified Size. (Used for gear that doesn't respond to Song Position)");
+        add_tooltip( rb_mod, "Midi Clock will be sent.  Midi Start will be sent and clocking will begin once the song position has reached the modulo of the specified Size. (Used for gear that doesn't respond to Song Position)");
 
         Gtk::RadioButton::Group group = rb_off->get_group ();
         rb_on->set_group (group);
@@ -120,6 +183,30 @@ options::options (Gtk::Window & parent, 
     
     clock_mod_adj->signal_value_changed().connect( sigc::bind(mem_fun(*this,&options::clock_mod_callback),clock_mod_adj));
 
+    // add controls for input method
+    {
+        Adjustment *adj = new Adjustment( global_interactionmethod, 0, e_number_of_interactions-1, 1 );
+        SpinButton *spin = new SpinButton( *adj );
+
+        HBox *hbox2 = manage (new HBox ());
+        HBox *hbox3 = manage (new HBox ());
+
+        //m_spinbutton_bpm->set_editable( false );
+        interaction_method_label = new Label( " Input Method " );
+        hbox2->pack_start(*(manage( interaction_method_label )), false, false, 4);
+        hbox2->pack_start(*spin, false, false );
+
+        vbox->pack_start( *hbox2, false, false );
+
+        interaction_method_desc_label = new Label( " ----- " );
+        hbox3->pack_start(*(manage( interaction_method_desc_label )), false, false, 4);
+        vbox->pack_start(*hbox3, false, false );
+
+        adj->signal_value_changed().connect( SigC::bind(mem_fun(*this,&options::interaction_method_callback),adj));
+
+        // force it to refresh.
+        interaction_method_callback( adj );
+    }
 
     // Input Buses
     buses = m_perf->get_master_midi_bus ()->get_num_in_buses ();
@@ -141,7 +228,125 @@ options::options (Gtk::Window & parent, 
 
         vbox->pack_start (*check, false, false);
     }
-    
+
+    // KeyBoard keybinding setup (editor for .seq24rc keybindings.
+    vbox = manage (new VBox ());
+    m_notebook->pages ().push_back (Notebook_Helpers::TabElem (*vbox, "Keyboard"));
+    {
+        Label* label;
+        KeyBindEntry* entry;
+        HBox *hbox;
+
+        #define AddKey(text, integer) \
+            label = manage (new Label( text )); \
+            hbox->pack_start (*label, false, false, 4); \
+            entry = manage (new KeyBindEntry( KeyBindEntry::location, &integer )); \
+            hbox->pack_start (*entry, false, false, 4);
+        #define AddKeyL(text) \
+            label = manage (new Label( text )); \
+            hbox->pack_start (*label, false, false, 4);
+        #define AddKeyM(text, type, slot) \
+            label = manage (new Label( text )); \
+            hbox->pack_start (*label, false, false, 4); \
+            entry = manage (new KeyBindEntry( type, NULL, m_perf, slot )); \
+            hbox->pack_start (*entry, false, false, 4);
+
+        hbox = manage (new HBox ());
+        check = manage (new CheckButton ("show key labels on sequences", 0));
+        check->signal_toggled ().
+            connect (bind (mem_fun (*this, &options::input_callback), (int)e_keylabelsonsequence, check));
+        check->set_active (m_perf->m_show_ui_sequence_key);
+        vbox->pack_start (*check, false, false);
+
+        hbox = manage (new HBox ());
+        AddKey( "stop:", m_perf->m_key_stop );
+        AddKey( "start:", m_perf->m_key_start );
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        AddKey( "bpm dn:", m_perf->m_key_bpm_dn );
+        AddKey( "bpm up:", m_perf->m_key_bpm_up );
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        AddKey( "snpsht1:", m_perf->m_key_snapshot_1 );
+        AddKey( "snpsht2:", m_perf->m_key_snapshot_2 );
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        AddKey( "replace:", m_perf->m_key_replace );
+        AddKey( "queue:", m_perf->m_key_queue );
+        AddKey( "keep queue:", m_perf->m_key_keep_queue );
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        AddKey( "scrnset dn:", m_perf->m_key_screenset_dn );
+        AddKey( "scrnset up:", m_perf->m_key_screenset_up );
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        AddKey( "set plying scrnset:", m_perf->m_key_set_playing_screenset );
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        AddKeyL( "sequence toggle keys >>" );
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        std::map<unsigned int, long>::const_iterator it;
+        int x = 0;
+        for (int ss = 0; ss < 32; ++ss)
+        {
+            int s = (ss%8) * 4 + ss/8; // count this way... 0,4,8,16...
+            unsigned int keycode = m_perf->lookup_keyevent_key( s );
+            char buf[16];
+            sprintf( buf, "%d", s );
+            AddKeyM( buf, KeyBindEntry::events, s );
+            ++x;
+            if (x == 8)
+            {
+                vbox->pack_start (*hbox, false, false);
+                hbox = manage (new HBox ());
+                x = 0;
+            }
+        }
+        vbox->pack_start (*hbox, false, false);
+        
+        hbox = manage (new HBox ());
+        AddKeyL( "mute-group slots >>" );
+        vbox->pack_start (*hbox, false, false);
+        
+        hbox = manage (new HBox ());
+        x = 0;
+        for (int s = 0; s < 32; ++s)
+        {
+            unsigned int keycode = m_perf->lookup_keygroup_key( s );
+            char buf[16];
+            sprintf( buf, "%d", s );
+            AddKeyM( buf, KeyBindEntry::groups, s );
+            ++x;
+            if (x == 8)
+            {
+                vbox->pack_start (*hbox, false, false);
+                hbox = manage (new HBox ());
+                x = 0;
+            }
+        }
+        vbox->pack_start (*hbox, false, false);
+
+        hbox = manage (new HBox ());
+        AddKey( "learn (while pressing a mute-group key):", m_perf->m_key_group_learn );
+        AddKey( "disable:", m_perf->m_key_group_off );
+        AddKey( "enable:", m_perf->m_key_group_on );
+        vbox->pack_start (*hbox, false, false);
+        
+        #undef AddKeyL
+        #undef AddKey
+    }
+
 #ifdef JACK_SUPPORT
     // Jack options menu
     VBox *vbox2 = manage (new VBox ());
@@ -152,7 +357,7 @@ options::options (Gtk::Window & parent, 
 
     check = manage (new CheckButton ("Jack Transport"));
     check->set_active (global_with_jack_transport);
-    tooltips->set_tip (*check, "Enable sync with JACK Transport.");
+    add_tooltip( check, "Enable sync with JACK Transport.");
     check->signal_toggled ().
         connect (bind
                 (mem_fun (*this, &options::transport_callback), e_jack_transport,
@@ -161,7 +366,7 @@ options::options (Gtk::Window & parent, 
 
     check = manage (new CheckButton ("Transport Master"));
     check->set_active (global_with_jack_master);
-    tooltips->set_tip (*check, "Seq24 will attempt to serve as JACK Master.");
+    add_tooltip( check, "Seq24 will attempt to serve as JACK Master.");
     check->signal_toggled ().
         connect (bind
                 (mem_fun (*this, &options::transport_callback), e_jack_master,
@@ -171,7 +376,7 @@ options::options (Gtk::Window & parent, 
 
     check = manage (new CheckButton ("Master Conditional"));
     check->set_active (global_with_jack_master_cond);
-    tooltips->set_tip (*check,
+    add_tooltip( check,
             "Seq24 will fail to be master if there is already a master set.");
     check->signal_toggled ().
         connect (bind
@@ -182,11 +387,11 @@ options::options (Gtk::Window & parent, 
 
 
     Gtk::RadioButton * rb_live = manage (new RadioButton ("Live Mode"));
-    tooltips->set_tip (*rb_live,
+    add_tooltip( rb_live,
             "Playback will be in live mode.  Use this to allow muting and unmuting of loops.");
 
     Gtk::RadioButton * rb_perform = manage (new RadioButton ("Song Mode"));
-    tooltips->set_tip (*rb_perform, "Playback will use the song editors data.");
+    add_tooltip( rb_perform, "Playback will use the song editors data.");
 
     Gtk::RadioButton::Group group = rb_live->get_group ();
     rb_perform->set_group (group);
@@ -209,13 +414,13 @@ options::options (Gtk::Window & parent, 
     vbox2->pack_start (*rb_perform, false, false);
 
     Gtk::Button * button = manage (new Button ("Connect"));
-    tooltips->set_tip (*button, "Connect to Jack.");
+    add_tooltip( button, "Connect to Jack.");
     button->signal_clicked().connect(bind(mem_fun(*this,
                     &options::transport_callback), e_jack_connect, button));
     vbox2->pack_start (*button, false, false);
 
     button = manage (new Button ("Disconnect"));
-    tooltips->set_tip (*button, "Disconnect Jack.");
+    add_tooltip( button, "Disconnect Jack.");
     button->signal_clicked().connect(bind(mem_fun(*this,
                     &options::transport_callback), e_jack_disconnect, button));
     vbox2->pack_start (*button, false, false);
@@ -254,12 +459,35 @@ options::clock_mod_callback( Adjustment 
     midibus::set_clock_mod((int)adj->get_value());
 }
  
+void
+options::interaction_method_callback( Adjustment *adj )
+{
+    global_interactionmethod = (interaction_method_e)(int)adj->get_value();
+    std::string text = " Interaction Method (";
+    text += c_interaction_method_names[(int)adj->get_value()];
+    text += "):  ";
+    interaction_method_label->set_text( text.c_str() );
+    
+    text = "     (";
+    text += c_interaction_method_descs[(int)adj->get_value()];
+    text += ") ";
+    interaction_method_desc_label->set_text( text.c_str() );
+}
+
 
     void
 options::input_callback (int a_bus, Button * i_button)
 {
     CheckButton *a_button = (CheckButton *) i_button;
     bool input = a_button->get_active ();
+    if (9999 == a_bus) {
+        m_perf->m_show_ui_sequence_key = input;
+        for (int i=0; i< c_max_sequence; i++ ){
+            if (m_perf->get_sequence( i ))
+                m_perf->get_sequence( i )->set_dirty();
+        }
+        return;
+    }
     m_perf->get_master_midi_bus ()->set_input (a_bus, input);
 }
 
diff -rupN seq24-rev48 adding win32/src/options.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/options.h
--- seq24-rev48 adding win32/src/options.h	2009-05-20 20:30:46.671875000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/options.h	2009-05-20 20:30:55.281250000 -0500
@@ -55,6 +55,7 @@ using namespace Gtk;
 
 
 
+
 class options : public Gtk::Dialog
 {
 
@@ -63,6 +64,8 @@ class options : public Gtk::Dialog
     perform *m_perf;
 
     Button  *m_button_ok;
+    Label* interaction_method_label;
+    Label* interaction_method_desc_label;
 
 
     Table   *m_table;
@@ -85,8 +88,10 @@ class options : public Gtk::Dialog
     void clock_callback_mod( int a_bus, RadioButton *a_button );
 
     void clock_mod_callback( Adjustment *adj );
+    void interaction_method_callback( Adjustment *adj );
     
     void input_callback( int a_bus, Button *a_button );
+
     void transport_callback( button a_type, Button *a_button );
     
 public:
diff -rupN seq24-rev48 adding win32/src/optionsfile.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/optionsfile.cpp
--- seq24-rev48 adding win32/src/optionsfile.cpp	2009-05-20 20:30:46.703125000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/optionsfile.cpp	2009-05-20 20:30:55.312500000 -0500
@@ -92,6 +92,35 @@ optionsfile::parse( perform *a_perf )
          
         next_data_line( &file );
     }
+    /* group midi control */
+    line_after( &file, "[mute-group]");
+
+    int gtrack = 0;
+    sscanf( m_line, "%d", &gtrack );
+    next_data_line( &file );
+   
+    int mtx[c_seqs_in_set], j=0;
+    for (int i=0; i< c_seqs_in_set; i++) {
+        a_perf->select_group_mute(j);
+        sscanf (m_line, "%d [%d %d %d %d %d %d %d %d] [%d %d %d %d %d %d %d %d] [%d %d %d %d %d %d %d %d] [%d %d %d %d %d %d %d %d]",
+            &j,
+            &mtx[0], &mtx[1], &mtx[2], &mtx[3],
+            &mtx[4], &mtx[5], &mtx[6], &mtx[7],
+           
+            &mtx[8], &mtx[9], &mtx[10], &mtx[11],
+            &mtx[12], &mtx[13], &mtx[14], &mtx[15],
+             
+            &mtx[16], &mtx[17], &mtx[18], &mtx[19],
+            &mtx[20], &mtx[21], &mtx[22], &mtx[23],
+             
+            &mtx[24], &mtx[25], &mtx[26], &mtx[27],
+            &mtx[28], &mtx[29], &mtx[30], &mtx[31]);
+        for (int k=0; k< c_seqs_in_set; k++) {
+            a_perf->set_group_mute_state(k, mtx[k]);
+        }
+        j++;
+        next_data_line( &file );
+    }
 
     line_after( &file, "[midi-clock]" );
     long buses = 0;
@@ -114,28 +143,60 @@ optionsfile::parse( perform *a_perf )
 
     a_perf->key_events.clear();
     
-    
+
     for ( int i=0; i<keys; ++i ){
         
         long key = 0, seq = 0;
         sscanf( m_line, "%ld %ld", &key, &seq );
-        a_perf->key_events[key] = seq;
+        a_perf->set_key_event( key, seq );
+        next_data_line( &file );
+    }
+    line_after( &file, "[keyboard-group]" );
+    long groups = 0;
+    sscanf( m_line, "%ld", &groups );
+    next_data_line( &file );
+
+    a_perf->key_groups.clear();
+   
+   
+    for ( int i=0; i<groups; ++i ){
+       
+        long key = 0, group = 0;
+        sscanf( m_line, "%ld %ld", &key, &group );
+        a_perf->set_key_group( key, group );
         next_data_line( &file );
     }
 
+
     sscanf( m_line, "%u %u", &a_perf->m_key_bpm_up,
                              &a_perf->m_key_bpm_dn );
     next_data_line( &file );
-
-    sscanf( m_line, "%u %u", &a_perf->m_key_screenset_up,
-                             &a_perf->m_key_screenset_dn );
+    sscanf( m_line, "%u %u %u", &a_perf->m_key_screenset_up,
+                             &a_perf->m_key_screenset_dn,
+                             &a_perf->m_key_set_playing_screenset);
+   
+
+     next_data_line( &file );
+
+    sscanf( m_line, "%u %u %u", &a_perf->m_key_group_on,
+                              &a_perf->m_key_group_off,
+                             &a_perf->m_key_group_learn);
+   
     next_data_line( &file );
 
-    sscanf( m_line, "%u %u %u %u",
+    sscanf( m_line, "%u %u %u %u %u",
             &a_perf->m_key_replace,
             &a_perf->m_key_queue,
             &a_perf->m_key_snapshot_1,
-            &a_perf->m_key_snapshot_2 );
+            &a_perf->m_key_snapshot_2,
+            &a_perf->m_key_keep_queue);
+
+    next_data_line( &file );
+    sscanf( m_line, "%ld", &a_perf->m_show_ui_sequence_key );
+    next_data_line( &file );
+    sscanf( m_line, "%ld", &a_perf->m_key_start );
+    next_data_line( &file );
+    sscanf( m_line, "%ld", &a_perf->m_key_stop );
 
     line_after( &file, "[jack-transport]" );
     long flag = 0;
@@ -187,7 +248,12 @@ optionsfile::parse( perform *a_perf )
     if (m_line[0] == '/')
         last_used_dir.assign(m_line);
 
-
+    /* interaction method  */
+    long method = 0;
+    line_after( &file, "[interaction-method]" );
+    sscanf( m_line, "%ld", &method );
+    global_interactionmethod = (interaction_method_e)method;
+    
     file.close();
 
     return true;
@@ -221,13 +287,20 @@ optionsfile::write( perform *a_perf  )
 
         switch( i ){
 
-            case c_midi_control_bpm_up       :  file << "# bpm up\n"; break;
+            /* 32 mute for channel
+               32 group mute */
+            case c_seqs_in_set               :  file << "# mute in group\n"; break;
+           case c_midi_control_bpm_up       :  file << "# bpm up\n"; break;
             case c_midi_control_bpm_dn       :  file << "# bpm down\n"; break;
             case c_midi_control_ss_up        :  file << "# screen set up\n"; break;
             case c_midi_control_ss_dn        :  file << "# screen set down\n"; break;
             case c_midi_control_mod_replace  :  file << "# mod replace\n"; break;
             case c_midi_control_mod_snapshot :  file << "# mod snapshot\n"; break;
             case c_midi_control_mod_queue    :  file << "# mod queue\n"; break;
+            case c_midi_control_mod_gmute    :  file << "# mod gmute\n"; break;
+            case c_midi_control_mod_glearn   :  file << "# mod glearn\n"; break;
+            case c_midi_control_play_ss      :  file << "# screen set play\n"; break;
+           
 
             default: break;
         }
@@ -260,6 +333,33 @@ optionsfile::write( perform *a_perf  )
 
         file << string(outs) << "\n";
     }
+   
+    /* group midi control */
+    file << "\n\n\n[mute-group]\n";
+   
+    int mtx[c_seqs_in_set];
+    file <<  c_gmute_tracks << "\n";
+    for (int j=0; j < c_seqs_in_set; j++ ){
+        a_perf->select_group_mute(j);
+        for (int i=0; i < c_seqs_in_set; i++) {
+            mtx[i] = a_perf->get_group_mute_state(i);
+        }
+        sprintf (outs, "%d [%1d %1d %1d %1d %1d %1d %1d %1d] [%1d %1d %1d %1d %1d %1d %1d %1d] [%1d %1d %1d %1d %1d %1d %1d %1d] [%1d %1d %1d %1d %1d %1d %1d %1d]",
+            j,
+            mtx[0], mtx[1], mtx[2], mtx[3],
+            mtx[4], mtx[5], mtx[6], mtx[7],
+           
+            mtx[8], mtx[9], mtx[10], mtx[11],
+            mtx[12], mtx[13], mtx[14], mtx[15],
+             
+            mtx[16], mtx[17], mtx[18], mtx[19],
+            mtx[20], mtx[21], mtx[22], mtx[23],
+             
+            mtx[24], mtx[25], mtx[26], mtx[27],
+            mtx[28], mtx[29], mtx[30], mtx[31]);
+       
+        file << string(outs) << "\n";
+    }
 
  
 
@@ -305,35 +405,84 @@ optionsfile::write( perform *a_perf  )
     file << "# not connect to other clients\n";
     file << global_manual_alsa_ports << "\n";
     
+    /* interaction-method */
+    int x = 0;
+    file << "\n\n\n[interaction-method]\n";
+    while (c_interaction_method_names[x] && c_interaction_method_descs[x])
+    {
+        file << "# " << x << " - '" << c_interaction_method_names[x]
+                     << "' (" << c_interaction_method_descs[x] << ")\n";
+        ++x;
+    }
+    file << global_interactionmethod << "\n";
+
  
 
     file << "\n\n\n[keyboard-control]\n";
     file << "# Key #, Sequence # \n";
-    file << c_seqs_in_set << "\n";
+    file << (a_perf->key_events.size() < c_seqs_in_set ?
+             a_perf->key_events.size() : c_seqs_in_set) << "\n";
 
-    for( std::map<long,long>::iterator i = a_perf->key_events.begin();
+    for( std::map<unsigned int,long>::const_iterator i = a_perf->key_events.begin();
          i != a_perf->key_events.end(); ++i ){
         
-        sprintf( outs, "%ld  %ld", i->first, i->second );
+        sprintf( outs, "%ld  %ld        # %s", i->first, i->second, key2text( i->first ) );
+        file << string(outs) << "\n";
+    }
+    file << "\n\n\n[keyboard-group]\n";
+    file << "# Key #, group # \n";
+    file << (a_perf->key_groups.size() < c_seqs_in_set ?
+             a_perf->key_groups.size() : c_seqs_in_set) << "\n";
+
+    for( std::map<unsigned int,long>::const_iterator i = a_perf->key_groups.begin();
+         i != a_perf->key_groups.end(); ++i ){
+       
+        sprintf( outs, "%ld  %ld        # %s", i->first, i->second, key2text(i->first) );
         file << string(outs) << "\n";
     }
 
     file << "# bpm up, down\n"
-         << a_perf->m_key_bpm_up
-         << " "
-         << a_perf->m_key_bpm_dn << "\n";
-
-    file << "# screen set up, down\n"
-         << a_perf->m_key_screenset_up
-         << " "
-         << a_perf->m_key_screenset_dn
-         << "\n";
-
-    file << "# replace, queue, snapshot_1, snapshot 2\n"
+         << a_perf->m_key_bpm_up << " "
+         << a_perf->m_key_bpm_dn << "        # "
+         << key2text( a_perf->m_key_bpm_up ) << " "
+         << key2text( a_perf->m_key_bpm_dn ) << "\n";
+
+    file << "# screen set up, down, play\n"
+         << a_perf->m_key_screenset_up << " "
+         << a_perf->m_key_screenset_dn << " "
+         << a_perf->m_key_set_playing_screenset << "        # "
+         << key2text( a_perf->m_key_screenset_up ) << " "
+         << key2text( a_perf->m_key_screenset_dn ) << " "
+         << key2text( a_perf->m_key_set_playing_screenset ) << "\n";
+
+    file << "# group on, off, learn\n"
+         << a_perf->m_key_group_on << " "
+         << a_perf->m_key_group_off << " "
+         << a_perf->m_key_group_learn << "        # "
+         << key2text( a_perf->m_key_group_on ) << " "
+         << key2text( a_perf->m_key_group_off ) << " "
+         << key2text( a_perf->m_key_group_learn ) << "\n";
+   
+    file << "# replace, queue, snapshot_1, snapshot 2, keep queue\n" 
          << a_perf->m_key_replace << " "
          << a_perf->m_key_queue << " "
          << a_perf->m_key_snapshot_1 << " "
-         << a_perf->m_key_snapshot_2 << "\n";
+         << a_perf->m_key_snapshot_2 << " "
+         << a_perf->m_key_keep_queue << "        # "
+         << key2text( a_perf->m_key_replace ) << " "
+         << key2text( a_perf->m_key_queue ) << " "
+         << key2text( a_perf->m_key_snapshot_1 ) << " "
+         << key2text( a_perf->m_key_snapshot_2 ) << " "
+         << key2text( a_perf->m_key_keep_queue ) << "\n";
+
+    file << a_perf->m_show_ui_sequence_key
+         << "        # show_ui_sequence_key (1=true/0=false)\n";
+    file << a_perf->m_key_start << "        # "
+         << key2text( a_perf->m_key_start )
+         << " start sequencer\n";
+    file << a_perf->m_key_stop << "        # "
+         << key2text( a_perf->m_key_stop )
+         << " stop sequencer\n";
 
     file << "\n\n\n[jack-transport]\n\n"
 
diff -rupN seq24-rev48 adding win32/src/perfedit.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perfedit.cpp
--- seq24-rev48 adding win32/src/perfedit.cpp	2009-05-20 20:30:46.750000000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perfedit.cpp	2009-05-20 20:30:55.375000000 -0500
@@ -34,6 +34,13 @@
 
 using namespace sigc;
 
+// tooltip helper, for old vs new gtk...
+#if GTK_MINOR_VERSION >= 12
+#   define add_tooltip( obj, text ) obj->set_tooltip_text( text);
+#else
+#   define add_tooltip( obj, text ) m_tooltips->set_tip( *obj, text );
+#endif
+
 perfedit::perfedit( perform *a_perf ) 
 {
     using namespace Menu_Helpers;
@@ -79,7 +86,7 @@ perfedit::perfedit( perform *a_perf ) 
     m_button_grow = manage( new Button());
     m_button_grow->add( *manage( new Arrow( Gtk::ARROW_RIGHT, Gtk::SHADOW_OUT )));
     m_button_grow->signal_clicked().connect( mem_fun( *this, &perfedit::grow)); 
-    m_tooltips->set_tip( *m_button_grow, "Increase size of Grid." );
+    add_tooltip( m_button_grow, "Increase size of Grid." );
 
 
     /* fill table */
@@ -113,7 +120,7 @@ perfedit::perfedit( perform *a_perf ) 
     m_button_snap = manage( new Button());
     m_button_snap->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( snap_xpm ))));
     m_button_snap->signal_clicked().connect(  bind<Menu *>( mem_fun( *this, &perfedit::popup_menu), m_menu_snap  ));
-    m_tooltips->set_tip( *m_button_snap, "Grid snap. (Fraction of Measure Length)" );
+    add_tooltip( m_button_snap, "Grid snap. (Fraction of Measure Length)" );
     m_entry_snap = manage( new Entry());
     m_entry_snap->set_size_request( 40, -1 );
     m_entry_snap->set_editable( false );
@@ -146,7 +153,7 @@ perfedit::perfedit( perform *a_perf ) 
     m_button_bpm = manage( new Button());
     m_button_bpm->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( down_xpm  ))));
     m_button_bpm->signal_clicked().connect(  bind<Menu *>( mem_fun( *this, &perfedit::popup_menu), m_menu_bpm  ));
-    m_tooltips->set_tip( *m_button_bpm, "Time Signature. Beats per Measure" );
+    add_tooltip( m_button_bpm, "Time Signature. Beats per Measure" );
     m_entry_bpm = manage( new Entry());
     m_entry_bpm->set_width_chars(2);
     m_entry_bpm->set_editable( false );
@@ -156,7 +163,7 @@ perfedit::perfedit( perform *a_perf ) 
     m_button_bw = manage( new Button());
     m_button_bw->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( down_xpm  ))));
     m_button_bw->signal_clicked().connect(  bind<Menu *>( mem_fun( *this, &perfedit::popup_menu), m_menu_bw  ));
-    m_tooltips->set_tip( *m_button_bw, "Time Signature.  Length of Beat" );
+    add_tooltip( m_button_bw, "Time Signature.  Length of Beat" );
     m_entry_bw = manage( new Entry());
     m_entry_bw->set_width_chars(2);
     m_entry_bw->set_editable( false );
@@ -165,43 +172,43 @@ perfedit::perfedit( perform *a_perf ) 
     m_button_undo = manage( new Button());
     m_button_undo->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( undo_xpm  ))));
     m_button_undo->signal_clicked().connect(  mem_fun( *this, &perfedit::undo));
-    m_tooltips->set_tip( *m_button_undo, "Undo." );
+    add_tooltip( m_button_undo, "Undo." );
  
 
     /* expand */
     m_button_expand = manage( new Button());
     m_button_expand->add(*manage( new Image(Gdk::Pixbuf::create_from_xpm_data( expand_xpm ))));
     m_button_expand->signal_clicked().connect(  mem_fun( *this, &perfedit::expand));
-    m_tooltips->set_tip( *m_button_expand, "Expand between L and R markers." );
+    add_tooltip( m_button_expand, "Expand between L and R markers." );
 
     /* collapse */
     m_button_collapse = manage( new Button());
     m_button_collapse->add(*manage( new Image(Gdk::Pixbuf::create_from_xpm_data( collapse_xpm ))));
     m_button_collapse->signal_clicked().connect(  mem_fun( *this, &perfedit::collapse));
-    m_tooltips->set_tip( *m_button_collapse, "Collapse between L and R markers." );
+    add_tooltip( m_button_collapse, "Collapse between L and R markers." );
 
     /* copy */
     m_button_copy = manage( new Button());
     m_button_copy->add(*manage( new Image(Gdk::Pixbuf::create_from_xpm_data( copy_xpm ))));
     m_button_copy->signal_clicked().connect(  mem_fun( *this, &perfedit::copy ));
-    m_tooltips->set_tip( *m_button_copy, "Expand and copy between L and R markers." );
+    add_tooltip( m_button_copy, "Expand and copy between L and R markers." );
 
 
     m_button_loop = manage( new ToggleButton() );
     m_button_loop->add(*manage( new Image(Gdk::Pixbuf::create_from_xpm_data( loop_xpm ))));
     m_button_loop->signal_toggled().connect(  mem_fun( *this, &perfedit::set_looped ));
-    m_tooltips->set_tip( *m_button_loop, "Play looped between L and R." );
+    add_tooltip( m_button_loop, "Play looped between L and R." );
 
 
     m_button_stop = manage( new Button() );
     m_button_stop->add(*manage( new Image(Gdk::Pixbuf::create_from_xpm_data( stop_xpm ))));
     m_button_stop->signal_clicked().connect( mem_fun( *this, &perfedit::stop_playing));
-    m_tooltips->set_tip( *m_button_stop, "Stop playing." );
+    add_tooltip( m_button_stop, "Stop playing." );
 
     m_button_play = manage( new Button() );
     m_button_play->add(*manage( new Image(Gdk::Pixbuf::create_from_xpm_data( play2_xpm ))));
     m_button_play->signal_clicked().connect(  mem_fun( *this, &perfedit::start_playing));
-    m_tooltips->set_tip( *m_button_play, "Begin playing at L marker." );
+    add_tooltip( m_button_play, "Begin playing at L marker." );
 
 
     m_hlbox->pack_end( *m_button_copy , false, false ); 
@@ -253,15 +260,17 @@ perfedit::on_key_press_event(GdkEventKey
     if ( a_ev->type == GDK_KEY_PRESS ){
 
         if ( global_print_keys ){
-            printf( "key_press[%d]\n", a_ev->keyval );
+            printf( "key_press[%d] == %s\n", a_ev->keyval, key2text( a_ev->keyval ) );
         }
-        if ( a_ev->keyval == m_mainperf->m_key_start )
+        // the start/end key may be the same key (i.e. SPACE)
+        // allow toggling when the same key is mapped to both triggers (i.e. SPACEBAR)
+        bool dont_toggle = m_mainperf->m_key_start != m_mainperf->m_key_stop;
+        if ( a_ev->keyval == m_mainperf->m_key_start && (dont_toggle || !m_mainperf->is_running()) )
         {
             start_playing();
             return true;
         }
-
-        if ( a_ev->keyval == m_mainperf->m_key_stop )
+        else if ( a_ev->keyval == m_mainperf->m_key_stop && (dont_toggle || m_mainperf->is_running()) )
         {
             stop_playing();
             return true;
diff -rupN seq24-rev48 adding win32/src/perform.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perform.cpp
--- seq24-rev48 adding win32/src/perform.cpp	2009-05-20 20:30:46.937500000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perform.cpp	2009-05-20 20:30:55.578125000 -0500
@@ -40,12 +40,17 @@ perform::perform()
 		
 		
     }
-
+    m_mute_group_selected = 0;
+    m_mode_group = true;
     m_running = false;
     m_looping = false;
     m_inputing = true;
     m_outputing = true;
     m_tick = 0;
+    m_midiclockrunning = false;
+    m_usemidiclock = false;
+    m_midiclocktick = 0;
+    m_midiclockpos = -1;
 
     thread_trigger_width_ms = c_thread_trigger_width_ms;
 
@@ -62,39 +67,72 @@ perform::perform()
 		m_midi_cc_off[i] = zero;
     }
 
-    key_events[ GDK_1 ] = 0; 
-    key_events[ GDK_q ] = 1; 
-    key_events[ GDK_a ] = 2;
-    key_events[ GDK_z ] = 3; 
-    key_events[ GDK_2 ] = 4; 
-    key_events[ GDK_w ] = 5;
-    key_events[ GDK_s ] = 6; 
-    key_events[ GDK_x ] = 7; 
-    key_events[ GDK_3 ] = 8;
-    key_events[ GDK_e ] = 9; 
-    key_events[ GDK_d ] = 10; 
-    key_events[ GDK_c ] = 11;
-    key_events[ GDK_4 ] = 12; 
-    key_events[ GDK_r ] = 13; 
-    key_events[ GDK_f ] = 14;
-    key_events[ GDK_v ] = 15; 
-    key_events[ GDK_5 ] = 16; 
-    key_events[ GDK_t ] = 17;
-    key_events[ GDK_g ] = 18; 
-    key_events[ GDK_b ] = 19; 
-    key_events[ GDK_6 ] = 20;
-    key_events[ GDK_y ] = 21; 
-    key_events[ GDK_h ] = 22; 
-    key_events[ GDK_n ] = 23;
-    key_events[ GDK_7 ] = 24; 
-    key_events[ GDK_u ] = 25; 
-    key_events[ GDK_j ] = 26;
-    key_events[ GDK_m ] = 27; 
-    key_events[ GDK_8 ] = 28; 
-    key_events[ GDK_i ] = 29;
-    key_events[ GDK_k ] = 30; 
-    key_events[ GDK_comma ] = 31;
-
+    m_show_ui_sequence_key = true;
+    
+    set_key_event( GDK_1, 0 );
+    set_key_event( GDK_q, 1 );
+    set_key_event( GDK_a, 2 );
+    set_key_event( GDK_z, 3 );
+    set_key_event( GDK_2, 4 );
+    set_key_event( GDK_w, 5 );
+    set_key_event( GDK_s, 6 );
+    set_key_event( GDK_x, 7 );
+    set_key_event( GDK_3, 8 );
+    set_key_event( GDK_e, 9 );
+    set_key_event( GDK_d, 10 );
+    set_key_event( GDK_c, 11 );
+    set_key_event( GDK_4, 12 );
+    set_key_event( GDK_r, 13 );
+    set_key_event( GDK_f, 14 );
+    set_key_event( GDK_v, 15 );
+    set_key_event( GDK_5, 16 );
+    set_key_event( GDK_t, 17 );
+    set_key_event( GDK_g, 18 );
+    set_key_event( GDK_b, 19 );
+    set_key_event( GDK_6, 20 );
+    set_key_event( GDK_y, 21 );
+    set_key_event( GDK_h, 22 );
+    set_key_event( GDK_n, 23 );
+    set_key_event( GDK_7, 24 );
+    set_key_event( GDK_u, 25 );
+    set_key_event( GDK_j, 26 );
+    set_key_event( GDK_m, 27 );
+    set_key_event( GDK_8, 28 );
+    set_key_event( GDK_i, 29 );
+    set_key_event( GDK_k, 30 );
+    set_key_event( GDK_comma, 31 );
+    set_key_group( 33, 0 );
+    set_key_group( 34, 1 );
+    set_key_group( 163, 2 );
+    set_key_group( 36, 3 );
+    set_key_group( 37, 4 );
+    set_key_group( 38, 5 );
+    set_key_group( 47, 6 );
+    set_key_group( 40, 7 );
+    set_key_group( 81, 8 );
+    set_key_group( 87, 9 );
+    set_key_group( 69, 10 );
+    set_key_group( 82, 11 );
+    set_key_group( 84, 12 );
+    set_key_group( 89, 13 );
+    set_key_group( 85, 14 );
+    set_key_group( 73, 15 );
+    set_key_group( 65, 16 );
+    set_key_group( 83, 17 );
+    set_key_group( 68, 18 );
+    set_key_group( 70, 19 );
+    set_key_group( 71, 20 );
+    set_key_group( 72, 21 );
+    set_key_group( 74, 22 );
+    set_key_group( 75, 23 );
+    set_key_group( 90, 24 );
+    set_key_group( 88, 25 );
+    set_key_group( 67, 26 );
+    set_key_group( 86, 27 );
+    set_key_group( 66, 28 );
+    set_key_group( 78, 29 );
+    set_key_group( 77, 30 );
+    set_key_group( 59, 31 );
     
     m_key_bpm_up = GDK_apostrophe;
     m_key_bpm_dn = GDK_semicolon;
@@ -103,9 +141,14 @@ perform::perform()
     m_key_queue = GDK_Control_R;
     m_key_snapshot_1 = GDK_Alt_L;
     m_key_snapshot_2 = GDK_Alt_R;
+    m_key_keep_queue = 92;
     
     m_key_screenset_up = GDK_bracketright;
     m_key_screenset_dn = GDK_bracketleft;
+    m_key_set_playing_screenset = 65360;
+    m_key_group_on = 236;
+    m_key_group_off = 39;
+    m_key_group_learn = GDK_Insert;
 
     m_key_start  = GDK_space;
     m_key_stop   = GDK_Escape;
@@ -170,8 +213,7 @@ perform::init_jack( void )
                 m_jack_master = false;
              
             }
-            
-            if (jack_activate(m_jack_client)) {
+             if (jack_activate(m_jack_client)) {
                 printf("Cannot register as JACK client\n");
                 m_jack_running = false;
                 break;
@@ -240,8 +282,123 @@ perform::clear_all( void )
     }
 
 }
+void
+perform::set_mode_group_mute ()
+{
+    m_mode_group = true;
+    return;
+}
 
+void
+perform::unset_mode_group_mute ()
+{
+    m_mode_group = false;
+    return;
+}
+
+void
+perform::set_group_mute_state (int a_g_track, bool a_mute_state)
+{
+    if (a_g_track < 0)
+        a_g_track = 0;
+    if (a_g_track > c_seqs_in_set)
+        a_g_track = c_seqs_in_set -1;
+    m_mute_group[a_g_track + m_mute_group_selected * c_seqs_in_set] = a_mute_state;
+    return;
+}
 
+bool
+perform::get_group_mute_state (int a_g_track)
+{
+    if (a_g_track < 0)
+        a_g_track = 0;
+    if (a_g_track > c_seqs_in_set)
+        a_g_track = c_seqs_in_set -1;
+    return m_mute_group[a_g_track + m_mute_group_selected * c_seqs_in_set];
+}
+
+void
+perform::select_group_mute (int a_g_mute)
+{
+    int j = (a_g_mute * c_seqs_in_set);
+    int k = m_playing_screen * c_seqs_in_set;
+    if (a_g_mute < 0)
+        a_g_mute = 0;
+    if (a_g_mute > c_seqs_in_set)
+        a_g_mute = c_seqs_in_set -1;
+    if (m_mode_group_learn)
+        for (int i = 0; i < c_seqs_in_set; i++) {
+            if (is_active(i + k)) {
+                assert(m_seqs[i + k]);
+                m_mute_group[i + j] = m_seqs[i + k]->get_playing();
+            }
+        }
+    m_mute_group_selected = a_g_mute;
+    return;
+}
+void perform::set_mode_group_learn (void)
+{
+    set_mode_group_mute();
+    m_mode_group_learn = true;
+    for (int x = 0; x < m_notify.size(); ++x)
+        m_notify[x]->on_grouplearnchange( true );
+    return;
+}
+
+void perform::unset_mode_group_learn (void)
+{
+    for (int x = 0; x < m_notify.size(); ++x)
+        m_notify[x]->on_grouplearnchange( false );
+    m_mode_group_learn = false;
+    return;
+}
+
+void
+perform::select_mute_group ( int a_group )
+{
+    int j = (a_group * c_seqs_in_set);
+    int k = m_playing_screen * c_seqs_in_set;
+    if (a_group < 0)
+        a_group = 0;
+    if (a_group > c_seqs_in_set)
+        a_group = c_seqs_in_set -1;
+    m_mute_group_selected = a_group;
+    for (int i = 0; i < c_seqs_in_set; i++) {
+        if ((m_mode_group_learn) && (is_active(i + k))) {
+            assert(m_seqs[i + k]);
+            m_mute_group[i + j] = m_seqs[i + k]->get_playing();
+        }
+        m_tracks_mute_state[i] = m_mute_group[i + m_mute_group_selected * c_seqs_in_set];
+    }
+    return;
+}
+
+void
+perform::mute_group_tracks (void)
+{
+    if (m_mode_group) {
+        for (int i=0; i< c_seqs_in_set; i++) {
+            for (int j=0; j < c_seqs_in_set; j++) {
+                if ( is_active(i * c_seqs_in_set + j) ) {
+                    if ((i == m_playing_screen) && (m_tracks_mute_state[j])) {
+                            sequence_playing_on (i * c_seqs_in_set + j);
+                    } else {
+                        sequence_playing_off (i * c_seqs_in_set + j);
+                    }
+                }
+            }
+               
+        }
+    }
+}
+
+void
+perform::select_and_mute_group (int a_g_group)
+{
+    select_mute_group(a_g_group);
+    mute_group_tracks();
+    return;
+}
 
 void
 perform::mute_all_tracks( void )
@@ -624,6 +781,26 @@ perform::get_screenset( void )
 {
     return m_screen_set;
 }
+ void
+ perform::set_playing_screenset (void)
+ {
+    for (int j, i = 0; i < c_seqs_in_set; i++) {
+        j = i + m_playing_screen * c_seqs_in_set;
+        if ( is_active(j) ){
+            assert( m_seqs[j] );
+            m_tracks_mute_state[i] = m_seqs[j]->get_playing();
+        }
+    }
+    m_playing_screen = m_screen_set;
+    mute_group_tracks();
+ }
+
+ int
+ perform::get_playing_screenset (void)
+ {
+    return m_playing_screen;
+ }
+
 
 void 
 perform::set_offset( int a_offset ) 
@@ -893,6 +1070,8 @@ perform::inner_stop( )
     set_running( false );
     //off_sequences();
     reset_sequences(  );
+    
+    m_usemidiclock = false;
 }
 
 
@@ -912,6 +1091,20 @@ perform::off_sequences( void )
 
 
 
+void 
+perform::all_notes_off( void )
+{
+    for (int i=0; i< c_max_sequence; i++ ){
+		
+		if ( is_active(i) == true ){
+			assert( m_seqs[i] );
+
+			m_seqs[i]->off_playing_notes( );
+		} 
+    }
+    /* flush the bus */
+    m_master_bus.flush();
+}
 
 void 
 perform::reset_sequences( void )
@@ -921,7 +1114,7 @@ perform::reset_sequences( void )
 		if ( is_active(i) == true ){
 			assert( m_seqs[i] );
 
-                        bool state = m_seqs[i]->get_playing();
+			bool state = m_seqs[i]->get_playing();
                         
 			m_seqs[i]->off_playing_notes( );
 			m_seqs[i]->set_playing( false );
@@ -1249,7 +1442,21 @@ perform::output_func(void)
             /* get delta ticks, delta_ticks_f is in 1000th of a tick */
             double delta_tick   =  (double) (bpm * ppqn * (delta_us/60000000.0f) ); 
 
-            //printf ( "delta_tick[%ld.%03ld]\n", delta_tick, delta_tick_f  );
+            if ( m_usemidiclock)
+            {
+               delta_tick = m_midiclocktick;
+               m_midiclocktick = 0;
+            }
+            if ( 0 <= m_midiclockpos)
+            {
+                delta_tick = 0;
+                clock_tick     = m_midiclockpos;
+                current_tick   = m_midiclockpos;
+                total_tick     = m_midiclockpos;
+                m_midiclockpos = -1;
+                //init_clock = true;
+            }
+
             //printf ( "    delta_tick[%lf]\n", delta_tick  );
 #ifdef JACK_SUPPORT
 
@@ -1716,9 +1923,40 @@ perform::handle_midi_control( int a_cont
                 set_sequence_control_status( c_status_queue );
             else
                 unset_sequence_control_status( c_status_queue );
+        //andy cases
+        case c_midi_control_mod_gmute:
+
+            printf ( "gmute\n" );
+           
+            if (a_state)
+                set_mode_group_mute();
+            else
+                unset_mode_group_mute();
             break;
-            
-        default:
+           
+        case c_midi_control_mod_glearn:
+
+            //printf ( "glearn\n" );
+
+            if (a_state)
+                set_mode_group_learn();
+            else
+                unset_mode_group_learn();
+            break;
+           
+        case c_midi_control_play_ss:
+
+            //printf ( "play_ss\n" );
+
+            set_playing_screenset();
+            break;
+           
+         default:
+            if ((a_control >= c_seqs_in_set) && (a_control < c_midi_track_ctrl)) {
+                //printf ( "group mute\n" );
+           
+                select_and_mute_group(a_control - c_seqs_in_set);
+            }
             break;
             
     }
@@ -1738,6 +1976,45 @@ perform::input_func( void ){
                 
                 if ( m_master_bus.get_midi_event( &ev ) ){
                     
+                    // Obey MidiTimeClock:
+                    if (ev.get_status() == EVENT_MIDI_START)
+                    {
+                         stop();
+                         start( false );
+                         m_midiclockrunning = true;
+                         m_usemidiclock = true;
+                         m_midiclocktick = 0;
+                         m_midiclockpos = 0;
+                    }
+                    // midi continue: start from current pos.
+                    else if (ev.get_status() == EVENT_MIDI_CONTINUE)
+                    {
+                         m_midiclockrunning = true;
+                         start( false );
+                         //m_usemidiclock = true;
+                    }
+                    else if (ev.get_status() == EVENT_MIDI_STOP)
+                    {
+                         // do nothing, just let the system pause
+                         // since we're not getting ticks after the stop, the song wont advance
+                         // when start is recieved, we'll reset the position, or
+                         // when continue is recieved, we wont
+                         m_midiclockrunning = false;
+                         all_notes_off();
+                    }
+                    else if (ev.get_status() == EVENT_MIDI_CLOCK)
+                    {
+                         if (m_midiclockrunning)
+                            m_midiclocktick += 8;
+                    }
+                    // not tested (todo: test it!)
+                    else if (ev.get_status() == EVENT_MIDI_SONG_POS)
+                    {
+                         unsigned char a, b;
+                         ev.get_data( &a, &b );
+                         m_midiclockpos = ((int)a << 7) && (int)b;
+                    }
+                    
                     /* filter system wide messages */
                     if ( ev.get_status() <= EVENT_SYSEX ){  
                         
@@ -1923,8 +2200,21 @@ void
 perform::sequence_playing_on( int a_sequence )
 {
     if ( is_active(a_sequence) == true ){
+        if (m_mode_group && (m_playing_screen == m_screen_set)
+          && (a_sequence >= (m_playing_screen * c_seqs_in_set))
+          && (a_sequence < ((m_playing_screen + 1) * c_seqs_in_set)))
+            m_tracks_mute_state[a_sequence - m_playing_screen * c_seqs_in_set] = true;
 		assert( m_seqs[a_sequence] );
-		m_seqs[a_sequence]->set_playing(true);
+        if (!(m_seqs[a_sequence]->get_playing())) {
+            if (m_control_status & c_status_queue ) {
+                if (!(m_seqs[a_sequence]->get_queued()))
+                    m_seqs[a_sequence]->toggle_queued();
+            } else
+                m_seqs[a_sequence]->set_playing(true);
+        } else {
+            if ((m_seqs[a_sequence]->get_queued()) && (m_control_status & c_status_queue ))
+                m_seqs[a_sequence]->toggle_queued();
+        }
     } 
 }
 
@@ -1933,11 +2223,76 @@ void
 perform::sequence_playing_off( int a_sequence )
 {
     if ( is_active(a_sequence) == true ){
-		assert( m_seqs[a_sequence] );
-		m_seqs[a_sequence]->set_playing(false);
+        if (m_mode_group && (m_playing_screen == m_screen_set)
+          && (a_sequence >= (m_playing_screen * c_seqs_in_set))
+          && (a_sequence < ((m_playing_screen + 1) * c_seqs_in_set)))
+            m_tracks_mute_state[a_sequence - m_playing_screen * c_seqs_in_set] = false;
+        assert( m_seqs[a_sequence] );
+        if (m_seqs[a_sequence]->get_playing()) {
+            if (m_control_status & c_status_queue ) {
+                if (!(m_seqs[a_sequence]->get_queued()))
+                    m_seqs[a_sequence]->toggle_queued();
+            } else
+                m_seqs[a_sequence]->set_playing(false);
+        } else {
+            if ((m_seqs[a_sequence]->get_queued()) && (m_control_status & c_status_queue ))
+                m_seqs[a_sequence]->toggle_queued();
+        }
     } 
 }
 
+void
+perform::set_key_event( unsigned int keycode, long sequence_slot )
+{
+     // unhook previous binding...
+     std::map<unsigned int,long>::iterator it1 = key_events.find( keycode );
+     if (it1 != key_events.end())
+     {
+         std::map<long,unsigned int>::iterator i = key_events_rev.find( it1->second );
+         if (i != key_events_rev.end())
+             key_events_rev.erase( i );
+         key_events.erase( it1 );
+     }
+     std::map<long,unsigned int>::iterator it2 = key_events_rev.find( sequence_slot );
+     if (it2 != key_events_rev.end())
+     {
+         std::map<unsigned int,long>::iterator i = key_events.find( it2->second );
+         if (i != key_events.end())
+             key_events.erase( i );
+         key_events_rev.erase( it2 );
+     }
+     // set
+     key_events[keycode] = sequence_slot;
+     key_events_rev[sequence_slot] = keycode;
+}
+
+void
+perform::set_key_group( unsigned int keycode, long group_slot )
+{
+     // unhook previous binding...
+     std::map<unsigned int,long>::iterator it1 = key_groups.find( keycode );
+     if (it1 != key_groups.end())
+     {
+         std::map<long,unsigned int>::iterator i = key_groups_rev.find( it1->second );
+         if (i != key_groups_rev.end())
+             key_groups_rev.erase( i );
+         key_groups.erase( it1 );
+     }
+     std::map<long,unsigned int>::iterator it2 = key_groups_rev.find( group_slot );
+     if (it2 != key_groups_rev.end())
+     {
+         std::map<unsigned int,long>::iterator i = key_groups.find( it2->second );
+         if (i != key_groups.end())
+             key_groups.erase( i );
+         key_groups_rev.erase( it2 );
+     }
+     // set
+     key_groups[keycode] = group_slot;
+     key_groups_rev[group_slot] = keycode;
+}
+
+
+
 #ifdef JACK_SUPPORT
 void jack_timebase_callback(jack_transport_state_t state, jack_nframes_t nframes, 
 	      jack_position_t *pos, int new_pos, void *arg)
diff -rupN seq24-rev48 adding win32/src/perform.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perform.h
--- seq24-rev48 adding win32/src/perform.h	2009-05-20 20:30:46.968750000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perform.h	2009-05-20 20:30:55.609375000 -0500
@@ -59,18 +59,39 @@ const int c_status_replace  = 0x01;
 const int c_status_snapshot = 0x02;
 const int c_status_queue    = 0x04;
 
-const int c_midi_control_bpm_up       = c_seqs_in_set ;
-const int c_midi_control_bpm_dn       = c_seqs_in_set + 1;
-const int c_midi_control_ss_up        = c_seqs_in_set + 2;
-const int c_midi_control_ss_dn        = c_seqs_in_set + 3;
-const int c_midi_control_mod_replace  = c_seqs_in_set + 4;
-const int c_midi_control_mod_snapshot = c_seqs_in_set + 5;
-const int c_midi_control_mod_queue    = c_seqs_in_set + 6;
-const int c_midi_controls             = c_seqs_in_set + 7;
+	 const int c_midi_track_ctrl = c_seqs_in_set * 2;
+	 const int c_midi_control_bpm_up       = c_midi_track_ctrl ;
+	 const int c_midi_control_bpm_dn       = c_midi_track_ctrl + 1;
+	 const int c_midi_control_ss_up        = c_midi_track_ctrl + 2;
+	 const int c_midi_control_ss_dn        = c_midi_track_ctrl + 3;
+	 const int c_midi_control_mod_replace  = c_midi_track_ctrl + 4;
+	 const int c_midi_control_mod_snapshot = c_midi_track_ctrl + 5;
+	 const int c_midi_control_mod_queue    = c_midi_track_ctrl + 6;
+	 //andy midi_control_mod_mute_group
+	 const int c_midi_control_mod_gmute    = c_midi_track_ctrl + 7;
+	 //andy learn_mute_toggle_mode
+	 const int c_midi_control_mod_glearn   = c_midi_track_ctrl + 8;
+	 //andy play only this screen set
+	 const int c_midi_control_play_ss      = c_midi_track_ctrl + 9;
+	 const int c_midi_controls             = c_midi_track_ctrl + 10;//7
+
+struct performcallback
+{
+    virtual void on_grouplearnchange(bool state) {}
+};
 
 class perform
 {
  private:
+    //andy mute group
+    bool m_mute_group[c_gmute_tracks];
+    bool m_tracks_mute_state[c_seqs_in_set];
+    bool m_mode_group;
+    bool m_mode_group_learn;
+    int m_mute_group_selected;
+    //andy playing screen
+    int m_playing_screen;
+   
 
     /* vector of sequences */
     sequence *m_seqs[c_max_sequence];
@@ -107,9 +128,15 @@ class perform
     long m_starting_tick;
     
     long m_tick;
+    bool m_usemidiclock;
+    bool m_midiclockrunning; // stopped or started
+    int  m_midiclocktick;
+    int  m_midiclockpos;
+    
+    bool m_show_ui_sequence_key;
+
    
     void set_running( bool a_running );
-    bool is_running();
 
     void set_playback_mode( bool a_playback_mode );
 
@@ -125,7 +152,12 @@ class perform
 
     condition_var m_condition_var;
 
-    std::map<long,long> key_events;
+    // do not access these directly, use set/lookup below
+    std::map<unsigned int,long> key_events;
+    std::map<unsigned int,long> key_groups;
+    std::map<long,unsigned int> key_events_rev; // reverse lookup, keep this in sync!!
+    std::map<long,unsigned int> key_groups_rev; // reverse lookup, keep this in sync!!
+
 
 #ifdef JACK_SUPPORT
     
@@ -146,21 +178,34 @@ class perform
     void inner_stop();
 
  public:
+    bool is_running();
+    bool is_learn_mode() const { return m_mode_group_learn; }
+
+    // can register here for events...
+    std::vector<performcallback*> m_notify;
 
     unsigned int m_key_bpm_up;
     unsigned int m_key_bpm_dn;
 
     unsigned int m_key_replace;
     unsigned int m_key_queue;
+    unsigned int m_key_keep_queue;
     unsigned int m_key_snapshot_1;
     unsigned int m_key_snapshot_2;
 
     unsigned int m_key_screenset_up;
     unsigned int m_key_screenset_dn;
+    unsigned int m_key_set_playing_screenset;
+   
+    unsigned int m_key_group_on;
+    unsigned int m_key_group_off;
+    unsigned int m_key_group_learn;
+   
 
-    unsigned int m_key_start; 
+    unsigned int m_key_start;
     unsigned int m_key_stop;
 
+    bool show_ui_sequence_key() const { return m_show_ui_sequence_key; }
 
     perform();
     ~perform();
@@ -211,7 +256,17 @@ class perform
 
     void set_screenset( int a_ss );
     int get_screenset( void );
-    
+    void set_playing_screenset( void );
+    int get_playing_screenset( void );
+    void mute_group_tracks (void);
+    void select_and_mute_group (int a_g_group);
+    void set_mode_group_mute ();
+    void select_group_mute (int a_g_mute);
+    void set_mode_group_learn (void);
+    void unset_mode_group_learn (void);
+    bool is_group_learning( void ) { return m_mode_group_learn; }
+    void select_mute_group ( int a_group );
+    void unset_mode_group_mute ();    
     void start( bool a_state );
     void stop();
 
@@ -220,6 +275,7 @@ class perform
     void position_jack( bool a_state );
 
     void off_sequences( void );
+    void all_notes_off( void );
 
     void set_active(int a_sequence, bool a_active);
     void set_was_active( int a_sequence );
@@ -250,6 +306,8 @@ class perform
     void sequence_playing_toggle( int a_sequence );
     void sequence_playing_on( int a_sequence );
     void sequence_playing_off( int a_sequence );
+    void set_group_mute_state (int a_g_track, bool a_mute_state);
+    bool get_group_mute_state (int a_g_track);
     
     void mute_all_tracks( void );
 
@@ -266,11 +324,24 @@ class perform
     void restore_playing_state( void );
     
     
-    std::map<long,long> *get_key_events( void ){ return &key_events; };
+    const std::map<unsigned int,long> *get_key_events( void ) const { return &key_events; };
+    const std::map<unsigned int,long> *get_key_groups( void ) const { return &key_groups; };
+    
+    void set_key_event( unsigned int keycode, long sequence_slot );
+    void set_key_group( unsigned int keycode, long group_slot );
+
+    // getters of keyboard mapping for sequence and groups,
+    // if not found, returns something "safe" (so use get_key()->count() to see if it's there first)
+    unsigned int lookup_keyevent_key( long seqnum ) { if (key_events_rev.count( seqnum )) return key_events_rev[seqnum]; else return '?';}
+    long lookup_keyevent_seq( unsigned int keycode ) { if (key_events.count( keycode )) return key_events[keycode]; else return 0; }
+    unsigned int lookup_keygroup_key( long groupnum ) { if (key_groups_rev.count( groupnum )) return key_groups_rev[groupnum]; else return '?'; }
+    long lookup_keygroup_group( unsigned int keycode ) { if (key_groups.count( keycode )) return key_groups[keycode]; else return 0; }
+
 
 
     friend class midifile;
     friend class optionsfile;
+    friend class options;
 
 #ifdef JACK_SUPPORT
 
@@ -280,7 +351,6 @@ class perform
     friend void jack_timebase_callback(jack_transport_state_t state, jack_nframes_t nframes, 
                                        jack_position_t *pos, int new_pos, void *arg);
 #endif
-
 };
 
 /* located in perform.C */
diff -rupN seq24-rev48 adding win32/src/perfroll.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perfroll.cpp
--- seq24-rev48 adding win32/src/perfroll.cpp	2009-05-20 20:30:47.031250000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perfroll.cpp	2009-05-20 20:30:55.687500000 -0500
@@ -47,8 +47,6 @@ perfroll::perfroll( perform *a_perf,
     m_vadjust = a_vadjust;
     m_hadjust = a_hadjust;
 
-    m_adding = false;
-    m_adding_pressed = false;
     m_moving = false;
     m_growing = false;
 
@@ -104,23 +102,6 @@ perfroll::change_vert( )
     }
 }
 
-/* popup menu calls this */
-void 
-perfroll::set_adding( bool a_adding )
-{
-    if ( a_adding ){
-
-	get_window()->set_cursor(  Gdk::Cursor( Gdk::PENCIL ));
-	m_adding = true;
-    
-    } else {
-
-	get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
-	m_adding = false;
-    }
-}
-
-
 void 
 perfroll::on_realize()
 {
@@ -620,168 +601,50 @@ perfroll::draw_drawable_row( Glib::RefPt
                           c_names_y );
 }
 
-bool
-perfroll::on_button_press_event(GdkEventButton* a_ev)
-{
-    grab_focus( ); 
-
-
-    if ( m_mainperf->is_active( m_drop_sequence ))
-    {
-        m_mainperf->get_sequence( m_drop_sequence )->unselect_triggers( );
-        draw_background_on( m_pixmap, m_drop_sequence );
-        draw_sequence_on( m_pixmap, m_drop_sequence );
-        draw_drawable_row( m_window, m_pixmap, m_drop_y);
-    }
-
-    m_drop_x = (int) a_ev->x;
-    m_drop_y = (int) a_ev->y;
-
-    convert_xy( m_drop_x, m_drop_y, &m_drop_tick, &m_drop_sequence );
-
-    /*      left mouse button     */
-    if ( a_ev->button == 1 ){
-
-        long tick = m_drop_tick;
-
-        /* add a new note if we didnt select anything */
-        if (  m_adding ){
-
-            m_adding_pressed = true;
-
-            if ( m_mainperf->is_active( m_drop_sequence )){
-
-                long seq_length = m_mainperf->get_sequence( m_drop_sequence )->get_length( );
-
-                bool state = m_mainperf->get_sequence( m_drop_sequence )->get_trigger_state( tick );
-
-                if ( state )
-                {
-                    m_mainperf->push_trigger_undo();
-                    m_mainperf->get_sequence( m_drop_sequence )->del_trigger( tick );
-                }
-                else
-                {
-
-                    // snap to length of sequence
-                    tick = tick - (tick % seq_length);
-                    m_adding_pressed_state = true;
-
-                    m_mainperf->push_trigger_undo();
-                    m_mainperf->get_sequence( m_drop_sequence )->add_trigger( tick, seq_length );
-                    draw_background_on( m_pixmap, m_drop_sequence );
-                    draw_sequence_on( m_pixmap, m_drop_sequence );
-                    draw_drawable_row( m_window, m_pixmap, m_drop_y);
-
-                    //m_drop_tick_last = (m_drop_tick + seq_length - 1);
-                }
-            }
-        }
-        else {
-
-            if ( m_mainperf->is_active( m_drop_sequence )){
-
-                m_mainperf->push_trigger_undo();
-                m_mainperf->get_sequence( m_drop_sequence )->select_trigger( tick );
-
-                long start_tick = m_mainperf->get_sequence( m_drop_sequence )->get_selected_trigger_start_tick();
-                long end_tick = m_mainperf->get_sequence( m_drop_sequence )->get_selected_trigger_end_tick();
-
-                if ( tick >= start_tick &&
-                        tick <= start_tick + (c_perfroll_size_box_click_w * c_perf_scale_x) &&
-                        (m_drop_y % c_names_y) <= c_perfroll_size_box_click_w + 1 )
-                {
-                    m_growing = true;
-                    m_grow_direction = true;
-                    m_drop_tick_trigger_offset = m_drop_tick -
-                        m_mainperf->get_sequence( m_drop_sequence )->
-                        get_selected_trigger_start_tick( );
-                }
-                else
-                    if ( tick >= end_tick - (c_perfroll_size_box_click_w * c_perf_scale_x) &&
-                            tick <= end_tick &&
-                            (m_drop_y % c_names_y) >= c_names_y - c_perfroll_size_box_click_w - 1 )
-                    {
-                        m_growing = true;
-                        m_grow_direction = false;
-                        m_drop_tick_trigger_offset =
-                            m_drop_tick -
-                            m_mainperf->get_sequence( m_drop_sequence )->get_selected_trigger_end_tick( );
-                    }
-                    else 
-                    {
-                        m_moving = true;
-                        m_drop_tick_trigger_offset = m_drop_tick -
-                            m_mainperf->get_sequence( m_drop_sequence )->
-                            get_selected_trigger_start_tick( );
 
-                    }
+void 
+perfroll::start_playing( void )
+{
+    // keep in sync with perfedit's start_playing... wish i could call it directly...
+    m_mainperf->position_jack( true );
+    m_mainperf->start_jack( );
+    m_mainperf->start( true );
+}
 
-                draw_background_on( m_pixmap, m_drop_sequence );
-                draw_sequence_on( m_pixmap, m_drop_sequence );
-                draw_drawable_row( m_window, m_pixmap, m_drop_y);
-            }
-        }        
-    }
+void
+perfroll::stop_playing( void )
+{ 
+    // keep in sync with perfedit's stop_playing... wish i could call it directly...
+    m_mainperf->stop_jack(); 
+    m_mainperf->stop();
+}
 
-    /*     right mouse button      */
-    if ( a_ev->button == 3 ){
-        set_adding( true );
-    }
 
-    /* middle, split */
-    if ( a_ev->button == 2 )
+bool
+perfroll::on_button_press_event(GdkEventButton* a_ev)
+{
+    switch (global_interactionmethod)
     {
-        long tick = m_drop_tick;
-
-        if ( m_mainperf->is_active( m_drop_sequence )){
-
-            bool state = m_mainperf->get_sequence( m_drop_sequence )->get_trigger_state( tick );
-
-            if ( state )
-            {
-                m_mainperf->push_trigger_undo();
-
-                m_mainperf->get_sequence( m_drop_sequence )->split_trigger( tick );
-
-                draw_background_on( m_pixmap, m_drop_sequence );
-                draw_sequence_on( m_pixmap, m_drop_sequence );
-                draw_drawable_row( m_window, m_pixmap, m_drop_y);
-            }
-        }
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_button_press_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_button_press_event(a_ev, *this);
     }
-    return true;
+    return false;
 }
 
 
 bool
 perfroll::on_button_release_event(GdkEventButton* a_ev)
 {
-    
-    if ( a_ev->button == 1 ){
-
-        if ( m_adding ){
-            m_adding_pressed = false;
-        }
-    }
-
-    else if ( a_ev->button == 3 ){
-	m_adding_pressed = false;
- 	set_adding( false );
-    }
-    
-    m_moving = false;
-    m_growing = false;
-    m_adding_pressed = false;
-
-    if ( m_mainperf->is_active( m_drop_sequence  )){
-        
-        draw_background_on( m_pixmap, m_drop_sequence );
-        draw_sequence_on( m_pixmap, m_drop_sequence );
-        draw_drawable_row( m_window, m_pixmap, m_drop_y );
+    switch (global_interactionmethod)
+    {
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_button_release_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_button_release_event(a_ev, *this);
     }
-    
-    return true;
+    return false;
 }
 
 bool
@@ -824,59 +687,14 @@ perfroll::on_scroll_event( GdkEventScrol
 bool
 perfroll::on_motion_notify_event(GdkEventMotion* a_ev)
 {
-
-    long tick;
-    int x = (int) a_ev->x;
-    
-    if (  m_adding && m_adding_pressed ){
-        
-	convert_x( x, &tick );
-
-	if ( m_mainperf->is_active( m_drop_sequence )){
-
-            long seq_length = m_mainperf->get_sequence( m_drop_sequence )->get_length( );
-            tick = tick - (tick % seq_length);
-
-            /*long min_tick = (tick < m_drop_tick) ? tick : m_drop_tick;*/
-            long length = seq_length;
-            
-	    m_mainperf->get_sequence( m_drop_sequence )->grow_trigger( m_drop_tick, tick, length);
-	    draw_background_on( m_pixmap, m_drop_sequence );
-	    draw_sequence_on( m_pixmap, m_drop_sequence );
-            draw_drawable_row( m_window, m_pixmap, m_drop_y);
-	}
-    }
-    else if ( m_moving || m_growing ){
-
-        if ( m_mainperf->is_active( m_drop_sequence)){
-            
-            convert_x( x, &tick );
-            tick -= m_drop_tick_trigger_offset;
-
-            tick = tick - tick % m_snap;
-
-            if ( m_moving )
-            {
-                m_mainperf->get_sequence( m_drop_sequence )->move_selected_triggers_to( tick, true );
-            }
-            if ( m_growing )
-            {
-                if ( m_grow_direction )
-                    m_mainperf->get_sequence( m_drop_sequence )->move_selected_triggers_to( tick, false, 0 );
-                else
-                    m_mainperf->get_sequence( m_drop_sequence )->move_selected_triggers_to( tick-1, false, 1 );
-            }
-
-            
-            draw_background_on( m_pixmap, m_drop_sequence );
-            draw_sequence_on( m_pixmap, m_drop_sequence );
-            draw_drawable_row( m_window, m_pixmap, m_drop_y);
-        }
+    switch (global_interactionmethod)
+    {
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_motion_notify_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_motion_notify_event(a_ev, *this);
     }
-
-    
-
-    return true;
+    return false;
 }
 
 bool 
@@ -1013,3 +831,522 @@ void 
 perfroll::on_size_request(GtkRequisition* a_r )
 {
 }
+
+
+
+
+//////////////////////////
+// interaction methods
+//////////////////////////
+
+
+void FruityPerfInput::updateMousePtr( perfroll& ths )
+{
+    // context sensitive mouse
+    long drop_tick;
+    int drop_sequence;
+    ths.convert_xy( m_current_x, m_current_y, &drop_tick, &drop_sequence );
+    if (ths.m_mainperf->is_active( drop_sequence ))
+    {
+         long start, end;
+         if (ths.m_mainperf->get_sequence(drop_sequence)->intersectTriggers( drop_tick, start, end ))
+	     {
+             if (start <= drop_tick && drop_tick <= start + (c_perfroll_size_box_click_w * c_perf_scale_x) &&
+                 (m_current_y % c_names_y) <= c_perfroll_size_box_click_w + 1)
+             {
+                ths.get_window()->set_cursor( Gdk::Cursor( Gdk::RIGHT_PTR ));
+             }
+             else if (end - (c_perfroll_size_box_click_w * c_perf_scale_x) <= drop_tick && drop_tick <= end &&
+                      (m_current_y % c_names_y) >= c_names_y - c_perfroll_size_box_click_w - 1)
+             {
+                ths.get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
+             }
+             else
+             {
+                ths.get_window()->set_cursor( Gdk::Cursor( Gdk::CENTER_PTR ));
+             }
+         }
+         else
+         {
+             ths.get_window()->set_cursor( Gdk::Cursor( Gdk::PENCIL ));
+         }
+    }
+    else
+    {
+        ths.get_window()->set_cursor( Gdk::Cursor( Gdk::CROSSHAIR ));
+    }
+}
+
+
+bool FruityPerfInput::on_button_press_event(GdkEventButton* a_ev, perfroll& ths)
+{
+    ths.grab_focus( );
+
+    if ( ths.m_mainperf->is_active( ths.m_drop_sequence ))
+    {
+        ths.m_mainperf->get_sequence( ths.m_drop_sequence )->unselect_triggers( );
+        ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+    }
+
+    ths.m_drop_x = (int) a_ev->x;
+    ths.m_drop_y = (int) a_ev->y;
+    m_current_x = (int) a_ev->x;
+    m_current_y = (int) a_ev->y;
+
+
+    ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &ths.m_drop_tick, &ths.m_drop_sequence );
+
+    /*      left mouse button     */
+    if ( a_ev->button == 1 && !(a_ev->state & GDK_CONTROL_MASK)){
+
+        long tick = ths.m_drop_tick;
+
+        /* add a new note if we didnt select anything */
+        //if (  m_adding )
+        {
+
+            m_adding_pressed = true;
+
+            if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+                long seq_length = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_length( );
+
+                bool state = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_trigger_state( tick );
+
+                // resize the event, or move it, depending on where clicked.
+                if ( state )
+                {
+                    //m_adding = false;
+                    m_adding_pressed = false;
+                    ths.m_mainperf->push_trigger_undo();
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )->select_trigger( tick );
+
+                    long start_tick = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_selected_trigger_start_tick();
+                    long end_tick = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_selected_trigger_end_tick();
+
+                    if ( tick >= start_tick &&
+                            tick <= start_tick + (c_perfroll_size_box_click_w * c_perf_scale_x) &&
+                            (ths.m_drop_y % c_names_y) <= c_perfroll_size_box_click_w + 1 )
+                    {
+                        // clicked left side: begin a grow/shrink for the left side
+                        ths.m_growing = true;
+                        ths.m_grow_direction = true;
+                        ths.m_drop_tick_trigger_offset = ths.m_drop_tick -
+                            ths.m_mainperf->get_sequence( ths.m_drop_sequence )->
+                            get_selected_trigger_start_tick( );
+                    }
+                    else
+                        if ( tick >= end_tick - (c_perfroll_size_box_click_w * c_perf_scale_x) &&
+                                tick <= end_tick &&
+                                (ths.m_drop_y % c_names_y) >= c_names_y - c_perfroll_size_box_click_w - 1 )
+                        {
+                            // clicked right side: grow/shrink the right side
+                            ths.m_growing = true;
+                            ths.m_grow_direction = false;
+                            ths.m_drop_tick_trigger_offset =
+                                ths.m_drop_tick -
+                                ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_selected_trigger_end_tick( );
+                        }
+                        else
+                        {
+                             // clicked in the middle - move it
+                            ths.m_moving = true;
+                            ths.m_drop_tick_trigger_offset = ths.m_drop_tick -
+                                ths.m_mainperf->get_sequence( ths.m_drop_sequence )->
+                                get_selected_trigger_start_tick( );
+
+                        }
+
+                    ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+                    ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+                    ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+                }
+
+                // add an event:
+                else
+                {
+
+                    // snap to length of sequence
+                    tick = tick - (tick % seq_length);
+
+                    ths.m_mainperf->push_trigger_undo();
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )->add_trigger( tick, seq_length );
+                    ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+                    ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+                    ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+
+                    //m_drop_tick_last = (m_drop_tick + seq_length - 1);
+                }
+            }
+        }
+    }
+
+    /*     right mouse button      */
+    if ( a_ev->button == 3 ){
+        //set_adding( false );
+
+        long tick = ths.m_drop_tick;
+
+        if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+            long seq_length = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_length( );
+
+            bool state = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_trigger_state( tick );
+
+            if ( state )
+            {
+                ths.m_mainperf->push_trigger_undo();
+                ths.m_mainperf->get_sequence( ths.m_drop_sequence )->del_trigger( tick );
+            }
+        }
+    }
+
+    /* left-ctrl, or middle: split */
+    if ( a_ev->button == 2 ||
+         a_ev->button == 1 && (a_ev->state & GDK_CONTROL_MASK) )
+    {
+        long tick = ths.m_drop_tick;
+
+        if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+            bool state = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_trigger_state( tick );
+
+            if ( state )
+            {
+                ths.m_mainperf->push_trigger_undo();
+
+                ths.m_mainperf->get_sequence( ths.m_drop_sequence )->split_trigger( tick );
+
+                ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+                ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+                ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+            }
+        }
+    }
+    updateMousePtr( ths );
+    return true;
+}
+
+bool FruityPerfInput::on_button_release_event(GdkEventButton* a_ev, perfroll& ths)
+{
+    m_current_x = (int) a_ev->x;
+    m_current_y = (int) a_ev->y;
+
+    if ( a_ev->button == 1 || a_ev->button == 3 )
+    {
+        m_adding_pressed = false;
+    }
+
+    ths.m_moving = false;
+    ths.m_growing = false;
+    m_adding_pressed = false;
+
+    if ( ths.m_mainperf->is_active( ths.m_drop_sequence  )){
+
+        ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y );
+    }
+
+    updateMousePtr( ths );
+    return true;
+}
+
+bool FruityPerfInput::on_motion_notify_event(GdkEventMotion* a_ev, perfroll& ths)
+{
+    long tick;
+    int x = (int) a_ev->x;
+    m_current_x = (int) a_ev->x;
+    m_current_y = (int) a_ev->y;
+    
+    if (  m_adding_pressed ){
+
+    	ths.convert_x( x, &tick );
+
+        if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+            long seq_length = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_length( );
+            tick = tick - (tick % seq_length);
+
+            /*long min_tick = (tick < m_drop_tick) ? tick : m_drop_tick;*/
+            long length = seq_length;
+
+    	    ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                          ->grow_trigger( ths.m_drop_tick, tick, length);
+    	    ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+    	    ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+            ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+    	}
+    }
+    else if ( ths.m_moving || ths.m_growing )
+    {
+        if ( ths.m_mainperf->is_active( ths.m_drop_sequence))
+        {
+            ths.convert_x( x, &tick );
+            tick -= ths.m_drop_tick_trigger_offset;
+
+            tick = tick - tick % ths.m_snap;
+
+            if ( ths.m_moving )
+            {
+                ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                              ->move_selected_triggers_to( tick, true );
+            }
+            if ( ths.m_growing )
+            {
+                if ( ths.m_grow_direction )
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                                  ->move_selected_triggers_to( tick, false, 0 );
+                else
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                                  ->move_selected_triggers_to( tick-1, false, 1 );
+            }
+
+
+            ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+            ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+            ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+        }
+    }
+
+    updateMousePtr( ths );
+    return true;
+}
+
+
+/* popup menu calls this */
+void
+Seq24PerfInput::set_adding( bool a_adding, perfroll& ths )
+{
+    if ( a_adding )
+    {
+	   ths.get_window()->set_cursor(  Gdk::Cursor( Gdk::PENCIL ));
+	   m_adding = true;
+    }
+    else
+    {
+	    ths.get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
+	    m_adding = false;
+    }
+}
+
+bool
+Seq24PerfInput::on_button_press_event(GdkEventButton* a_ev, perfroll& ths)
+{
+    ths.grab_focus( );
+
+
+    if ( ths.m_mainperf->is_active( ths.m_drop_sequence ))
+    {
+        ths.m_mainperf->get_sequence( ths.m_drop_sequence )->unselect_triggers( );
+        ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+    }
+
+    ths.m_drop_x = (int) a_ev->x;
+    ths.m_drop_y = (int) a_ev->y;
+
+    ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &ths.m_drop_tick, &ths.m_drop_sequence );
+
+    /*      left mouse button     */
+    if ( a_ev->button == 1 ){
+
+        long tick = ths.m_drop_tick;
+
+        /* add a new note if we didnt select anything */
+        if (  m_adding ){
+
+            m_adding_pressed = true;
+
+            if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+                long seq_length = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_length( );
+
+                bool state = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_trigger_state( tick );
+
+                if ( state )
+                {
+                    ths.m_mainperf->push_trigger_undo();
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )->del_trigger( tick );
+                }
+                else
+                {
+
+                    // snap to length of sequence
+                    tick = tick - (tick % seq_length);
+                    //m_adding_pressed_state = true;
+
+                    ths.m_mainperf->push_trigger_undo();
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )->add_trigger( tick, seq_length );
+                    ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+                    ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+                    ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+
+                    //m_drop_tick_last = (m_drop_tick + seq_length - 1);
+                }
+            }
+        }
+        else {
+
+            if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+                ths.m_mainperf->push_trigger_undo();
+                ths.m_mainperf->get_sequence( ths.m_drop_sequence )->select_trigger( tick );
+
+                long start_tick = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_selected_trigger_start_tick();
+                long end_tick = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_selected_trigger_end_tick();
+
+                if ( tick >= start_tick &&
+                        tick <= start_tick + (c_perfroll_size_box_click_w * c_perf_scale_x) &&
+                        (ths.m_drop_y % c_names_y) <= c_perfroll_size_box_click_w + 1 )
+                {
+                    ths.m_growing = true;
+                    ths.m_grow_direction = true;
+                    ths.m_drop_tick_trigger_offset = ths.m_drop_tick -
+                        ths.m_mainperf->get_sequence( ths.m_drop_sequence )->
+                                        get_selected_trigger_start_tick( );
+                }
+                else
+                    if ( tick >= end_tick - (c_perfroll_size_box_click_w * c_perf_scale_x) &&
+                            tick <= end_tick &&
+                            (ths.m_drop_y % c_names_y) >= c_names_y - c_perfroll_size_box_click_w - 1 )
+                    {
+                        ths.m_growing = true;
+                        ths.m_grow_direction = false;
+                        ths.m_drop_tick_trigger_offset =
+                            ths.m_drop_tick -
+                            ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_selected_trigger_end_tick( );
+                    }
+                    else 
+                    {
+                        ths.m_moving = true;
+                        ths.m_drop_tick_trigger_offset = ths.m_drop_tick -
+                            ths.m_mainperf->get_sequence( ths.m_drop_sequence )->
+                                            get_selected_trigger_start_tick( );
+
+                    }
+
+                ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+                ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+                ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+            }
+        }        
+    }
+
+    /*     right mouse button      */
+    if ( a_ev->button == 3 ){
+        set_adding( true, ths );
+    }
+
+    /* middle, split */
+    if ( a_ev->button == 2 )
+    {
+        long tick = ths.m_drop_tick;
+
+        if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+            bool state = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_trigger_state( tick );
+
+            if ( state )
+            {
+                ths.m_mainperf->push_trigger_undo();
+
+                ths.m_mainperf->get_sequence( ths.m_drop_sequence )->split_trigger( tick );
+
+                ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+                ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+                ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+            }
+        }
+    }
+    return true;
+}
+
+bool Seq24PerfInput::on_button_release_event(GdkEventButton* a_ev, perfroll& ths)
+{
+    if ( a_ev->button == 1 ){
+
+        if ( m_adding ){
+            m_adding_pressed = false;
+        }
+    }
+
+    if ( a_ev->button == 3 ){
+	m_adding_pressed = false;
+ 	set_adding( false, ths );
+    }
+    
+    ths.m_moving = false;
+    ths.m_growing = false;
+    m_adding_pressed = false;
+
+    if ( ths.m_mainperf->is_active( ths.m_drop_sequence  )){
+        
+        ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+        ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y );
+    }
+    
+    return true;
+}
+
+bool Seq24PerfInput::on_motion_notify_event(GdkEventMotion* a_ev, perfroll& ths)
+{
+    long tick;
+    int x = (int) a_ev->x;
+    
+    if (  m_adding && m_adding_pressed ){
+        
+    	ths.convert_x( x, &tick );
+
+    	if ( ths.m_mainperf->is_active( ths.m_drop_sequence )){
+
+            long seq_length = ths.m_mainperf->get_sequence( ths.m_drop_sequence )->get_length( );
+            tick = tick - (tick % seq_length);
+
+            /*long min_tick = (tick < m_drop_tick) ? tick : m_drop_tick;*/
+            long length = seq_length;
+            
+    	    ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                          ->grow_trigger( ths.m_drop_tick, tick, length);
+    	    ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+    	    ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+            ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+    	}
+    }
+    else if ( ths.m_moving || ths.m_growing ){
+
+        if ( ths.m_mainperf->is_active( ths.m_drop_sequence)){
+            
+            ths.convert_x( x, &tick );
+            tick -= ths.m_drop_tick_trigger_offset;
+
+            tick = tick - tick % ths.m_snap;
+
+            if ( ths.m_moving )
+            {
+                ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                              ->move_selected_triggers_to( tick, true );
+            }
+            if ( ths.m_growing )
+            {
+                if ( ths.m_grow_direction )
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                                  ->move_selected_triggers_to( tick, false, 0 );
+                else
+                    ths.m_mainperf->get_sequence( ths.m_drop_sequence )
+                                  ->move_selected_triggers_to( tick-1, false, 1 );
+            }
+
+            
+            ths.draw_background_on( ths.m_pixmap, ths.m_drop_sequence );
+            ths.draw_sequence_on( ths.m_pixmap, ths.m_drop_sequence );
+            ths.draw_drawable_row( ths.m_window, ths.m_pixmap, ths.m_drop_y);
+        }
+    }
+
+    return true;
+}
diff -rupN seq24-rev48 adding win32/src/perfroll.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perfroll.h
--- seq24-rev48 adding win32/src/perfroll.h	2009-05-20 20:30:47.046875000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/perfroll.h	2009-05-20 20:30:55.703125000 -0500
@@ -45,12 +45,41 @@
 
 using namespace Gtk;
 
+class perfroll;
+struct FruityPerfInput
+{
+    FruityPerfInput() : m_adding_pressed( false ), m_current_x( 0 ),
+                         m_current_y( 0 )
+    {}
+    bool on_button_press_event(GdkEventButton* a_ev, perfroll& ths);
+    bool on_button_release_event(GdkEventButton* a_ev, perfroll& ths);
+    bool on_motion_notify_event(GdkEventMotion* a_ev, perfroll& ths);
+    void updateMousePtr(perfroll& ths);
+    bool m_adding_pressed;
+    long m_current_x, m_current_y;
+};
+struct Seq24PerfInput
+{
+    Seq24PerfInput() : m_adding( false ), m_adding_pressed( false ) {}
+    bool on_button_press_event(GdkEventButton* a_ev, perfroll& ths);
+    bool on_button_release_event(GdkEventButton* a_ev, perfroll& ths);
+    bool on_motion_notify_event(GdkEventMotion* a_ev, perfroll& ths);
+    void set_adding( bool a_adding, perfroll& ths );
+    bool m_adding;
+    bool m_adding_pressed;
+};
 
 /* performance roll */
 class perfroll : public Gtk::DrawingArea
 {
 
  private: 
+    friend struct FruityPerfInput;
+    FruityPerfInput m_fruity_interaction;
+
+    friend struct Seq24PerfInput;
+    Seq24PerfInput m_seq24_interaction;
+
 
     Glib::RefPtr<Gdk::GC> m_gc;
     Glib::RefPtr<Gdk::Window> m_window;
@@ -86,9 +115,6 @@ class perfroll : public Gtk::DrawingArea
     Adjustment   *m_vadjust;
     Adjustment   *m_hadjust;
 
-    bool m_adding;
-    bool m_adding_pressed;
-    bool m_adding_pressed_state;
     bool m_moving;
     bool m_growing;
     bool m_grow_direction;
@@ -113,6 +139,10 @@ class perfroll : public Gtk::DrawingArea
     void snap_x( int *a_x );
 
    
+    void start_playing( void );
+    void stop_playing( void );
+
+   
 
     void draw_sequence_on( Glib::RefPtr<Gdk::Drawable> a_draw, int a_sequence );
     void draw_background_on( Glib::RefPtr<Gdk::Drawable> a_draw, int a_sequence );
@@ -136,8 +166,6 @@ class perfroll : public Gtk::DrawingArea
 
     void draw_progress();
 
-    void set_adding( bool a_adding );
-
     void redraw_dirty_sequences( void );
 
     perfroll( perform *a_perf, 
diff -rupN seq24-rev48 adding win32/src/seq24.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seq24.cpp
--- seq24-rev48 adding win32/src/seq24.cpp	2009-05-20 20:30:47.125000000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seq24.cpp	2009-05-20 20:30:55.796875000 -0500
@@ -45,9 +45,11 @@ option long_options[] = {
     {"file",     required_argument, 0, 'f'},
     {"help",     0, 0, 'h'},
     {"showmidi",     0, 0, 's'},
+    {"show_keys",     0, 0, 'k' },
     {"stats",     0, 0, 'S' },
     {"priority", 0, 0, 'p' },
     {"ignore",required_argument, 0, 'i'},
+    {"interaction_method",required_argument, 0, 'x'},
     {"jack_transport",0, 0, 'j'},
     {"jack_master",0, 0, 'J'},
     {"jack_master_cond",0,0,'C'},
@@ -71,6 +73,7 @@ Glib::ustring last_used_dir ="/";
 std::string config_filename = ".seq24rc";
 std::string user_filename = ".seq24usr";
 bool global_print_keys = false;
+interaction_method_e global_interactionmethod = e_seq24_interaction;
 
 bool global_with_jack_transport = false;
 bool global_with_jack_master = false;
@@ -183,6 +186,7 @@ main (int argc, char *argv[])
                 printf( "    --priority : runs higher priority with FIFO scheduler (must be root)\n" );
                 printf( "    --pass_sysex : passes any incoming sysex messages to all outputs \n" );
                 printf( "    --show_keys : prints pressed key value\n" );
+                printf( "    --interaction_method <number>: see .seq24rc for methods to use\n" );
                 printf( "    --jack_transport : seq24 will sync to jack transport\n" );
                 printf( "    --jack_master : seq24 will try to be jack master\n" );
                 printf( "    --jack_master_cond : jack master will fail if there is already a master\n" );
@@ -249,6 +253,10 @@ main (int argc, char *argv[])
                 global_device_ignore_num = atoi( optarg );
                 break;
                 
+            case 'x':
+                global_interactionmethod = (interaction_method_e)atoi( optarg );
+                break;
+
                 
             default:
                 break;
@@ -302,3 +310,40 @@ main (int argc, char *argv[])
 
     return 0;
 }
+
+
+/* declared in globals.h */
+char* key2text( unsigned long int val )
+{
+    unsigned long int s[] = {GDK_Insert, GDK_Escape, GDK_Return, GDK_Delete,
+                          GDK_BackSpace, GDK_Pause, GDK_Break, GDK_Tab, GDK_Scroll_Lock,
+                          GDK_Left, GDK_Right, GDK_Up, GDK_Down, GDK_Page_Up, GDK_Page_Down,
+                          GDK_Break, GDK_Num_Lock, GDK_F1, GDK_F2, GDK_F3, GDK_F4, GDK_F5,
+                          GDK_F6, GDK_F7, GDK_F8, GDK_F9, GDK_F10, GDK_F11, GDK_F12,
+                          GDK_Shift_L, GDK_Shift_R, GDK_Control_L, GDK_Control_R,
+                          GDK_Caps_Lock, GDK_Alt_L, GDK_Alt_R, GDK_Home, GDK_End, '\n', '\t', 0 };
+    char* str[] = {"Ins", "Esc", "Ret", "Del",
+                   "Bck", "Paus", "Brk", "Tab", "ScLk",
+                   "<-", "->", "Up", "Dn", "PgUp", "PgDn",
+                   "Brk", "NmLk", "F1", "F2", "F3", "F4", "F5",
+                   "F6", "F7", "F8", "F9", "F10", "F11", "F12",
+                   "ShL", "ShR", "CtlL", "CtlR",
+                   "Caps", "AltL", "AltR", "Home", "End", "Ret", "Tab", 0 };
+    unsigned long int* s2 = s;
+    char** str2 = str;
+    while (*s2 != 0) { if (val == s2[0]) return str2[0];  s2++; str2++; }
+    //for ( int x = '!'; x <= '~'; ++x) printf( "ascii: %d  '%c'\n", x, (char)x);
+
+    static char buf[16][16];
+    static int rotate = 0; //lets us use this in iostreams for example...
+    rotate = (rotate + 1) % 16;
+    strcpy( buf[rotate], "??" );
+
+    //printf( "%d %d %d %d - ", val, ' ' <= val, val <= '~', *s2 );
+    if (*s2 == 0 && ' ' <= val && val <= '~')
+    {
+        sprintf( buf[rotate], "'%c'", (char)val );
+    }
+    //printf( "  ret: %s\n", buf[rotate] );
+    return buf[rotate];
+}
diff -rupN seq24-rev48 adding win32/src/seqedit.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqedit.cpp
--- seq24-rev48 adding win32/src/seqedit.cpp	2009-05-20 20:30:47.265625000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqedit.cpp	2009-05-20 20:30:55.937500000 -0500
@@ -48,6 +48,13 @@
 #include "tools.xpm"
 #include "seq-editor.xpm"
 
+// tooltip helper, for old vs new gtk...
+#if GTK_MINOR_VERSION >= 12
+#   define add_tooltip( obj, text ) obj->set_tooltip_text( text);
+#else
+#   define add_tooltip( obj, text ) m_tooltips->set_tip( *obj, text );
+#endif
+
 int seqedit::m_initial_zoom = 2;
 int seqedit::m_initial_snap = c_ppqn / 4;
 int seqedit::m_initial_note_length = c_ppqn / 4;
@@ -225,31 +232,31 @@ seqedit::seqedit( sequence *a_seq, 
     m_toggle_play->add(  *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( play_xpm ))));
     m_toggle_play->signal_clicked().connect(
             mem_fun( *this, &seqedit::play_change_callback));
-    m_tooltips->set_tip( *m_toggle_play, "Sequence dumps data to midi bus." );
+    add_tooltip( m_toggle_play, "Sequence dumps data to midi bus." );
     
     m_toggle_record = manage( new ToggleButton(  ));
     m_toggle_record->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( rec_xpm ))));
     m_toggle_record->signal_clicked().connect(
             mem_fun( *this, &seqedit::record_change_callback));
-    m_tooltips->set_tip( *m_toggle_record, "Records incoming midi data." );
+    add_tooltip( m_toggle_record, "Records incoming midi data." );
 
     m_toggle_q_rec = manage( new ToggleButton(  ));
     m_toggle_q_rec->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( q_rec_xpm ))));
     m_toggle_q_rec->signal_clicked().connect(
             mem_fun( *this, &seqedit::q_rec_change_callback));
-    m_tooltips->set_tip( *m_toggle_q_rec, "Quantized Record." );
+    add_tooltip( m_toggle_q_rec, "Quantized Record." );
 
     m_button_rec_vol = manage( new Button());
     m_button_rec_vol->add( *manage( new Label("Vol")));
     m_button_rec_vol->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu), m_menu_rec_vol  ));
-    m_tooltips->set_tip( *m_button_rec_vol, "Select recording volume" );
+    add_tooltip( m_button_rec_vol, "Select recording volume" );
 
     m_toggle_thru = manage( new ToggleButton(  ));
     m_toggle_thru->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( thru_xpm ))));
     m_toggle_thru->signal_clicked().connect(
             mem_fun( *this, &seqedit::thru_change_callback));
-    m_tooltips->set_tip( *m_toggle_thru, "Incoming midi data passes "
+    add_tooltip( m_toggle_thru, "Incoming midi data passes "
             "thru to sequences midi bus and channel." );
 
     m_toggle_play->set_active( m_seq->get_playing());
@@ -689,7 +696,7 @@ seqedit::fill_top_bar( void )
     m_button_bpm->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_bpm  ));
-    m_tooltips->set_tip( *m_button_bpm, "Time Signature. Beats per Measure" );
+    add_tooltip( m_button_bpm, "Time Signature. Beats per Measure" );
     m_entry_bpm = manage( new Entry());
     m_entry_bpm->set_width_chars(2);
     m_entry_bpm->set_editable( false );
@@ -705,7 +712,7 @@ seqedit::fill_top_bar( void )
     m_button_bw->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_bw  ));
-    m_tooltips->set_tip( *m_button_bw, "Time Signature. Length of Beat" );
+    add_tooltip( m_button_bw, "Time Signature. Length of Beat" );
     m_entry_bw = manage( new Entry());
     m_entry_bw->set_width_chars(2);
     m_entry_bw->set_editable( false );
@@ -719,7 +726,7 @@ seqedit::fill_top_bar( void )
     m_button_length->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_length  ));
-    m_tooltips->set_tip( *m_button_length, "Sequence length in Bars." );
+    add_tooltip( m_button_length, "Sequence length in Bars." );
     m_entry_length = manage( new Entry());
     m_entry_length->set_width_chars(2);
     m_entry_length->set_editable( false );
@@ -735,7 +742,7 @@ seqedit::fill_top_bar( void )
     m_button_bus->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( bus_xpm  ))));
     m_button_bus->signal_clicked().connect(
             mem_fun( *this, &seqedit::popup_midibus_menu));
-    m_tooltips->set_tip( *m_button_bus, "Select Output Bus." );
+    add_tooltip( m_button_bus, "Select Output Bus." );
 
     m_entry_bus = manage( new Entry());
     m_entry_bus->set_max_length(60);
@@ -750,7 +757,7 @@ seqedit::fill_top_bar( void )
     m_button_channel->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( midi_xpm  ))));
     m_button_channel->signal_clicked().connect(
             mem_fun( *this, &seqedit::popup_midich_menu ));
-    m_tooltips->set_tip( *m_button_channel, "Select Midi channel." );
+    add_tooltip( m_button_channel, "Select Midi channel." );
     m_entry_channel = manage( new Entry());
     m_entry_channel->set_width_chars(2);
     m_entry_channel->set_editable( false );
@@ -764,7 +771,7 @@ seqedit::fill_top_bar( void )
     m_button_undo->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( undo_xpm  ))));
     m_button_undo->signal_clicked().connect(
             mem_fun( *this, &seqedit::undo_callback));
-    m_tooltips->set_tip( *m_button_undo, "Undo." );
+    add_tooltip( m_button_undo, "Undo." );
  
     m_hbox2->pack_start( *m_button_undo , false, false );
     
@@ -773,7 +780,7 @@ seqedit::fill_top_bar( void )
     m_button_redo->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( redo_xpm  ))));
     m_button_redo->signal_clicked().connect(
             mem_fun( *this, &seqedit::redo_callback));
-    m_tooltips->set_tip( *m_button_redo, "Redo." );
+    add_tooltip( m_button_redo, "Redo." );
  
     m_hbox2->pack_start( *m_button_redo , false, false );
     
@@ -782,7 +789,7 @@ seqedit::fill_top_bar( void )
     m_button_quanize->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( quanize_xpm  ))));
     m_button_quanize->signal_clicked().connect(
             sigc::bind(mem_fun(*this, &seqedit::do_action), quantize_notes, 0));
-    m_tooltips->set_tip( *m_button_quanize, "Quantize Selection." );
+    add_tooltip( m_button_quanize, "Quantize Selection." );
  
     m_hbox2->pack_start( *m_button_quanize , false, false );
 
@@ -804,7 +811,7 @@ seqedit::fill_top_bar( void )
     m_button_snap->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_snap  ));
-    m_tooltips->set_tip( *m_button_snap, "Grid snap." );
+    add_tooltip( m_button_snap, "Grid snap." );
     m_entry_snap = manage( new Entry());
     m_entry_snap->set_width_chars(5);
     m_entry_snap->set_editable( false );
@@ -818,7 +825,7 @@ seqedit::fill_top_bar( void )
     m_button_note_length->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_note_length  ));
-    m_tooltips->set_tip( *m_button_note_length, "Note Length." );
+    add_tooltip( m_button_note_length, "Note Length." );
     m_entry_note_length = manage( new Entry());
     m_entry_note_length->set_width_chars(5);
     m_entry_note_length->set_editable( false );
@@ -833,7 +840,7 @@ seqedit::fill_top_bar( void )
     m_button_zoom->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_zoom  ));
-    m_tooltips->set_tip( *m_button_zoom, "Zoom. Pixels to Ticks" );
+    add_tooltip( m_button_zoom, "Zoom. Pixels to Ticks" );
     m_entry_zoom = manage( new Entry());
     m_entry_zoom->set_width_chars(4);
     m_entry_zoom->set_editable( false );
@@ -850,7 +857,7 @@ seqedit::fill_top_bar( void )
     m_button_key->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_key  ));
-    m_tooltips->set_tip( *m_button_key, "Key of Sequence" );
+    add_tooltip( m_button_key, "Key of Sequence" );
     m_entry_key = manage( new Entry());
     m_entry_key->set_width_chars(5);
     m_entry_key->set_editable( false );
@@ -864,7 +871,7 @@ seqedit::fill_top_bar( void )
     m_button_scale->signal_clicked().connect(
             sigc::bind<Menu *>( mem_fun( *this, &seqedit::popup_menu),
                 m_menu_scale  ));
-    m_tooltips->set_tip( *m_button_scale, "Musical Scale" );
+    add_tooltip( m_button_scale, "Musical Scale" );
     m_entry_scale = manage( new Entry());
     m_entry_scale->set_width_chars(5);
     m_entry_scale->set_editable( false );
@@ -879,7 +886,7 @@ seqedit::fill_top_bar( void )
     m_button_sequence->add( *manage( new Image(Gdk::Pixbuf::create_from_xpm_data( sequences_xpm  ))));
     m_button_sequence->signal_clicked().connect(
             mem_fun( *this, &seqedit::popup_sequence_menu));
-    m_tooltips->set_tip( *m_button_sequence, "Background Sequence" );
+    add_tooltip( m_button_sequence, "Background Sequence" );
     m_entry_sequence = manage( new Entry());
     m_entry_sequence->set_width_chars(14);
     m_entry_sequence->set_editable( false );
diff -rupN seq24-rev48 adding win32/src/seqevent.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqevent.cpp
--- seq24-rev48 adding win32/src/seqevent.cpp	2009-05-20 20:30:47.375000000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqevent.cpp	2009-05-20 20:30:56.093750000 -0500
@@ -57,7 +57,6 @@ seqevent::seqevent(sequence *a_seq,
     m_moving    = false;
     m_moving_init = false;
     m_growing   = false;
-    m_adding    = false;
     m_paste     = false;
     m_painting  = false;
     
@@ -559,203 +558,409 @@ seqevent::convert_t( long a_ticks, int *
 }
 
 
-/* popup menu calls this */
+
+
+
+bool 
+seqevent::on_button_press_event(GdkEventButton* a_ev)
+{
+    switch (global_interactionmethod)
+    {
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_button_press_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_button_press_event(a_ev, *this);
+    }
+    return false;
+}
+
 void
-seqevent::set_adding( bool a_adding )
+seqevent::drop_event( long a_tick )
 {
-    if ( a_adding ){
- 
-	get_window()->set_cursor(  Gdk::Cursor( Gdk::PENCIL ));
-	m_adding = true;
+
+    unsigned char status = m_status;
+    unsigned char d0 = m_cc;
+    unsigned char d1 = 0x40;
+
+    if ( m_status == EVENT_AFTERTOUCH )
+        d0 = 0;
+
+    if ( m_status == EVENT_PROGRAM_CHANGE )
+        d0 = 0; /* d0 == new patch */
+
+    if ( m_status == EVENT_CHANNEL_PRESSURE )
+        d0 = 0x40; /* d0 == pressure */
+
+    if ( m_status == EVENT_PITCH_WHEEL )
+        d0 = 0;
+
+    m_seq->add_event( a_tick, 
+            status,
+            d0,
+            d1, true );
+}
+
+
+bool 
+seqevent::on_button_release_event(GdkEventButton* a_ev)
+{
+    switch (global_interactionmethod)
+    {
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_button_release_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_button_release_event(a_ev, *this);
     }
-    else {
+    return false;
+}
+
+
 
-	get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
-	m_adding = false;
+bool
+seqevent::on_motion_notify_event(GdkEventMotion* a_ev)
+{
+    switch (global_interactionmethod)
+    {
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_motion_notify_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_motion_notify_event(a_ev, *this);
     }
+    return false;
+}
+
+
+
+/* performs a 'snap' on y */
+void 
+seqevent::snap_y( int *a_y )
+{
+    *a_y = *a_y - (*a_y % c_key_y);
+}
+
+/* performs a 'snap' on x */
+void 
+seqevent::snap_x( int *a_x )
+{
+    //snap = number pulses to snap to
+    //m_zoom = number of pulses per pixel
+    //so snap / m_zoom  = number pixels to snap to
+    int mod = (m_snap / m_zoom);
+    if ( mod <= 0 )
+        mod = 1;
+    
+    *a_x = *a_x - (*a_x % mod );
+  
 }
 
 
 bool 
-seqevent::on_button_press_event(GdkEventButton* a_ev)
+seqevent::on_focus_in_event(GdkEventFocus*)
 {
-    int x,w,numsel;
+    set_flags(Gtk::HAS_FOCUS);
+    return false;
+}
+
+bool 
+seqevent::on_focus_out_event(GdkEventFocus*)
+{
+    unset_flags(Gtk::HAS_FOCUS);
+    return false;
+}
+
+bool
+seqevent::on_key_press_event(GdkEventKey* a_p0)
+{
+    bool ret = false;
+
+    if ( a_p0->type == GDK_KEY_PRESS ){
+
+        if ( a_p0->keyval ==  GDK_Delete || a_p0->keyval == GDK_BackSpace ){
+
+            m_seq->push_undo();
+            m_seq->mark_selected();
+            m_seq->remove_marked();
+            ret = true;
+        }
+
+        if ( a_p0->state & GDK_CONTROL_MASK ){
+
+            /* cut */
+            if ( a_p0->keyval == GDK_x || a_p0->keyval == GDK_X ){
+
+                m_seq->copy_selected();
+                m_seq->mark_selected();
+                m_seq->remove_marked();
+
+                ret = true;
+            }
+            /* copy */
+            if ( a_p0->keyval == GDK_c || a_p0->keyval == GDK_C ){
+
+                m_seq->copy_selected();
+                ret = true;
+            }
+            /* paste */
+            if ( a_p0->keyval == GDK_v || a_p0->keyval == GDK_V ){
+
+                start_paste();
+                ret = true;
+            }
+            /* Undo */
+            if ( a_p0->keyval == GDK_z || a_p0->keyval == GDK_Z ){
+
+                m_seq->pop_undo();
+                ret = true;
+            }
+         }
+    }
+
+    if ( ret == true ){
+
+        redraw();
+        m_seq->set_dirty();
+        return true;
+    }
+    else
+
+        return false;
+}
+
+
+
+
+
+//////////////////////////
+// interaction methods
+//////////////////////////
+
+
+void FruitySeqEventInput::updateMousePtr(seqevent& ths)
+{
+    // context sensitive mouse
+    long tick_s, tick_w, tick_f, pos;
+    ths.convert_x( ths.m_current_x, &tick_s );
+    ths.convert_x( c_eventevent_x, &tick_w  );
+    tick_f = tick_s + tick_w;
+    if ( tick_s < 0 )
+        tick_s = 0; // clamp to 0
+
+
+    if (m_is_drag_pasting || ths.m_selecting || ths.m_moving || ths.m_paste)
+    {
+        ths.get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
+    }
+    else if (ths.m_seq->intersectEvents( tick_s, tick_f, ths.m_status, pos ))
+    {
+        ths.get_window()->set_cursor( Gdk::Cursor( Gdk::CENTER_PTR ));
+    }
+    else
+    {
+        ths.get_window()->set_cursor( Gdk::Cursor( Gdk::PENCIL ));
+    }
+}
+
+
+bool FruitySeqEventInput::on_button_press_event(GdkEventButton* a_ev, seqevent& ths)
+{
+     int x,w,numsel;
 
     long tick_s;
     long tick_f;
     long tick_w;
 
-    convert_x( c_eventevent_x, &tick_w  );
+    ths.convert_x( c_eventevent_x, &tick_w  );
 
     /* if it was a button press */
 
     /* set values for dragging */
-    m_drop_x = m_current_x = (int) a_ev->x + m_scroll_offset_x;
+    ths.m_drop_x = ths.m_current_x = (int) a_ev->x + ths.m_scroll_offset_x;
 
     /* reset box that holds dirty redraw spot */
-    m_old.x = 0;
-    m_old.y = 0;
-    m_old.width = 0;
-    m_old.height = 0;
-
-    if ( m_paste ){
-
-        snap_x( &m_current_x );
-        convert_x( m_current_x, &tick_s );
-        m_paste = false;
-        m_seq->push_undo();
-        m_seq->paste_selected( tick_s, 0 );
+    ths.m_old.x = 0;
+    ths.m_old.y = 0;
+    ths.m_old.width = 0;
+    ths.m_old.height = 0;
+
+    if ( ths.m_paste ){
+
+        ths.snap_x( &ths.m_current_x );
+        ths.convert_x( ths.m_current_x, &tick_s );
+        ths.m_paste = false;
+        ths.m_seq->push_undo();
+        ths.m_seq->paste_selected( tick_s, 0 );
 
     } else {
 
         /*      left mouse button     */
-        if ( a_ev->button == 1 ){ 
-
+        if ( a_ev->button == 1 )
+        {
             /* turn x,y in to tick/note */
-            convert_x( m_drop_x, &tick_s );
+            ths.convert_x( ths.m_drop_x, &tick_s );
 
             /* shift back a few ticks */
-            tick_f = tick_s + (m_zoom);
+            tick_f = tick_s + (ths.m_zoom);
             tick_s -= (tick_w);
 
-            if ( tick_s < 0 ) 
+            if ( tick_s < 0 )
                 tick_s = 0;
 
-            if ( m_adding )
+            if ( ! ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_would_select ) &&
+                 !(a_ev->state & GDK_CONTROL_MASK) )
             {
-                m_painting = true;
+                ths.m_painting = true;
 
-                snap_x( &m_drop_x );
+                ths.snap_x( &ths.m_drop_x );
                 /* turn x,y in to tick/note */
-                convert_x( m_drop_x, &tick_s );
-                /* add note, length = little less than snap */
+                ths.convert_x( ths.m_drop_x, &tick_s );
 
-                if ( ! m_seq->select_events( tick_s, tick_f,
-                            m_status, m_cc, sequence::e_would_select ))
+                /* add note, length = little less than snap */
+                if ( ! ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_would_select ))
                 {
-                    m_seq->push_undo();
-                    drop_event( tick_s );
+                    ths.m_seq->push_undo();
+                    ths.drop_event( tick_s );
                 }
 
             }
             else /* selecting */
             {
-                if ( ! m_seq->select_events( tick_s, tick_f,
-                            m_status, m_cc, sequence::e_is_selected ))
+                if ( ! ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_is_selected ))
                 {
-                    if ( ! (a_ev->state & GDK_CONTROL_MASK) )
+                    // if clicking event...
+                    if (ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_would_select ) )
                     {
-                        m_seq->unselect();
-                    }
-
-                    numsel = m_seq->select_events( tick_s, tick_f,
-                            m_status,
-                            m_cc, sequence::e_select_one );
-
-                    /* if we didnt select anyhing (user clicked empty space)
-                       unselect all notes, and start selecting */
-
-                    /* none selected, start selection box */
-                    if ( numsel == 0 )
-                    { 
-                        m_selecting = true;
+                        if ( ! (a_ev->state & GDK_CONTROL_MASK) )
+                           ths.m_seq->unselect();
                     }
+                    // if clicking empty space ...
                     else
                     {
-                        /// needs update
+                        // ... unselect all if ctrl-shift not held
+                        if (! ((a_ev->state & GDK_CONTROL_MASK) &&
+                               (a_ev->state & GDK_SHIFT_MASK)) )
+                           ths.m_seq->unselect();
                     }
+
+                    /* on direct click select only one event */
+                    numsel = ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status,
+                            ths.m_cc, sequence::e_select_one );
+
+                    // prevent deselect in button_release()
+                    if (numsel)
+                       m_justselected_one = true;
+
+                    // if nothing selected, start the selection box
+                    if (numsel == 0 && (a_ev->state & GDK_CONTROL_MASK))
+                        ths.m_selecting = true;
                 }
 
-                if ( m_seq->select_events( tick_s, tick_f,
-                            m_status, m_cc, sequence::e_is_selected ))
+                // if event under cursor is selected
+                if ( ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_is_selected ))
                 {
 
-                    m_moving_init = true;
-                    int note;
+                    // grab/move the note
+                    if ( !(a_ev->state & GDK_CONTROL_MASK) )
+                    {
+                        ths.m_moving_init = true;
+                        int note;
 
-                    /* get the box that selected elements are in */
-                    m_seq->get_selected_box( &tick_s, &note, 
-                            &tick_f, &note );
+                        /* get the box that selected elements are in */
+                        ths.m_seq->get_selected_box( &tick_s, &note,
+                                &tick_f, &note );
 
-                    tick_f += tick_w;
+                        tick_f += tick_w;
 
-                    /* convert box to X,Y values */
-                    convert_t( tick_s, &x );
-                    convert_t( tick_f, &w );
+                        /* convert box to X,Y values */
+                        ths.convert_t( tick_s, &x );
+                        ths.convert_t( tick_f, &w );
 
-                    /* w is actually corrids now, so we have to change */
-                    w = w-x; 
+                        /* w is actually corrids now, so we have to change */
+                        w = w-x;
 
-                    /* set the m_selected rectangle to hold the
-                       x,y,w,h of our selected events */
+                        /* set the m_selected rectangle to hold the
+                           x,y,w,h of our selected events */
 
-                    m_selected.x = x;                  
-                    m_selected.width=w;
+                        ths.m_selected.x = x;
+                        ths.m_selected.width=w;
 
-                    m_selected.y = (c_eventarea_y - c_eventevent_y)/2;
-                    m_selected.height = c_eventevent_y;
+                        ths.m_selected.y = (c_eventarea_y - c_eventevent_y)/2;
+                        ths.m_selected.height = c_eventevent_y;
 
 
-                    /* save offset that we get from the snap above */
-                    int adjusted_selected_x = m_selected.x;
-                    snap_x( &adjusted_selected_x );
-                    m_move_snap_offset_x = ( m_selected.x - adjusted_selected_x);
+                        /* save offset that we get from the snap above */
+                        int adjusted_selected_x = ths.m_selected.x;
+                        ths.snap_x( &adjusted_selected_x );
+                        ths.m_move_snap_offset_x = ( ths.m_selected.x - adjusted_selected_x);
 
-                    /* align selection for drawing */
-                    snap_x( &m_selected.x );
-                    snap_x( &m_current_x );
-                    snap_x( &m_drop_x );
+                        /* align selection for drawing */
+                        ths.snap_x( &ths.m_selected.x );
+                        ths.snap_x( &ths.m_current_x );
+                        ths.snap_x( &ths.m_drop_x );
+                    }
+                    // ctrl left click when stuff is already selected
+                    else if ((a_ev->state & GDK_CONTROL_MASK) &&
+                             ths.m_seq->select_events( tick_s, tick_f,
+                                                  ths. m_status, ths.m_cc, sequence::e_is_selected ))
+                    {
+                        m_is_drag_pasting_start = true;
+                    }
 
-                } 
+                }
             }
 
         } /* end if button == 1 */
 
-        if ( a_ev->button == 3 ){ 
-
-            set_adding( true );
-        }
-    }
-
-    /* if they clicked, something changed */
-    update_pixmap();
-    draw_pixmap_on_window();  
-
-    return true;
-
-
-}
-
+        if ( a_ev->button == 3 )
+        {
+            /* turn x,y in to tick/note */
+            ths.convert_x( ths.m_drop_x, &tick_s );
 
-void
-seqevent::drop_event( long a_tick )
-{
+            /* shift back a few ticks */
+            tick_f = tick_s + (ths.m_zoom);
+            tick_s -= (tick_w);
 
-    unsigned char status = m_status;
-    unsigned char d0 = m_cc;
-    unsigned char d1 = 0x40;
+            if ( tick_s < 0 )
+                tick_s = 0;
 
-    if ( m_status == EVENT_AFTERTOUCH )
-        d0 = 0;
+            //erase event under cursor if there is one
+            if (ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_would_select ))
+            {
+                /* remove only the note under the cursor,
+                   leave the selection intact */
+                ths.m_seq->push_undo();
+                ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_remove_one );
+                ths.redraw();
+                ths.m_seq->set_dirty();
+            }
+            else /* selecting */
+            {
+                if  ( ! (a_ev->state & GDK_CONTROL_MASK) )
+                    ths.m_seq->unselect();
 
-    if ( m_status == EVENT_PROGRAM_CHANGE )
-        d0 = 0; /* d0 == new patch */
+                // nothing selected, start the selection box
+                ths.m_selecting = true;
+            }
+        }
+    }
 
-    if ( m_status == EVENT_CHANNEL_PRESSURE )
-        d0 = 0x40; /* d0 == pressure */
+    /* if they clicked, something changed */
+    ths.update_pixmap();
+    ths.draw_pixmap_on_window();
 
-    if ( m_status == EVENT_PITCH_WHEEL )
-        d0 = 0;
+    updateMousePtr( ths );
 
-    m_seq->add_event( a_tick, 
-            status,
-            d0,
-            d1, true );
+    return true;
 }
 
-
-bool 
-seqevent::on_button_release_event(GdkEventButton* a_ev)
+bool FruitySeqEventInput::on_button_release_event(GdkEventButton* a_ev, seqevent& ths)
 {
     long tick_s;
     long tick_f;
@@ -763,197 +968,420 @@ seqevent::on_button_release_event(GdkEve
     int x,w;
     int numsel;
 
-    grab_focus( );
+    ths.grab_focus( );
 
-    m_current_x = (int) a_ev->x  + m_scroll_offset_x;;
+    ths.m_current_x = (int) a_ev->x  + ths.m_scroll_offset_x;;
 
-    if ( m_moving )
-        snap_x( &m_current_x );
+    if ( ths.m_moving || m_is_drag_pasting )
+        ths.snap_x( &ths.m_current_x );
 
-    int delta_x = m_current_x - m_drop_x;
+    int delta_x = ths.m_current_x - ths.m_drop_x;
 
     long delta_tick;
 
     if ( a_ev->button == 1 ){
 
-        if ( m_selecting ){
-
-            x_to_w( m_drop_x, m_current_x, &x, &w );
+        int current_x = ths.m_current_x;
+        long t_s, t_f;
+        ths.snap_x( &current_x );
+        ths.convert_x( current_x, &t_s );
+
+        /* shift back a few ticks */
+        t_f = t_s + (ths.m_zoom);
+        if ( t_s < 0 )
+            t_s = 0;
+
+        // ctrl-left click button up for select/drag copy/paste
+        // left click button up for ending a move of selected notes
+        if ( m_is_drag_pasting )
+        {
+            m_is_drag_pasting = false;
+            m_is_drag_pasting_start = false;
 
-            convert_x( x,   &tick_s );
-            convert_x( x+w, &tick_f );
+            /* convert deltas into screen corridinates */
+            ths.m_paste = false;
+            ths.m_seq->push_undo();
+            ths.m_seq->paste_selected( t_s, 0 );
 
-            numsel = m_seq->select_events( tick_s, tick_f,
-                    m_status,
-                    m_cc, sequence::e_select );
+            //m_seq->unselect();
         }
+        // ctrl-left click but without movement - select a note
+        if (m_is_drag_pasting_start)
+        {
+            m_is_drag_pasting_start = false;
+
+            // if ctrl-left click without movement and
+            // if note under cursor is selected, and ctrl is held
+            // and buttondown didn't just select one
+            if (!m_justselected_one &&
+                ths.m_seq->select_events( t_s, t_f,
+                                          ths.m_status, ths.m_cc,
+                                          sequence::e_is_selected ) &&
+                (a_ev->state & GDK_CONTROL_MASK))
+            {
+                // deselect the event
+                numsel = ths.m_seq->select_events( t_s, t_f,
+                                                  ths.m_status, ths.m_cc,
+                                                  sequence::e_deselect );
+            }
+        }
+        m_justselected_one = false; // clear flag on left button up
 
-        if ( m_moving ){
+        if ( ths.m_moving ){
 
             /* adjust for snap */
-            delta_x -= m_move_snap_offset_x;
+            delta_x -= ths.m_move_snap_offset_x;
 
             /* convert deltas into screen corridinates */
-            convert_x( delta_x, &delta_tick );
+            ths.convert_x( delta_x, &delta_tick );
 
             /* not really notes, but still moves events */
-            m_seq->push_undo();
-            m_seq->move_selected_notes( delta_tick, 0 );
+            ths.m_seq->push_undo();
+            ths.m_seq->move_selected_notes( delta_tick, 0 );
         }
-
-        set_adding( m_adding );
     }
 
-    if ( a_ev->button == 3 ){
+    if ( a_ev->button == 3 || a_ev->button == 1 ){
+
+       if ( ths.m_selecting ){
+
+            ths.x_to_w( ths.m_drop_x, ths.m_current_x, &x, &w );
 
-        set_adding( false );
+            ths.convert_x( x,   &tick_s );
+            ths.convert_x( x+w, &tick_f );
 
+            numsel = ths.m_seq->select_events( tick_s, tick_f,
+                                            ths.m_status,
+                                            ths.m_cc, sequence::e_toggle_selection );
+        }
     }
 
     /* turn off */
-    m_selecting = false;
-    m_moving = false;
-    m_growing = false;
-    m_moving_init = false;
-    m_painting = false;
+    ths.m_selecting = false;
+    ths.m_moving = false;
+    ths.m_growing = false;
+    ths.m_moving_init = false;
+    ths.m_painting = false;
 
-    m_seq->unpaint_all();
+    ths.m_seq->unpaint_all();
 
     /* if they clicked, something changed */
-    update_pixmap();
-    draw_pixmap_on_window(); 
+    ths.update_pixmap();
+    ths.draw_pixmap_on_window();
+
+    updateMousePtr(ths);
 
     return true;
 }
 
-
-
-bool
-seqevent::on_motion_notify_event(GdkEventMotion* a_ev)
+bool FruitySeqEventInput::on_motion_notify_event(GdkEventMotion* a_ev, seqevent& ths)
 {
-
     long tick = 0;
-    
-    if ( m_moving_init ){
-        m_moving_init = false;
-        m_moving = true;
+    ths.m_current_x = (int) a_ev->x  + ths.m_scroll_offset_x;
+
+
+    if ( ths.m_moving_init )
+    {
+        ths.m_moving_init = false;
+        ths.m_moving = true;
     }
 
-    if ( m_selecting || m_moving || m_paste  ){
+    // context sensitive mouse pointer...
+    updateMousePtr(ths);
+
+    // ctrl-left click drag on selected note(s) starts a copy/unselect/paste
+    if ( m_is_drag_pasting_start )
+    {
+        ths.m_seq->copy_selected();
+        ths.m_seq->unselect();
+        ths.start_paste();
+
+        m_is_drag_pasting_start = false;
+        m_is_drag_pasting = true;
+    }
 
-        m_current_x = (int) a_ev->x  + m_scroll_offset_x;;
+    if ( ths.m_selecting || ths.m_moving || ths.m_paste  ){
 
-        if ( m_moving || m_paste )
-            snap_x( &m_current_x );
+        if ( ths.m_moving || ths.m_paste )
+            ths.snap_x( &ths.m_current_x );
 
-        draw_selection_on_window();
+        ths.draw_selection_on_window();
     }
 
 
-    if ( m_painting )
+    if ( ths.m_painting )
     {
-        m_current_x = (int) a_ev->x  + m_scroll_offset_x;;
-        snap_x( &m_current_x );
-        convert_x( m_current_x, &tick );
-        drop_event( tick );
+        ths.m_current_x = (int) a_ev->x  + ths.m_scroll_offset_x;;
+        ths.snap_x( &ths.m_current_x );
+        ths.convert_x( ths.m_current_x, &tick );
+        ths.drop_event( tick );
     }
-    
+
     return true;
 }
 
 
-
-/* performs a 'snap' on y */
-void 
-seqevent::snap_y( int *a_y )
+void
+Seq24SeqEventInput::set_adding( bool a_adding, seqevent& ths )
 {
-    *a_y = *a_y - (*a_y % c_key_y);
+    if ( a_adding )
+    {
+    	ths.get_window()->set_cursor(  Gdk::Cursor( Gdk::PENCIL ) );
+    	m_adding = true;
+    }
+    else
+    {
+    	ths.get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ) );
+    	m_adding = false;
+    }
 }
 
-/* performs a 'snap' on x */
-void 
-seqevent::snap_x( int *a_x )
+
+bool Seq24SeqEventInput::on_button_press_event(GdkEventButton* a_ev, seqevent& ths)
 {
-    //snap = number pulses to snap to
-    //m_zoom = number of pulses per pixel
-    //so snap / m_zoom  = number pixels to snap to
-    int mod = (m_snap / m_zoom);
-    if ( mod <= 0 )
-        mod = 1;
-    
-    *a_x = *a_x - (*a_x % mod );
-  
-}
+    int x,w,numsel;
 
+    long tick_s;
+    long tick_f;
+    long tick_w;
 
-bool 
-seqevent::on_focus_in_event(GdkEventFocus*)
-{
-    set_flags(Gtk::HAS_FOCUS);
-    return false;
-}
+    ths.convert_x( c_eventevent_x, &tick_w  );
 
-bool 
-seqevent::on_focus_out_event(GdkEventFocus*)
-{
-    unset_flags(Gtk::HAS_FOCUS);
-    return false;
+    /* if it was a button press */
+
+    /* set values for dragging */
+    ths.m_drop_x = ths.m_current_x = (int) a_ev->x + ths.m_scroll_offset_x;
+
+    /* reset box that holds dirty redraw spot */
+    ths.m_old.x = 0;
+    ths.m_old.y = 0;
+    ths.m_old.width = 0;
+    ths.m_old.height = 0;
+
+    if ( ths.m_paste ){
+
+        ths.snap_x( &ths.m_current_x );
+        ths.convert_x( ths.m_current_x, &tick_s );
+        ths.m_paste = false;
+        ths.m_seq->push_undo();
+        ths.m_seq->paste_selected( tick_s, 0 );
+
+    } else {
+
+        /*      left mouse button     */
+        if ( a_ev->button == 1 ){
+
+            /* turn x,y in to tick/note */
+            ths.convert_x( ths.m_drop_x, &tick_s );
+
+            /* shift back a few ticks */
+            tick_f = tick_s + (ths.m_zoom);
+            tick_s -= (tick_w);
+
+            if ( tick_s < 0 )
+                tick_s = 0;
+
+            if ( m_adding )
+            {
+                ths.m_painting = true;
+
+                ths.snap_x( &ths.m_drop_x );
+                /* turn x,y in to tick/note */
+                ths.convert_x( ths.m_drop_x, &tick_s );
+                /* add note, length = little less than snap */
+
+                if ( ! ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_would_select ))
+                {
+                    ths.m_seq->push_undo();
+                    ths.drop_event( tick_s );
+                }
+
+            }
+            else /* selecting */
+            {
+                if ( ! ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_is_selected ))
+                {
+                    if ( ! (a_ev->state & GDK_CONTROL_MASK) )
+                    {
+                        ths.m_seq->unselect();
+                    }
+
+                    numsel = ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status,
+                            ths.m_cc, sequence::e_select_one );
+
+                    /* if we didnt select anyhing (user clicked empty space)
+                       unselect all notes, and start selecting */
+
+                    /* none selected, start selection box */
+                    if ( numsel == 0 )
+                    {
+                        ths.m_selecting = true;
+                    }
+                    else
+                    {
+                        /// needs update
+                    }
+                }
+
+                if ( ths.m_seq->select_events( tick_s, tick_f,
+                            ths.m_status, ths.m_cc, sequence::e_is_selected ))
+                {
+
+                    ths.m_moving_init = true;
+                    int note;
+
+                    /* get the box that selected elements are in */
+                    ths.m_seq->get_selected_box( &tick_s, &note,
+                            &tick_f, &note );
+
+                    tick_f += tick_w;
+
+                    /* convert box to X,Y values */
+                    ths.convert_t( tick_s, &x );
+                    ths.convert_t( tick_f, &w );
+
+                    /* w is actually corrids now, so we have to change */
+                    w = w-x;
+
+                    /* set the m_selected rectangle to hold the
+                       x,y,w,h of our selected events */
+
+                    ths.m_selected.x = x;
+                    ths.m_selected.width=w;
+
+                    ths.m_selected.y = (c_eventarea_y - c_eventevent_y)/2;
+                    ths.m_selected.height = c_eventevent_y;
+
+
+                    /* save offset that we get from the snap above */
+                    int adjusted_selected_x = ths.m_selected.x;
+                    ths.snap_x( &adjusted_selected_x );
+                    ths.m_move_snap_offset_x = ( ths.m_selected.x - adjusted_selected_x);
+
+                    /* align selection for drawing */
+                    ths.snap_x( &ths.m_selected.x );
+                    ths.snap_x( &ths.m_current_x );
+                    ths.snap_x( &ths.m_drop_x );
+
+                }
+            }
+
+        } /* end if button == 1 */
+
+        if ( a_ev->button == 3 ){
+
+            set_adding( true, ths );
+        }
+    }
+
+    /* if they clicked, something changed */
+    ths.update_pixmap();
+    ths.draw_pixmap_on_window();
+
+    return true;
 }
 
-bool
-seqevent::on_key_press_event(GdkEventKey* a_p0)
+bool Seq24SeqEventInput::on_button_release_event(GdkEventButton* a_ev, seqevent& ths)
 {
-    bool ret = false;
+    long tick_s;
+    long tick_f;
 
-    if ( a_p0->type == GDK_KEY_PRESS ){
+    int x,w;
+    int numsel;
 
-        if ( a_p0->keyval ==  GDK_Delete || a_p0->keyval == GDK_BackSpace ){
+    ths.grab_focus( );
 
-            m_seq->push_undo();
-            m_seq->mark_selected();
-            m_seq->remove_marked();
-            ret = true;
+    ths.m_current_x = (int) a_ev->x  + ths.m_scroll_offset_x;;
+
+    if ( ths.m_moving )
+        ths.snap_x( &ths.m_current_x );
+
+    int delta_x = ths.m_current_x - ths.m_drop_x;
+
+    long delta_tick;
+
+    if ( a_ev->button == 1 ){
+
+        if ( ths.m_selecting ){
+
+            ths.x_to_w( ths.m_drop_x, ths.m_current_x, &x, &w );
+
+            ths.convert_x( x,   &tick_s );
+            ths.convert_x( x+w, &tick_f );
+
+            numsel = ths.m_seq->select_events( tick_s, tick_f,
+                    ths.m_status,
+                    ths.m_cc, sequence::e_select );
         }
 
-        if ( a_p0->state & GDK_CONTROL_MASK ){
+        if ( ths.m_moving ){
 
-            /* cut */
-            if ( a_p0->keyval == GDK_x || a_p0->keyval == GDK_X ){
+            /* adjust for snap */
+            delta_x -= ths.m_move_snap_offset_x;
 
-                m_seq->copy_selected();
-                m_seq->mark_selected();
-                m_seq->remove_marked();
+            /* convert deltas into screen corridinates */
+            ths.convert_x( delta_x, &delta_tick );
 
-                ret = true;
-            }
-            /* copy */
-            if ( a_p0->keyval == GDK_c || a_p0->keyval == GDK_C ){
+            /* not really notes, but still moves events */
+            ths.m_seq->push_undo();
+            ths.m_seq->move_selected_notes( delta_tick, 0 );
+        }
 
-                m_seq->copy_selected();
-                ret = true;
-            }
-            /* paste */
-            if ( a_p0->keyval == GDK_v || a_p0->keyval == GDK_V ){
+        set_adding( m_adding, ths );
+    }
 
-                start_paste();
-                ret = true;
-            }
-            /* Undo */
-            if ( a_p0->keyval == GDK_z || a_p0->keyval == GDK_Z ){
+    if ( a_ev->button == 3 ){
+
+        set_adding( false, ths );
 
-                m_seq->pop_undo();
-                ret = true;
-            }
-         }
     }
 
-    if ( ret == true ){
+    /* turn off */
+    ths.m_selecting = false;
+    ths.m_moving = false;
+    ths.m_growing = false;
+    ths.m_moving_init = false;
+    ths.m_painting = false;
 
-        redraw();
-        m_seq->set_dirty();
-        return true;
+    ths.m_seq->unpaint_all();
+
+    /* if they clicked, something changed */
+    ths.update_pixmap();
+    ths.draw_pixmap_on_window();
+
+    return true;
+}
+
+bool Seq24SeqEventInput::on_motion_notify_event(GdkEventMotion* a_ev, seqevent& ths)
+{
+    long tick = 0;
+
+    if ( ths.m_moving_init ){
+        ths.m_moving_init = false;
+        ths.m_moving = true;
     }
-    else
 
-        return false;
+    if ( ths.m_selecting || ths.m_moving || ths.m_paste  ){
+
+        ths.m_current_x = (int) a_ev->x  + ths.m_scroll_offset_x;;
+
+        if ( ths.m_moving || ths.m_paste )
+            ths.snap_x( &ths.m_current_x );
+
+        ths.draw_selection_on_window();
+    }
+
+
+    if ( ths.m_painting )
+    {
+        ths.m_current_x = (int) a_ev->x  + ths.m_scroll_offset_x;;
+        ths.snap_x( &ths.m_current_x );
+        ths.convert_x( ths.m_current_x, &tick );
+        ths.drop_event( tick );
+    }
+
+    return true;
 }
diff -rupN seq24-rev48 adding win32/src/seqevent.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqevent.h
--- seq24-rev48 adding win32/src/seqevent.h	2009-05-20 20:30:47.390625000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqevent.h	2009-05-20 20:30:56.109375000 -0500
@@ -45,11 +45,43 @@
 
 using namespace Gtk;
 
+// interaction methods
+class seqevent;
+struct FruitySeqEventInput
+{
+    FruitySeqEventInput() : m_justselected_one(false),
+                            m_is_drag_pasting_start(false),
+                            m_is_drag_pasting(false)
+    {}
+    bool m_justselected_one;
+    bool m_is_drag_pasting_start;
+    bool m_is_drag_pasting;
+    bool on_button_press_event(GdkEventButton* a_ev, seqevent& ths);
+    bool on_button_release_event(GdkEventButton* a_ev, seqevent& ths);
+    bool on_motion_notify_event(GdkEventMotion* a_ev, seqevent& ths);
+    void updateMousePtr(seqevent& ths);
+};
+struct Seq24SeqEventInput
+{
+    Seq24SeqEventInput() : m_adding( false ) {}
+    bool on_button_press_event(GdkEventButton* a_ev, seqevent& ths);
+    bool on_button_release_event(GdkEventButton* a_ev, seqevent& ths);
+    bool on_motion_notify_event(GdkEventMotion* a_ev, seqevent& ths);
+    void set_adding( bool a_adding, seqevent& ths );
+    bool m_adding;
+};
+
+
 /* piano event */
 class seqevent : public Gtk::DrawingArea
 {
 
  private: 
+    friend struct FruitySeqEventInput;
+    FruitySeqEventInput m_fruity_interaction;
+
+    friend struct Seq24SeqEventInput;
+    Seq24SeqEventInput m_seq24_interaction;
 
     Glib::RefPtr<Gdk::GC> m_gc;
     Glib::RefPtr<Gdk::Window> m_window;
@@ -81,7 +113,6 @@ class seqevent : public Gtk::DrawingArea
     bool m_moving_init;
     bool m_moving;
     bool m_growing;
-    bool m_adding;
     bool m_painting;
     bool m_paste;
 
@@ -150,7 +181,6 @@ class seqevent : public Gtk::DrawingArea
     
     int idle_redraw();
 
-    void set_adding( bool a_adding );
 
 
   
diff -rupN seq24-rev48 adding win32/src/seqmenu.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqmenu.cpp
--- seq24-rev48 adding win32/src/seqmenu.cpp	2009-05-20 20:30:47.484375000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqmenu.cpp	2009-05-20 20:30:56.187500000 -0500
@@ -32,7 +32,10 @@ seqmenu::seqmenu( perform *a_p  )
     m_mainperf = a_p;
     m_menu = NULL;
 
-} 
+    // init the clipboard, so that we don't get a crash
+    // on paste with no previous copy...
+    m_clipboard.set_master_midi_bus( a_p->get_master_midi_bus() );
+}
 
 
     void
diff -rupN seq24-rev48 adding win32/src/seqroll.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqroll.cpp
--- seq24-rev48 adding win32/src/seqroll.cpp	2009-05-20 20:30:47.578125000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqroll.cpp	2009-05-20 20:30:56.328125000 -0500
@@ -20,6 +20,14 @@
 #include "event.h"
 #include "seqroll.h"
 
+const long c_handlesize = 16;
+
+inline static long
+clamp( long val, long low, long hi )
+{
+     return val < low ? low : hi < val ? hi : val;
+}
+
 
 seqroll::seqroll(perform *a_perf,
                  sequence *a_seq, 
@@ -74,9 +82,11 @@ seqroll::seqroll(perform *a_perf,
     m_moving    = false;
 	m_moving_init = false;
     m_growing   = false;
-    m_adding    = false;
     m_painting  = false;
     m_paste     = false;
+    m_is_drag_pasting = false;
+    m_is_drag_pasting_start = false;
+    m_justselected_one = false;
 
     m_old_progress_x = 0;
 
@@ -126,21 +136,8 @@ seqroll::~seqroll( )
 	delete m_clipboard;
 }
 
-/* popup menu calls this */
-void 
-seqroll::set_adding( bool a_adding )
-{
-    if ( a_adding ){
-
-	get_window()->set_cursor(  Gdk::Cursor( Gdk::PENCIL ));
-	m_adding = true;
     
-    } else {
 
-	get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
-	m_adding = false;
-    }
-}
 
 
 void 
@@ -342,23 +339,44 @@ seqroll::update_background()
                              m_window_x, 
                              m_window_y );
     
+    gint8 dash = 1;
+    m_gc->set_dashes( 0, &dash, 1 );
     /* draw horz grey lines */
     m_gc->set_foreground(m_grey);
     m_gc->set_line_attributes( 1,
                                Gdk::LINE_ON_OFF_DASH,
                                Gdk::CAP_NOT_LAST,
                                Gdk::JOIN_MITER );
-    gint8 dash = 1;
-    m_gc->set_dashes( 0, &dash, 1 ); 
     
     for ( int i=0; i< (m_window_y / c_key_y) + 1; i++ )
     {
+        if (global_interactionmethod == e_fruity_interaction)
+        {
+            if (0 == (((c_num_keys - i) - m_scroll_offset_key + ( 12 - m_key )) % 12))
+            {
+                /* draw horz black lines at C */
+                m_gc->set_foreground(m_dk_grey);
+                m_gc->set_line_attributes( 1,
+                                           Gdk::LINE_SOLID,
+                                           Gdk::CAP_NOT_LAST,
+                                           Gdk::JOIN_MITER );
+            }
+            else if (11 == (((c_num_keys - i) - m_scroll_offset_key + ( 12 - m_key )) % 12))
+            {
+                /* draw horz grey lines for the other notes */
+                m_gc->set_foreground(m_grey);
+                m_gc->set_line_attributes( 1,
+                                           Gdk::LINE_ON_OFF_DASH,
+                                           Gdk::CAP_NOT_LAST,
+                                           Gdk::JOIN_MITER );
+            }
+        }
+        
         m_background->draw_line(m_gc,
                             0,
                             i * c_key_y,
                             m_window_x,
                             i * c_key_y );
-        
         if ( m_scale != c_scale_off ){
             
             if ( !c_scales_policy[m_scale][ ((c_num_keys - i)
@@ -941,336 +959,42 @@ seqroll::start_paste( )
 bool
 seqroll::on_button_press_event(GdkEventButton* a_ev)
 {
-    int numsel;
-	
-    long tick_s;
-    long tick_f;
-    int note_h;
-    int note_l;
-	
-    int norm_x, norm_y, snapped_x, snapped_y;
-	
-    grab_focus(  );
-
-    bool needs_update = false;
-	
-    snapped_x = norm_x = (int) (a_ev->x + m_scroll_offset_x );
-    snapped_y = norm_y = (int) (a_ev->y + m_scroll_offset_y );
-	
-    snap_x( &snapped_x );
-    snap_y( &snapped_y );
-	
-    /* y is always snapped */
-    m_current_y = m_drop_y = snapped_y;
-
-    /* reset box that holds dirty redraw spot */
-    m_old.x = 0;
-    m_old.y = 0;
-    m_old.width = 0;
-    m_old.height = 0;
-
-    if ( m_paste ){
-
-        convert_xy( snapped_x, snapped_y, &tick_s, &note_h );
-        m_paste = false;
-        m_seq->push_undo();
-        m_seq->paste_selected( tick_s, note_h );
-
-        needs_update = true;
-
-    } else { 
-
-        /*  left mouse button     */
-        if ( a_ev->button == 1 ||
-             a_ev->button == 2 )
-        { 
-
-            /* selection, normal x */
-            m_current_x = m_drop_x = norm_x;
-
-            /* turn x,y in to tick/note */
-            convert_xy( m_drop_x, m_drop_y, &tick_s, &note_h );
-
-            if ( m_adding )
-            {
-                /* start the paint job */
-                m_painting = true;
-
-                /* adding, snapped x */
-                m_current_x = m_drop_x = snapped_x;
-                convert_xy( m_drop_x, m_drop_y, &tick_s, &note_h );
-
-                // test if a note is already there
-                // fake select, if so, no add
-                if ( ! m_seq->select_note_events( tick_s, note_h, 
-                                                  tick_s, note_h,
-                                                  sequence::e_would_select ))
-                {
-                
-                    /* add note, length = little less than snap */
-                    m_seq->push_undo();
-                    m_seq->add_note( tick_s, m_note_length - 2, note_h, true );
-
-                    needs_update = true;
-                }
-
-            }
-            else /* selecting */
-            {
-
-               
-                if ( !m_seq->select_note_events( tick_s, note_h,
-                                                tick_s, note_h, 
-                                                sequence::e_is_selected ))
-                {
-                    if ( ! (a_ev->state & GDK_CONTROL_MASK) )
-                    {
-                        m_seq->unselect();	    
-                    }
- 
-
-                    /* on direct click select only one event */
-                    numsel = m_seq->select_note_events( tick_s,note_h,tick_s,note_h, 
-                                                        sequence::e_select_one );
-
-                    /* none selected, start selection box */
-                    if ( numsel == 0 )
-                    {
-                        if ( a_ev->button == 1 )
-                            m_selecting = true;
-                    }
-                    else
-                    {
-                        needs_update = true;
-                    }
-                }
- 
-                
-                if ( m_seq->select_note_events( tick_s, note_h,
-                                                tick_s, note_h, 
-                                                sequence::e_is_selected ))
-                {
-                    if ( a_ev->button == 1 )
-                    {
-                        m_moving_init = true;
-                        needs_update = true;
-
-
-                        /* get the box that selected elements are in */
-                        m_seq->get_selected_box( &tick_s, &note_h, 
-                                &tick_f, &note_l );
-
-
-                        convert_tn_box_to_rect( tick_s, tick_f, note_h, note_l,
-                                &m_selected.x,
-                                &m_selected.y,
-                                &m_selected.width,
-                                &m_selected.height );
-
-                        /* save offset that we get from the snap above */
-                        int adjusted_selected_x = m_selected.x;
-                        snap_x( &adjusted_selected_x );
-                        m_move_snap_offset_x = ( m_selected.x - adjusted_selected_x);
-
-                        /* align selection for drawing */
-                        snap_x( &m_selected.x );
-
-                        m_current_x = m_drop_x = snapped_x;
-                    }
-
-                    /* middle mouse button  */
-                    if ( a_ev->button == 2 ){	
-
-                        /* moving, normal x */
-                        //m_current_x = m_drop_x = norm_x;
-                        //convert_xy( m_drop_x, m_drop_y, &tick_s, &note_h );
-
-                        m_growing = true;
-
-                        /* get the box that selected elements are in */
-                        m_seq->get_selected_box( &tick_s, &note_h, 
-                                    &tick_f, &note_l );
-
-                        convert_tn_box_to_rect( tick_s, tick_f, note_h, note_l,
-                                &m_selected.x,
-                                &m_selected.y,
-                                &m_selected.width,
-                                &m_selected.height );	
-                        
-                    }
-                }
-           }
-       }
-
-        /*     right mouse button      */
-        if ( a_ev->button == 3 ){
-            set_adding( true );
-        }
-
-   }
-
-    /* if they clicked, something changed */
-    if ( needs_update ){
-    
-        ////printf( "needs update\n" );
-        m_seq->set_dirty();
-        //redraw_events();
+    switch (global_interactionmethod)
+    {
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_button_press_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_button_press_event(a_ev, *this);
     }
-    return true;
-
+    return false;
 }
 
 
 bool
 seqroll::on_button_release_event(GdkEventButton* a_ev)
 {
-    long tick_s;
-    long tick_f;
-    int note_h;
-    int note_l;
-    int x,y,w,h;
-    int numsel;
-
-    bool needs_update = false;
-
-    m_current_x = (int) (a_ev->x + m_scroll_offset_x );
-    m_current_y = (int) (a_ev->y + m_scroll_offset_y );
-
-    snap_y ( &m_current_y );
-
-    if ( m_moving )
-        snap_x( &m_current_x );
-
-    int delta_x = m_current_x - m_drop_x;
-    int delta_y = m_current_y - m_drop_y;
-
-    long delta_tick;
-    int delta_note;
-
-    if ( a_ev->button == 1 ){
-
-        if ( m_selecting ){
-
-            xy_to_rect ( m_drop_x,
-                    m_drop_y,
-                    m_current_x,
-                    m_current_y,
-                    &x, &y,
-                    &w, &h );
-
-            convert_xy( x,     y, &tick_s, &note_h );
-            convert_xy( x+w, y+h, &tick_f, &note_l );
-
-            numsel = m_seq->select_note_events( tick_s, note_h, tick_f, note_l, sequence::e_select );
-
-            needs_update = true;
-        }
-
-        if (  m_moving  ){
-
-            /* adjust for snap */
-            delta_x -= m_move_snap_offset_x;
-
-            /* convert deltas into screen corridinates */
-            convert_xy( delta_x, delta_y, &delta_tick, &delta_note );
-
-            /* since delta_note was from delta_y, it will be filpped
-               ( delta_y[0] = note[127], etc.,so we have to adjust */
-            delta_note = delta_note - (c_num_keys-1);
-
-            m_seq->push_undo();
-            m_seq->move_selected_notes( delta_tick, delta_note );
-            needs_update = true;
-        }
-
-   }
-
-    if ( a_ev->button == 2 ){	
-
-        if ( m_growing ){
-
-            /* convert deltas into screen corridinates */
-            convert_xy( delta_x, delta_y, &delta_tick, &delta_note );
-            m_seq->push_undo();
-
-            if ( a_ev->state & GDK_SHIFT_MASK )
-            {
-                m_seq->stretch_selected( delta_tick );
-            }
-            else
-            {
-                m_seq->grow_selected( delta_tick );
-            }
-
-            needs_update = true;
-        }
-    }
-
-    if ( a_ev->button == 3 ){	
-        set_adding( false );
+    switch (global_interactionmethod)
+    {
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_button_release_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_button_release_event(a_ev, *this);
     }
+    return false;
 
-    /* turn off */
-    m_selecting = false;
-    m_moving = false;
-    m_growing = false;
-    m_paste = false;
-    m_moving_init = false;
-    m_painting = false;
-
-    m_seq->unpaint_all();
- 
-    /* if they clicked, something changed */
-    if (  needs_update ){
-
-        ////printf( "needs_update2\n" );
-        m_seq->set_dirty();
-        //redraw_events();
-    }
-    return true;
 }
  
 
 bool
 seqroll::on_motion_notify_event(GdkEventMotion* a_ev)
 {
-    m_current_x = (int) (a_ev->x  + m_scroll_offset_x );
-    m_current_y = (int) (a_ev->y  + m_scroll_offset_y );
-
-    int note;
-    long tick;
-
-    if ( m_moving_init ){
-        m_moving_init = false;
-        m_moving = true;
-    }
-		 
-    
-    snap_y( &m_current_y );
-    convert_xy( 0, m_current_y, &tick, &note );
-	
-    m_seqkeys_wid->set_hint_key( note );
-	
-    if ( m_selecting || m_moving || m_growing || m_paste ){
-        
-        if ( m_moving || m_paste ){
-            snap_x( &m_current_x );
-        }
-        
-        draw_selection_on_window();
-        return true;
-	
-    }
-
-    if ( m_painting )
+    switch (global_interactionmethod)
     {
-        snap_x( &m_current_x );
-        convert_xy( m_current_x, m_current_y, &tick, &note );
-
-        m_seq->add_note( tick, m_note_length - 2, note, true );
-        return true;
+        case e_fruity_interaction:
+             return m_fruity_interaction.on_motion_notify_event(a_ev, *this);
+        case e_seq24_interaction:
+             return m_seq24_interaction.on_motion_notify_event(a_ev, *this);
     }
-    
     return false;
 }
 
@@ -1338,18 +1062,20 @@ seqroll::on_key_press_event(GdkEventKey*
 {
     bool ret = false;
 
-	if ( a_p0->keyval ==  GDK_space ){
-		    if (is_pattern_playing) {
-			    m_perform->stop_jack();
-			    m_perform->stop();
-		    	    is_pattern_playing=false;
-		    } else {
-			    m_perform->position_jack( false );  
-			    m_perform->start( false );
-			    m_perform->start_jack( );
-		    	    is_pattern_playing=true;
- 		    }
-            }
+    // the start/end key may be the same key (i.e. SPACEBAR)
+    // allow toggling when the same key is mapped to both triggers (i.e. SPACEBAR)
+    bool dont_toggle = m_perform->m_key_start != m_perform->m_key_stop;
+    if ( a_p0->keyval ==  m_perform->m_key_start && (dont_toggle || !is_pattern_playing) ){
+	    m_perform->position_jack( false );
+	    m_perform->start( false );
+	    m_perform->start_jack( );
+        is_pattern_playing=true;
+    }
+    else if ( a_p0->keyval ==  m_perform->m_key_stop && (dont_toggle || is_pattern_playing) ){
+        m_perform->stop_jack();
+        m_perform->stop();
+        is_pattern_playing=false;
+    }
 
     if ( a_p0->type == GDK_KEY_PRESS ){
 
@@ -1393,6 +1119,13 @@ seqroll::on_key_press_event(GdkEventKey*
                 m_seq->pop_undo();
                 ret = true;
             }
+            
+            /* select all events */
+            if ( a_p0->keyval == GDK_a || a_p0->keyval == GDK_A ){
+
+                m_seq->select_all();
+                ret = true;
+            }
         }
     }
 
@@ -1455,3 +1188,919 @@ seqroll::on_scroll_event( GdkEventScroll
 
 }
 
+
+
+
+
+
+
+//////////////////////////
+// interaction methods
+//////////////////////////
+
+
+
+void FruitySeqRollInput::updateMousePtr(seqroll& ths)
+{
+     // context sensitive mouse
+     {
+        long drop_tick;
+        int drop_note;
+        ths.convert_xy( ths.m_current_x, ths.m_current_y, &drop_tick, &drop_note );
+        long start, end, note;
+        if (ths.m_is_drag_pasting || ths.m_selecting || ths.m_moving || ths.m_growing || ths.m_paste)
+        {
+            ths.get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
+        }
+        else if (!m_adding &&
+            ths.m_seq->intersectNotes( drop_tick, drop_note, start, end, note ) && note == drop_note)
+	    {
+            long handle_size = clamp( c_handlesize, 0, (end-start)/3 );
+            if (start <= drop_tick && drop_tick <= start + handle_size)
+            {
+               //get_window()->set_cursor( Gdk::Cursor( Gdk::RIGHT_PTR ));
+               ths.get_window()->set_cursor( Gdk::Cursor( Gdk::CENTER_PTR )); // not supported yet
+            }
+            else if (end - handle_size <= drop_tick && drop_tick <= end)
+            {
+               ths.get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
+            }
+            else
+            {
+               ths.get_window()->set_cursor( Gdk::Cursor( Gdk::CENTER_PTR ));
+            }
+        }
+        else
+        {
+            ths.get_window()->set_cursor( Gdk::Cursor( Gdk::PENCIL ));
+        }
+    }
+}
+
+
+bool FruitySeqRollInput::on_button_press_event(GdkEventButton* a_ev, seqroll& ths)
+{
+    int numsel;
+
+    long tick_s;
+    long tick_f;
+    int note_h;
+    int note_l;
+
+    int norm_x, norm_y, snapped_x, snapped_y;
+
+    ths.grab_focus(  );
+
+    bool needs_update = false;
+
+    snapped_x = norm_x = (int) (a_ev->x + ths.m_scroll_offset_x );
+    snapped_y = norm_y = (int) (a_ev->y + ths.m_scroll_offset_y );
+
+    ths.snap_x( &snapped_x );
+    ths.snap_y( &snapped_y );
+
+    /* y is always snapped */
+    ths.m_current_y = ths.m_drop_y = snapped_y;
+
+    /* reset box that holds dirty redraw spot */
+    ths.m_old.x = 0;
+    ths.m_old.y = 0;
+    ths.m_old.width = 0;
+    ths.m_old.height = 0;
+
+    // ctrl-v pressed, we're waiting for a click to show where to paste
+    if ( ths.m_paste ){
+
+        ths.convert_xy( snapped_x, snapped_y, &tick_s, &note_h );
+        ths.m_paste = false;
+        ths.m_seq->push_undo();
+        ths.m_seq->paste_selected( tick_s, note_h );
+
+        needs_update = true;
+
+    } else {
+
+        /*  left mouse button     */
+        if ( a_ev->button == 1)
+        {
+
+            /* selection, normal x */
+            ths.m_current_x = ths.m_drop_x = norm_x;
+
+            /* turn x,y in to tick/note */
+            ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &tick_s, &note_h );
+
+            // if not on top of event then add one...
+            if ( m_canadd && ! ths.m_seq->select_note_events( tick_s, note_h,
+                                             tick_s, note_h,
+                                             sequence::e_would_select ) &&
+                 !(a_ev->state & GDK_CONTROL_MASK) )
+            {
+                /* start the paint job */
+                ths.m_painting = true;
+                m_adding = true;
+
+                /* adding, snapped x */
+                ths.m_current_x = ths.m_drop_x = snapped_x;
+                ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &tick_s, &note_h );
+
+                // test if a note is already there
+                // fake select, if so, no add
+                if ( ! ths.m_seq->select_note_events( tick_s, note_h,
+                                                  tick_s, note_h,
+                                                  sequence::e_would_select ))
+                {
+                    /* add note, length = little less than snap */
+                    ths.m_seq->push_undo();
+                    ths.m_seq->add_note( tick_s, ths.m_note_length - 2, note_h, true );
+
+                    needs_update = true;
+                }
+
+            }
+            else /* selecting */
+            {
+                // if the under the cursor is not a selected note...
+                if ( !ths.m_seq->select_note_events( tick_s, note_h,
+                                                tick_s, note_h,
+                                                sequence::e_is_selected ))
+                {
+                    // if clicking a note ...
+                    if (ths.m_seq->select_note_events( tick_s,note_h,tick_s,note_h,
+                                                   sequence::e_would_select ) )
+                    {
+                        // ... unselect all if ctrl not held
+                        if (! (a_ev->state & GDK_CONTROL_MASK))
+                           ths.m_seq->unselect();
+                    }
+                    // if clicking empty space ...
+                    else
+                    {
+                        // ... unselect all if ctrl-shift not held
+                        if (! ((a_ev->state & GDK_CONTROL_MASK) &&
+                               (a_ev->state & GDK_SHIFT_MASK)) )
+                           ths.m_seq->unselect();
+                    }
+
+                    /* on direct click select only one event */
+                    numsel = ths.m_seq->select_note_events( tick_s,note_h,tick_s,note_h,
+                                                        sequence::e_select_one );
+                    // prevent deselect in button_release()
+                    if (numsel)
+                       ths.m_justselected_one = true;
+
+                    // if nothing selected, start the selection box
+                    if (numsel == 0 && (a_ev->state & GDK_CONTROL_MASK))
+                        ths.m_selecting = true;
+
+                    needs_update = true;
+                }
+
+                // if note under cursor is selected
+                if ( ths.m_seq->select_note_events( tick_s, note_h,
+                                                tick_s, note_h,
+                                                sequence::e_is_selected ))
+                {
+                    // context sensitive mouse
+                    bool left_mouse_handle = false;
+                    bool right_mouse_handle = false;
+                    bool center_mouse_handle = false;
+                    {
+                        long drop_tick;
+                        int drop_note;
+                        ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &drop_tick, &drop_note );
+                        long start, end, note;
+                        if (ths.m_seq->intersectNotes( drop_tick, drop_note, start, end, note ) && note == drop_note)
+                	    {
+                            long handle_size = clamp( c_handlesize, 0, (end-start)/3 ); // 16 wide unless very small...
+                            if (start <= drop_tick && drop_tick <= start + handle_size)
+                            {
+                                //left_mouse_handle = true; // not supported yet
+                                center_mouse_handle = true;  
+                            }
+                            else if (end - handle_size <= drop_tick && drop_tick <= end)
+                            {
+                                right_mouse_handle = true;
+                            }
+                            else
+                            {
+                                center_mouse_handle = true;
+                            }
+                        }
+                    }
+
+                    // grab/move the note
+                    if ( center_mouse_handle &&
+                         a_ev->button == 1 && !(a_ev->state & GDK_CONTROL_MASK) )
+                    {
+                        ths.m_moving_init = true;
+                        needs_update = true;
+
+
+                        /* get the box that selected elements are in */
+                        ths.m_seq->get_selected_box( &tick_s, &note_h,
+                                &tick_f, &note_l );
+
+
+                        ths.convert_tn_box_to_rect( tick_s, tick_f, note_h, note_l,
+                                &ths.m_selected.x,
+                                &ths.m_selected.y,
+                                &ths.m_selected.width,
+                                &ths.m_selected.height );
+
+                        /* save offset that we get from the snap above */
+                        int adjusted_selected_x = ths.m_selected.x;
+                        ths.snap_x( &adjusted_selected_x );
+                        ths.m_move_snap_offset_x = ( ths.m_selected.x - adjusted_selected_x);
+
+                        /* align selection for drawing */
+                        ths.snap_x( &ths.m_selected.x );
+
+                        ths.m_current_x = ths.m_drop_x = snapped_x;
+                    }
+                    // ctrl left click when stuff is already selected
+                    else if (a_ev->button == 1 && (a_ev->state & GDK_CONTROL_MASK) &&
+                             ths.m_seq->select_note_events( tick_s, note_h,
+                                                        tick_s, note_h,
+                                                        sequence::e_is_selected ))
+                    {
+                        ths.m_is_drag_pasting_start = true;
+                        m_drag_paste_start_pos[0] = (long)a_ev->x;
+                        m_drag_paste_start_pos[1] = (long)a_ev->y;
+                        //printf( "start: %lf  %lf\n", a_ev->x, a_ev->y );
+                    }
+
+                    /* left click on the right handle - grow/resize event  */
+                    if ( (right_mouse_handle &&
+                          a_ev->button == 1 && ! (a_ev->state & GDK_CONTROL_MASK)) ||
+                          a_ev->button == 2 ){
+                        ths.m_growing = true;
+
+                        /* get the box that selected elements are in */
+                        ths.m_seq->get_selected_box( &tick_s, &note_h,
+                                    &tick_f, &note_l );
+
+                        ths.convert_tn_box_to_rect( tick_s, tick_f, note_h, note_l,
+                                &ths.m_selected.x,
+                                &ths.m_selected.y,
+                                &ths.m_selected.width,
+                                &ths.m_selected.height );
+                    }
+                }
+           }
+       }
+
+
+        /*     right click      */
+        if ( a_ev->button == 3 ){
+            /* selection, normal x */
+            ths.m_current_x = ths.m_drop_x = norm_x;
+
+            /* turn x,y in to tick/note */
+            ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &tick_s, &note_h );
+
+
+            // erase event(s) under cursor if there is one
+            if ( ths.m_seq->select_note_events( tick_s, note_h,
+                                            tick_s, note_h,
+                                            sequence::e_would_select) )
+            {
+                /* right ctrl click: remove all selected notes */
+                if (a_ev->state & GDK_CONTROL_MASK)
+                {
+                    ths.m_seq->select_note_events( tick_s, note_h,
+                                                tick_s, note_h,
+                                                sequence::e_select_one );
+                    ths.m_seq->push_undo();
+                    ths.m_seq->mark_selected();
+                    ths.m_seq->remove_marked();
+                }
+                /* right click: remove only the note under the cursor,
+                   leave the selection intact */
+                else
+                {
+                    ths.m_seq->push_undo();
+                    ths.m_seq->select_note_events( tick_s, note_h,
+                                                tick_s, note_h,
+                                                sequence::e_remove_one );
+                }
+
+                // hold down the right button, drag mouse around erasing notes:
+                m_erase_painting = true;
+
+                // repaint... we've changed the notes.
+                needs_update = true;
+            }
+            else /* selecting */
+            {
+
+                if  ( ! (a_ev->state & GDK_CONTROL_MASK) )
+                    ths.m_seq->unselect();
+
+                // nothing selected, start the selection box
+                ths.m_selecting = true;
+
+                needs_update = true;
+            }
+        }
+    }
+
+    // context sensative mouse pointer...
+    updateMousePtr( ths );
+
+    /* if they clicked, something changed */
+    if ( needs_update ){
+        ////printf( "needs update\n" );
+        ths.m_seq->set_dirty();
+        //redraw_events();
+    }
+    return true;
+}
+
+bool FruitySeqRollInput::on_button_release_event(GdkEventButton* a_ev, seqroll& ths)
+{
+    long tick_s;
+    long tick_f;
+    int note_h;
+    int note_l;
+    int x,y,w,h;
+    int numsel;
+
+    bool needs_update = false;
+
+    ths.m_current_x = (int) (a_ev->x + ths.m_scroll_offset_x );
+    ths.m_current_y = (int) (a_ev->y + ths.m_scroll_offset_y );
+
+    ths.snap_y( &ths.m_current_y );
+
+    if ( ths.m_moving || ths.m_is_drag_pasting )
+        ths.snap_x( &ths.m_current_x );
+
+    int delta_x = ths.m_current_x - ths.m_drop_x;
+    int delta_y = ths.m_current_y - ths.m_drop_y;
+
+    long delta_tick;
+    int delta_note;
+
+    // middle click, or ctrlleft click button up
+    if ( a_ev->button == 1 ||
+         a_ev->button == 2 ){
+        if ( ths.m_growing )
+        {
+            /* convert deltas into screen corridinates */
+            ths.convert_xy( delta_x, delta_y, &delta_tick, &delta_note );
+            ths.m_seq->push_undo();
+
+            if ( a_ev->state & GDK_SHIFT_MASK )
+                ths.m_seq->stretch_selected( delta_tick );
+            else
+                ths.m_seq->grow_selected( delta_tick );
+
+            needs_update = true;
+        }
+    }
+    long int current_tick;
+    int current_note;
+    ths.convert_xy( ths.m_current_x, ths.m_current_y, &current_tick, &current_note );
+
+
+    // ctrl-left click button up for select/drag copy/paste
+    // left click button up for ending a move of selected notes
+    if ( a_ev->button == 1 ){
+        m_adding = false;
+        if ( ths.m_is_drag_pasting )
+        {
+            ths.m_is_drag_pasting = false;
+            ths.m_is_drag_pasting_start = false;
+
+            /* convert deltas into screen corridinates */
+            ths.m_paste = false;
+            ths.m_seq->push_undo();
+            ths.m_seq->paste_selected( current_tick, current_note );
+
+            needs_update = true;
+
+            //m_seq->unselect();
+        }
+        // ctrl-left click but without movement - select a note
+        if (ths.m_is_drag_pasting_start)
+        {
+            ths.m_is_drag_pasting_start = false;
+
+            // if ctrl-left click without movement and
+            // if note under cursor is selected, and ctrl is held
+            // and buttondown didn't just select one
+            if (!ths.m_justselected_one &&
+                ths.m_seq->select_note_events( current_tick, current_note,
+                                           current_tick, current_note,
+                                           sequence::e_is_selected ) &&
+                (a_ev->state & GDK_CONTROL_MASK))
+            {
+                // deselect the note
+                numsel = ths.m_seq->select_note_events( current_tick, current_note,
+                                                    current_tick, current_note,
+                                                    sequence::e_deselect );
+                needs_update = true;
+            }
+        }
+        ths.m_justselected_one = false; // clear flag on left button up
+
+        if (  ths.m_moving  ){
+
+            /* adjust for snap */
+            delta_x -= ths.m_move_snap_offset_x;
+
+            /* convert deltas into screen corridinates */
+            ths.convert_xy( delta_x, delta_y, &delta_tick, &delta_note );
+
+            /* since delta_note was from delta_y, it will be filpped
+               ( delta_y[0] = note[127], etc.,so we have to adjust */
+            delta_note = delta_note - (c_num_keys-1);
+
+            ths.m_seq->push_undo();
+            ths.m_seq->move_selected_notes( delta_tick, delta_note );
+            needs_update = true;
+        }
+    }
+
+    // right click or leftctrl click button up for selection box
+    if ( a_ev->button == 3 || a_ev->button == 1 ){
+        if ( ths.m_selecting )
+        {
+            ths.xy_to_rect ( ths.m_drop_x,
+                    ths.m_drop_y,
+                    ths.m_current_x,
+                    ths.m_current_y,
+                    &x, &y,
+                    &w, &h );
+
+            ths.convert_xy( x,     y, &tick_s, &note_h );
+            ths.convert_xy( x+w, y+h, &tick_f, &note_l );
+
+            numsel = ths.m_seq->select_note_events( tick_s, note_h, tick_f, note_l, sequence::e_toggle_selection );
+
+            needs_update = true;
+        }
+    }
+    if ( a_ev->button == 3 )
+    {
+        m_erase_painting = false;
+    }
+
+    /* turn off */
+    ths.m_selecting = false;
+    ths.m_moving = false;
+    ths.m_growing = false;
+    ths.m_paste = false;
+    ths.m_moving_init = false;
+    ths.m_painting = false;
+
+    ths.m_seq->unpaint_all();
+
+    // context sensative mouse pointer...
+    updateMousePtr( ths );
+
+
+    /* if they clicked, something changed */
+    if (  needs_update ){
+
+        ////printf( "needs_update2\n" );
+
+        ths.m_seq->set_dirty();
+        //redraw_events();
+
+    }
+    return true;
+}
+
+bool FruitySeqRollInput::on_motion_notify_event(GdkEventMotion* a_ev, seqroll& ths)
+{
+    ths.m_current_x = (int) (a_ev->x  + ths.m_scroll_offset_x );
+    ths.m_current_y = (int) (a_ev->y  + ths.m_scroll_offset_y );
+
+    int note;
+    long tick;
+
+    if ( ths.m_moving_init ){
+        ths.m_moving_init = false;
+        ths.m_moving = true;
+    }
+
+    // context sensitive mouse pointer...
+    updateMousePtr( ths );
+
+    // ctrl-left click drag on selected note(s) starts a copy/unselect/paste
+    // don't begin the paste until mouse moves a few pixels, filter out the unsteady hand
+    if ( ths.m_is_drag_pasting_start &&
+         (6 <= abs(m_drag_paste_start_pos[0] - (long)a_ev->x) ||
+          6 <= abs(m_drag_paste_start_pos[1] - (long)a_ev->y))  )
+    {
+        ths.m_seq->copy_selected();
+        ths.m_seq->unselect();
+        ths.start_paste();
+
+        ths.m_is_drag_pasting_start = false;
+        ths.m_is_drag_pasting = true;
+    }
+
+
+
+    ths.snap_y( &ths.m_current_y );
+    ths.convert_xy( 0, ths.m_current_y, &tick, &note );
+
+    ths.m_seqkeys_wid->set_hint_key( note );
+
+    if ( ths.m_selecting || ths.m_moving || ths.m_growing || ths.m_paste ){
+
+        if ( ths.m_moving || ths.m_paste ){
+            ths.snap_x( &ths.m_current_x );
+        }
+
+        ths.draw_selection_on_window();
+        return true;
+
+    }
+
+    if ( ths.m_painting )
+    {
+        ths.snap_x( &ths.m_current_x );
+        ths.convert_xy( ths.m_current_x, ths.m_current_y, &tick, &note );
+
+        ths.m_seq->add_note( tick, ths.m_note_length - 2, note, true );
+        return true;
+    }
+
+    if (m_erase_painting)
+    {
+        ths.convert_xy( ths.m_current_x, ths.m_current_y, &tick, &note );
+        if ( ths.m_seq->select_note_events( tick, note,
+                                            tick, note,
+                                            sequence::e_would_select) )
+        {
+            /* remove only the note under the cursor,
+               leave the selection intact */
+            ths.m_seq->push_undo();
+            ths.m_seq->select_note_events( tick, note,
+                                        tick, note,
+                                        sequence::e_remove_one );
+            ths.m_seq->set_dirty();
+        }
+    }
+    
+
+    return false;
+}
+
+
+
+
+
+/* popup menu calls this */
+void
+Seq24SeqRollInput::set_adding( bool a_adding, seqroll& ths )
+{
+    if ( a_adding ){
+
+	ths.get_window()->set_cursor(  Gdk::Cursor( Gdk::PENCIL ));
+	m_adding = true;
+
+    } else {
+
+	ths.get_window()->set_cursor( Gdk::Cursor( Gdk::LEFT_PTR ));
+	m_adding = false;
+    }
+}
+
+bool Seq24SeqRollInput::on_button_press_event(GdkEventButton* a_ev, seqroll& ths)
+{
+    int numsel;
+
+    long tick_s;
+    long tick_f;
+    int note_h;
+    int note_l;
+
+    int norm_x, norm_y, snapped_x, snapped_y;
+
+    ths.grab_focus(  );
+
+    bool needs_update = false;
+
+    snapped_x = norm_x = (int) (a_ev->x + ths.m_scroll_offset_x );
+    snapped_y = norm_y = (int) (a_ev->y + ths.m_scroll_offset_y );
+
+    ths.snap_x( &snapped_x );
+    ths.snap_y( &snapped_y );
+
+    /* y is always snapped */
+    ths.m_current_y = ths.m_drop_y = snapped_y;
+
+    /* reset box that holds dirty redraw spot */
+    ths.m_old.x = 0;
+    ths.m_old.y = 0;
+    ths.m_old.width = 0;
+    ths.m_old.height = 0;
+
+    if ( ths.m_paste ){
+
+        ths.convert_xy( snapped_x, snapped_y, &tick_s, &note_h );
+        ths.m_paste = false;
+        ths.m_seq->push_undo();
+        ths.m_seq->paste_selected( tick_s, note_h );
+
+        needs_update = true;
+
+    } else {
+
+        /*  left mouse button     */
+        if ( a_ev->button == 1 ||
+             a_ev->button == 2 )
+        {
+
+            /* selection, normal x */
+            ths.m_current_x = ths.m_drop_x = norm_x;
+
+            /* turn x,y in to tick/note */
+            ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &tick_s, &note_h );
+
+            if ( m_adding )
+            {
+                /* start the paint job */
+                ths.m_painting = true;
+
+                /* adding, snapped x */
+                ths.m_current_x = ths.m_drop_x = snapped_x;
+                ths.convert_xy( ths.m_drop_x, ths.m_drop_y, &tick_s, &note_h );
+
+                // test if a note is already there
+                // fake select, if so, no add
+                if ( ! ths.m_seq->select_note_events( tick_s, note_h,
+                                                  tick_s, note_h,
+                                                  sequence::e_would_select ))
+                {
+
+                    /* add note, length = little less than snap */
+                    ths.m_seq->push_undo();
+                    ths.m_seq->add_note( tick_s, ths.m_note_length - 2, note_h, true );
+
+                    needs_update = true;
+                }
+
+            }
+            else /* selecting */
+            {
+
+
+                if ( !ths.m_seq->select_note_events( tick_s, note_h,
+                                                tick_s, note_h,
+                                                sequence::e_is_selected ))
+                {
+                    if ( ! (a_ev->state & GDK_CONTROL_MASK) )
+                    {
+                        ths.m_seq->unselect();
+                    }
+
+
+                    /* on direct click select only one event */
+                    numsel = ths.m_seq->select_note_events( tick_s,note_h,tick_s,note_h,
+                                                        sequence::e_select_one );
+
+                    /* none selected, start selection box */
+                    if ( numsel == 0 )
+                    {
+                        if ( a_ev->button == 1 )
+                            ths.m_selecting = true;
+                    }
+                    else
+                    {
+                        needs_update = true;
+                    }
+                }
+
+
+                if ( ths.m_seq->select_note_events( tick_s, note_h,
+                                                tick_s, note_h,
+                                                sequence::e_is_selected ))
+                {
+                    // moving - left click only
+                    if ( a_ev->button == 1 && !(a_ev->state & GDK_CONTROL_MASK) )
+                    {
+                        ths.m_moving_init = true;
+                        needs_update = true;
+
+
+                        /* get the box that selected elements are in */
+                        ths.m_seq->get_selected_box( &tick_s, &note_h,
+                                &tick_f, &note_l );
+
+
+                        ths.convert_tn_box_to_rect( tick_s, tick_f, note_h, note_l,
+                                &ths.m_selected.x,
+                                &ths.m_selected.y,
+                                &ths.m_selected.width,
+                                &ths.m_selected.height );
+
+                        /* save offset that we get from the snap above */
+                        int adjusted_selected_x = ths.m_selected.x;
+                        ths.snap_x( &adjusted_selected_x );
+                        ths.m_move_snap_offset_x = ( ths.m_selected.x - adjusted_selected_x);
+
+                        /* align selection for drawing */
+                        ths.snap_x( &ths.m_selected.x );
+
+                        ths.m_current_x = ths.m_drop_x = snapped_x;
+                    }
+
+                    /* middle mouse button, or left-ctrl click (for 2button mice) */
+                    if ( a_ev->button == 2 ||
+                         (a_ev->button == 1 && (a_ev->state & GDK_CONTROL_MASK)) ){
+
+                        /* moving, normal x */
+                        //m_current_x = m_drop_x = norm_x;
+                        //convert_xy( m_drop_x, m_drop_y, &tick_s, &note_h );
+
+                        ths.m_growing = true;
+
+                        /* get the box that selected elements are in */
+                        ths.m_seq->get_selected_box( &tick_s, &note_h,
+                                    &tick_f, &note_l );
+
+                        ths.convert_tn_box_to_rect( tick_s, tick_f, note_h, note_l,
+                                &ths.m_selected.x,
+                                &ths.m_selected.y,
+                                &ths.m_selected.width,
+                                &ths.m_selected.height );
+
+                    }
+                }
+           }
+       }
+
+        /*     right mouse button      */
+        if ( a_ev->button == 3 ){
+            set_adding( true, ths );
+        }
+
+   }
+
+    /* if they clicked, something changed */
+    if ( needs_update ){
+
+        ////printf( "needs update\n" );
+        ths.m_seq->set_dirty();
+        //redraw_events();
+    }
+    return true;
+}
+
+bool Seq24SeqRollInput::on_button_release_event(GdkEventButton* a_ev, seqroll& ths)
+{
+    long tick_s;
+    long tick_f;
+    int note_h;
+    int note_l;
+    int x,y,w,h;
+    int numsel;
+
+    bool needs_update = false;
+
+    ths.m_current_x = (int) (a_ev->x + ths.m_scroll_offset_x );
+    ths.m_current_y = (int) (a_ev->y + ths.m_scroll_offset_y );
+
+    ths.snap_y ( &ths.m_current_y );
+
+    if ( ths.m_moving )
+        ths.snap_x( &ths.m_current_x );
+
+    int delta_x = ths.m_current_x - ths.m_drop_x;
+    int delta_y = ths.m_current_y - ths.m_drop_y;
+
+    long delta_tick;
+    int delta_note;
+
+    if ( a_ev->button == 1 ){
+
+        if ( ths.m_selecting ){
+
+            ths.xy_to_rect ( ths.m_drop_x,
+                    ths.m_drop_y,
+                    ths.m_current_x,
+                    ths.m_current_y,
+                    &x, &y,
+                    &w, &h );
+
+            ths.convert_xy( x,     y, &tick_s, &note_h );
+            ths.convert_xy( x+w, y+h, &tick_f, &note_l );
+
+            numsel = ths.m_seq->select_note_events( tick_s, note_h, tick_f, note_l, sequence::e_select );
+
+            needs_update = true;
+        }
+
+        if (  ths.m_moving  ){
+
+            /* adjust for snap */
+            delta_x -= ths.m_move_snap_offset_x;
+
+            /* convert deltas into screen corridinates */
+            ths.convert_xy( delta_x, delta_y, &delta_tick, &delta_note );
+
+            /* since delta_note was from delta_y, it will be filpped
+               ( delta_y[0] = note[127], etc.,so we have to adjust */
+            delta_note = delta_note - (c_num_keys-1);
+
+            ths.m_seq->push_undo();
+            ths.m_seq->move_selected_notes( delta_tick, delta_note );
+            needs_update = true;
+        }
+
+   }
+
+    if ( a_ev->button == 2 || a_ev->button == 1 ){
+
+        if ( ths.m_growing ){
+
+            /* convert deltas into screen corridinates */
+            ths.convert_xy( delta_x, delta_y, &delta_tick, &delta_note );
+            ths.m_seq->push_undo();
+
+            if ( a_ev->state & GDK_SHIFT_MASK )
+            {
+                ths.m_seq->stretch_selected( delta_tick );
+            }
+            else
+            {
+                ths.m_seq->grow_selected( delta_tick );
+            }
+
+            needs_update = true;
+        }
+    }
+
+    if ( a_ev->button == 3 ){
+        set_adding( false, ths );
+    }
+
+    /* turn off */
+    ths.m_selecting = false;
+    ths.m_moving = false;
+    ths.m_growing = false;
+    ths.m_paste = false;
+    ths.m_moving_init = false;
+    ths.m_painting = false;
+
+    ths.m_seq->unpaint_all();
+
+    /* if they clicked, something changed */
+    if (  needs_update ){
+
+        ////printf( "needs_update2\n" );
+
+        ths.m_seq->set_dirty();
+        //redraw_events();
+
+    }
+    return true;
+}
+
+bool Seq24SeqRollInput::on_motion_notify_event(GdkEventMotion* a_ev, seqroll& ths)
+{
+    ths.m_current_x = (int) (a_ev->x  + ths.m_scroll_offset_x );
+    ths.m_current_y = (int) (a_ev->y  + ths.m_scroll_offset_y );
+
+    int note;
+    long tick;
+
+    if ( ths.m_moving_init ){
+        ths.m_moving_init = false;
+        ths.m_moving = true;
+    }
+
+
+    ths.snap_y( &ths.m_current_y );
+    ths.convert_xy( 0, ths.m_current_y, &tick, &note );
+
+    ths.m_seqkeys_wid->set_hint_key( note );
+
+    if ( ths.m_selecting || ths.m_moving || ths.m_growing || ths.m_paste ){
+
+        if ( ths.m_moving || ths.m_paste ){
+            ths.snap_x( &ths.m_current_x );
+        }
+
+        ths.draw_selection_on_window();
+        return true;
+
+    }
+
+    if ( ths.m_painting )
+    {
+        ths.snap_x( &ths.m_current_x );
+        ths.convert_xy( ths.m_current_x, ths.m_current_y, &tick, &note );
+
+        ths.m_seq->add_note( tick, ths.m_note_length - 2, note, true );
+        return true;
+    }
+
+    return false;
+}
diff -rupN seq24-rev48 adding win32/src/seqroll.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqroll.h
--- seq24-rev48 adding win32/src/seqroll.h	2009-05-20 20:30:47.609375000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/seqroll.h	2009-05-20 20:30:56.343750000 -0500
@@ -53,12 +53,43 @@ class rect
     int x, y, height, width;
 };
 
+class seqroll;
+struct FruitySeqRollInput
+{
+    FruitySeqRollInput() : m_adding( false ), m_canadd( true ), m_erase_painting( false )
+    {}
+    bool on_button_press_event(GdkEventButton* a_ev, seqroll& ths);
+    bool on_button_release_event(GdkEventButton* a_ev, seqroll& ths);
+    bool on_motion_notify_event(GdkEventMotion* a_ev, seqroll& ths);
+    void updateMousePtr(seqroll& ths);
+    bool m_adding;
+    bool m_canadd;
+    bool m_erase_painting;
+    long m_drag_paste_start_pos[2];
+};
+struct Seq24SeqRollInput
+{
+    Seq24SeqRollInput() : m_adding( false )
+    {}
+    bool on_button_press_event(GdkEventButton* a_ev, seqroll& ths);
+    bool on_button_release_event(GdkEventButton* a_ev, seqroll& ths);
+    bool on_motion_notify_event(GdkEventMotion* a_ev, seqroll& ths);
+    void set_adding( bool a_adding, seqroll& ths );
+    bool m_adding;
+};
+
 
 /* piano roll */
 class seqroll : public Gtk::DrawingArea
 {
 
  private: 
+    friend struct FruitySeqRollInput;
+    FruitySeqRollInput m_fruity_interaction;
+
+    friend struct Seq24SeqRollInput;
+    Seq24SeqRollInput m_seq24_interaction;
+
 
     Glib::RefPtr<Gdk::GC> m_gc;
     Glib::RefPtr<Gdk::Window>   m_window;
@@ -98,9 +129,11 @@ class seqroll : public Gtk::DrawingArea
     bool m_moving;
     bool m_moving_init;
     bool m_growing;
-    bool m_adding;
     bool m_painting;
     bool m_paste;
+    bool m_is_drag_pasting;
+    bool m_is_drag_pasting_start;
+    bool m_justselected_one;
 
     /* where the dragging started */
     int m_drop_x; 
@@ -194,7 +227,6 @@ class seqroll : public Gtk::DrawingArea
     int idle_redraw();
 
     void draw_progress_on_window();
-    void set_adding( bool a_adding );
 
     void start_paste( );
     
diff -rupN seq24-rev48 adding win32/src/sequence.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/sequence.cpp
--- seq24-rev48 adding win32/src/sequence.cpp	2009-05-20 20:30:47.875000000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/sequence.cpp	2009-05-20 20:30:56.609375000 -0500
@@ -81,7 +81,7 @@ sequence::pop_undo( void )
     lock();
 
     if (m_list_undo.size() > 0 ){
-		m_list_redo.push( m_list_event );
+    m_list_redo.push( m_list_event );
         m_list_event = m_list_undo.top();
         m_list_undo.pop();
         verify_and_link();
@@ -97,7 +97,7 @@ sequence::pop_redo( void )
     lock();
 
     if (m_list_redo.size() > 0 ){
-		m_list_undo.push( m_list_event );
+        m_list_undo.push( m_list_event );
         m_list_event = m_list_redo.top();
         m_list_redo.pop();
         verify_and_link();
@@ -117,7 +117,7 @@ sequence::push_trigger_undo( void )
     
     for ( i  = m_list_trigger_undo.top().begin();
           i != m_list_trigger_undo.top().end(); i++ ){
-	  (*i).m_selected = false;
+      (*i).m_selected = false;
     }
 
     
@@ -132,7 +132,7 @@ sequence::pop_trigger_undo( void )
 
     if (m_list_trigger_undo.size() > 0 ){
 
-		m_list_trigger_redo.push( m_list_trigger );
+    m_list_trigger_redo.push( m_list_trigger );
         m_list_trigger = m_list_trigger_undo.top();
         m_list_trigger_undo.pop();
     }
@@ -448,84 +448,84 @@ sequence::verify_and_link()
     lock();
 
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
-	(*i).clear_link();
+    (*i).clear_link();
         (*i).unmark();
     }
 
     on = m_list_event.begin();
-	    
+
     /* pair ons and offs */
     while ( on != m_list_event.end() ){
 
-	/* check for a note on, then look for its
-	   note off */
-	if ( (*on).is_note_on() ){
-
-	    /* get next possible off node */
-	    off = on; off++;
-	    end_found = false;
-	    
-	    while ( off != m_list_event.end() ){
-		
-		/* is a off event, == notes, and isnt
-		   markeded  */
-		if ( (*off).is_note_off()                  &&
-		     (*off).get_note() == (*on).get_note() && 
-		     ! (*off).is_marked()                  ){
-
-		    /* link + mark */
-		    (*on).link( &(*off) );
-		    (*off).link( &(*on) );
-		    (*on).mark(  );
-		    (*off).mark( );
-		    end_found = true;
+    /* check for a note on, then look for its
+       note off */
+    if ( (*on).is_note_on() ){
+
+        /* get next possible off node */
+        off = on; off++;
+        end_found = false;
+
+        while ( off != m_list_event.end() ){
+
+        /* is a off event, == notes, and isnt
+           markeded  */
+        if ( (*off).is_note_off()                  &&
+             (*off).get_note() == (*on).get_note() &&
+             ! (*off).is_marked()                  ){
+
+            /* link + mark */
+            (*on).link( &(*off) );
+            (*off).link( &(*on) );
+            (*on).mark(  );
+            (*off).mark( );
+            end_found = true;
 
-		    break;
-		}
-		off++;
-	    }
-	    if (!end_found) {
-		off = m_list_event.begin();
-		while ( off != on){
-		    if ( (*off).is_note_off()                  &&
-		         (*off).get_note() == (*on).get_note() && 
-		         ! (*off).is_marked()                  ){
-
-		        /* link + mark */
-		        (*on).link( &(*off) );
-		        (*off).link( &(*on) );
-		        (*on).mark(  );
-		        (*off).mark( );
-		        end_found = true;
-
-		        break;
-		    }
-  		    off++;
-		     
-		}
-	    }
-	}    
-	on++;
+            break;
+        }
+        off++;
+        }
+        if (!end_found) {
+        off = m_list_event.begin();
+        while ( off != on){
+            if ( (*off).is_note_off()                  &&
+                 (*off).get_note() == (*on).get_note() &&
+                 ! (*off).is_marked()                  ){
+
+                /* link + mark */
+                (*on).link( &(*off) );
+                (*off).link( &(*on) );
+                (*on).mark(  );
+                (*off).mark( );
+                end_found = true;
+
+                break;
+            }
+              off++;
+
+        }
+        }
+    }
+    on++;
     }
 
     /* unmark all */
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
-	(*i).unmark();
+    (*i).unmark();
     }
 
     /* kill those not in range */
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
-	
-	/* if our current time stamp is greater then the length */
 
-	if ( (*i).get_timestamp() >= m_length ||
-	     (*i).get_timestamp() < 0            ){
-	    
-	    /* we have to prune it */
-	    (*i).mark();
-	    if ( (*i).is_linked() )
-		(*i).get_linked()->mark();
-	}
+    /* if our current time stamp is greater then the length */
+
+    if ( (*i).get_timestamp() >= m_length ||
+         (*i).get_timestamp() < 0            ){
+
+        /* we have to prune it */
+        (*i).mark();
+        if ( (*i).is_linked() )
+        (*i).get_linked()->mark();
+    }
     }
 
     remove_marked( );
@@ -550,63 +550,99 @@ sequence::link_new( )
     /* pair ons and offs */
     while ( on != m_list_event.end()){
 
-	/* check for a note on, then look for its
-	   note off */
-	if ( (*on).is_note_on() &&
-	     ! (*on).is_linked() ){
-	    
-	    /* get next element */
-	    off = on; off++;
-	    end_found = false;
-	    while ( off != m_list_event.end()){
-
-		/* is a off event, == notes, and isnt
-		   selected  */
-		if ( (*off).is_note_off()                    &&
-		     (*off).get_note() == (*on).get_note() && 
-		     ! (*off).is_linked()                    ){
-		    
-		    /* link */
-		    (*on).link( &(*off) );
-		    (*off).link( &(*on) );
-		    end_found = true;
-		    
-		    break;
-		}
-		off++;
-	    }
+    /* check for a note on, then look for its
+       note off */
+    if ( (*on).is_note_on() &&
+         ! (*on).is_linked() ){
+
+        /* get next element */
+        off = on; off++;
+        end_found = false;
+        while ( off != m_list_event.end()){
+
+            /* is a off event, == notes, and isnt
+                  selected  */
+            if ( (*off).is_note_off()                    &&
+                 (*off).get_note() == (*on).get_note() &&
+                 ! (*off).is_linked()                    ){
+
+                /* link */
+                (*on).link( &(*off) );
+                (*off).link( &(*on) );
+            end_found = true;
 
-	    if (!end_found) {
+            break;
+        }
+        off++;
+        }
+
+        if (!end_found) {
                 off = m_list_event.begin();
-	        while ( off != on){
+            while ( off != on){
 
-		    /* is a off event, == notes, and isnt
-   		       selected  */
-		    if ( (*off).is_note_off()                    &&
-		         (*off).get_note() == (*on).get_note() && 
-		         ! (*off).is_linked()                    ){
-		    
-		        /* link */
-		        (*on).link( &(*off) );
-		        (*off).link( &(*on) );
-			end_found = true;
-		    
-		        break;
-		}
-		off++;
-	    }
-	    }
-	}    
-	on++;
+            /* is a off event, == notes, and isnt
+                  selected  */
+            if ( (*off).is_note_off()                    &&
+                 (*off).get_note() == (*on).get_note() &&
+                 ! (*off).is_linked()                    ){
+
+                /* link */
+                (*on).link( &(*off) );
+                (*off).link( &(*on) );
+            end_found = true;
+
+                break;
+        }
+        off++;
+        }
+        }
+    }
+    on++;
     }
     unlock();
 }
 
 
 
+// helper function, does not lock/unlock, unsafe to call without them
+// supply iterator from m_list_event...
+// lock();  remove();  reset_draw_marker(); unlock()
+void
+sequence::remove(list<event>::iterator i)
+{
+    /* if its a note off, and that note is currently
+       playing, send a note off */
+    if ( (*i).is_note_off()  &&
+     m_playing_notes[ (*i).get_note()] > 0 ){
+
+        m_masterbus->play( m_bus, &(*i), m_midi_channel );
+        m_playing_notes[(*i).get_note()]--;
+    }
+    m_list_event.erase(i);
+}
+
+// helper function, does not lock/unlock, unsafe to call without them
+// supply iterator from m_list_event...
+// lock();  remove();  reset_draw_marker(); unlock()
+// finds e in m_list_event, removes the first iterator matching that.
+void
+sequence::remove( event* e )
+{
+    list<event>::iterator i = m_list_event.begin();
+    while( i != m_list_event.end() )
+    {
+        if (e == &(*i))
+        {
+            remove( i );
+            return;
+        }
+        ++i;
+    }
+}
+
 
 void 
-sequence::remove_marked( )
+sequence::remove_marked()
 {
     list<event>::iterator i, t;
 
@@ -614,26 +650,17 @@ sequence::remove_marked( )
 
     i = m_list_event.begin();
     while( i != m_list_event.end() ){
-	
-	if ((*i).is_marked()){
-	    
-	    /* if its a note off, and that note is currently
-	       playing, send a note off */
-	    if ( (*i).is_note_off()  &&
-		 m_playing_notes[ (*i).get_note()] > 0 ){
-		
-                m_masterbus->play( m_bus, &(*i), m_midi_channel );
-                m_playing_notes[(*i).get_note()]--;
-	    }
-	   
-	    t = i; t++;
-	    m_list_event.erase(i);
-	    i = t;
-	}
-	else {
 
-	    i++;
-	}
+    if ((*i).is_marked()){
+
+        t = i; t++;
+        remove( i );
+        i = t;
+    }
+    else {
+
+        i++;
+    }
     }
 
     reset_draw_marker();
@@ -652,8 +679,8 @@ sequence::mark_selected( )
 
     i = m_list_event.begin();
     while( i != m_list_event.end() ){
-	
-	if ((*i).is_selected()){
+
+    if ((*i).is_selected()){
 
         (*i).mark();
     }
@@ -683,7 +710,7 @@ sequence::unpaint_all( )
 /* returns the 'box' of the selected items */
 void 
 sequence::get_selected_box( long *a_tick_s, int *a_note_h, 
-			    long *a_tick_f, int *a_note_l )
+                long *a_tick_f, int *a_note_l )
 {
 
     list<event>::iterator i;
@@ -701,18 +728,20 @@ sequence::get_selected_box( long *a_tick
 
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
 
-	if( (*i).is_selected() ){
-	    
-	    time = (*i).get_timestamp();
-	    
-	    if ((*i).is_note_on() && ( time < *a_tick_s )) *a_tick_s = time;
-	    if ((*i).is_note_off() && (time > *a_tick_f )) *a_tick_f = time;
-	    
-	    note = (*i).get_note();
+        if( (*i).is_selected() ){
 
-	    if ( note < *a_note_l ) *a_note_l = note;
-	    if ( note > *a_note_h ) *a_note_h = note;
-	}
+            time = (*i).get_timestamp();
+
+            // can't check on/off here. screws up seqevent
+            // selection which has no "off"
+            if ( time < *a_tick_s ) *a_tick_s = time;
+            if ( time > *a_tick_f ) *a_tick_f = time;
+
+            note = (*i).get_note();
+
+            if ( note < *a_note_l ) *a_note_l = note;
+            if ( note > *a_note_h ) *a_note_h = note;
+        }
     }
     
     unlock();
@@ -720,7 +749,7 @@ sequence::get_selected_box( long *a_tick
 
 void 
 sequence::get_clipboard_box( long *a_tick_s, int *a_note_h, 
-			     long *a_tick_f, int *a_note_l )
+                 long *a_tick_f, int *a_note_l )
 {
 
     list<event>::iterator i;
@@ -737,20 +766,20 @@ sequence::get_clipboard_box( long *a_tic
     lock();
 
     if ( m_list_clipboard.size() == 0 ) {
-	*a_tick_s = *a_tick_f = *a_note_h = *a_note_l = 0; 
+    *a_tick_s = *a_tick_f = *a_note_h = *a_note_l = 0;
     }
 
     for ( i = m_list_clipboard.begin(); i != m_list_clipboard.end(); i++ ){
-	
-	time = (*i).get_timestamp();
-	
-	if ( time < *a_tick_s ) *a_tick_s = time;
-	if ( time > *a_tick_f ) *a_tick_f = time;
-	
-	note = (*i).get_note();
-	
-	if ( note < *a_note_l ) *a_note_l = note;
-	if ( note > *a_note_h ) *a_note_h = note;
+
+    time = (*i).get_timestamp();
+
+    if ( time < *a_tick_s ) *a_tick_s = time;
+    if ( time > *a_tick_f ) *a_tick_f = time;
+
+    note = (*i).get_note();
+
+    if ( note < *a_note_l ) *a_note_l = note;
+    if ( note > *a_note_h ) *a_note_h = note;
     }
    
     unlock();
@@ -828,32 +857,30 @@ sequence::select_note_events( long a_tic
 
     lock();
 
-    for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
+    for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ) {
 
         if( (*i).get_note()      <= a_note_h &&
             (*i).get_note()      >= a_note_l ) {
-	    
-	    if ( (*i).is_linked() ) {
+
+            if ( (*i).is_linked() ) {
                 event *ev = (*i).get_linked();
-		
-	        if ( (*i).is_note_off() ) {
-		    tick_s = ev->get_timestamp();
-		    tick_f = (*i).get_timestamp();
-		}
-		
-	        if ( (*i).is_note_on() ) {
-		    tick_f = ev->get_timestamp();
-		    tick_s = (*i).get_timestamp();
-	        }
-
-		if (
-		   (    (tick_s <= tick_f) && 
-		        ((tick_s <= a_tick_f) && (tick_f >= a_tick_s)) ) ||
-		   (    (tick_s > tick_f) && 
-			((tick_s <= a_tick_f) || (tick_f >= a_tick_s)) ) )
-		
-		{
-			
+
+                if ( (*i).is_note_off() ) {
+                    tick_s = ev->get_timestamp();
+                    tick_f = (*i).get_timestamp();
+                }
+
+                if ( (*i).is_note_on() ) {
+                    tick_f = ev->get_timestamp();
+                    tick_s = (*i).get_timestamp();
+                }
+
+                if (
+                       (    (tick_s <= tick_f) &&
+                            ((tick_s <= a_tick_f) && (tick_f >= a_tick_s)) ) ||
+                       (    (tick_s > tick_f) &&
+                        ((tick_s <= a_tick_f) || (tick_f >= a_tick_s)) ) )
+                {
                     if ( a_action == e_select ||
                          a_action == e_select_one )
                     {
@@ -876,32 +903,89 @@ sequence::select_note_events( long a_tic
                         ret = 1;
                         break;
                     }
-		    
-		}
-
-			
-  	    } else {
-		tick_s = tick_f = (*i).get_timestamp();    
-                if ( tick_s  >= a_tick_s - 16 && tick_f <= a_tick_f) {
-
-                    if ( a_action == e_select || a_action == e_select_one ) {
+                    if ( a_action == e_deselect )
+                    {
+                        ret = 0;
+                        (*i).unselect( );
+                        ev->unselect();
+                        //break;
+                    }
+                    if ( a_action == e_toggle_selection &&
+                         (*i).is_note_on()) // don't toggle twice
+                    {
+                        if ((*i).is_selected())
+                        {
+                            (*i).unselect( );
+                            ev->unselect();
+                            ret ++;
+                        }
+                        else
+                        {
+                            (*i).select();
+                            ev->select();
+                            ret ++;
+                        }
+                    }
+                    if ( a_action == e_remove_one )
+                    {
+                        remove( &(*i) );
+                        remove( ev );
+                        reset_draw_marker();
+                        ret++;
+                        break;
+                    }
+                }
+            } else {
+                tick_s = tick_f = (*i).get_timestamp();
+                if ( tick_s  >= a_tick_s - 16 && tick_f <= a_tick_f)
+                {
+                    if ( a_action == e_select || a_action == e_select_one )
+                    {
                         (*i).select( );
                         ret++;
                         if ( a_action == e_select_one )
                             break;
                     }
-                    if ( a_action == e_is_selected ) {
-                        if ( (*i).is_selected()) {
+                    if ( a_action == e_is_selected )
+                    {
+                        if ( (*i).is_selected())
+                        {
                             ret = 1;
                             break;
                         }
                     }
-                    if ( a_action == e_would_select ) {
+                    if ( a_action == e_would_select )
+                    {
                         ret = 1;
                         break;
                     }
+                    if ( a_action == e_deselect )
+                    {
+                        ret = 0;
+                        (*i).unselect();
+                    }
+                    if ( a_action == e_toggle_selection )
+                    {
+                        if ((*i).is_selected())
+                        {
+                            (*i).unselect();
+                            ret ++;
+                        }
+                        else
+                        {
+                            (*i).select();
+                            ret ++;
+                        }
+                    }
+                    if ( a_action == e_remove_one )
+                    {
+                         remove( &(*i) );
+                         reset_draw_marker();
+                         ret++;
+                         break;
+                    }
                 }
-	    }
+            }
         }
     }
 
@@ -914,7 +998,7 @@ sequence::select_note_events( long a_tic
    selected */
 int 
 sequence::select_events( long a_tick_s, long a_tick_f, 
-			 unsigned char a_status, 
+			 unsigned char a_status,
 			 unsigned char a_cc, select_action_e a_action)
 {
     int ret=0;
@@ -957,6 +1041,28 @@ sequence::select_events( long a_tick_s, 
                     ret = 1;
                     break;
                 }
+                if ( a_action == e_toggle_selection )
+                {
+                    if ( (*i).is_selected())
+                    {
+                        (*i).unselect( );
+                    }
+                    else
+                    {
+                        (*i).select( );
+                    }
+                }
+                if ( a_action == e_deselect )
+                {
+                    (*i).unselect( );
+                }
+                if ( a_action == e_remove_one )
+                {
+                     remove( &(*i) );
+                     reset_draw_marker();
+                     ret++;
+                     break;
+                }
             }
         }
     }
@@ -1243,22 +1349,22 @@ sequence::increment_selected( unsigned c
     list<event>::iterator i;
 
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
-	
+
         if ( (*i).is_selected() &&
              (*i).get_status() == a_status ){
-			
+
             if ( a_status == EVENT_NOTE_ON || 
                  a_status == EVENT_NOTE_OFF || 
                  a_status == EVENT_AFTERTOUCH ||
                  a_status == EVENT_CONTROL_CHANGE || 
                  a_status == EVENT_PITCH_WHEEL ){
-				
+
                 (*i).increment_data2();
             }
-			
+
             if ( a_status == EVENT_PROGRAM_CHANGE ||
                  a_status == EVENT_CHANNEL_PRESSURE ){
-				
+
                 (*i).increment_data1();
             }
         }
@@ -1276,25 +1382,25 @@ sequence::decrement_selected(unsigned ch
     list<event>::iterator i;
 
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
-	
+
         if ( (*i).is_selected() &&
              (*i).get_status() == a_status ){
-			
+
             if ( a_status == EVENT_NOTE_ON || 
                  a_status == EVENT_NOTE_OFF || 
                  a_status == EVENT_AFTERTOUCH ||
                  a_status == EVENT_CONTROL_CHANGE || 
                  a_status == EVENT_PITCH_WHEEL ){
-				
+
                 (*i).decrement_data2();
             }
-			
+
             if ( a_status == EVENT_PROGRAM_CHANGE ||
                  a_status == EVENT_CHANNEL_PRESSURE ){
-				
+
                 (*i).decrement_data1();
             }
-			
+
         }
     }
         
@@ -1345,7 +1451,7 @@ sequence::paste_selected( long a_tick, i
 
     if ((*clipboard.begin()).is_note_on() ||
 	(*clipboard.begin()).is_note_off() ){
-	
+
 	for ( i = clipboard.begin(); i != clipboard.end(); i++ )
 	    if ( (*i).get_note( ) > highest_note ) highest_note = (*i).get_note();
 
@@ -1390,7 +1496,7 @@ sequence::change_event_data_range( long 
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
 
         /* initially false */
-        bool set = false;	
+        bool set = false;
         (*i).get_data( &d0, &d1 );
 
         /* correct status and not CC */
@@ -1465,7 +1571,7 @@ sequence::change_event_data_range( long 
                 d1 = newdata;
 
             (*i).set_data( d0, d1 );
-        }	    
+        }
     }
 
     unlock();
@@ -1481,6 +1587,7 @@ sequence::add_note( long a_tick, long a_
     lock();
 
     event e;
+    bool ignore = false;
     
     if ( a_tick >= 0 && 
          a_note >= 0 &&
@@ -1498,6 +1605,12 @@ sequence::add_note( long a_tick, long a_
                      (*i).is_note_on() &&
                      (*i).get_timestamp() == a_tick )
                 {
+                    if ((*i).get_note() == a_note )
+                    {
+                        ignore = true;
+                        break;
+                    }
+                     
                     (*i).mark();
 
                     if ( (*i).is_linked())
@@ -1512,6 +1625,8 @@ sequence::add_note( long a_tick, long a_
             remove_marked();
         }                        
         
+        if ( !ignore )
+        {
 
         if ( a_paint )
             e.paint();
@@ -1528,6 +1643,7 @@ sequence::add_note( long a_tick, long a_
 
         add_event( &e );
     }
+    }
 
     verify_and_link();
     unlock();
@@ -1599,7 +1715,7 @@ sequence::stream_event(  event *a_ev  )
     a_ev->mod_timestamp( m_length );
 
     if ( m_recording ){
-	
+
         add_event( a_ev );
         set_dirty();
     }
@@ -1612,10 +1728,10 @@ sequence::stream_event(  event *a_ev  )
     link_new();
 
     if ( m_quanized_rec ){
-	if (a_ev->is_note_off()) {
-	    select_note_events( a_ev->get_timestamp(), a_ev->get_note(), 
-			        a_ev->get_timestamp(), a_ev->get_note(), e_select);
-            quanize_events( EVENT_NOTE_ON, 0, m_snap_tick, 1 , true );
+        if (a_ev->is_note_off()) {
+           select_note_events( a_ev->get_timestamp(), a_ev->get_note(),
+             a_ev->get_timestamp(), a_ev->get_note(), e_select);
+           quanize_events( EVENT_NOTE_ON, 0, m_snap_tick, 1 , true );
 	    
 	}
     }
@@ -1818,6 +1934,89 @@ sequence::add_trigger( long a_tick, long
     unlock();
 }
 
+bool sequence::intersectTriggers( long position, long& start, long& end )
+{
+    lock();
+    
+    list<trigger>::iterator i = m_list_trigger.begin();
+    while ( i != m_list_trigger.end() )
+    {
+        if ((*i).m_tick_start <= position && position <= (*i).m_tick_end)
+        {
+            start = (*i).m_tick_start;
+            end = (*i).m_tick_end;
+            unlock();
+            return true;
+        }
+        ++i;
+    }
+    
+    unlock();
+    return false;
+}
+
+bool sequence::intersectNotes( long position, long position_note, long& start, long& end, long& note )
+{
+    lock();
+    
+    list<event>::iterator on = m_list_event.begin();
+    list<event>::iterator off = m_list_event.begin();
+    while ( on != m_list_event.end() )
+    {
+        if (position_note == (*on).get_note() &&
+            (*on).is_note_on())
+        {
+            // find next "off" event for the note
+            off = on; ++off;
+            while (off != m_list_event.end() &&
+                   ((*on).get_note() != (*off).get_note() || (*off).is_note_on()))
+            {
+                ++off;
+            }
+            if ((*on).get_note() == (*off).get_note() && (*off).is_note_off() &&
+                (*on).get_timestamp() <= position && position <= (*off).get_timestamp())
+            {
+                start = (*on).get_timestamp();
+                end = (*off).get_timestamp();
+                note = (*on).get_note();
+                unlock();
+                return true;
+            }
+
+
+        }
+        ++on;
+    }
+    
+    unlock();
+    return false;
+}
+
+bool sequence::intersectEvents( long posstart, long posend, long status, long& start )
+{
+    lock();
+
+    list<event>::iterator on = m_list_event.begin();
+    while ( on != m_list_event.end() )
+    {
+        //printf( "intersect   looking for:%ld  found:%ld\n", status, (*on).get_status() );
+        if (status == (*on).get_status())
+        {
+            if ((*on).get_timestamp() <= posstart && posstart <= ((*on).get_timestamp()+(posend-posstart)))
+            {
+                start = (*on).get_timestamp();
+                unlock();
+                return true;
+            }
+        }
+        ++on;
+    }
+
+    unlock();
+    return false;
+}
+
+
 void
 sequence::grow_trigger (long a_tick_from, long a_tick_to, long a_length)
 {
@@ -2020,7 +2219,7 @@ L       R    
     lock();
 
     move_triggers( a_start_tick, 
-		   a_distance, 
+		   a_distance,
 		   true );
     
     list<trigger>::iterator i = m_list_trigger.begin();
@@ -2098,7 +2297,7 @@ sequence::split_trigger( long a_tick )
 
 void 
 sequence::move_triggers( long a_start_tick, 
-			 long a_distance, 
+			 long a_distance,
 			 bool a_direction )
 {
 
@@ -2607,28 +2806,28 @@ sequence::get_next_note_event( long *a_t
 	    (*m_iterator_draw).is_linked() ){
 
 	    *a_tick_f   = (*m_iterator_draw).get_linked()->get_timestamp();
-	    
+
 	    ret = DRAW_NORMAL_LINKED;
 	    m_iterator_draw++;
 	    return ret;
 	}
-	
+
 	else if( (*m_iterator_draw).is_note_on() &&
 		 (! (*m_iterator_draw).is_linked()) ){
-	    
+
 	    ret = DRAW_NOTE_ON;
 	    m_iterator_draw++;
 	    return ret;
 	}
-	
+
 	else if( (*m_iterator_draw).is_note_off() &&
 		 (! (*m_iterator_draw).is_linked()) ){
-	    
+
 	    ret = DRAW_NOTE_OFF;
 	    m_iterator_draw++;
 	    return ret;
 	}
-	
+
 	/* keep going until we hit null or find a NoteOn */
 	m_iterator_draw++;
     }
@@ -2665,20 +2864,20 @@ sequence::get_next_event( unsigned char 
 			  bool *a_selected )
 {
     while (  m_iterator_draw  != m_list_event.end() ){ 
-	
+
 	/* note on, so its linked */
 	if( (*m_iterator_draw).get_status() == a_status ){
-	    
+
 	    (*m_iterator_draw).get_data( a_D0, a_D1 );
 	    *a_tick   = (*m_iterator_draw).get_timestamp();
 	    *a_selected = (*m_iterator_draw).is_selected();
-	    
+
 	    /* either we have a control chage with the right CC
 	       or its a different type of event */
 	    if ( (a_status == EVENT_CONTROL_CHANGE &&
 		  *a_D0 == a_cc )
 		 || (a_status != EVENT_CONTROL_CHANGE) ){
-		
+
 		/* we have a good one */
 		/* update and return */
 		m_iterator_draw++;
@@ -2713,7 +2912,7 @@ sequence::remove_all( void )
 {
     lock();
 
-	
+
 
     m_list_event.clear();
 
@@ -2728,7 +2927,7 @@ sequence::operator= (const sequence& a_r
 
     /* dont copy to self */
     if (this != &a_rhs){
-	
+
 	m_list_event   = a_rhs.m_list_event;
 	m_list_trigger   = a_rhs.m_list_trigger;
 
@@ -2858,7 +3057,7 @@ sequence::set_playing( bool a_p )
     {
     
         if (a_p){
-	
+
             /* turn on */
             m_playing = true;
 
@@ -3011,14 +3210,14 @@ sequence::print_triggers()
 
 void 
 sequence::put_event_on_bus( event *a_e )
-{		
+{
     lock();
 
     unsigned char note = a_e->get_note();
     bool skip = false;
-	
+
     if ( a_e->is_note_on() ){
-		
+
         m_playing_notes[note]++;
     }
     if ( a_e->is_note_off() ){
@@ -3028,7 +3227,7 @@ sequence::put_event_on_bus( event *a_e )
         }
         else {
             m_playing_notes[note]--;
-        }		
+        }
     }
 
     if ( !skip ){
@@ -3050,14 +3249,14 @@ sequence::off_playing_notes()
     event e;
     
     for ( int x=0; x< c_midi_notes; x++ ){
-	
+
         while( m_playing_notes[x] > 0 ){
-			
+
             e.set_status( EVENT_NOTE_OFF );
             e.set_data( x, 0 );
-			
+
             m_masterbus->play( m_bus, &e, m_midi_channel ); 
-			
+
             m_playing_notes[x]--;
         }
     }
@@ -3089,20 +3288,20 @@ sequence::select_events( unsigned char a
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
 
 	/* initially false */
-	bool set = false;	
+	bool set = false;
 	(*i).get_data( &d0, &d1 );
-	
+
 	/* correct status and not CC */
 	if ( a_status != EVENT_CONTROL_CHANGE &&
 	     (*i).get_status() == a_status )
 	    set = true;
-	
+
 	/* correct status and correct cc */
 	if ( a_status == EVENT_CONTROL_CHANGE &&
 	     (*i).get_status() == a_status &&
 	     d0 == a_cc )
 	    set = true;
-	
+
         if ( set ){
 
             if ( a_inverse ){
@@ -3114,7 +3313,7 @@ sequence::select_events( unsigned char a
             }
             else 
                 (*i).select( );
-	}	    
+	}
     }
 
     unlock();
@@ -3144,7 +3343,7 @@ sequence::transpose_notes( int a_steps, 
     }
 
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
-	
+
 	/* is it being moved ? */
 	if ( ((*i).get_status() ==  EVENT_NOTE_ON ||
               (*i).get_status() ==  EVENT_NOTE_OFF) &&
@@ -3207,14 +3406,14 @@ sequence::quanize_events( unsigned char 
     for ( i = m_list_event.begin(); i != m_list_event.end(); i++ ){
 
         /* initially false */
-	bool set = false;	
+	bool set = false;
 	(*i).get_data( &d0, &d1 );
-	
+
 	/* correct status and not CC */
 	if ( a_status != EVENT_CONTROL_CHANGE &&
 	     (*i).get_status() == a_status )
 	    set = true;
-	
+
 	/* correct status and correct cc */
 	if ( a_status == EVENT_CONTROL_CHANGE &&
 	     (*i).get_status() == a_status &&
@@ -3223,7 +3422,7 @@ sequence::quanize_events( unsigned char 
 
         if( !(*i).is_marked() )
             set = false;
-	
+
         if ( set ){
 
             /* copy event */
@@ -3244,7 +3443,7 @@ sequence::quanize_events( unsigned char 
 	    if ((timestamp_delta + timestamp) >= m_length) {
 		timestamp_delta = - e.get_timestamp() ;
 	    }
-	    
+
             e.set_timestamp( e.get_timestamp() + timestamp_delta );
             quantized_events.push_front(e);
             
@@ -3290,7 +3489,7 @@ addListVar( list<char> *a_list, long a_v
     }
 
     while (true){
-	
+
 	a_list->push_front( (char) buffer & 0xFF );
 
 	if (buffer & 0x80)
@@ -3337,7 +3536,7 @@ sequence::fill_list( list<char> *a_list,
     a_list->push_front( length );
 
     for ( int i=0; i< length; i++ )
-	a_list->push_front( m_name.c_str()[i] );	
+	a_list->push_front( m_name.c_str()[i] );
  
     long timestamp = 0, delta_time = 0, prev_timestamp = 0;
     list<event>::iterator i;
@@ -3359,28 +3558,28 @@ sequence::fill_list( list<char> *a_list,
 
 	switch( e.m_status & 0xF0 ){
 
-            case 0x80:	  
+            case 0x80:
             case 0x90:
             case 0xA0:
             case 0xB0:
             case 0xE0: 
-	    
+
                 a_list->push_front(  e.m_data[0] );
                 a_list->push_front(  e.m_data[1] );
 
                 //printf ( "- d[%2X %2X]\n" , e.m_data[0], e.m_data[1] ); 
 
                 break;
-	    
+
             case 0xC0:
             case 0xD0:
-	    
+
                 a_list->push_front(  e.m_data[0] );
 
                 //printf ( "- d[%2X]\n" , e.m_data[0] ); 
 
                 break;
-	    
+
             default: 
                 break;
 	}
diff -rupN seq24-rev48 adding win32/src/sequence.h seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/sequence.h
--- seq24-rev48 adding win32/src/sequence.h	2009-05-20 20:30:47.906250000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/sequence.h	2009-05-20 20:30:56.640625000 -0500
@@ -174,6 +174,9 @@ class sequence
     void split_trigger( trigger &trig, long a_split_tick);
     void adjust_trigger_offsets_to_legnth( long a_new_len );
     long adjust_offset( long a_offset );
+    void remove( list<event>::iterator i );
+    void remove( event* e );
+
 
   public:
 
@@ -298,6 +301,11 @@ class sequence
     bool select_trigger(long a_tick);
     bool unselect_triggers (void);
 
+    bool intersectTriggers( long position, long& start, long& end );
+    bool intersectNotes( long position, long position_note, long& start, long& end, long& note );
+    bool intersectEvents( long posstart, long posend, long status, long& start );
+
+
     void del_selected_trigger( void );
     void cut_selected_trigger( void );
     void copy_selected_trigger( void );
@@ -327,7 +335,10 @@ class sequence
         e_select,
         e_select_one,
         e_is_selected,
-        e_would_select
+        e_would_select,
+        e_deselect, // deselect under cursor
+        e_toggle_selection, // sel/deselect under cursor
+        e_remove_one // remove one note under cursor
     };
     
     /* select note events in range, returns number
diff -rupN seq24-rev48 adding win32/src/stamp-h.in seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/stamp-h.in
--- seq24-rev48 adding win32/src/stamp-h.in	1969-12-31 18:00:00.000000000 -0600
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/stamp-h.in	2009-03-15 22:49:41.286393000 -0500
@@ -0,0 +1 @@
+timestamp
diff -rupN seq24-rev48 adding win32/src/userfile.cpp seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/userfile.cpp
--- seq24-rev48 adding win32/src/userfile.cpp	2009-05-20 20:30:47.937500000 -0500
+++ seq24-rev48 win32 midiclock mouseinteraction adding mutegroups+keybind/src/userfile.cpp	2009-05-20 20:30:56.671875000 -0500
@@ -109,11 +109,11 @@ userfile::parse( perform *a_perf )
 #if 0
     line_after( &file, "[midi-control]" );
 
-    int sequences = 0;
-    sscanf( m_line, "%d", &sequences );
+    unsigned int sequences = 0;
+    sscanf( m_line, "%u", &sequences );
     next_data_line( &file );
 
-    for ( int i=0; i<sequences; ++i ){
+    for ( unsigned int i=0; i<sequences; ++i ){
 
         int sequence = 0;
         
@@ -144,6 +144,35 @@ userfile::parse( perform *a_perf )
          
         next_data_line( &file );
     }
+    /* group midi control */
+    line_after( &file, "[mute-group]");
+
+    int gtrack = 0;
+    sscanf( m_line, "%d", &gtrack );
+    next_data_line( &file );
+   
+    int mtx[c_seqs_in_set], j=0;
+    for (int i=0; i< c_seqs_in_set; i++) {
+        a_perf->select_group_mute(j);
+        sscanf (m_line, "%d [%d %d %d %d %d %d %d %d] [%d %d %d %d %d %d %d %d] [%d %d %d %d %d %d %d %d] [%d %d %d %d %d %d %d %d]",
+            &j,
+            &mtx[0], &mtx[1], &mtx[2], &mtx[3],
+            &mtx[4], &mtx[5], &mtx[6], &mtx[7],
+           
+            &mtx[8], &mtx[9], &mtx[10], &mtx[11],
+            &mtx[12], &mtx[13], &mtx[14], &mtx[15],
+             
+            &mtx[16], &mtx[17], &mtx[18], &mtx[19],
+            &mtx[20], &mtx[21], &mtx[22], &mtx[23],
+             
+            &mtx[24], &mtx[25], &mtx[26], &mtx[27],
+            &mtx[28], &mtx[29], &mtx[30], &mtx[31]);
+        for (int k=0; k< c_seqs_in_set; k++) {
+            a_perf->set_group_mute_state(k, mtx[k]);
+        }
+        j++;
+        next_data_line( &file );
+    }
 
     line_after( &file, "[midi-clock]" );
     long buses = 0;
@@ -171,23 +200,54 @@ userfile::parse( perform *a_perf )
         
         long key = 0, seq = 0;
         sscanf( m_line, "%ld %ld", &key, &seq );
-        a_perf->key_events[key] = seq;
+        a_perf->set_key_event( key, seq );
         next_data_line( &file );
     }
+    line_after( &file, "[keyboard-group]" );
+    long groups = 0;
+    sscanf( m_line, "%ld", &groups );
+    next_data_line( &file );
+
+    a_perf->key_groups.clear();
+   
+   
+    for ( int i=0; i<groups; ++i ){
+       
+        long key = 0, group = 0;
+        sscanf( m_line, "%ld %ld", &key, &group );
+        a_perf->set_key_group( key, group );
+        next_data_line( &file );
+    }
+
 
-    sscanf( m_line, "%ld %ld", &a_perf->m_key_bpm_up,
+
+    sscanf( m_line, "%u %u", &a_perf->m_key_bpm_up,
                              &a_perf->m_key_bpm_dn );
     next_data_line( &file );
+    sscanf( m_line, "%u %u %u", &a_perf->m_key_screenset_up,
+                             &a_perf->m_key_screenset_dn,
+                             &a_perf->m_key_set_playing_screenset);
+    next_data_line( &file );
 
-    sscanf( m_line, "%ld %ld", &a_perf->m_key_screenset_up,
-                             &a_perf->m_key_screenset_dn );
+    sscanf( m_line, "%u %u %u", &a_perf->m_key_group_on,
+                              &a_perf->m_key_group_off,
+                             &a_perf->m_key_group_learn);
+   
     next_data_line( &file );
 
-    sscanf( m_line, "%ld %ld %ld %ld",
+    sscanf( m_line, "%u %u %u %u %u",
             &a_perf->m_key_replace,
             &a_perf->m_key_queue,
             &a_perf->m_key_snapshot_1,
-            &a_perf->m_key_snapshot_2 );
+            &a_perf->m_key_snapshot_2,
+            &a_perf->m_key_keep_queue);
+
+    next_data_line( &file );
+    sscanf( m_line, "%ld", &a_perf->m_show_ui_sequence_key );
+    next_data_line( &file );
+    sscanf( m_line, "%ld", &a_perf->m_key_start );
+    next_data_line( &file );
+    sscanf( m_line, "%ld", &a_perf->m_key_stop );
 
     line_after( &file, "[jack-transport]" );
     long flag = 0;
@@ -233,12 +293,22 @@ userfile::parse( perform *a_perf )
     sscanf( m_line, "%ld", &flag );
     global_manual_alsa_ports = (bool) flag;
 
+    /* last used dir */
+    line_after( &file, "[last-used-dir]" );
+    //FIXME: check for a valid path is missing
+    if (m_line[0] == '/')
+        last_used_dir.assign(m_line);
+
+    /* interaction method  */
+    long method = 0;
+    line_after( &file, "[interaction-method]" );
+    sscanf( m_line, "%ld", &method );
+    global_interactionmethod = (interaction_method_e)method;
+
 #endif
-    
     file.close();
 
     return true;
-    
 }
 
 
