(merge from 2.0-ongoing -r1911:1912) fix audio clock handling of key press; fix crash...
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 23 May 2007 00:06:16 +0000 (00:06 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 23 May 2007 00:06:16 +0000 (00:06 +0000)
git-svn-id: svn://localhost/ardour2/trunk@1913 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/audio_clock.cc
gtk2_ardour/audio_clock.h
gtk2_ardour/editor_region_list.cc
gtk2_ardour/keyboard.cc
gtk2_ardour/keyboard.h
gtk2_ardour/utils.cc
libs/ardour/ardour/route.h
libs/ardour/route.cc

index d0e6b9ad76befe6b06f3815e381e13668593ce2d..5359bae1cb85e7ac8781303cd1b6b569a7a16beb 100644 (file)
@@ -337,6 +337,18 @@ AudioClock::setup_events ()
        ms_minutes_ebox.signal_scroll_event().connect (bind (mem_fun(*this, &AudioClock::field_button_scroll_event), MS_Minutes));
        ms_seconds_ebox.signal_scroll_event().connect (bind (mem_fun(*this, &AudioClock::field_button_scroll_event), MS_Seconds));
 
+       hours_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), SMPTE_Hours));
+       minutes_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), SMPTE_Minutes));
+       seconds_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), SMPTE_Seconds));
+       frames_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), SMPTE_Frames));
+       audio_frames_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), AudioFrames));
+       bars_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), Bars));
+       beats_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), Beats));
+       ticks_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), Ticks));
+       ms_hours_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), MS_Hours));
+       ms_minutes_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), MS_Minutes));
+       ms_seconds_ebox.signal_key_press_event().connect (bind (mem_fun(*this, &AudioClock::field_key_press_event), MS_Seconds));
+
        hours_ebox.signal_key_release_event().connect (bind (mem_fun(*this, &AudioClock::field_key_release_event), SMPTE_Hours));
        minutes_ebox.signal_key_release_event().connect (bind (mem_fun(*this, &AudioClock::field_key_release_event), SMPTE_Minutes));
        seconds_ebox.signal_key_release_event().connect (bind (mem_fun(*this, &AudioClock::field_key_release_event), SMPTE_Seconds));
@@ -372,6 +384,15 @@ AudioClock::setup_events ()
        ms_hours_ebox.signal_focus_out_event().connect (bind (mem_fun(*this, &AudioClock::field_focus_out_event), MS_Hours));
        ms_minutes_ebox.signal_focus_out_event().connect (bind (mem_fun(*this, &AudioClock::field_focus_out_event), MS_Minutes));
        ms_seconds_ebox.signal_focus_out_event().connect (bind (mem_fun(*this, &AudioClock::field_focus_out_event), MS_Seconds));
+
+       clock_base.signal_focus_in_event().connect (mem_fun (*this, &AudioClock::drop_focus_handler));
+}
+
+bool
+AudioClock::drop_focus_handler (GdkEventFocus* ignored)
+{
+       Keyboard::magic_widget_drop_focus ();
+       return false;
 }
 
 void
@@ -633,6 +654,13 @@ AudioClock::set_session (Session *s)
        }
 }
 
+bool
+AudioClock::field_key_press_event (GdkEventKey *ev, Field field)
+{
+       /* all key activity is handled on key release */
+       return true;
+}
+
 bool
 AudioClock::field_key_release_event (GdkEventKey *ev, Field field)
 {
@@ -679,7 +707,7 @@ AudioClock::field_key_release_event (GdkEventKey *ev, Field field)
                label = &ticks_label;
                break;
        default:
-               return FALSE;
+               return false;
        }
 
        switch (ev->keyval) {
@@ -729,22 +757,22 @@ AudioClock::field_key_release_event (GdkEventKey *ev, Field field)
                if (_mode == MinSec && field == MS_Seconds) {
                        new_char = '.';
                } else {
-                       return FALSE;
+                       return false;
                }
                break;
 
-       case GDK_Return:
-       case GDK_KP_Enter:
        case GDK_Tab:
                move_on = true;
                break;
 
        case GDK_Escape:
+       case GDK_Return:
+       case GDK_KP_Enter:
                clock_base.grab_focus ();
-               return TRUE;
+               return true;
 
        default:
-               return FALSE;
+               return false;
        }
 
        if (!move_on) {
@@ -865,7 +893,7 @@ AudioClock::field_key_release_event (GdkEventKey *ev, Field field)
 
        }
 
