tweak region layering editor to use only a single click to change layering
[ardour.git] / gtk2_ardour / region_layering_order_editor.cc
1 /*
2     Copyright (C) 2011-2012 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <gtkmm/table.h>
21 #include <gtkmm/stock.h>
22 #include <gtkmm/alignment.h>
23
24 #include "pbd/stateful_diff_command.h"
25
26 #include "ardour/region.h"
27
28 #include "gui_thread.h"
29 #include "keyboard.h"
30 #include "public_editor.h"
31 #include "region_layering_order_editor.h"
32 #include "region_view.h"
33 #include "utils.h"
34 #include "i18n.h"
35
36 using namespace std;
37 using namespace Gtk;
38 using namespace ARDOUR;
39
40 RegionLayeringOrderEditor::RegionLayeringOrderEditor (PublicEditor& pe)
41         : ArdourWindow (_("RegionLayeringOrderEditor"))
42         , position (0)
43         , in_row_change (false)
44         , regions_at_position (0)
45         , layering_order_model (Gtk::ListStore::create (layering_order_columns))
46         , clock ("layer dialog", true, "", false, false, false)
47         , editor (pe)
48         , _time_axis_view (0)
49 {
50         set_name ("RegionLayeringOrderEditorWindow");
51
52         layering_order_display.set_model (layering_order_model);
53
54         layering_order_display.append_column (_("Region Name"), layering_order_columns.name);
55         layering_order_display.set_headers_visible (true);
56         layering_order_display.set_reorderable (false);
57         layering_order_display.set_rules_hint (true);
58
59         scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
60         scroller.add (layering_order_display);
61
62         clock.set_mode (AudioClock::BBT);
63
64         Gtk::Table* scroller_table = manage (new Gtk::Table);
65         scroller_table->set_size_request (300, 250);
66         scroller_table->attach (scroller, 0, 1, 0, 1);
67         scroller_table->set_col_spacings (5);
68         scroller_table->set_row_spacings (5);
69
70         track_label.set_name ("RegionLayeringOrderEditorLabel");
71         track_label.set_text (_("Track:"));
72         track_label.set_alignment (0, 0.5);
73         clock_label.set_name ("RegionLayeringOrderEditorLabel");
74         clock_label.set_text (_("Position:"));
75         clock_label.set_alignment (0, 0.5);
76         track_name_label.set_name ("RegionLayeringOrderEditorNameLabel");
77         track_name_label.set_alignment (0, 0.5);
78         clock.set_mode (AudioClock::BBT);
79
80         Gtk::Table* info_table = manage (new Gtk::Table (2, 2));
81         info_table->set_col_spacings (5);
82         info_table->set_row_spacings (5);
83         info_table->attach (track_label, 0, 1, 0, 1, FILL, FILL);
84         info_table->attach (track_name_label, 1, 2, 0, 1, FILL, FILL);
85         info_table->attach (clock_label, 0, 1, 1, 2, FILL, FILL);
86         info_table->attach (clock, 1, 2, 1, 2, Gtk::AttachOptions(0), FILL);
87
88         Gtk::VBox* vbox = Gtk::manage (new Gtk::VBox ());
89         vbox->set_spacing (12);
90         vbox->pack_start (*info_table, false, false);
91         vbox->pack_start (*scroller_table, true, true);
92         add (*vbox);
93
94         info_table->set_name ("RegionLayeringOrderTable");
95         scroller_table->set_name ("RegionLayeringOrderTable");
96
97         layering_order_display.set_name ("RegionLayeringOrderDisplay");
98         layering_order_display.get_selection()->set_mode (SELECTION_SINGLE);
99         layering_order_display.get_selection()->signal_changed ().connect (mem_fun (*this, &RegionLayeringOrderEditor::row_selected));
100
101         layering_order_display.grab_focus ();
102
103         set_title (_("Choose Top Region"));
104         show_all();
105 }
106
107 RegionLayeringOrderEditor::~RegionLayeringOrderEditor ()
108 {
109         
110 }
111
112 void
113 RegionLayeringOrderEditor::row_selected ()
114 {
115         if (in_row_change) {
116                 return;
117         }
118
119         Glib::RefPtr<TreeSelection> selection = layering_order_display.get_selection();
120         TreeModel::iterator iter = selection->get_selected(); // only used with Gtk::SELECTION_SINGLE
121
122         if (!iter) {
123                 return;
124         }
125         
126         TreeModel::Row row = *iter;
127         RegionView* rv = row[layering_order_columns.region_view];
128         
129         vector<RegionView*> eq;
130         editor.get_equivalent_regions (rv, eq, Properties::edit.property_id);
131
132         /* XXX this should be reversible, really */
133         
134         for (vector<RegionView*>::iterator i = eq.begin(); i != eq.end(); ++i) {
135                 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
136                 if (pl) {
137                         pl->raise_region_to_top ((*i)->region());
138                 }
139         }
140 }
141
142 struct RegionViewCompareByLayer {
143         bool operator() (RegionView* a, RegionView* b) const {
144                 return a->region()->layer() > b->region()->layer();
145         }
146 };
147
148 void
149 RegionLayeringOrderEditor::refill ()
150 {
151         assert (_time_axis_view);
152         
153         regions_at_position = 0;
154         in_row_change = true;
155         layering_order_model->clear ();
156
157         RegionSelection region_list;
158         TrackViewList ts;
159         ts.push_back (_time_axis_view);
160         editor.get_regions_at (region_list, position, ts);
161
162         regions_at_position = region_list.size ();
163
164         if (regions_at_position < 2) {
165                 playlist_modified_connection.disconnect ();
166                 hide ();
167                 in_row_change = false;
168                 return;
169         }
170
171         RegionViewCompareByLayer cmp;
172         region_list.sort (cmp);
173
174         for (RegionSelection::const_iterator i = region_list.begin(); i != region_list.end(); ++i) {
175                 TreeModel::Row newrow = *(layering_order_model->append());
176                 newrow[layering_order_columns.name] = (*i)->region()->name();
177                 newrow[layering_order_columns.region_view] = *i;
178
179                if (i == region_list.begin()) {
180                        layering_order_display.get_selection()->select(newrow);
181                }
182         }
183
184         in_row_change = false;
185 }
186
187 void
188 RegionLayeringOrderEditor::set_context (const string& a_name, Session* s, TimeAxisView* tav, boost::shared_ptr<Playlist> pl, framepos_t pos)
189 {
190         track_name_label.set_text (a_name);
191
192         clock.set_session (s);
193         clock.set (pos, true);
194
195         playlist_modified_connection.disconnect ();
196         pl->ContentsChanged.connect (playlist_modified_connection, invalidator (*this), boost::bind
197                                      (&RegionLayeringOrderEditor::playlist_modified, this), gui_context());
198
199         _time_axis_view = tav;
200
201         position = pos;
202         refill ();
203 }
204
205 bool
206 RegionLayeringOrderEditor::on_key_press_event (GdkEventKey* ev)
207 {
208         bool handled = false;
209
210         /* in general, we want shortcuts working while in this
211            dialog. However, we'd like to treat "return" specially
212            since it is used for row activation. So ..
213
214            for return: try normal handling first
215            then try the editor (to get accelerators/shortcuts)
216            then try normal handling (for keys other than return)
217         */
218
219         if (ev->keyval == GDK_Return) {
220                 handled = ArdourWindow::on_key_press_event (ev);
221         }
222
223         if (!handled) {
224                 handled = key_press_focus_accelerator_handler (editor, ev);
225         }
226
227         if (!handled) {
228                 handled = ArdourWindow::on_key_press_event (ev);
229         }
230
231         return handled;
232 }
233
234 void
235 RegionLayeringOrderEditor::maybe_present ()
236 {
237         if (regions_at_position < 2) {
238                 hide ();
239                 return;
240         }
241
242         present ();
243 }
244
245 void
246 RegionLayeringOrderEditor::playlist_modified ()
247 {
248         refill ();
249 }