/[svn]/gigedit/trunk/src/gigedit/regionchooser.cpp
ViewVC logotype

Annotation of /gigedit/trunk/src/gigedit/regionchooser.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2507 - (hide annotations) (download)
Sun Jan 12 19:37:55 2014 UTC (10 years, 3 months ago) by persson
File size: 31837 byte(s)
* added dialog for editing the CtrlTrigger and Legato midi rules

1 schoenebeck 1225 /*
2 persson 2507 * Copyright (C) 2006-2014 Andreas Persson
3 schoenebeck 1225 *
4     * This program is free software; you can redistribute it and/or
5     * modify it under the terms of the GNU General Public License as
6     * published by the Free Software Foundation; either version 2, or (at
7     * your option) any later version.
8     *
9     * This program is distributed in the hope that it will be useful, but
10     * WITHOUT ANY WARRANTY; without even the implied warranty of
11     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12     * General Public License for more details.
13     *
14     * You should have received a copy of the GNU General Public License
15     * along with program; see the file COPYING. If not, write to the Free
16     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17     * 02110-1301 USA.
18     */
19    
20     #include "regionchooser.h"
21 persson 2151
22 persson 1623 #include <algorithm>
23 persson 2151
24     #include <cairomm/context.h>
25     #include <gdkmm/general.h>
26 schoenebeck 1225 #include <gdkmm/cursor.h>
27     #include <gtkmm/stock.h>
28     #include <gtkmm/spinbutton.h>
29     #include <gtkmm/dialog.h>
30    
31 schoenebeck 1396 #include "global.h"
32 schoenebeck 1225
33 persson 2246 #define REGION_BLOCK_HEIGHT 20
34     #define KEYBOARD_HEIGHT 40
35 schoenebeck 1660
36 persson 1623 void SortedRegions::update(gig::Instrument* instrument) {
37     // Usually, the regions in a gig file are ordered after their key
38     // range, but there are files where they are not. The
39     // RegionChooser code needs a sorted list of regions.
40     regions.clear();
41     if (instrument) {
42 persson 2151 for (gig::Region* r = instrument->GetFirstRegion() ;
43 persson 1623 r ;
44     r = instrument->GetNextRegion()) {
45     regions.push_back(r);
46     }
47     sort(regions.begin(), regions.end(), *this);
48     }
49     }
50    
51     gig::Region* SortedRegions::first() {
52     region_iterator = regions.begin();
53     return region_iterator == regions.end() ? 0 : *region_iterator;
54     }
55    
56     gig::Region* SortedRegions::next() {
57     region_iterator++;
58     return region_iterator == regions.end() ? 0 : *region_iterator;
59     }
60    
61    
62    
63 schoenebeck 1661 RegionChooser::RegionChooser() :
64 persson 2169 activeKeyColor("red"),
65     red("#8070ff"),
66     grey1("grey69"),
67     white("white"),
68     black("black"),
69 schoenebeck 1661 m_VirtKeybModeChoice(_("Virtual Keyboard Mode")),
70     currentActiveKey(-1)
71 schoenebeck 1225 {
72 persson 2169 set_size_request(500, KEYBOARD_HEIGHT + REGION_BLOCK_HEIGHT);
73 schoenebeck 1225
74     instrument = 0;
75     region = 0;
76     resize.active = false;
77 persson 1303 move.active = false;
78 schoenebeck 1225 cursor_is_resize = false;
79 schoenebeck 1660 h1 = REGION_BLOCK_HEIGHT;
80 schoenebeck 1225
81 persson 1831 // properties of the virtual keyboard
82 schoenebeck 1661 {
83 persson 2246 const char* choices[] = { _("normal"), _("chord"), 0 };
84 schoenebeck 1661 static const virt_keyboard_mode_t values[] = {
85     VIRT_KEYBOARD_MODE_NORMAL,
86     VIRT_KEYBOARD_MODE_CHORD
87     };
88     m_VirtKeybModeChoice.set_choices(choices, values);
89     m_VirtKeybModeChoice.set_value(VIRT_KEYBOARD_MODE_NORMAL);
90     }
91     m_VirtKeybVelocityLabelDescr.set_text(_("Note-On Velocity:"));
92     m_VirtKeybVelocityLabel.set_text("-");
93     m_VirtKeybOffVelocityLabelDescr.set_text(_("Note-Off Velocity:"));
94     m_VirtKeybOffVelocityLabel.set_text("-");
95     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.label, Gtk::PACK_SHRINK);
96     m_VirtKeybPropsBox.pack_start(m_VirtKeybModeChoice.widget, Gtk::PACK_SHRINK);
97     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabelDescr, Gtk::PACK_SHRINK);
98     m_VirtKeybPropsBox.pack_start(m_VirtKeybVelocityLabel, Gtk::PACK_SHRINK);
99     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabelDescr, Gtk::PACK_SHRINK);
100     m_VirtKeybPropsBox.pack_start(m_VirtKeybOffVelocityLabel, Gtk::PACK_SHRINK);
101     m_VirtKeybPropsBox.set_spacing(10);
102     m_VirtKeybPropsBox.show();
103 persson 2246 for (int i = 0 ; i < 128 ; i++) key_pressed[i] = false;
104 schoenebeck 1661
105 schoenebeck 1225 actionGroup = Gtk::ActionGroup::create();
106     actionGroup->add(Gtk::Action::create("Properties",
107     Gtk::Stock::PROPERTIES),
108     sigc::mem_fun(*this,
109     &RegionChooser::show_region_properties));
110     actionGroup->add(Gtk::Action::create("Remove", Gtk::Stock::REMOVE),
111     sigc::mem_fun(*this, &RegionChooser::delete_region));
112     actionGroup->add(Gtk::Action::create("Add", Gtk::Stock::ADD),
113     sigc::mem_fun(*this, &RegionChooser::add_region));
114     actionGroup->add(Gtk::Action::create("Dimensions", _("Dimensions...")),
115     sigc::mem_fun(*this, &RegionChooser::manage_dimensions));
116    
117     uiManager = Gtk::UIManager::create();
118     uiManager->insert_action_group(actionGroup);
119     Glib::ustring ui_info =
120     "<ui>"
121     " <popup name='PopupMenuInsideRegion'>"
122     " <menuitem action='Properties'/>"
123     " <menuitem action='Dimensions'/>"
124     " <menuitem action='Remove'/>"
125     " </popup>"
126     " <popup name='PopupMenuOutsideRegion'>"
127     " <menuitem action='Add'/>"
128     " </popup>"
129     "</ui>";
130     uiManager->add_ui_from_string(ui_info);
131    
132     popup_menu_inside_region = dynamic_cast<Gtk::Menu*>(
133     uiManager->get_widget("/PopupMenuInsideRegion"));
134     popup_menu_outside_region = dynamic_cast<Gtk::Menu*>(
135     uiManager->get_widget("/PopupMenuOutsideRegion"));
136    
137     add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
138     Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK);
139    
140 schoenebeck 1322 dimensionManager.region_to_be_changed_signal.connect(
141     region_to_be_changed_signal.make_slot()
142 schoenebeck 1225 );
143 schoenebeck 1322 dimensionManager.region_changed_signal.connect(
144     region_changed_signal.make_slot()
145     );
146     dimensionManager.region_changed_signal.connect(
147     sigc::hide(
148     sigc::mem_fun(*this, &RegionChooser::on_dimension_manager_changed)
149     )
150     );
151 schoenebeck 1660 keyboard_key_hit_signal.connect(
152     sigc::mem_fun(*this, &RegionChooser::on_note_on_event)
153     );
154     keyboard_key_released_signal.connect(
155     sigc::mem_fun(*this, &RegionChooser::on_note_off_event)
156     );
157 schoenebeck 1225 }
158    
159     RegionChooser::~RegionChooser()
160     {
161     }
162    
163 persson 2246 void RegionChooser::invalidate_key(int key) {
164     const int h = KEYBOARD_HEIGHT;
165     const int w = get_width() - 1;
166     int x1 = key_to_x(key - 0.5, w);
167     int x2 = key_to_x(key + 1.5, w);
168    
169     Gdk::Rectangle rect(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
170     get_window()->invalidate_rect(rect, false);
171     }
172    
173 schoenebeck 1654 void RegionChooser::on_note_on_event(int key, int velocity) {
174 persson 2246 key_pressed[key] = true;
175     invalidate_key(key);
176 schoenebeck 1661 m_VirtKeybVelocityLabel.set_text(ToString(velocity));
177 schoenebeck 1654 }
178    
179     void RegionChooser::on_note_off_event(int key, int velocity) {
180 persson 2246 key_pressed[key] = false;
181     invalidate_key(key);
182 schoenebeck 1661 m_VirtKeybOffVelocityLabel.set_text(ToString(velocity));
183 schoenebeck 1654 }
184    
185 persson 2246
186 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
187 persson 2246 bool RegionChooser::on_expose_event(GdkEventExpose* e) {
188     double clipx1 = e->area.x;
189     double clipx2 = e->area.x + e->area.width;
190     double clipy1 = e->area.y;
191     double clipy2 = e->area.y + e->area.height;
192    
193     const Cairo::RefPtr<Cairo::Context>& cr =
194     get_window()->create_cairo_context();
195     #if 0
196 persson 2169 }
197     #endif
198 persson 2246 #else
199     bool RegionChooser::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
200     double clipx1, clipx2, clipy1, clipy2;
201     cr->get_clip_extents(clipx1, clipy1, clipx2, clipy2);
202     #endif
203 schoenebeck 1225
204 persson 2169 cr->save();
205     cr->set_line_width(1);
206 schoenebeck 1225
207 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
208     const Gdk::Color bg = get_style()->get_bg(Gtk::STATE_NORMAL);
209     #else
210     const Gdk::RGBA bg = get_style_context()->get_background_color();
211     #endif
212     Gdk::Cairo::set_source_rgba(cr, bg);
213     cr->paint();
214 schoenebeck 1225
215 persson 2246 if (clipy2 > h1) {
216     draw_keyboard(cr, clipx1, clipx2);
217     }
218    
219     if (clipy1 < h1 && instrument) {
220     draw_regions(cr, clipx1, clipx2);
221     }
222    
223     cr->restore();
224    
225     return true;
226     }
227    
228     void RegionChooser::draw_keyboard(const Cairo::RefPtr<Cairo::Context>& cr,
229     int clip_low, int clip_high) {
230     const int h = KEYBOARD_HEIGHT;
231     const int w = get_width() - 1;
232     const int bh = int(h * 0.55);
233    
234 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
235     cr->rectangle(0.5, h1 + 0.5, w, h - 1);
236     cr->stroke();
237 schoenebeck 1225
238 persson 2246 int x1 = key_to_x(20.5, w);
239 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
240     cr->rectangle(1, h1 + 1, x1 - 1, h - 2);
241     cr->fill();
242 schoenebeck 1225
243 persson 2246 int x2 = key_to_x(109.5, w);
244 persson 2169 Gdk::Cairo::set_source_rgba(cr, white);
245     cr->rectangle(x1 + 1, h1 + 1, x2 - x1 - 1, h - 2);
246     cr->fill();
247 persson 2151
248 persson 2169 Gdk::Cairo::set_source_rgba(cr, grey1);
249     cr->rectangle(x2 + 1, h1 + 1, w - x2 - 1, h - 2);
250     cr->fill();
251 persson 2151
252 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
253 persson 2246
254     int clipkey1 = std::max(0, x_to_key_right(clip_low - 1, w));
255     int clipkey2 = std::min(x_to_key_right(clip_high - 1, w) + 1, 128);
256    
257     for (int i = clipkey1 ; i < clipkey2 ; i++) {
258 persson 2169 int note = (i + 3) % 12;
259 persson 2246 int x = key_to_x(i, w);
260 persson 2151
261 persson 2169 if (note == 1 || note == 4 || note == 6 ||
262     note == 9 || note == 11) {
263 persson 2246 // black key: short line in the middle, with a rectangle
264     // on top
265     int x2 = key_to_x(i + 0.5, w);
266 persson 2169 cr->move_to(x2 + 0.5, h1 + bh + 0.5);
267     cr->line_to(x2 + 0.5, h1 + h - 1);
268     cr->stroke();
269 persson 2151
270 persson 2246 int x3 = key_to_x(i + 1, w);
271 persson 2169 cr->rectangle(x, h1 + 1, x3 - x + 1, bh);
272     cr->fill();
273     } else if (note == 3 || note == 8) {
274 persson 2246 // C or F: long line to the left
275 persson 2169 cr->move_to(x + 0.5, h1 + 1);
276     cr->line_to(x + 0.5, h1 + h - 1);
277     cr->stroke();
278 persson 2246 }
279 persson 2151
280 persson 2246 if (key_pressed[i]) draw_key(cr, i);
281    
282     if (note == 3) draw_digit(cr, i);
283 persson 2169 }
284 persson 2246 }
285 schoenebeck 1225
286 persson 2151
287 persson 2246 void RegionChooser::draw_regions(const Cairo::RefPtr<Cairo::Context>& cr,
288     int clip_low, int clip_high) {
289     const int w = get_width() - 1;
290    
291     Gdk::Cairo::set_source_rgba(cr, black);
292     gig::Region* next_region;
293     int x3 = -1;
294     for (gig::Region* r = regions.first() ; r ; r = next_region) {
295     next_region = regions.next();
296    
297     if (x3 < 0) {
298     x3 = key_to_x(r->KeyRange.low, w);
299     if (x3 >= clip_high) break;
300     }
301     if (!next_region ||
302     r->KeyRange.high + 1 != next_region->KeyRange.low ||
303     r == region || next_region == region) {
304    
305     int x2 = key_to_x(r->KeyRange.high + 1, w);
306     if (x2 >= clip_low) {
307 persson 2169 cr->move_to(x3, 0.5);
308     cr->line_to(x2 + 0.5, 0.5);
309     cr->line_to(x2 + 0.5, h1 - 0.5);
310     cr->line_to(x3, h1 - 0.5);
311     cr->stroke();
312 persson 2151
313 persson 2246 Gdk::Cairo::set_source_rgba(cr, region == r ? red : white);
314 persson 2169 cr->rectangle(x3 + 1, 1, x2 - x3 - 1, h1 - 2);
315     cr->fill();
316     Gdk::Cairo::set_source_rgba(cr, black);
317 persson 2151 }
318 persson 2246 x3 = -1;
319 persson 2169 }
320 persson 2246 }
321 persson 2151
322 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
323     int x = key_to_x(r->KeyRange.low, w);
324 persson 2151
325 persson 2246 if (x < clip_low) continue;
326     if (x >= clip_high) break;
327    
328     cr->move_to(x + 0.5, 1);
329     cr->line_to(x + 0.5, h1 - 1);
330     cr->stroke();
331 schoenebeck 1225 }
332     }
333    
334 schoenebeck 1654 bool RegionChooser::is_black_key(int key) {
335     const int note = (key + 3) % 12;
336     return note == 1 || note == 4 || note == 6 || note == 9 || note == 11;
337     }
338 schoenebeck 1225
339 persson 2246 void RegionChooser::draw_digit(const Cairo::RefPtr<Cairo::Context>& cr,
340     int key) {
341 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
342 persson 1658 const int w = get_width() - 1;
343 persson 2246 Glib::RefPtr<Pango::Layout> layout =
344     Pango::Layout::create(get_pango_context());
345 persson 1658 char buf[30];
346     sprintf(buf, "<span size=\"8000\">%d</span>", key / 12 - 1);
347     layout->set_markup(buf);
348     Pango::Rectangle rectangle = layout->get_logical_extents();
349     double text_w = double(rectangle.get_width()) / Pango::SCALE;
350     double text_h = double(rectangle.get_height()) / Pango::SCALE;
351     double x = w * (key + 0.75) / 128.0;
352 persson 2169 Gdk::Cairo::set_source_rgba(cr, black);
353 persson 2151 cr->move_to(int(x - text_w / 2 + 1), int(h1 + h - text_h + 0.5));
354     #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 16) || GTKMM_MAJOR_VERSION < 2
355     pango_cairo_show_layout(cr->cobj(), layout->gobj());
356     #else
357     layout->show_in_cairo_context(cr);
358     #endif
359 persson 1658 }
360    
361 persson 2246 void RegionChooser::draw_key(const Cairo::RefPtr<Cairo::Context>& cr,
362     int key) {
363 schoenebeck 1660 const int h = KEYBOARD_HEIGHT;
364 persson 1658 const int w = get_width() - 1;
365 schoenebeck 1225 const int bh = int(h * 0.55);
366    
367 persson 2246 Gdk::Cairo::set_source_rgba(cr, activeKeyColor);
368 schoenebeck 1225
369 persson 2151 int note = (key + 3) % 12;
370 persson 2246 int x = key_to_x(key, w) + 1;
371     int x2 = key_to_x(key + 1.5, w);
372     int x3 = key_to_x(key + 1, w);
373     int x4 = key_to_x(key - 0.5, w);
374 persson 2151 int w1 = x3 - x;
375     switch (note) {
376     case 0: case 5: case 10:
377     cr->rectangle(x, h1 + 1, w1, bh);
378     cr->fill();
379     cr->rectangle(x4 + 1, h1 + bh + 1, x2 - x4 - 1, h - bh - 2);
380     cr->fill();
381     break;
382     case 2: case 7:
383     cr->rectangle(x, h1 + 1, w1, bh);
384     cr->fill();
385     cr->rectangle(x4 + 1, h1 + bh + 1, x3 - x4 - 1, h - bh - 2);
386     cr->fill();
387     break;
388     case 3: case 8:
389     cr->rectangle(x, h1 + 1, w1, bh);
390     cr->fill();
391     cr->rectangle(x, h1 + bh + 1, x2 - x, h - bh - 2);
392     cr->fill();
393     break;
394     default:
395     cr->rectangle(x, h1 + 1, w1, bh - 1);
396     cr->fill();
397     break;
398 schoenebeck 1225 }
399 persson 2246 Gdk::Cairo::set_source_rgba(cr, black);
400 schoenebeck 1225 }
401    
402     void RegionChooser::set_instrument(gig::Instrument* instrument)
403     {
404     this->instrument = instrument;
405 persson 1623 regions.update(instrument);
406     region = regions.first();
407 schoenebeck 1225 queue_draw();
408 persson 1261 region_selected();
409 persson 1677 dimensionManager.set_region(region);
410 schoenebeck 1225 }
411    
412     bool RegionChooser::on_button_release_event(GdkEventButton* event)
413     {
414 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
415 schoenebeck 1660
416 schoenebeck 1661 // handle-note off on virtual keyboard
417 schoenebeck 1660 if (event->type == GDK_BUTTON_RELEASE) {
418 schoenebeck 1661 int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
419     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
420     if (velocity <= 0) velocity = 1;
421     switch (m_VirtKeybModeChoice.get_value()) {
422     case VIRT_KEYBOARD_MODE_CHORD:
423     if (event->y >= REGION_BLOCK_HEIGHT)
424     keyboard_key_released_signal.emit(k, velocity);
425     break;
426     case VIRT_KEYBOARD_MODE_NORMAL:
427     default:
428     if (currentActiveKey >= 0 && currentActiveKey <= 127) {
429     keyboard_key_released_signal.emit(currentActiveKey, velocity);
430     currentActiveKey = -1;
431     }
432     break;
433 schoenebeck 1660 }
434     }
435    
436 schoenebeck 1225 if (resize.active) {
437 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
438 schoenebeck 1225 get_window()->pointer_ungrab(event->time);
439 persson 2169 #else
440     Glib::wrap(event->device, true)->ungrab(event->time);
441     #endif
442 schoenebeck 1225 resize.active = false;
443    
444     if (!is_in_resize_zone(event->x, event->y) && cursor_is_resize) {
445     get_window()->set_cursor();
446     cursor_is_resize = false;
447     }
448 persson 1262 } else if (move.active) {
449 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
450 persson 1262 get_window()->pointer_ungrab(event->time);
451 persson 2169 #else
452     Glib::wrap(event->device, true)->ungrab(event->time);
453     #endif
454 persson 1262 move.active = false;
455    
456     if (is_in_resize_zone(event->x, event->y)) {
457 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
458 persson 1262 get_window()->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
459 persson 2169 #else
460     get_window()->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
461     #endif
462 persson 1262 cursor_is_resize = true;
463     }
464 schoenebeck 1225 }
465     return true;
466     }
467    
468 persson 2246 void RegionChooser::update_after_resize()
469     {
470     if (resize.mode == resize.moving_high_limit) {
471     if (resize.region->KeyRange.high != resize.pos - 1) {
472     instrument_struct_to_be_changed_signal.emit(instrument);
473     resize.region->SetKeyRange(resize.region->KeyRange.low,
474     resize.pos - 1);
475     regions.update(instrument);
476     instrument_changed.emit();
477     instrument_struct_changed_signal.emit(instrument);
478     }
479     } else if (resize.mode == resize.moving_low_limit) {
480     if (resize.region->KeyRange.low != resize.pos) {
481     instrument_struct_to_be_changed_signal.emit(instrument);
482     resize.region->SetKeyRange(resize.pos,
483     resize.region->KeyRange.high);
484     regions.update(instrument);
485     instrument_changed.emit();
486     instrument_struct_changed_signal.emit(instrument);
487     }
488     }
489     }
490    
491     void RegionChooser::update_after_move(int pos)
492     {
493     instrument_struct_to_be_changed_signal.emit(instrument);
494     region->SetKeyRange(pos, pos + region->KeyRange.high -
495     region->KeyRange.low);
496     regions.update(instrument);
497     instrument_changed.emit();
498     instrument_struct_changed_signal.emit(instrument);
499     }
500    
501 schoenebeck 1225 bool RegionChooser::on_button_press_event(GdkEventButton* event)
502     {
503     if (!instrument) return true;
504    
505 persson 2246 const int w = get_width() - 1;
506     const int k = x_to_key(event->x, w);
507 schoenebeck 1225
508 schoenebeck 1660 if (event->type == GDK_BUTTON_PRESS) {
509     if (event->y >= REGION_BLOCK_HEIGHT) {
510     int velocity = (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
511     int(float(event->y - REGION_BLOCK_HEIGHT) / float(KEYBOARD_HEIGHT) * 128.0f) + 1;
512 schoenebeck 1661 currentActiveKey = k;
513 schoenebeck 1660 keyboard_key_hit_signal.emit(k, velocity);
514     }
515     }
516    
517     if (event->y >= REGION_BLOCK_HEIGHT) return true;
518 schoenebeck 1225 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
519     gig::Region* r = get_region(k);
520     if (r) {
521     region = r;
522     queue_draw();
523 persson 1261 region_selected();
524 persson 1677 dimensionManager.set_region(region);
525 schoenebeck 1225 popup_menu_inside_region->popup(event->button, event->time);
526     } else {
527     new_region_pos = k;
528     popup_menu_outside_region->popup(event->button, event->time);
529     }
530     } else {
531     if (is_in_resize_zone(event->x, event->y)) {
532 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
533 schoenebeck 1225 get_window()->pointer_grab(false,
534     Gdk::BUTTON_RELEASE_MASK |
535     Gdk::POINTER_MOTION_MASK |
536     Gdk::POINTER_MOTION_HINT_MASK,
537 persson 2169 Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW),
538     event->time);
539     #else
540     Glib::wrap(event->device, true)->grab(get_window(),
541     Gdk::OWNERSHIP_NONE,
542     false,
543     Gdk::BUTTON_RELEASE_MASK |
544     Gdk::POINTER_MOTION_MASK |
545     Gdk::POINTER_MOTION_HINT_MASK,
546     Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW),
547     event->time);
548     #endif
549 schoenebeck 1225 resize.active = true;
550     } else {
551     gig::Region* r = get_region(k);
552     if (r) {
553     region = r;
554     queue_draw();
555 persson 1261 region_selected();
556 persson 1677 dimensionManager.set_region(region);
557 persson 1262
558 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
559 persson 1262 get_window()->pointer_grab(false,
560     Gdk::BUTTON_RELEASE_MASK |
561     Gdk::POINTER_MOTION_MASK |
562     Gdk::POINTER_MOTION_HINT_MASK,
563 persson 2169 Gdk::Cursor(Gdk::FLEUR),
564     event->time);
565     #else
566     Glib::wrap(event->device, true)->grab(get_window(),
567     Gdk::OWNERSHIP_NONE,
568     false,
569     Gdk::BUTTON_RELEASE_MASK |
570     Gdk::POINTER_MOTION_MASK |
571     Gdk::POINTER_MOTION_HINT_MASK,
572     Gdk::Cursor::create(Gdk::FLEUR),
573     event->time);
574     #endif
575 persson 1262 move.active = true;
576 persson 2246 move.offset = event->x - key_to_x(region->KeyRange.low, w);
577 schoenebeck 1225 }
578     }
579     }
580     return true;
581     }
582    
583     gig::Region* RegionChooser::get_region(int key)
584     {
585 persson 2246 for (gig::Region* r = regions.first() ; r ; r = regions.next()) {
586 schoenebeck 1225 if (key < r->KeyRange.low) return 0;
587 persson 2246 if (key <= r->KeyRange.high) return r;
588 schoenebeck 1225 }
589     return 0;
590     }
591    
592 persson 1262 void RegionChooser::motion_resize_region(int x, int y)
593 schoenebeck 1225 {
594 persson 1623 const int w = get_width() - 1;
595 schoenebeck 1225
596 persson 1262 int k = int(double(x) / w * 128.0 + 0.5);
597 schoenebeck 1225
598 persson 1262 if (k < resize.min) k = resize.min;
599     else if (k > resize.max) k = resize.max;
600    
601     if (k != resize.pos) {
602     if (resize.mode == resize.undecided) {
603     if (k < resize.pos) {
604     // edit high limit of prev_region
605     resize.max = resize.region->KeyRange.low;
606     resize.region = resize.prev_region;
607     resize.mode = resize.moving_high_limit;
608     } else {
609     // edit low limit of region
610     resize.min = resize.prev_region->KeyRange.high + 1;
611     resize.mode = resize.moving_low_limit;
612 schoenebeck 1225 }
613 persson 1262 }
614 persson 2246 resize.pos = k;
615 persson 2151
616 persson 2246 int x1, x2;
617 persson 1262 if (resize.mode == resize.moving_high_limit) {
618 persson 2246 if (resize.region->KeyRange.high < resize.pos - 1) {
619     x1 = resize.region->KeyRange.high;
620     x2 = resize.pos - 1;
621 persson 1262 } else {
622 persson 2246 x1 = resize.pos - 1;
623     x2 = resize.region->KeyRange.high;
624 schoenebeck 1225 }
625 persson 1262 } else {
626 persson 2246 if (resize.region->KeyRange.low < resize.pos) {
627     x1 = resize.region->KeyRange.low;
628     x2 = resize.pos;
629 persson 1262 } else {
630 persson 2246 x1 = resize.pos;
631     x2 = resize.region->KeyRange.low;
632 persson 1262 }
633     }
634 persson 2246 x1 = key_to_x(x1, w);
635     x2 = key_to_x(x2 + 1, w) + 1;
636     Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
637    
638     update_after_resize();
639    
640     get_window()->invalidate_rect(rect, false);
641 persson 1262 }
642     }
643 schoenebeck 1225
644 persson 1262 void RegionChooser::motion_move_region(int x, int y)
645     {
646 persson 1623 const int w = get_width() - 1;
647 persson 1262
648 persson 2246 int l = int(double(x - move.offset) / w * 128.0 + 0.5);
649    
650     if (l == region->KeyRange.low) return;
651     int new_l;
652     int regionsize = region->KeyRange.high - region->KeyRange.low;
653 persson 1262 int a = 0;
654 persson 2246 if (l > region->KeyRange.low) {
655 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
656 persson 1262 if (r != region) {
657     int b = r ? r->KeyRange.low : 128;
658    
659     // gap: from a to b (not inclusive b)
660    
661 persson 2246 if (region->KeyRange.high >= b) {
662 persson 1262 // not found the current gap yet, just continue
663 schoenebeck 1225 } else {
664 persson 1262
665 persson 2246 if (a > l) {
666 persson 1262 // this gap is too far to the right, break
667     break;
668     }
669    
670 persson 2246 int newhigh = std::min(l + regionsize, b - 1);
671     int newlo = newhigh - regionsize;
672 persson 1262
673     if (newlo >= a) {
674     // yes it fits - it's a candidate
675 persson 2246 new_l = newlo;
676 persson 1262 }
677 schoenebeck 1225 }
678 persson 1262 if (!r) break;
679     a = r->KeyRange.high + 1;
680     }
681     }
682     } else {
683 persson 1623 for (gig::Region* r = regions.first() ; ; r = regions.next()) {
684 persson 1262 if (r != region) {
685     int b = r ? r->KeyRange.low : 128;
686    
687     // gap from a to b (not inclusive b)
688    
689 persson 2246 if (l + regionsize >= b) {
690 persson 1262 // not found the current gap yet, just continue
691 schoenebeck 1225 } else {
692 persson 1262
693 persson 2246 if (a > region->KeyRange.low) {
694 persson 1262 // this gap is too far to the right, break
695     break;
696     }
697    
698 persson 2246 int newlo = std::max(l, a);
699     int newhigh = newlo + regionsize;
700 persson 1262
701     if (newhigh < b) {
702     // yes it fits - break as the first one is the best
703 persson 2246 new_l = newlo;
704 persson 1262 break;
705     }
706 schoenebeck 1225 }
707 persson 1262 if (!r) break;
708     a = r->KeyRange.high + 1;
709 schoenebeck 1225 }
710     }
711 persson 1262 }
712 persson 2246 if (new_l == region->KeyRange.low) return;
713 persson 1262
714 persson 2246 int x1 = key_to_x(std::min(int(region->KeyRange.low), new_l), w);
715     int x2 = key_to_x(std::max(int(region->KeyRange.high),
716     new_l + regionsize) + 1, w) + 1;
717 persson 2169
718 persson 2246 Gdk::Rectangle rect(x1, 0, x2 - x1, h1);
719     update_after_move(new_l);
720 persson 1262
721 persson 2246 get_window()->invalidate_rect(rect, false);
722 persson 1262 }
723    
724    
725     bool RegionChooser::on_motion_notify_event(GdkEventMotion* event)
726     {
727     Glib::RefPtr<Gdk::Window> window = get_window();
728     int x, y;
729     Gdk::ModifierType state = Gdk::ModifierType(0);
730     window->get_pointer(x, y, state);
731    
732 schoenebeck 1661 // handle virtual MIDI keyboard
733     if (m_VirtKeybModeChoice.get_value() != VIRT_KEYBOARD_MODE_CHORD &&
734     currentActiveKey > 0 &&
735     event->y >= REGION_BLOCK_HEIGHT &&
736     event->y < REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT)
737     {
738 persson 2246 const int k = x_to_key(event->x, get_width() - 1);
739 persson 1898 if (k != currentActiveKey) {
740     int velocity =
741     (event->y >= REGION_BLOCK_HEIGHT + KEYBOARD_HEIGHT - 1) ? 127 :
742     int(float(event->y - REGION_BLOCK_HEIGHT) /
743     float(KEYBOARD_HEIGHT) * 128.0f) + 1;
744     if (velocity <= 0) velocity = 1;
745     keyboard_key_released_signal.emit(currentActiveKey, velocity);
746     currentActiveKey = k;
747     keyboard_key_hit_signal.emit(k, velocity);
748     }
749 schoenebeck 1661 }
750    
751 persson 1262 if (resize.active) {
752     motion_resize_region(x, y);
753     } else if (move.active) {
754     motion_move_region(x, y);
755     } else {
756 schoenebeck 1225 if (is_in_resize_zone(x, y)) {
757     if (!cursor_is_resize) {
758 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
759 persson 1262 window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
760 persson 2169 #else
761     window->set_cursor(Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW));
762     #endif
763 schoenebeck 1225 cursor_is_resize = true;
764     }
765     } else if (cursor_is_resize) {
766     window->set_cursor();
767     cursor_is_resize = false;
768     }
769     }
770    
771     return true;
772     }
773    
774     bool RegionChooser::is_in_resize_zone(double x, double y) {
775 persson 1623 const int w = get_width() - 1;
776 schoenebeck 1225
777     if (instrument && y >= 0 && y <= h1) {
778     gig::Region* prev_region = 0;
779     gig::Region* next_region;
780 persson 1623 for (gig::Region* r = regions.first(); r ; r = next_region) {
781     next_region = regions.next();
782 schoenebeck 1225
783 persson 2246 int lo = key_to_x(r->KeyRange.low, w);
784 schoenebeck 1225 if (x <= lo - 2) break;
785     if (x < lo + 2) {
786     resize.region = r;
787     resize.pos = r->KeyRange.low;
788     resize.max = r->KeyRange.high;
789    
790     if (prev_region && prev_region->KeyRange.high + 1 == r->KeyRange.low) {
791     // we don't know yet if it's the high limit of
792     // prev_region or the low limit of r that's going
793     // to be edited
794     resize.mode = resize.undecided;
795     resize.min = prev_region->KeyRange.low + 1;
796     resize.prev_region = prev_region;
797 persson 1623 return resize.min != resize.max;
798 schoenebeck 1225 }
799    
800     // edit low limit
801     resize.mode = resize.moving_low_limit;
802     resize.min = prev_region ? prev_region->KeyRange.high + 1 : 0;
803 persson 1623 return resize.min != resize.max;
804 schoenebeck 1225 }
805     if (!next_region || r->KeyRange.high + 1 != next_region->KeyRange.low) {
806 persson 2246 int hi = key_to_x(r->KeyRange.high + 1, w);
807 schoenebeck 1225 if (x <= hi - 2) break;
808     if (x < hi + 2) {
809     // edit high limit
810     resize.region = r;
811     resize.pos = r->KeyRange.high + 1;
812     resize.mode = resize.moving_high_limit;
813     resize.min = r->KeyRange.low + 1;
814     resize.max = next_region ? next_region->KeyRange.low : 128;
815 persson 1623 return resize.min != resize.max;
816 schoenebeck 1225 }
817     }
818     prev_region = r;
819     }
820     }
821     return false;
822     }
823    
824 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_region_selected()
825 schoenebeck 1225 {
826 persson 1261 return region_selected;
827 schoenebeck 1225 }
828    
829 schoenebeck 1339 sigc::signal<void>& RegionChooser::signal_instrument_changed()
830 persson 1261 {
831     return instrument_changed;
832     }
833    
834 schoenebeck 1225 void RegionChooser::show_region_properties()
835     {
836     if (!region) return;
837 persson 1831 Gtk::Dialog dialog(_("Region Properties"), true /*modal*/);
838 schoenebeck 1225 // add "Keygroup" checkbox
839 persson 1831 Gtk::CheckButton checkBoxKeygroup(_("Member of a Keygroup (Exclusive Group)"));
840 schoenebeck 1225 checkBoxKeygroup.set_active(region->KeyGroup);
841     dialog.get_vbox()->pack_start(checkBoxKeygroup);
842     checkBoxKeygroup.show();
843     // add "Keygroup" spinbox
844 persson 2169 #if (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION < 90) || GTKMM_MAJOR_VERSION < 2
845     Gtk::Adjustment adjustment(1, 1, 999);
846 schoenebeck 1225 Gtk::SpinButton spinBox(adjustment);
847 persson 2169 #else
848     Gtk::SpinButton spinBox(Gtk::Adjustment::create(1, 1, 999));
849     #endif
850 schoenebeck 1225 if (region->KeyGroup) spinBox.set_value(region->KeyGroup);
851     dialog.get_vbox()->pack_start(spinBox);
852     spinBox.show();
853     // add OK and CANCEL buttons to the dialog
854     dialog.add_button(Gtk::Stock::OK, 0);
855     dialog.add_button(Gtk::Stock::CANCEL, 1);
856     dialog.show_all_children();
857     if (!dialog.run()) { // OK selected ...
858     region->KeyGroup =
859     (checkBoxKeygroup.get_active()) ? spinBox.get_value_as_int() : 0;
860     }
861     }
862    
863     void RegionChooser::add_region()
864     {
865 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
866    
867 schoenebeck 1225 region = instrument->AddRegion();
868 schoenebeck 1336 region->SetKeyRange(new_region_pos, new_region_pos);
869 schoenebeck 1225
870 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
871 persson 1623 regions.update(instrument);
872 schoenebeck 1322
873 schoenebeck 1225 queue_draw();
874 persson 1261 region_selected();
875 persson 1677 dimensionManager.set_region(region);
876 persson 1261 instrument_changed();
877 schoenebeck 1225 }
878    
879     void RegionChooser::delete_region()
880     {
881 schoenebeck 1322 instrument_struct_to_be_changed_signal.emit(instrument);
882 schoenebeck 1225 instrument->DeleteRegion(region);
883 schoenebeck 1322 instrument_struct_changed_signal.emit(instrument);
884 persson 1623 regions.update(instrument);
885 schoenebeck 1322
886 schoenebeck 1225 region = 0;
887     queue_draw();
888 persson 1261 region_selected();
889 persson 1677 dimensionManager.set_region(region);
890 persson 1261 instrument_changed();
891 schoenebeck 1225 }
892    
893     void RegionChooser::manage_dimensions()
894     {
895     gig::Region* region = get_region();
896     if (!region) return;
897     dimensionManager.show(region);
898     }
899    
900     void RegionChooser::on_dimension_manager_changed() {
901 persson 1261 region_selected();
902     instrument_changed();
903 schoenebeck 1225 }
904 schoenebeck 1322
905 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_to_be_changed() {
906 schoenebeck 1322 return instrument_struct_to_be_changed_signal;
907     }
908    
909 schoenebeck 1339 sigc::signal<void, gig::Instrument*>& RegionChooser::signal_instrument_struct_changed() {
910 schoenebeck 1322 return instrument_struct_changed_signal;
911     }
912    
913 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_to_be_changed() {
914 schoenebeck 1322 return region_to_be_changed_signal;
915     }
916    
917 schoenebeck 1339 sigc::signal<void, gig::Region*>& RegionChooser::signal_region_changed_signal() {
918 schoenebeck 1322 return region_changed_signal;
919     }
920 schoenebeck 1660
921     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_hit() {
922     return keyboard_key_hit_signal;
923     }
924    
925     sigc::signal<void, int/*key*/, int/*velocity*/>& RegionChooser::signal_keyboard_key_released() {
926     return keyboard_key_released_signal;
927     }

  ViewVC Help
Powered by ViewVC