-       return TRUE;
+       return true;
 }
 
 bool
@@ -873,6 +901,8 @@ AudioClock::field_focus_in_event (GdkEventFocus *ev, Field field)
 {
        key_entry_state = 0;
 
+       Keyboard::magic_widget_grab_focus ();
+
        switch (field) {
        case SMPTE_Hours:
                hours_ebox.set_flags (Gtk::HAS_FOCUS);
@@ -922,7 +952,7 @@ AudioClock::field_focus_in_event (GdkEventFocus *ev, Field field)
                break;
        }
 
-       return FALSE;
+       return false;
 }
 
 bool
@@ -979,21 +1009,20 @@ AudioClock::field_focus_out_event (GdkEventFocus *ev, Field field)
                break;
        }
 
-       return FALSE;
+       Keyboard::magic_widget_drop_focus ();
+
+       return false;
 }
 
 bool
 AudioClock::field_button_release_event (GdkEventButton *ev, Field field)
 {
-
-
        if (dragging) {
-               gdk_pointer_ungrab(GDK_CURRENT_TIME);
+               gdk_pointer_ungrab (GDK_CURRENT_TIME);
                dragging = false;
                if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)){
-                  // we actually dragged so return without setting editing focus, or we shift clicked
-
-                       return TRUE;
+                       // we actually dragged so return without setting editing focus, or we shift clicked
+                       return true;
                }
        }
 
@@ -1002,7 +1031,7 @@ AudioClock::field_button_release_event (GdkEventButton *ev, Field field)
                        build_ops_menu ();
                }
                ops_menu->popup (1, ev->time);
-               return TRUE;
+               return true;
        }
 
        if (Keyboard::is_context_menu_event (ev)) {
@@ -1010,7 +1039,7 @@ AudioClock::field_button_release_event (GdkEventButton *ev, Field field)
                        build_ops_menu ();
                }
                ops_menu->popup (1, ev->time);
-               return TRUE;
+               return true;
        } 
 
        switch (ev->button) {
@@ -1059,13 +1088,13 @@ AudioClock::field_button_release_event (GdkEventButton *ev, Field field)
                break;
        }
 
-       return TRUE;
+       return true;
 }
 
 bool
 AudioClock::field_button_press_event (GdkEventButton *ev, Field field)
 {
-       if (session == 0) return FALSE;
+       if (session == 0) return false;
 
        nframes_t frames = 0;
 
@@ -1077,7 +1106,7 @@ AudioClock::field_button_press_event (GdkEventButton *ev, Field field)
                                        }
        
                 /* make absolutely sure that the pointer is grabbed */
-               gdk_pointer_grab(ev->window,FALSE ,
+               gdk_pointer_grab(ev->window,false ,
                                 GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), 
                                 NULL,NULL,ev->time);
                dragging = true;
@@ -1095,21 +1124,23 @@ AudioClock::field_button_press_event (GdkEventButton *ev, Field field)
 
        case 3:
                /* used for context sensitive menu */
-               return FALSE;
+               return false;
                break;
 
        default:
-               return FALSE;
+               return false;
                break;
        }
        
-       return TRUE;
+       return true;
 }
 
 bool
 AudioClock::field_button_scroll_event (GdkEventScroll *ev, Field field)
 {
-       if (session == 0) return FALSE;
+       if (session == 0) {
+               return false;
+       }
 
        nframes_t frames = 0;
 
@@ -1144,18 +1175,18 @@ AudioClock::field_button_scroll_event (GdkEventScroll *ev, Field field)
               break;
 
        default:
-               return FALSE;
+               return false;
                break;
        }
        
