From: Paul Davis Date: Wed, 23 May 2007 00:06:16 +0000 (+0000) Subject: (merge from 2.0-ongoing -r1911:1912) fix audio clock handling of key press; fix crash... X-Git-Tag: 3.0-alpha5~4857 X-Git-Url: https://main.carlh.net/gitweb/?a=commitdiff_plain;h=376c5381edca72ce25a04d449e7df39ecbc0d4d0;p=ardour.git (merge from 2.0-ongoing -r1911:1912) fix audio clock handling of key press; fix crash bug caused by mapping over a region list selection that includes rows without regions; also merge sampo's redirect undo/state fixes from 2.0-ongoing git-svn-id: svn://localhost/ardour2/trunk@1913 d708f5d6-7413-0410-9779-e7cbd77b26cf --- diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index d0e6b9ad76..5359bae1cb 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -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 diff --git a/gtk2_ardour/audio_clock.h b/gtk2_ardour/audio_clock.h index 27b36d7b3e..16156625c8 100644 --- a/gtk2_ardour/audio_clock.h +++ b/gtk2_ardour/audio_clock.h @@ -62,6 +62,8 @@ class AudioClock : public Gtk::HBox static sigc::signal ModeChanged; static std::vector 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__ */ diff --git a/gtk2_ardour/editor_region_list.cc b/gtk2_ardour/editor_region_list.cc index 6b6a2981b5..8a50332193 100644 --- a/gtk2_ardour/editor_region_list.cc +++ b/gtk2_ardour/editor_region_list.cc @@ -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 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 > 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 r = (*iter)[region_list_columns.region]; + + if (r) { + sl (r); + } } } } diff --git a/gtk2_ardour/keyboard.cc b/gtk2_ardour/keyboard.cc index 83918da918..90fea321de 100644 --- a/gtk2_ardour/keyboard.cc +++ b/gtk2_ardour/keyboard.cc @@ -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 () { diff --git a/gtk2_ardour/keyboard.h b/gtk2_ardour/keyboard.h index ec55dc54db..c1351bfaf6 100644 --- a/gtk2_ardour/keyboard.h +++ b/gtk2_ardour/keyboard.h @@ -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__ */ diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index b4d02591e9..d574ece427 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -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) { diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index af27a99b9d..34fa7b5463 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -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 (); diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 81fcdb4cec..17322229ed 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -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 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 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 () {