(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
This commit is contained in:
parent
dc348fb613
commit
376c5381ed
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -456,14 +456,25 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
|
|||
/* no modifiers, propagate first */
|
||||
|
||||
#ifdef DEBUG_ACCELERATOR_HANDLING
|
||||
if (debug) {
|
||||
cerr << "\tactivate, then propagate\n";
|
||||
}
|
||||
if (debug) {
|
||||
cerr << "\tpropagate, then activate\n";
|
||||
}
|
||||
#endif
|
||||
if (!gtk_window_propagate_key_event (win, ev)) {
|
||||
#ifdef DEBUG_ACCELERATOR_HANDLING
|
||||
if (debug) {
|
||||
cerr << "\tpropagation didn't handle, so activate\n";
|
||||
}
|
||||
#endif
|
||||
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) {
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_set_redirect_states(redirect_nodes);
|
||||
|
||||
|
||||
if (!IO::ports_legal) {
|
||||
for (niter = nlist.begin(); niter != nlist.end(); ++niter){
|
||||
child = *niter;
|
||||
// All redirects (sends and inserts) have been applied already
|
||||
|
||||
deferred_state->add_child_copy (*child);
|
||||
|
||||
} 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);
|
||||
}
|
||||
|
||||
} 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 ()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user