-       return TRUE;
+       return true;
 }
 
 bool
 AudioClock::field_motion_notify_event (GdkEventMotion *ev, Field field)
 {
        if (session == 0 || !dragging) {
-               return FALSE;
+               return false;
        }
        
        float pixel_frame_scale_factor = 0.2f;
@@ -1202,7 +1233,7 @@ AudioClock::field_motion_notify_event (GdkEventMotion *ev, Field field)
 
        }
 
-       return TRUE;
+       return true;
 }
 
 nframes_t
index 27b36d7b3e07d65618345b43ed227cbc2ad961d9..16156625c880a0829288014daebf58f7fa689d7a 100644 (file)
@@ -62,6 +62,8 @@ class AudioClock : public Gtk::HBox
        static sigc::signal<void> ModeChanged;
        static std::vector<AudioClock*> clocks;
 
+       static bool has_focus() { return _has_focus; }
+
   private:
        ARDOUR::Session  *session;
        Mode             _mode;
@@ -173,9 +175,11 @@ class AudioClock : public Gtk::HBox
        bool field_button_press_event (GdkEventButton *ev, Field);
        bool field_button_release_event (GdkEventButton *ev, Field);
        bool field_button_scroll_event (GdkEventScroll *ev, Field);
+       bool field_key_press_event (GdkEventKey *, Field);
        bool field_key_release_event (GdkEventKey *, Field);
        bool field_focus_in_event (GdkEventFocus *, Field);
        bool field_focus_out_event (GdkEventFocus *, Field);
+       bool drop_focus_handler (GdkEventFocus*);
 
        void set_smpte (nframes_t, bool);
        void set_bbt (nframes_t, bool);
@@ -198,6 +202,8 @@ class AudioClock : public Gtk::HBox
        void set_size_requests ();
 
        static const uint32_t field_length[(int)AudioFrames+1];
+       static bool _has_focus;
+       
 };
 
 #endif /* __audio_clock_h__ */
index 6b6a2981b5b9016c7f7e1cdba4822ad80d940331..8a50332193400448173cfba7c0b5392dae996abe 100644 (file)
@@ -231,12 +231,19 @@ Editor::region_list_selection_changed()
                TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
                TreeIter iter;
 
-               /* just set the first selected region (in fact, the selection model might be SINGLE, which
-                  means there can only be one.
-               */
-               
                if ((iter = region_list_model->get_iter (*i))) {
-                       set_selected_regionview_from_region_list (((*iter)[region_list_columns.region]), Selection::Set);
+                       boost::shared_ptr<Region> r = (*iter)[region_list_columns.region];
+                       
+                       /* they could have clicked on a row that is just a placeholder, like "Hidden" */
+                       
+                       if (r) {
+                               
+                               /* just set the first selected region (in fact, the selection model might be SINGLE, which
+                                  means there can only be one.
+                               */
+                               
+                               set_selected_regionview_from_region_list (r, Selection::Set);
+                       }
                }
        }
 }
@@ -564,7 +571,16 @@ Editor::region_list_selection_mapover (slot<void,boost::shared_ptr<Region> > sl)
                TreeIter iter;
 
                if ((iter = region_list_model->get_iter (*i))) {
-                       sl (((*iter)[region_list_columns.region]));
+
+                       /* some rows don't have a region associated with them, but can still be
+                          selected (XXX maybe prevent them from being selected)
+                       */
+
+                       boost::shared_ptr<Region> r = (*iter)[region_list_columns.region];
+
+                       if (r) {
+                               sl (r);
+                       }
                }
        }
 }
index 83918da91836da6173ec10cc520856651a749968..90fea321de89320a5500bcacbfa12c6662182088 100644 (file)
@@ -54,6 +54,25 @@ Keyboard* Keyboard::_the_keyboard = 0;
 
 GdkModifierType Keyboard::RelevantModifierKeyMask;
 
+bool Keyboard::_some_magic_widget_has_focus = false;
+
+void
+Keyboard::magic_widget_grab_focus () 
+{
+       _some_magic_widget_has_focus = true;
+}
+
+void
+Keyboard::magic_widget_drop_focus ()
+{
+       _some_magic_widget_has_focus = false;
+}
+
+bool
+Keyboard::some_magic_widget_has_focus ()
+{
+       return _some_magic_widget_has_focus;
+}
 
 Keyboard::Keyboard ()
 {
index ec55dc54db7e4ddd111fb3a983c7d1fc031211be..c1351bfaf60777bc28982b7c891ac114aee23e20 100644 (file)
@@ -90,6 +90,10 @@ class Keyboard : public sigc::trackable, Stateful
 
        static Keyboard& the_keyboard() { return *_the_keyboard; }
 
+       static bool some_magic_widget_has_focus ();
+       static void magic_widget_grab_focus ();
+       static void magic_widget_drop_focus ();
+
   private:
        static Keyboard* _the_keyboard;
 
@@ -104,6 +108,8 @@ class Keyboard : public sigc::trackable, Stateful
 
        static gint _snooper (GtkWidget*, GdkEventKey*, gpointer);
        gint snooper (GtkWidget*, GdkEventKey*);
+
+       static bool _some_magic_widget_has_focus;
 };
 
 #endif /* __ardour_keyboard_h__ */
index b4d02591e9d7ff4111962120e5a633317556b0f5..d574ece427f4827d807671e22e0ad70b67ebe29c 100644 (file)
@@ -347,14 +347,14 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 #endif
 
        if (focus) {
-               if (GTK_IS_ENTRY(focus)) {
+               if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
                        special_handling_of_unmodified_accelerators = true;
                } 
        } 
 
 #ifdef DEBUG_ACCELERATOR_HANDLING
        if (debug) {
-               cerr << "Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " focus is an entry ? " 
+               cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? " 
                     << special_handling_of_unmodified_accelerators
                     << endl;
        }
@@ -455,15 +455,26 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
        
        /* no modifiers, propagate first */
        
+#ifdef DEBUG_ACCELERATOR_HANDLING
+       if (debug) {
+               cerr << "\tpropagate, then activate\n";
+       }
+#endif
+       if (!gtk_window_propagate_key_event (win, ev)) {
 #ifdef DEBUG_ACCELERATOR_HANDLING
                if (debug) {
-                       cerr << "\tactivate, then propagate\n";
+                       cerr << "\tpropagation didn't handle, so activate\n";
                }
 #endif
-       if (!gtk_window_propagate_key_event (win, ev)) {
                return gtk_window_activate_key (win, ev);
-       } 
-
+       } else {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\thandled by propagate\n";
+               }
+#endif
+               return true;
+       }
 
 #ifdef DEBUG_ACCELERATOR_HANDLING
        if (debug) {
index af27a99b9d5eca7cbbdb42022e1718d5713fd143..34fa7b5463313e88752d47a9c002f77cc1018dda 100644 (file)
@@ -324,7 +324,8 @@ class Route : public IO
        uint32_t pans_required() const;
        ChanCount n_process_buffers ();
 
-       virtual int _set_state (const XMLNode&, bool call_base);
+       virtual int  _set_state (const XMLNode&, bool call_base);
+       virtual void _set_redirect_states (const XMLNodeList&);
 
   private:
        void init ();
index 81fcdb4cec56d779198ea120577f3023ce8d3884..17322229edc0518c4fa0d57aa8ceb161074f707c 100644 (file)
@@ -1804,34 +1804,27 @@ Route::_set_state (const XMLNode& node, bool call_base)
                        break;
                }
        }
+
+       XMLNodeList redirect_nodes;
                        
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
 
                child = *niter;
                        
-               if (child->name() == X_("Send")) {
-
+               if (child->name() == X_("Send") || child->name() == X_("Insert")) {
+                       redirect_nodes.push_back(child);
+               }
 
-                       if (!IO::ports_legal) {
+       }
 
-                               deferred_state->add_child_copy (*child);
+       _set_redirect_states(redirect_nodes);
 
-                       } else {
-                               add_redirect_from_xml (*child);
-                       }
 
-               } else if (child->name() == X_("Insert")) {
-                       
-                       if (!IO::ports_legal) {
-                               
-                               deferred_state->add_child_copy (*child);
-
-                       } else {
-                               
-                               add_redirect_from_xml (*child);
-                       }
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               child = *niter;
+               // All redirects (sends and inserts) have been applied already
 
-               } else if (child->name() == X_("Automation")) {
+               if (child->name() == X_("Automation")) {
                        
                        if ((prop = child->property (X_("path"))) != 0)  {
                                load_automation (prop->value());
@@ -1888,6 +1881,102 @@ Route::_set_state (const XMLNode& node, bool call_base)
        return 0;
 }
 
+void
+Route::_set_redirect_states(const XMLNodeList &nlist)
+{
+       XMLNodeConstIterator niter;
+       char buf[64];
+
+       RedirectList::iterator i, o;
+
+       // Iterate through existing redirects, remove those which are not in the state list
+       for (i = _redirects.begin(); i != _redirects.end(); ) {
+               RedirectList::iterator tmp = i;
+               ++tmp;
+
+               bool redirectInStateList = false;
+       
+               (*i)->id().print (buf, sizeof (buf));
+
+
+               for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+                       if (strncmp(buf,(*niter)->child(X_("Redirect"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) {
+                               redirectInStateList = true;
+                               break;
+                       }
+               }
+               
+               if (!redirectInStateList) {
+                       remove_redirect ( *i, this);
+               }
+
+
+               i = tmp;
+       }
+
+
+       // Iterate through state list and make sure all redirects are on the track and in the correct order,
+       // set the state of existing redirects according to the new state on the same go
+       i = _redirects.begin();
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) {
+
+               // Check whether the next redirect in the list 
+               o = i;
+
+               while (o != _redirects.end()) {
+                       (*o)->id().print (buf, sizeof (buf));
+                       if ( strncmp(buf, (*niter)->child(X_("Redirect"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0)
+                               break;
+                       ++o;
+               }
+
+               if (o == _redirects.end()) {
+                       // If the redirect (*niter) is not on the route, we need to create it
+                       // and move it to the correct location
+
+                       RedirectList::iterator prev_last = _redirects.end();
+                       --prev_last; // We need this to check whether adding succeeded
+                       
+                       add_redirect_from_xml (**niter);
+
+                       RedirectList::iterator last = _redirects.end();
+                       --last;
+
+                       if (prev_last == last) {
+                               cerr << "Could not fully restore state as some redirects were not possible to create" << endl;
+                               continue;
+
+                       }
+
+                       boost::shared_ptr<Redirect> tmp = (*last);
+                       // remove the redirect from the wrong location
+                       _redirects.erase(last);
+                       // insert the new redirect at the current location
+                       _redirects.insert(i, tmp);
+
+                       --i; // move pointer to the newly inserted redirect
+                       continue;
+               }
+
+               // We found the redirect (*niter) on the route, first we must make sure the redirect
+               // is at the location provided in the XML state
+               if (i != o) {
+                       boost::shared_ptr<Redirect> tmp = (*o);
+                       // remove the old copy
+                       _redirects.erase(o);
+                       // insert the redirect at the correct location
+                       _redirects.insert(i, tmp);
+
+                       --i; // move pointer so it points to the right redirect
+               }
+
+               (*i)->set_state( (**niter) );
+       }
+       
+       redirects_changed(this);
+}
+
 void
 Route::curve_reallocate ()
 {